summaryrefslogtreecommitdiff
path: root/arch/arm/mach-imx/src.c
diff options
context:
space:
mode:
authorAnson Huang <b20788@freescale.com>2021-05-26 03:14:16 +0300
committerShawn Guo <shawnguo@kernel.org>2021-06-12 07:02:57 +0300
commite34645f45805d8308866de7b69f117f554605bb6 (patch)
tree728088350539c203ab9e19c38de9feac943cacc5 /arch/arm/mach-imx/src.c
parentcc8870bf4c3ab0af385538460500a9d342ed945f (diff)
downloadlinux-e34645f45805d8308866de7b69f117f554605bb6.tar.xz
ARM: imx: add smp support for imx7d
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>
Diffstat (limited to 'arch/arm/mach-imx/src.c')
-rw-r--r--arch/arm/mach-imx/src.c101
1 files changed, 92 insertions, 9 deletions
diff --git a/arch/arm/mach-imx/src.c b/arch/arm/mach-imx/src.c
index f52f371292ac..95fd1fbb0826 100644
--- a/arch/arm/mach-imx/src.c
+++ b/arch/arm/mach-imx/src.c
@@ -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
@@ -23,9 +27,18 @@
#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;
+}