ARM: imx: add smp support for imx7d
authorAnson Huang <b20788@freescale.com>
Wed, 26 May 2021 00:14:16 +0000 (21:14 -0300)
committerShawn Guo <shawnguo@kernel.org>
Sat, 12 Jun 2021 04:02:57 +0000 (12:02 +0800)
Add SMP support for i.MX7D, including CPU hotplug support, for
systems where TFA is not present.

The motivation for bringing up the second i.MX7D core inside the kernel
is that legacy vendor bootloaders usually do not implement PSCI support.

This is a significant blocker for systems in the field that are running old
bootloader versions to upgrade to a modern mainline kernel version, as only
one CPU of the i.MX7D would be brought up.

Bring up the second i.MX7D core inside the kernel to make the migration
path to mainline kernel easier for the existing iMX7D users.

Signed-off-by: Anson Huang <b20788@freescale.com>
Signed-off-by: Arulpandiyan Vadivel <arulpandiyan_vadivel@mentor.com> # Fix merge conflicts
Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>
Signed-off-by: Marek Vasut <marex@denx.de> # heavy cleanup
Signed-off-by: Fabio Estevam <festevam@denx.de>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
arch/arm/mach-imx/Makefile
arch/arm/mach-imx/common.h
arch/arm/mach-imx/headsmp.S
arch/arm/mach-imx/hotplug.c
arch/arm/mach-imx/mach-imx7d.c
arch/arm/mach-imx/platsmp.c
arch/arm/mach-imx/src.c

index 9cebd360d58e8bef781d24d082307f36fc54f1a0..d1506ef7a5374333e09e437b8325069717733576 100644 (file)
@@ -35,7 +35,7 @@ obj-$(CONFIG_HAVE_IMX_ANATOP) += anatop.o
 obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
 obj-$(CONFIG_HAVE_IMX_MMDC) += mmdc.o
 obj-$(CONFIG_HAVE_IMX_SRC) += src.o
-ifneq ($(CONFIG_SOC_IMX6)$(CONFIG_SOC_LS1021A),)
+ifneq ($(CONFIG_SOC_IMX6)$(CONFIG_SOC_IMX7D_CA7)$(CONFIG_SOC_LS1021A),)
 AFLAGS_headsmp.o :=-Wa,-march=armv7-a
 obj-$(CONFIG_SMP) += headsmp.o platsmp.o
 obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
index 474dedb73bc75f950cc14e1bba33572d57982f55..f0a073a71401a37667fccf0d3741d63d68f7567c 100644 (file)
@@ -68,11 +68,13 @@ void imx_set_cpu_arg(int cpu, u32 arg);
 void v7_secondary_startup(void);
 void imx_scu_map_io(void);
 void imx_smp_prepare(void);
+void imx_gpcv2_set_core1_pdn_pup_by_software(bool pdn);
 #else
 static inline void imx_scu_map_io(void) {}
 static inline void imx_smp_prepare(void) {}
 #endif
 void imx_src_init(void);
+void imx7_src_init(void);
 void imx_gpc_pre_suspend(bool arm_power_off);
 void imx_gpc_post_resume(void);
 void imx_gpc_mask_all(void);
@@ -131,6 +133,7 @@ static inline void imx_init_l2cache(void) {}
 #endif
 
 extern const struct smp_operations imx_smp_ops;
+extern const struct smp_operations imx7_smp_ops;
 extern const struct smp_operations ls1021a_smp_ops;
 
 #endif
index 766dbdb2ae27b9eb51cca555c8170103d17dde17..fcba58be8e798c9ae502fb70c3002c484120207e 100644 (file)
@@ -21,6 +21,15 @@ diag_reg_offset:
 
 ENTRY(v7_secondary_startup)
 ARM_BE8(setend be)                     @ go BE8 if entered LE
+       mrc     p15, 0, r0, c0, c0, 0
+       lsl     r0, r0, #16
+       lsr     r0, r0, #20
+       /* 0xc07 is cortex A7's ID */
+       mov     r1, #0xc00
+       orr     r1, #0x7
+       cmp     r0, r1
+       beq     secondary_startup
+
        set_diag_reg
        b       secondary_startup
 ENDPROC(v7_secondary_startup)
index 82e22398d43da891ea2717f3bffb4858c9573733..e24a46dc57036cea15b3ca32229bb72067bbb03f 100644 (file)
@@ -11,6 +11,7 @@
 #include <asm/proc-fns.h>
 
 #include "common.h"
+#include "hardware.h"
 
 /*
  * platform-specific code to shutdown a CPU
@@ -40,5 +41,7 @@ int imx_cpu_kill(unsigned int cpu)
                        return 0;
        imx_enable_cpu(cpu, false);
        imx_set_cpu_arg(cpu, 0);
+       if (cpu_is_imx7d())
+               imx_gpcv2_set_core1_pdn_pup_by_software(true);
        return 1;
 }
index ccf64ddf8b7ed6cbb9b0cf5c100d7f6859e05dbe..6fdd06bcf98844427359728fde5c05da9a16bcfe 100644 (file)
@@ -69,7 +69,7 @@ static void __init imx7d_init_late(void)
 static void __init imx7d_init_irq(void)
 {
        imx_init_revision_from_anatop();
-       imx_src_init();
+       imx7_src_init();
        irqchip_init();
 }
 
@@ -80,6 +80,7 @@ static const char *const imx7d_dt_compat[] __initconst = {
 };
 
 DT_MACHINE_START(IMX7D, "Freescale i.MX7 Dual (Device Tree)")
+       .smp            = smp_ops(imx7_smp_ops),
        .init_irq       = imx7d_init_irq,
        .init_machine   = imx7d_init_machine,
        .init_late      = imx7d_init_late,
index cf4e9335831c6908350d5c03781b85e0a86928f1..972639038be5734d8b0b0828cd34da270c462054 100644 (file)
@@ -92,6 +92,32 @@ const struct smp_operations imx_smp_ops __initconst = {
 #endif
 };
 
+/*
+ * Initialise the CPU possible map early - this describes the CPUs
+ * which may be present or become present in the system.
+ */
+static void __init imx7_smp_init_cpus(void)
+{
+       struct device_node *np;
+       int i, ncores = 0;
+
+       /* The iMX7D SCU does not report core count, get it from DT */
+       for_each_of_cpu_node(np)
+               ncores++;
+
+       for (i = ncores; i < NR_CPUS; i++)
+               set_cpu_possible(i, false);
+}
+
+const struct smp_operations imx7_smp_ops __initconst = {
+       .smp_init_cpus          = imx7_smp_init_cpus,
+       .smp_boot_secondary     = imx_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+       .cpu_die                = imx_cpu_die,
+       .cpu_kill               = imx_cpu_kill,
+#endif
+};
+
 #define DCFG_CCSR_SCRATCHRW1   0x200
 
 static int ls1021a_boot_secondary(unsigned int cpu, struct task_struct *idle)
index f52f371292ac3a9f7bea64d002b3f19470804f64..95fd1fbb082607128aca4cf146d2e82a430150c2 100644 (file)
@@ -6,15 +6,19 @@
 
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/reset-controller.h>
 #include <linux/smp.h>
 #include <asm/smp_plat.h>
 #include "common.h"
+#include "hardware.h"
 
 #define SRC_SCR                                0x000
-#define SRC_GPR1                       0x020
+#define SRC_GPR1_V1                    0x020
+#define SRC_GPR1_V2                    0x074
+#define SRC_GPR1(gpr_v2)               ((gpr_v2) ? SRC_GPR1_V2 : SRC_GPR1_V1)
 #define BP_SRC_SCR_WARM_RESET_ENABLE   0
 #define BP_SRC_SCR_SW_GPU_RST          1
 #define BP_SRC_SCR_SW_VPU_RST          2
 #define BP_SRC_SCR_SW_IPU2_RST         12
 #define BP_SRC_SCR_CORE1_RST           14
 #define BP_SRC_SCR_CORE1_ENABLE                22
+/* below is for i.MX7D */
+#define SRC_A7RCR1                     0x008
+#define BP_SRC_A7RCR1_A7_CORE1_ENABLE  1
+#define GPC_CPU_PGC_SW_PUP_REQ         0xf0
+#define GPC_CPU_PGC_SW_PDN_REQ         0xfc
+#define GPC_PGC_C1                     0x840
+#define BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7     0x2
 
 static void __iomem *src_base;
 static DEFINE_SPINLOCK(scr_lock);
+static bool gpr_v2;
+static void __iomem *gpc_base;
 
 static const int sw_reset_bits[5] = {
        BP_SRC_SCR_SW_GPU_RST,
@@ -73,17 +86,64 @@ static struct reset_controller_dev imx_reset_controller = {
        .nr_resets = ARRAY_SIZE(sw_reset_bits),
 };
 
+static void imx_gpcv2_set_m_core_pgc(bool enable, u32 offset)
+{
+       writel_relaxed(enable, gpc_base + offset);
+}
+
+/*
+ * The motivation for bringing up the second i.MX7D core inside the kernel
+ * is that legacy vendor bootloaders usually do not implement PSCI support.
+ * This is a significant blocker for systems in the field that are running old
+ * bootloader versions to upgrade to a modern mainline kernel version, as only
+ * one CPU of the i.MX7D would be brought up.
+ * Bring up the second i.MX7D core inside the kernel to make the migration
+ * path to mainline kernel easier for the existing iMX7D users.
+ */
+void imx_gpcv2_set_core1_pdn_pup_by_software(bool pdn)
+{
+       u32 reg = pdn ? GPC_CPU_PGC_SW_PDN_REQ : GPC_CPU_PGC_SW_PUP_REQ;
+       u32 val, pup;
+       int ret;
+
+       imx_gpcv2_set_m_core_pgc(true, GPC_PGC_C1);
+       val = readl_relaxed(gpc_base + reg);
+       val |= BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7;
+       writel_relaxed(val, gpc_base + reg);
+
+       ret = readl_relaxed_poll_timeout_atomic(gpc_base + reg, pup,
+                               !(pup & BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7),
+                               5, 1000000);
+       if (ret < 0) {
+               pr_err("i.MX7D: CORE1_A7 power up timeout\n");
+               val &= ~BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7;
+               writel_relaxed(val, gpc_base + reg);
+       }
+
+       imx_gpcv2_set_m_core_pgc(false, GPC_PGC_C1);
+}
+
 void imx_enable_cpu(int cpu, bool enable)
 {
        u32 mask, val;
 
        cpu = cpu_logical_map(cpu);
-       mask = 1 << (BP_SRC_SCR_CORE1_ENABLE + cpu - 1);
        spin_lock(&scr_lock);
-       val = readl_relaxed(src_base + SRC_SCR);
-       val = enable ? val | mask : val & ~mask;
-       val |= 1 << (BP_SRC_SCR_CORE1_RST + cpu - 1);
-       writel_relaxed(val, src_base + SRC_SCR);
+       if (gpr_v2) {
+               if (enable)
+                       imx_gpcv2_set_core1_pdn_pup_by_software(false);
+
+               mask = 1 << (BP_SRC_A7RCR1_A7_CORE1_ENABLE + cpu - 1);
+               val = readl_relaxed(src_base + SRC_A7RCR1);
+               val = enable ? val | mask : val & ~mask;
+               writel_relaxed(val, src_base + SRC_A7RCR1);
+       } else {
+               mask = 1 << (BP_SRC_SCR_CORE1_ENABLE + cpu - 1);
+               val = readl_relaxed(src_base + SRC_SCR);
+               val = enable ? val | mask : val & ~mask;
+               val |= 1 << (BP_SRC_SCR_CORE1_RST + cpu - 1);
+               writel_relaxed(val, src_base + SRC_SCR);
+       }
        spin_unlock(&scr_lock);
 }
 
@@ -91,19 +151,19 @@ void imx_set_cpu_jump(int cpu, void *jump_addr)
 {
        cpu = cpu_logical_map(cpu);
        writel_relaxed(__pa_symbol(jump_addr),
-                      src_base + SRC_GPR1 + cpu * 8);
+                      src_base + SRC_GPR1(gpr_v2) + cpu * 8);
 }
 
 u32 imx_get_cpu_arg(int cpu)
 {
        cpu = cpu_logical_map(cpu);
-       return readl_relaxed(src_base + SRC_GPR1 + cpu * 8 + 4);
+       return readl_relaxed(src_base + SRC_GPR1(gpr_v2) + cpu * 8 + 4);
 }
 
 void imx_set_cpu_arg(int cpu, u32 arg)
 {
        cpu = cpu_logical_map(cpu);
-       writel_relaxed(arg, src_base + SRC_GPR1 + cpu * 8 + 4);
+       writel_relaxed(arg, src_base + SRC_GPR1(gpr_v2) + cpu * 8 + 4);
 }
 
 void __init imx_src_init(void)
@@ -131,3 +191,26 @@ void __init imx_src_init(void)
        writel_relaxed(val, src_base + SRC_SCR);
        spin_unlock(&scr_lock);
 }
+
+void __init imx7_src_init(void)
+{
+       struct device_node *np;
+
+       gpr_v2 = true;
+
+       np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-src");
+       if (!np)
+               return;
+
+       src_base = of_iomap(np, 0);
+       if (!src_base)
+               return;
+
+       np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-gpc");
+       if (!np)
+               return;
+
+       gpc_base = of_iomap(np, 0);
+       if (!gpc_base)
+               return;
+}