From: Chen-Yu Tsai <[email protected]>

Allwinner A80 and A83T SoCs have two clusters of CPU, each cluster
contains 4 cores. A80 is Cortex-A15 + Cortex-A7 configuration, while
A83T has two clusters of Cortex-A7.

This patch adds a basic version that allows bringing up the four cores
in the first cluster. The structure is based on existing sunxi PSCI code.

Signed-off-by: Chen-Yu Tsai <[email protected]>
[Icenowy: adapt for A83T]
Signed-off-by: Icenowy Zheng <[email protected]>
---
 arch/arm/cpu/armv7/sunxi/Makefile    |   4 +
 arch/arm/cpu/armv7/sunxi/psci-mcpm.c | 258 +++++++++++++++++++++++++++++++++++
 2 files changed, 262 insertions(+)
 create mode 100644 arch/arm/cpu/armv7/sunxi/psci-mcpm.c

diff --git a/arch/arm/cpu/armv7/sunxi/Makefile 
b/arch/arm/cpu/armv7/sunxi/Makefile
index 8c026ff052..c789f686fd 100644
--- a/arch/arm/cpu/armv7/sunxi/Makefile
+++ b/arch/arm/cpu/armv7/sunxi/Makefile
@@ -14,8 +14,12 @@ obj-$(CONFIG_MACH_SUN8I_H3)  += tzpc.o
 obj-$(CONFIG_MACH_SUN8I_A83T)  += tzpc.o
 
 ifndef CONFIG_SPL_BUILD
+ifdef CONFIG_MACH_SUN8I_A83T
+obj-$(CONFIG_ARMV7_PSCI)       += psci-mcpm.o
+else
 obj-$(CONFIG_ARMV7_PSCI)       += psci.o
 endif
+endif
 
 ifdef CONFIG_SPL_BUILD
 obj-y  += fel_utils.o
diff --git a/arch/arm/cpu/armv7/sunxi/psci-mcpm.c 
b/arch/arm/cpu/armv7/sunxi/psci-mcpm.c
new file mode 100644
index 0000000000..ba8d669c7e
--- /dev/null
+++ b/arch/arm/cpu/armv7/sunxi/psci-mcpm.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2016
+ * Author: Chen-Yu Tsai <[email protected]>
+ *
+ * Based on assembly code by Marc Zyngier <[email protected]>,
+ * which was based on code by Carl van Schaik <[email protected]>.
+ *
+ * SPDX-License-Identifier:    GPL-2.0
+ */
+#include <config.h>
+#include <common.h>
+
+#include <asm/arch/cpu.h>
+#include <asm/arch/cpucfg.h>
+#include <asm/arch/prcm.h>
+#include <asm/armv7.h>
+#include <asm/io.h>
+#include <asm/psci.h>
+#include <asm/secure.h>
+
+#include <linux/bitops.h>
+
+/*
+ * NOTE dense CPU IDs (0~3 for first cluster of 4 cores, 4~7 for the
+ * second cluster) are used throughout the PSCI code. Any MPIDR style
+ * values must be converted.
+ */
+
+/*
+ * Provide a dense CPU ID for 2-cluster systems. This must be coded in
+ * assembly as it gets called from psci_stack_setup, when the stack isn't
+ * available yet.
+ *
+ * Only r0 and r3 is usable. r8 - r12 are available if this function is
+ * only called from psci_stack_setup, which we cannot guarantee.
+ */
+u32 __secure __naked psci_get_cpu_id(void)
+{
+       asm volatile (
+               "mrc    p15, 0, r3, c0, c0, 5   @ Get MPIDR\n"
+               "lsr    r0, r3, #6\n"
+               "and    r3, r3, #3\n"
+               "and    r0, r0, #4\n"
+               "orr    r0, r0, r3\n"
+               "bx     lr\n"
+       );
+
+       /*
+        * The last five lines are the compiler generated assembly code for
+        *
+        *      return (reg & 0x3) | (((reg >> 8) & 0x1) << 2);
+        *
+        * We can't guarantee that all compilers correctly use only r0 and
+        * r3, so we use inline assembly here.
+        */
+}
+
+static void __secure cp15_write_cntp_tval(u32 tval)
+{
+       asm volatile ("mcr p15, 0, %0, c14, c2, 0" : : "r" (tval));
+}
+
+static void __secure cp15_write_cntp_ctl(u32 val)
+{
+       asm volatile ("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
+}
+
+static u32 __secure cp15_read_cntp_ctl(void)
+{
+       u32 val;
+
+       asm volatile ("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
+
+       return val;
+}
+
+#define ONE_US (COUNTER_FREQUENCY / 1000000)
+
+/* Use a different name to avoid clashing with the non-secure function */
+static void __secure __udelay_sec(unsigned long us)
+{
+       u32 reg = ONE_US * us;
+
+       cp15_write_cntp_tval(reg);
+       isb();
+       cp15_write_cntp_ctl(3);
+
+       do {
+               isb();
+               reg = cp15_read_cntp_ctl();
+       } while (!(reg & BIT(2)));
+
+       cp15_write_cntp_ctl(0);
+       isb();
+}
+
+static void __secure clamp_release(u32 *clamp)
+{
+       writel(0xff, clamp);
+       __udelay_sec(10);
+       writel(0xfe, clamp);
+       __udelay_sec(10);
+       writel(0xf8, clamp);
+       __udelay_sec(10);
+       writel(0xf0, clamp);
+       __udelay_sec(10);
+       writel(0x00, clamp);
+}
+
+static void __secure clamp_set(u32 *clamp)
+{
+       writel(0xff, clamp);
+}
+
+static void __secure sunxi_core_power_switch(u32 *clamp, u32 *pwroff,
+                                            bool on, int cpu)
+{
+       if (on) {
+               /* Release power clamp */
+               clamp_release(clamp);
+
+               __udelay_sec(20);
+
+               /* Clear power gating */
+               clrbits_le32(pwroff, BIT(cpu));
+       } else {
+               /* Set power gating */
+               setbits_le32(pwroff, BIT(cpu));
+
+               __udelay_sec(20);
+
+               /* Activate power clamp */
+               clamp_set(clamp);
+       }
+}
+
+static int __secure sunxi_cluster_is_a7(int cluster)
+{
+#ifdef CONFIG_MACH_SUN8I_A83T
+       return 1;
+#else
+       return (clustter == 0);
+#endif
+}
+
+static void __secure sunxi_cpu_set_power(int cluster, int cpu, bool on)
+{
+       struct sunxi_prcm_reg *prcm =
+               (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
+
+       sunxi_core_power_switch(&prcm->cpu_pwr_clamp[cluster][cpu],
+                               &prcm->cpu_pwroff[cluster], on, cpu);
+}
+
+static u32 __secure cp15_read_scr(void)
+{
+       u32 scr;
+
+       asm volatile ("mrc p15, 0, %0, c1, c1, 0" : "=r" (scr));
+
+       return scr;
+}
+
+static void __secure cp15_write_scr(u32 scr)
+{
+       asm volatile ("mcr p15, 0, %0, c1, c1, 0" : : "r" (scr));
+       isb();
+}
+
+int __secure psci_cpu_on(u32 __always_unused unused, u32 mpidr, u32 pc)
+{
+       struct sunxi_cpucfg_reg *cpucfg =
+               (struct sunxi_cpucfg_reg *)SUNXI_CPUCFG_BASE;
+#ifdef CONFIG_MACH_SUN8I_A83T
+       struct sunxi_r_cpucfg_reg *r_cpucfg =
+               (struct sunxi_r_cpucfg_reg *)SUNXI_R_CPUCFG_BASE;
+#else
+       struct sunxi_prcm_reg *prcm =
+               (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
+#endif
+       u32 cluster = (mpidr >> 8) & 0x1;
+       u32 cpu = mpidr & 0x3;
+       u32 cpuid = cpu | (cluster << 2);
+
+       /* TODO We don't support multi-cluster yet */
+       if (cluster > 0)
+               return ARM_PSCI_RET_INVAL;
+
+       /* store target PC */
+       psci_save_target_pc(cpuid, pc);
+
+       /* Set secondary core power on PC */
+#ifdef CONFIG_MACH_SUN8I_A83T
+       writel((u32)&psci_cpu_entry, &r_cpucfg->priv0);
+#else
+       writel((u32)&psci_cpu_entry, &prcm->cpu_soft_entry);
+#endif
+
+       /* Assert power-on reset on target CPU */
+#ifdef CONFIG_MACH_SUN8I_A83T
+       clrbits_le32(&r_cpucfg->cpu_rst[cluster], BIT(cpu));
+#else
+       clrbits_le32(&prcm->cpu_rst[cluster], BIT(cpu));
+#endif
+
+       /* Cortex-A7: hold L1 cache reset disable signal low */
+       if (sunxi_cluster_is_a7(cluster))
+               clrbits_le32(&cpucfg->cluster[cluster].ctrl0,
+                            CPUCFG_CX_CTRL0_L1_RST_DISABLE(cpu));
+
+       /* Lock CPU (Disable external debug access) */
+       clrbits_le32(&cpucfg->cluster_reset[cluster],
+                    CPUCFG_CX_RST_DBG(cpu));
+
+       /* Cortex-A7: Assert ETM reset */
+       if (sunxi_cluster_is_a7(cluster))
+               clrbits_le32(&cpucfg->cluster_reset[cluster],
+                            CPUCFG_CX_RST_ETM(cpu));
+
+       /*
+        * Allwinner code also asserts resets for NEON on A15. According
+        * to ARM manuals, asserting power-on reset is sufficient.
+        */
+
+       /* Power up target CPU */
+       sunxi_cpu_set_power(cluster, cpu, true);
+
+       /* De-assert power-on reset on target CPU */
+#ifdef CONFIG_MACH_SUN8I_A83T
+       setbits_le32(&r_cpucfg->cpu_rst[cluster], BIT(cpu));
+#else
+       setbits_le32(&prcm->cpu_rst[cluster], BIT(cpu));
+#endif
+
+       /* De-assert core reset on target CPU */
+       setbits_le32(&cpucfg->cluster_reset[cluster],
+                    CPUCFG_CX_RST_CORE(cpu));
+
+       /* Cortex-A7: De-assert ETM reset */
+       if (sunxi_cluster_is_a7(cluster))
+               setbits_le32(&cpucfg->cluster_reset[cluster],
+                            CPUCFG_CX_RST_ETM(cpu));
+
+       /* Unlock CPU (Disable external debug access) */
+       setbits_le32(&cpucfg->cluster_reset[cluster],
+                    CPUCFG_CX_RST_DBG(cpu));
+
+       return ARM_PSCI_RET_SUCCESS;
+}
+
+void __secure psci_arch_init(void)
+{
+       u32 reg;
+
+       reg = cp15_read_scr();
+       reg &= ~BIT(0); /* Secure mode */
+       cp15_write_scr(reg);
+}
-- 
2.12.2

_______________________________________________
U-Boot mailing list
[email protected]
https://lists.denx.de/listinfo/u-boot

Reply via email to