diff options
Diffstat (limited to 'arch/arm/cpu/armv8')
-rw-r--r-- | arch/arm/cpu/armv8/cache_v8.c | 57 | ||||
-rw-r--r-- | arch/arm/cpu/armv8/fsl-lsch3/Makefile | 9 | ||||
-rw-r--r-- | arch/arm/cpu/armv8/fsl-lsch3/README | 10 | ||||
-rw-r--r-- | arch/arm/cpu/armv8/fsl-lsch3/cpu.c | 436 | ||||
-rw-r--r-- | arch/arm/cpu/armv8/fsl-lsch3/cpu.h | 7 | ||||
-rw-r--r-- | arch/arm/cpu/armv8/fsl-lsch3/lowlevel.S | 65 | ||||
-rw-r--r-- | arch/arm/cpu/armv8/fsl-lsch3/speed.c | 176 | ||||
-rw-r--r-- | arch/arm/cpu/armv8/fsl-lsch3/speed.h | 7 | ||||
-rw-r--r-- | arch/arm/cpu/armv8/transition.S | 2 |
9 files changed, 737 insertions, 32 deletions
diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index a96ecda7e3..9dbcdf22af 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -12,15 +12,14 @@ DECLARE_GLOBAL_DATA_PTR; #ifndef CONFIG_SYS_DCACHE_OFF - -static void set_pgtable_section(u64 section, u64 memory_type) +void set_pgtable_section(u64 *page_table, u64 index, u64 section, + u64 memory_type) { - u64 *page_table = (u64 *)gd->arch.tlb_addr; u64 value; - value = (section << SECTION_SHIFT) | PMD_TYPE_SECT | PMD_SECT_AF; + value = section | PMD_TYPE_SECT | PMD_SECT_AF; value |= PMD_ATTRINDX(memory_type); - page_table[section] = value; + page_table[index] = value; } /* to activate the MMU we need to set up virtual memory */ @@ -28,10 +27,13 @@ static void mmu_setup(void) { int i, j, el; bd_t *bd = gd->bd; + u64 *page_table = (u64 *)gd->arch.tlb_addr; /* Setup an identity-mapping for all spaces */ - for (i = 0; i < (PGTABLE_SIZE >> 3); i++) - set_pgtable_section(i, MT_DEVICE_NGNRNE); + for (i = 0; i < (PGTABLE_SIZE >> 3); i++) { + set_pgtable_section(page_table, i, i << SECTION_SHIFT, + MT_DEVICE_NGNRNE); + } /* Setup an identity-mapping for all RAM space */ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { @@ -39,38 +41,26 @@ static void mmu_setup(void) ulong end = bd->bi_dram[i].start + bd->bi_dram[i].size; for (j = start >> SECTION_SHIFT; j < end >> SECTION_SHIFT; j++) { - set_pgtable_section(j, MT_NORMAL); + set_pgtable_section(page_table, j, j << SECTION_SHIFT, + MT_NORMAL); } } /* load TTBR0 */ el = current_el(); if (el == 1) { - asm volatile("msr ttbr0_el1, %0" - : : "r" (gd->arch.tlb_addr) : "memory"); - asm volatile("msr tcr_el1, %0" - : : "r" (TCR_FLAGS | TCR_EL1_IPS_BITS) - : "memory"); - asm volatile("msr mair_el1, %0" - : : "r" (MEMORY_ATTRIBUTES) : "memory"); + set_ttbr_tcr_mair(el, gd->arch.tlb_addr, + TCR_FLAGS | TCR_EL1_IPS_BITS, + MEMORY_ATTRIBUTES); } else if (el == 2) { - asm volatile("msr ttbr0_el2, %0" - : : "r" (gd->arch.tlb_addr) : "memory"); - asm volatile("msr tcr_el2, %0" - : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS) - : "memory"); - asm volatile("msr mair_el2, %0" - : : "r" (MEMORY_ATTRIBUTES) : "memory"); + set_ttbr_tcr_mair(el, gd->arch.tlb_addr, + TCR_FLAGS | TCR_EL2_IPS_BITS, + MEMORY_ATTRIBUTES); } else { - asm volatile("msr ttbr0_el3, %0" - : : "r" (gd->arch.tlb_addr) : "memory"); - asm volatile("msr tcr_el3, %0" - : : "r" (TCR_FLAGS | TCR_EL2_IPS_BITS) - : "memory"); - asm volatile("msr mair_el3, %0" - : : "r" (MEMORY_ATTRIBUTES) : "memory"); + set_ttbr_tcr_mair(el, gd->arch.tlb_addr, + TCR_FLAGS | TCR_EL3_IPS_BITS, + MEMORY_ATTRIBUTES); } - /* enable the mmu */ set_sctlr(get_sctlr() | CR_M); } @@ -83,12 +73,17 @@ void invalidate_dcache_all(void) __asm_invalidate_dcache_all(); } +void __weak flush_l3_cache(void) +{ +} + /* * Performs a clean & invalidation of the entire data cache at all levels */ void flush_dcache_all(void) { __asm_flush_dcache_all(); + flush_l3_cache(); } /* @@ -221,7 +216,7 @@ void invalidate_icache_all(void) * Enable dCache & iCache, whether cache is actually enabled * depend on CONFIG_SYS_DCACHE_OFF and CONFIG_SYS_ICACHE_OFF */ -void enable_caches(void) +void __weak enable_caches(void) { icache_enable(); dcache_enable(); diff --git a/arch/arm/cpu/armv8/fsl-lsch3/Makefile b/arch/arm/cpu/armv8/fsl-lsch3/Makefile new file mode 100644 index 0000000000..9249537c65 --- /dev/null +++ b/arch/arm/cpu/armv8/fsl-lsch3/Makefile @@ -0,0 +1,9 @@ +# +# Copyright 2014, Freescale Semiconductor +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-y += cpu.o +obj-y += lowlevel.o +obj-y += speed.o diff --git a/arch/arm/cpu/armv8/fsl-lsch3/README b/arch/arm/cpu/armv8/fsl-lsch3/README new file mode 100644 index 0000000000..cc47466112 --- /dev/null +++ b/arch/arm/cpu/armv8/fsl-lsch3/README @@ -0,0 +1,10 @@ +# +# Copyright 2014 Freescale Semiconductor +# +# SPDX-License-Identifier: GPL-2.0+ +# + +Freescale LayerScape with Chassis Generation 3 + +This architecture supports Freescale ARMv8 SoCs with Chassis generation 3, +for example LS2085A. diff --git a/arch/arm/cpu/armv8/fsl-lsch3/cpu.c b/arch/arm/cpu/armv8/fsl-lsch3/cpu.c new file mode 100644 index 0000000000..c129d032f4 --- /dev/null +++ b/arch/arm/cpu/armv8/fsl-lsch3/cpu.c @@ -0,0 +1,436 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/armv8/mmu.h> +#include <asm/io.h> +#include <asm/arch-fsl-lsch3/immap_lsch3.h> +#include "cpu.h" +#include "speed.h" +#include <fsl_mc.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_SYS_DCACHE_OFF +/* + * To start MMU before DDR is available, we create MMU table in SRAM. + * The base address of SRAM is CONFIG_SYS_FSL_OCRAM_BASE. We use three + * levels of translation tables here to cover 40-bit address space. + * We use 4KB granule size, with 40 bits physical address, T0SZ=24 + * Level 0 IA[39], table address @0 + * Level 1 IA[31:30], table address @01000, 0x2000 + * Level 2 IA[29:21], table address @0x3000 + */ + +#define SECTION_SHIFT_L0 39UL +#define SECTION_SHIFT_L1 30UL +#define SECTION_SHIFT_L2 21UL +#define BLOCK_SIZE_L0 0x8000000000UL +#define BLOCK_SIZE_L1 (1 << SECTION_SHIFT_L1) +#define BLOCK_SIZE_L2 (1 << SECTION_SHIFT_L2) +#define CONFIG_SYS_IFC_BASE 0x30000000 +#define CONFIG_SYS_IFC_SIZE 0x10000000 +#define CONFIG_SYS_IFC_BASE2 0x500000000 +#define CONFIG_SYS_IFC_SIZE2 0x100000000 +#define TCR_EL2_PS_40BIT (2 << 16) +#define LSCH3_VA_BITS (40) +#define LSCH3_TCR (TCR_TG0_4K | \ + TCR_EL2_PS_40BIT | \ + TCR_SHARED_NON | \ + TCR_ORGN_NC | \ + TCR_IRGN_NC | \ + TCR_T0SZ(LSCH3_VA_BITS)) + +/* + * Final MMU + * Let's start from the same layout as early MMU and modify as needed. + * IFC regions will be cache-inhibit. + */ +#define FINAL_QBMAN_CACHED_MEM 0x818000000UL +#define FINAL_QBMAN_CACHED_SIZE 0x4000000 + + +static inline void early_mmu_setup(void) +{ + int el; + u64 i; + u64 section_l1t0, section_l1t1, section_l2; + u64 *level0_table = (u64 *)CONFIG_SYS_FSL_OCRAM_BASE; + u64 *level1_table_0 = (u64 *)(CONFIG_SYS_FSL_OCRAM_BASE + 0x1000); + u64 *level1_table_1 = (u64 *)(CONFIG_SYS_FSL_OCRAM_BASE + 0x2000); + u64 *level2_table = (u64 *)(CONFIG_SYS_FSL_OCRAM_BASE + 0x3000); + + + level0_table[0] = + (u64)level1_table_0 | PMD_TYPE_TABLE; + level0_table[1] = + (u64)level1_table_1 | PMD_TYPE_TABLE; + + /* + * set level 1 table 0 to cache_inhibit, covering 0 to 512GB + * set level 1 table 1 to cache enabled, covering 512GB to 1TB + * set level 2 table to cache-inhibit, covering 0 to 1GB + */ + section_l1t0 = 0; + section_l1t1 = BLOCK_SIZE_L0; + section_l2 = 0; + for (i = 0; i < 512; i++) { + set_pgtable_section(level1_table_0, i, section_l1t0, + MT_DEVICE_NGNRNE); + set_pgtable_section(level1_table_1, i, section_l1t1, + MT_NORMAL); + set_pgtable_section(level2_table, i, section_l2, + MT_DEVICE_NGNRNE); + section_l1t0 += BLOCK_SIZE_L1; + section_l1t1 += BLOCK_SIZE_L1; + section_l2 += BLOCK_SIZE_L2; + } + + level1_table_0[0] = + (u64)level2_table | PMD_TYPE_TABLE; + level1_table_0[1] = + 0x40000000 | PMD_SECT_AF | PMD_TYPE_SECT | + PMD_ATTRINDX(MT_DEVICE_NGNRNE); + level1_table_0[2] = + 0x80000000 | PMD_SECT_AF | PMD_TYPE_SECT | + PMD_ATTRINDX(MT_NORMAL); + level1_table_0[3] = + 0xc0000000 | PMD_SECT_AF | PMD_TYPE_SECT | + PMD_ATTRINDX(MT_NORMAL); + + /* Rewrite table to enable cache */ + set_pgtable_section(level2_table, + CONFIG_SYS_FSL_OCRAM_BASE >> SECTION_SHIFT_L2, + CONFIG_SYS_FSL_OCRAM_BASE, + MT_NORMAL); + for (i = CONFIG_SYS_IFC_BASE >> SECTION_SHIFT_L2; + i < (CONFIG_SYS_IFC_BASE + CONFIG_SYS_IFC_SIZE) + >> SECTION_SHIFT_L2; i++) { + section_l2 = i << SECTION_SHIFT_L2; + set_pgtable_section(level2_table, i, + section_l2, MT_NORMAL); + } + + el = current_el(); + set_ttbr_tcr_mair(el, (u64)level0_table, LSCH3_TCR, MEMORY_ATTRIBUTES); + set_sctlr(get_sctlr() | CR_M); +} + +/* + * This final tale looks similar to early table, but different in detail. + * These tables are in regular memory. Cache on IFC is disabled. One sub table + * is added to enable cache for QBMan. + */ +static inline void final_mmu_setup(void) +{ + int el; + u64 i, tbl_base, tbl_limit, section_base; + u64 section_l1t0, section_l1t1, section_l2; + u64 *level0_table = (u64 *)gd->arch.tlb_addr; + u64 *level1_table_0 = (u64 *)(gd->arch.tlb_addr + 0x1000); + u64 *level1_table_1 = (u64 *)(gd->arch.tlb_addr + 0x2000); + u64 *level2_table_0 = (u64 *)(gd->arch.tlb_addr + 0x3000); + u64 *level2_table_1 = (u64 *)(gd->arch.tlb_addr + 0x4000); + + + level0_table[0] = + (u64)level1_table_0 | PMD_TYPE_TABLE; + level0_table[1] = + (u64)level1_table_1 | PMD_TYPE_TABLE; + + /* + * set level 1 table 0 to cache_inhibit, covering 0 to 512GB + * set level 1 table 1 to cache enabled, covering 512GB to 1TB + * set level 2 table 0 to cache-inhibit, covering 0 to 1GB + */ + section_l1t0 = 0; + section_l1t1 = BLOCK_SIZE_L0; + section_l2 = 0; + for (i = 0; i < 512; i++) { + set_pgtable_section(level1_table_0, i, section_l1t0, + MT_DEVICE_NGNRNE); + set_pgtable_section(level1_table_1, i, section_l1t1, + MT_NORMAL); + set_pgtable_section(level2_table_0, i, section_l2, + MT_DEVICE_NGNRNE); + section_l1t0 += BLOCK_SIZE_L1; + section_l1t1 += BLOCK_SIZE_L1; + section_l2 += BLOCK_SIZE_L2; + } + + level1_table_0[0] = + (u64)level2_table_0 | PMD_TYPE_TABLE; + level1_table_0[2] = + 0x80000000 | PMD_SECT_AF | PMD_TYPE_SECT | + PMD_ATTRINDX(MT_NORMAL); + level1_table_0[3] = + 0xc0000000 | PMD_SECT_AF | PMD_TYPE_SECT | + PMD_ATTRINDX(MT_NORMAL); + + /* Rewrite table to enable cache */ + set_pgtable_section(level2_table_0, + CONFIG_SYS_FSL_OCRAM_BASE >> SECTION_SHIFT_L2, + CONFIG_SYS_FSL_OCRAM_BASE, + MT_NORMAL); + + /* + * Fill in other part of tables if cache is needed + * If finer granularity than 1GB is needed, sub table + * should be created. + */ + section_base = FINAL_QBMAN_CACHED_MEM & ~(BLOCK_SIZE_L1 - 1); + i = section_base >> SECTION_SHIFT_L1; + level1_table_0[i] = (u64)level2_table_1 | PMD_TYPE_TABLE; + section_l2 = section_base; + for (i = 0; i < 512; i++) { + set_pgtable_section(level2_table_1, i, section_l2, + MT_DEVICE_NGNRNE); + section_l2 += BLOCK_SIZE_L2; + } + tbl_base = FINAL_QBMAN_CACHED_MEM & (BLOCK_SIZE_L1 - 1); + tbl_limit = (FINAL_QBMAN_CACHED_MEM + FINAL_QBMAN_CACHED_SIZE) & + (BLOCK_SIZE_L1 - 1); + for (i = tbl_base >> SECTION_SHIFT_L2; + i < tbl_limit >> SECTION_SHIFT_L2; i++) { + section_l2 = section_base + (i << SECTION_SHIFT_L2); + set_pgtable_section(level2_table_1, i, + section_l2, MT_NORMAL); + } + + /* flush new MMU table */ + flush_dcache_range(gd->arch.tlb_addr, + gd->arch.tlb_addr + gd->arch.tlb_size); + + /* point TTBR to the new table */ + el = current_el(); + asm volatile("dsb sy"); + if (el == 1) { + asm volatile("msr ttbr0_el1, %0" + : : "r" ((u64)level0_table) : "memory"); + } else if (el == 2) { + asm volatile("msr ttbr0_el2, %0" + : : "r" ((u64)level0_table) : "memory"); + } else if (el == 3) { + asm volatile("msr ttbr0_el3, %0" + : : "r" ((u64)level0_table) : "memory"); + } else { + hang(); + } + asm volatile("isb"); + + /* + * MMU is already enabled, just need to invalidate TLB to load the + * new table. The new table is compatible with the current table, if + * MMU somehow walks through the new table before invalidation TLB, + * it still works. So we don't need to turn off MMU here. + */ +} + +int arch_cpu_init(void) +{ + icache_enable(); + __asm_invalidate_dcache_all(); + __asm_invalidate_tlb_all(); + early_mmu_setup(); + set_sctlr(get_sctlr() | CR_C); + return 0; +} + +/* + * flush_l3_cache + * Dickens L3 cache can be flushed by transitioning from FAM to SFONLY power + * state, by writing to HP-F P-state request register. + * Fixme: This function should moved to a common file if other SoCs also use + * the same Dickens. + */ +#define HNF0_PSTATE_REQ 0x04200010 +#define HNF1_PSTATE_REQ 0x04210010 +#define HNF2_PSTATE_REQ 0x04220010 +#define HNF3_PSTATE_REQ 0x04230010 +#define HNF4_PSTATE_REQ 0x04240010 +#define HNF5_PSTATE_REQ 0x04250010 +#define HNF6_PSTATE_REQ 0x04260010 +#define HNF7_PSTATE_REQ 0x04270010 +#define HNFPSTAT_MASK (0xFFFFFFFFFFFFFFFC) +#define HNFPSTAT_FAM 0x3 +#define HNFPSTAT_SFONLY 0x01 + +static void hnf_pstate_req(u64 *ptr, u64 state) +{ + int timeout = 1000; + out_le64(ptr, (in_le64(ptr) & HNFPSTAT_MASK) | (state & 0x3)); + ptr++; + /* checking if the transition is completed */ + while (timeout > 0) { + if (((in_le64(ptr) & 0x0c) >> 2) == (state & 0x3)) + break; + udelay(100); + timeout--; + } +} + +void flush_l3_cache(void) +{ + hnf_pstate_req((u64 *)HNF0_PSTATE_REQ, HNFPSTAT_SFONLY); + hnf_pstate_req((u64 *)HNF1_PSTATE_REQ, HNFPSTAT_SFONLY); + hnf_pstate_req((u64 *)HNF2_PSTATE_REQ, HNFPSTAT_SFONLY); + hnf_pstate_req((u64 *)HNF3_PSTATE_REQ, HNFPSTAT_SFONLY); + hnf_pstate_req((u64 *)HNF4_PSTATE_REQ, HNFPSTAT_SFONLY); + hnf_pstate_req((u64 *)HNF5_PSTATE_REQ, HNFPSTAT_SFONLY); + hnf_pstate_req((u64 *)HNF6_PSTATE_REQ, HNFPSTAT_SFONLY); + hnf_pstate_req((u64 *)HNF7_PSTATE_REQ, HNFPSTAT_SFONLY); + hnf_pstate_req((u64 *)HNF0_PSTATE_REQ, HNFPSTAT_FAM); + hnf_pstate_req((u64 *)HNF1_PSTATE_REQ, HNFPSTAT_FAM); + hnf_pstate_req((u64 *)HNF2_PSTATE_REQ, HNFPSTAT_FAM); + hnf_pstate_req((u64 *)HNF3_PSTATE_REQ, HNFPSTAT_FAM); + hnf_pstate_req((u64 *)HNF4_PSTATE_REQ, HNFPSTAT_FAM); + hnf_pstate_req((u64 *)HNF5_PSTATE_REQ, HNFPSTAT_FAM); + hnf_pstate_req((u64 *)HNF6_PSTATE_REQ, HNFPSTAT_FAM); + hnf_pstate_req((u64 *)HNF7_PSTATE_REQ, HNFPSTAT_FAM); +} + +/* + * This function is called from lib/board.c. + * It recreates MMU table in main memory. MMU and d-cache are enabled earlier. + * There is no need to disable d-cache for this operation. + */ +void enable_caches(void) +{ + final_mmu_setup(); + __asm_invalidate_tlb_all(); +} +#endif + +static inline u32 initiator_type(u32 cluster, int init_id) +{ + struct ccsr_gur *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR); + u32 idx = (cluster >> (init_id * 8)) & TP_CLUSTER_INIT_MASK; + u32 type = in_le32(&gur->tp_ityp[idx]); + + if (type & TP_ITYP_AV) + return type; + + return 0; +} + +u32 cpu_mask(void) +{ + struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR); + int i = 0, count = 0; + u32 cluster, type, mask = 0; + + do { + int j; + cluster = in_le32(&gur->tp_cluster[i].lower); + for (j = 0; j < TP_INIT_PER_CLUSTER; j++) { + type = initiator_type(cluster, j); + if (type) { + if (TP_ITYP_TYPE(type) == TP_ITYP_TYPE_ARM) + mask |= 1 << count; + count++; + } + } + i++; + } while ((cluster & TP_CLUSTER_EOC) != TP_CLUSTER_EOC); + + return mask; +} + +/* + * Return the number of cores on this SOC. + */ +int cpu_numcores(void) +{ + return hweight32(cpu_mask()); +} + +int fsl_qoriq_core_to_cluster(unsigned int core) +{ + struct ccsr_gur __iomem *gur = + (void __iomem *)(CONFIG_SYS_FSL_GUTS_ADDR); + int i = 0, count = 0; + u32 cluster; + + do { + int j; + cluster = in_le32(&gur->tp_cluster[i].lower); + for (j = 0; j < TP_INIT_PER_CLUSTER; j++) { + if (initiator_type(cluster, j)) { + if (count == core) + return i; + count++; + } + } + i++; + } while ((cluster & TP_CLUSTER_EOC) != TP_CLUSTER_EOC); + + return -1; /* cannot identify the cluster */ +} + +u32 fsl_qoriq_core_to_type(unsigned int core) +{ + struct ccsr_gur __iomem *gur = + (void __iomem *)(CONFIG_SYS_FSL_GUTS_ADDR); + int i = 0, count = 0; + u32 cluster, type; + + do { + int j; + cluster = in_le32(&gur->tp_cluster[i].lower); + for (j = 0; j < TP_INIT_PER_CLUSTER; j++) { + type = initiator_type(cluster, j); + if (type) { + if (count == core) + return type; + count++; + } + } + i++; + } while ((cluster & TP_CLUSTER_EOC) != TP_CLUSTER_EOC); + + return -1; /* cannot identify the cluster */ +} + +#ifdef CONFIG_DISPLAY_CPUINFO +int print_cpuinfo(void) +{ + struct sys_info sysinfo; + char buf[32]; + unsigned int i, core; + u32 type; + + get_sys_info(&sysinfo); + puts("Clock Configuration:"); + for_each_cpu(i, core, cpu_numcores(), cpu_mask()) { + if (!(i % 3)) + puts("\n "); + type = TP_ITYP_VER(fsl_qoriq_core_to_type(core)); + printf("CPU%d(%s):%-4s MHz ", core, + type == TY_ITYP_VER_A7 ? "A7 " : + (type == TY_ITYP_VER_A53 ? "A53" : + (type == TY_ITYP_VER_A57 ? "A57" : " ")), + strmhz(buf, sysinfo.freq_processor[core])); + } + printf("\n Bus: %-4s MHz ", + strmhz(buf, sysinfo.freq_systembus)); + printf("DDR: %-4s MHz", strmhz(buf, sysinfo.freq_ddrbus)); + puts("\n"); + + return 0; +} +#endif + +int cpu_eth_init(bd_t *bis) +{ + int error = 0; + +#ifdef CONFIG_FSL_MC_ENET + error = mc_init(bis); +#endif + return error; +} diff --git a/arch/arm/cpu/armv8/fsl-lsch3/cpu.h b/arch/arm/cpu/armv8/fsl-lsch3/cpu.h new file mode 100644 index 0000000000..28544d7c14 --- /dev/null +++ b/arch/arm/cpu/armv8/fsl-lsch3/cpu.h @@ -0,0 +1,7 @@ +/* + * Copyright 2014, Freescale Semiconductor + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +int fsl_qoriq_core_to_cluster(unsigned int core); diff --git a/arch/arm/cpu/armv8/fsl-lsch3/lowlevel.S b/arch/arm/cpu/armv8/fsl-lsch3/lowlevel.S new file mode 100644 index 0000000000..ad32b6cd52 --- /dev/null +++ b/arch/arm/cpu/armv8/fsl-lsch3/lowlevel.S @@ -0,0 +1,65 @@ +/* + * (C) Copyright 2014 Freescale Semiconductor + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Extracted from armv8/start.S + */ + +#include <config.h> +#include <linux/linkage.h> +#include <asm/macro.h> + +ENTRY(lowlevel_init) + mov x29, lr /* Save LR */ + + /* Set the SMMU page size in the sACR register */ + ldr x1, =SMMU_BASE + ldr w0, [x1, #0x10] + orr w0, w0, #1 << 16 /* set sACR.pagesize to indicate 64K page */ + str w0, [x1, #0x10] + + /* Initialize GIC Secure Bank Status */ +#if defined(CONFIG_GICV2) || defined(CONFIG_GICV3) + branch_if_slave x0, 1f + ldr x0, =GICD_BASE + bl gic_init_secure +1: +#ifdef CONFIG_GICV3 + ldr x0, =GICR_BASE + bl gic_init_secure_percpu +#elif defined(CONFIG_GICV2) + ldr x0, =GICD_BASE + ldr x1, =GICC_BASE + bl gic_init_secure_percpu +#endif +#endif + + branch_if_master x0, x1, 1f + + /* + * Slave should wait for master clearing spin table. + * This sync prevent salves observing incorrect + * value of spin table and jumping to wrong place. + */ +#if defined(CONFIG_GICV2) || defined(CONFIG_GICV3) +#ifdef CONFIG_GICV2 + ldr x0, =GICC_BASE +#endif + bl gic_wait_for_interrupt +#endif + + /* + * All processors will enter EL2 and optionally EL1. + */ + bl armv8_switch_to_el2 +#ifdef CONFIG_ARMV8_SWITCH_TO_EL1 + bl armv8_switch_to_el1 +#endif + b 2f + +1: +2: + mov lr, x29 /* Restore LR */ + ret +ENDPROC(lowlevel_init) diff --git a/arch/arm/cpu/armv8/fsl-lsch3/speed.c b/arch/arm/cpu/armv8/fsl-lsch3/speed.c new file mode 100644 index 0000000000..dc4a34bce5 --- /dev/null +++ b/arch/arm/cpu/armv8/fsl-lsch3/speed.c @@ -0,0 +1,176 @@ +/* + * Copyright 2014, Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Derived from arch/power/cpu/mpc85xx/speed.c + */ + +#include <common.h> +#include <linux/compiler.h> +#include <fsl_ifc.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/arch-fsl-lsch3/immap_lsch3.h> +#include <asm/arch/clock.h> +#include "cpu.h" + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_SYS_FSL_NUM_CC_PLLS +#define CONFIG_SYS_FSL_NUM_CC_PLLS 6 +#endif + + +void get_sys_info(struct sys_info *sys_info) +{ + struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR); +#ifdef CONFIG_FSL_IFC + struct fsl_ifc *ifc_regs = (void *)CONFIG_SYS_IFC_ADDR; + u32 ccr; +#endif + struct ccsr_clk_cluster_group __iomem *clk_grp[2] = { + (void *)(CONFIG_SYS_FSL_CH3_CLK_GRPA_ADDR), + (void *)(CONFIG_SYS_FSL_CH3_CLK_GRPB_ADDR) + }; + struct ccsr_clk_ctrl __iomem *clk_ctrl = + (void *)(CONFIG_SYS_FSL_CH3_CLK_CTRL_ADDR); + unsigned int cpu; + const u8 core_cplx_pll[16] = { + [0] = 0, /* CC1 PPL / 1 */ + [1] = 0, /* CC1 PPL / 2 */ + [2] = 0, /* CC1 PPL / 4 */ + [4] = 1, /* CC2 PPL / 1 */ + [5] = 1, /* CC2 PPL / 2 */ + [6] = 1, /* CC2 PPL / 4 */ + [8] = 2, /* CC3 PPL / 1 */ + [9] = 2, /* CC3 PPL / 2 */ + [10] = 2, /* CC3 PPL / 4 */ + [12] = 3, /* CC4 PPL / 1 */ + [13] = 3, /* CC4 PPL / 2 */ + [14] = 3, /* CC4 PPL / 4 */ + }; + + const u8 core_cplx_pll_div[16] = { + [0] = 1, /* CC1 PPL / 1 */ + [1] = 2, /* CC1 PPL / 2 */ + [2] = 4, /* CC1 PPL / 4 */ + [4] = 1, /* CC2 PPL / 1 */ + [5] = 2, /* CC2 PPL / 2 */ + [6] = 4, /* CC2 PPL / 4 */ + [8] = 1, /* CC3 PPL / 1 */ + [9] = 2, /* CC3 PPL / 2 */ + [10] = 4, /* CC3 PPL / 4 */ + [12] = 1, /* CC4 PPL / 1 */ + [13] = 2, /* CC4 PPL / 2 */ + [14] = 4, /* CC4 PPL / 4 */ + }; + + uint i, cluster; + uint freq_c_pll[CONFIG_SYS_FSL_NUM_CC_PLLS]; + uint ratio[CONFIG_SYS_FSL_NUM_CC_PLLS]; + unsigned long sysclk = CONFIG_SYS_CLK_FREQ; + int cc_group[12] = CONFIG_SYS_FSL_CLUSTER_CLOCKS; + u32 c_pll_sel, cplx_pll; + void *offset; + + sys_info->freq_systembus = sysclk; +#ifdef CONFIG_DDR_CLK_FREQ + sys_info->freq_ddrbus = CONFIG_DDR_CLK_FREQ; +#else + sys_info->freq_ddrbus = sysclk; +#endif + + sys_info->freq_systembus *= (in_le32(&gur->rcwsr[0]) >> + FSL_CHASSIS3_RCWSR0_SYS_PLL_RAT_SHIFT) & + FSL_CHASSIS3_RCWSR0_SYS_PLL_RAT_MASK; + sys_info->freq_ddrbus *= (in_le32(&gur->rcwsr[0]) >> + FSL_CHASSIS3_RCWSR0_MEM_PLL_RAT_SHIFT) & + FSL_CHASSIS3_RCWSR0_MEM_PLL_RAT_MASK; + + for (i = 0; i < CONFIG_SYS_FSL_NUM_CC_PLLS; i++) { + /* + * fixme: prefer to combine the following into one line, but + * cannot pass compiling without warning about in_le32. + */ + offset = (void *)((size_t)clk_grp[i/3] + + offsetof(struct ccsr_clk_cluster_group, + pllngsr[i%3].gsr)); + ratio[i] = (in_le32(offset) >> 1) & 0x3f; + if (ratio[i] > 4) + freq_c_pll[i] = sysclk * ratio[i]; + else + freq_c_pll[i] = sys_info->freq_systembus * ratio[i]; + } + + for_each_cpu(i, cpu, cpu_numcores(), cpu_mask()) { + cluster = fsl_qoriq_core_to_cluster(cpu); + c_pll_sel = (in_le32(&clk_ctrl->clkcncsr[cluster].csr) >> 27) + & 0xf; + cplx_pll = core_cplx_pll[c_pll_sel]; + cplx_pll += cc_group[cluster] - 1; + sys_info->freq_processor[cpu] = + freq_c_pll[cplx_pll] / core_cplx_pll_div[c_pll_sel]; + } + +#if defined(CONFIG_FSL_IFC) + ccr = in_le32(&ifc_regs->ifc_ccr); + ccr = ((ccr & IFC_CCR_CLK_DIV_MASK) >> IFC_CCR_CLK_DIV_SHIFT) + 1; + + sys_info->freq_localbus = sys_info->freq_systembus / ccr; +#endif +} + + +int get_clocks(void) +{ + struct sys_info sys_info; + get_sys_info(&sys_info); + gd->cpu_clk = sys_info.freq_processor[0]; + gd->bus_clk = sys_info.freq_systembus; + gd->mem_clk = sys_info.freq_ddrbus; + +#if defined(CONFIG_FSL_ESDHC) + gd->arch.sdhc_clk = gd->bus_clk / 2; +#endif /* defined(CONFIG_FSL_ESDHC) */ + + if (gd->cpu_clk != 0) + return 0; + else + return 1; +} + +/******************************************** + * get_bus_freq + * return system bus freq in Hz + *********************************************/ +ulong get_bus_freq(ulong dummy) +{ + if (!gd->bus_clk) + get_clocks(); + + return gd->bus_clk; +} + +/******************************************** + * get_ddr_freq + * return ddr bus freq in Hz + *********************************************/ +ulong get_ddr_freq(ulong dummy) +{ + if (!gd->mem_clk) + get_clocks(); + + return gd->mem_clk; +} + +unsigned int mxc_get_clock(enum mxc_clock clk) +{ + switch (clk) { + case MXC_I2C_CLK: + return get_bus_freq(0) / 2; + default: + printf("Unsupported clock\n"); + } + return 0; +} diff --git a/arch/arm/cpu/armv8/fsl-lsch3/speed.h b/arch/arm/cpu/armv8/fsl-lsch3/speed.h new file mode 100644 index 0000000000..15af5b9f91 --- /dev/null +++ b/arch/arm/cpu/armv8/fsl-lsch3/speed.h @@ -0,0 +1,7 @@ +/* + * Copyright 2014, Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +void get_sys_info(struct sys_info *sys_info); diff --git a/arch/arm/cpu/armv8/transition.S b/arch/arm/cpu/armv8/transition.S index e0a5946009..38dea5c518 100644 --- a/arch/arm/cpu/armv8/transition.S +++ b/arch/arm/cpu/armv8/transition.S @@ -43,7 +43,7 @@ ENTRY(armv8_switch_to_el1) mrs x0, cnthctl_el2 orr x0, x0, #0x3 /* Enable EL1 access to timers */ msr cnthctl_el2, x0 - msr cntvoff_el2, x0 + msr cntvoff_el2, xzr mrs x0, cntkctl_el1 orr x0, x0, #0x3 /* Enable EL0 access to timers */ msr cntkctl_el1, x0 |