summaryrefslogtreecommitdiff
path: root/arch/arm/cpu/armv8
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/cpu/armv8')
-rw-r--r--arch/arm/cpu/armv8/cache_v8.c57
-rw-r--r--arch/arm/cpu/armv8/fsl-lsch3/Makefile9
-rw-r--r--arch/arm/cpu/armv8/fsl-lsch3/README10
-rw-r--r--arch/arm/cpu/armv8/fsl-lsch3/cpu.c436
-rw-r--r--arch/arm/cpu/armv8/fsl-lsch3/cpu.h7
-rw-r--r--arch/arm/cpu/armv8/fsl-lsch3/lowlevel.S65
-rw-r--r--arch/arm/cpu/armv8/fsl-lsch3/speed.c176
-rw-r--r--arch/arm/cpu/armv8/fsl-lsch3/speed.h7
-rw-r--r--arch/arm/cpu/armv8/transition.S2
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