diff mbox series

[14/17] ARM: EXYNOS: Switch SMP support for Exynos5260 to use MCPM

Message ID 20190128230700.7325-15-stuart.menefy@mathembedded.com (mailing list archive)
State Changes Requested
Headers show
Series Resuscitate Exynos 5260 support | expand

Commit Message

Stuart Menefy Jan. 28, 2019, 11:06 p.m. UTC
Switch the SMP support for the Exynos5260 to use the MCPM infrastructure,
including support for suspend/resume.

As far as possible this replicates the operations performed by the last
3.4 kernel released by Samsung.

Signed-off-by: Stuart Menefy <stuart.menefy@mathembedded.com>
---
 arch/arm/boot/dts/exynos5260.dtsi  |  21 +++++++
 arch/arm/mach-exynos/common.h      |   1 +
 arch/arm/mach-exynos/exynos.c      |   1 -
 arch/arm/mach-exynos/mcpm-exynos.c | 118 +++++++++++++++++++++++++++++--------
 arch/arm/mach-exynos/platsmp.c     |  37 ++++++++++--
 arch/arm/mach-exynos/suspend.c     |  86 ++++++++++++++++++++++++---
 6 files changed, 227 insertions(+), 37 deletions(-)
diff mbox series

Patch

diff --git a/arch/arm/boot/dts/exynos5260.dtsi b/arch/arm/boot/dts/exynos5260.dtsi
index 84c9b4443dc6..e8a5c8715b94 100644
--- a/arch/arm/boot/dts/exynos5260.dtsi
+++ b/arch/arm/boot/dts/exynos5260.dtsi
@@ -233,6 +233,24 @@ 
 			status = "disabled";
 		};
 
+		sysram@2020000 {
+			compatible = "mmio-sram";
+			reg = <0x02020000 0x54000>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0 0x02020000 0x54000>;
+
+			smp-sysram@0 {
+				compatible = "samsung,exynos4210-sysram";
+				reg = <0x0 0x1000>;
+			};
+
+			smp-sysram@53000 {
+				compatible = "samsung,exynos4210-sysram-ns";
+				reg = <0x53000 0x1000>;
+			};
+		};
+
 		mct: mct@100b0000 {
 			compatible = "samsung,exynos4210-mct";
 			reg = <0x100B0000 0x1000>;
@@ -299,6 +317,9 @@ 
 		pmu_system_controller: system-controller@10d50000 {
 			compatible = "samsung,exynos5260-pmu", "syscon";
 			reg = <0x10D50000 0x10000>;
+			interrupt-controller;
+			#interrupt-cells = <3>;
+			interrupt-parent = <&gic>;
 		};
 
 		uart0: serial@12c00000 {
diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
index 58e6014e3578..ede449bc73f1 100644
--- a/arch/arm/mach-exynos/common.h
+++ b/arch/arm/mach-exynos/common.h
@@ -110,6 +110,7 @@  enum {
 void exynos_firmware_init(void);
 
 /* CPU BOOT mode flag for Exynos3250 SoC bootloader */
+#define HOTPLUG		(1 << 2)	/* 5260 */
 #define C2_STATE	(1 << 3)
 /*
  * Magic values for bootloader indicating chosen low power mode.
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
index 865dcc4c3181..2866cb0ff51a 100644
--- a/arch/arm/mach-exynos/exynos.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -119,7 +119,6 @@  void exynos_set_delayed_reset_assertion(bool enable)
  * feat, the matches below should be moved to suspend.c.
  */
 static const struct of_device_id exynos_dt_pmu_match[] = {
-	{ .compatible = "samsung,exynos5260-pmu" },
 	{ .compatible = "samsung,exynos5410-pmu" },
 	{ /*sentinel*/ },
 };
diff --git a/arch/arm/mach-exynos/mcpm-exynos.c b/arch/arm/mach-exynos/mcpm-exynos.c
index 571349e1e02e..e5e8d1f27995 100644
--- a/arch/arm/mach-exynos/mcpm-exynos.c
+++ b/arch/arm/mach-exynos/mcpm-exynos.c
@@ -17,10 +17,15 @@ 
 #include <asm/smp_plat.h>
 
 #include "common.h"
+#include "smc.h"
 
 #define EXYNOS5420_CPUS_PER_CLUSTER	4
 #define EXYNOS5420_NR_CLUSTERS		2
 
+#define EXYNOS5260_CPUS_PER_BIG_CLUSTER		2
+#define EXYNOS5260_CPUS_PER_LITTLE_CLUSTER	4
+#define EXYNOS5260_NR_CLUSTERS			2
+
 #define EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN	BIT(9)
 #define EXYNOS5420_USE_ARM_CORE_DOWN_STATE	BIT(29)
 #define EXYNOS5420_USE_L2_COMMON_UP_STATE	BIT(30)
@@ -57,6 +62,17 @@  static void __iomem *ns_sram_base_addr __ro_after_init;
 
 static int exynos_cpunr(unsigned int cpu, unsigned int cluster)
 {
+	if (soc_is_exynos5260()) {
+		static const int cpus_per_cluster[EXYNOS5260_NR_CLUSTERS] = {
+			EXYNOS5260_CPUS_PER_BIG_CLUSTER,
+			EXYNOS5260_CPUS_PER_LITTLE_CLUSTER
+		};
+		if (cluster >= EXYNOS5260_NR_CLUSTERS ||
+		    cpu >= cpus_per_cluster[cluster])
+			return -EINVAL;
+		return cpu + (cluster * 4);
+	}
+
 	if (cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
 	    cluster >= EXYNOS5420_NR_CLUSTERS)
 		return -EINVAL;
@@ -92,8 +108,20 @@  static int exynos_cpu_powerup(unsigned int cpu, unsigned int cluster)
 			while (!pmu_raw_readl(S5P_PMU_SPARE2))
 				udelay(10);
 
-			pmu_raw_writel(EXYNOS5420_KFC_CORE_RESET(cpu),
-					EXYNOS_SWRESET);
+			if (soc_is_exynos5260()) {
+				u32 val, offset;
+
+				offset = EXYNOS_ARM_CORE_STATUS(cpunr);
+				val = pmu_raw_readl(offset);
+				val |= EXYNOS5260_STATUS_WAKEUP_FROM_LOCAL_CFG;
+				pmu_raw_writel(val, offset);
+
+				pmu_raw_writel(EXYNOS5260_SWRESET_PORESET,
+					       EXYNOS_ARM_CORE_RESET(cpunr));
+			} else {
+				pmu_raw_writel(EXYNOS5420_KFC_CORE_RESET(cpu),
+					       EXYNOS_SWRESET);
+			}
 		}
 	}
 
@@ -106,6 +134,9 @@  static int exynos_cluster_powerup(unsigned int cluster)
 	if (cluster >= EXYNOS5420_NR_CLUSTERS)
 		return -EINVAL;
 
+	if (soc_is_exynos5260())
+		return 0;
+
 	exynos_cluster_power_up(cluster);
 	return 0;
 }
@@ -119,21 +150,44 @@  static void exynos_cpu_powerdown_prepare(unsigned int cpu, unsigned int cluster)
 	exynos_cpu_power_down(cpunr);
 }
 
+static void exynos_wfi_alternative(bool last_man)
+{
+	if (soc_is_exynos5260()) {
+		exynos_smc(SMC_CMD_SHUTDOWN,
+			   last_man ? OP_TYPE_CLUSTER : OP_TYPE_CORE,
+			   SMC_POWERSTATE_IDLE,
+			   0);
+		return;
+	}
+
+	wfi();
+}
+
 static void exynos_cluster_powerdown_prepare(unsigned int cluster)
 {
 	pr_debug("%s: cluster %u\n", __func__, cluster);
 	BUG_ON(cluster >= EXYNOS5420_NR_CLUSTERS);
+
+	if (soc_is_exynos5260())
+		return;
+
 	exynos_cluster_power_down(cluster);
 }
 
 static void exynos_cpu_cache_disable(void)
 {
+	if (soc_is_exynos5260())
+		return;
+
 	/* Disable and flush the local CPU cache. */
 	exynos_v7_exit_coherency_flush(louis);
 }
 
 static void exynos_cluster_cache_disable(void)
 {
+	if (soc_is_exynos5260())
+		return;
+
 	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A15) {
 		/*
 		 * On the Cortex-A15 we need to disable
@@ -180,12 +234,20 @@  static void exynos_cpu_is_up(unsigned int cpu, unsigned int cluster)
 {
 	/* especially when resuming: make sure power control is set */
 	exynos_cpu_powerup(cpu, cluster);
+
+	if (soc_is_exynos5260()) {
+		/* Note this is different to exynos_cpunr() */
+		int cpunr = cpu + (cluster ? 0 : 4);
+
+		exynos_clear_boot_flag(cpunr, HOTPLUG);
+	}
 }
 
 static const struct mcpm_platform_ops exynos_power_ops = {
 	.cpu_powerup		= exynos_cpu_powerup,
 	.cluster_powerup	= exynos_cluster_powerup,
 	.cpu_powerdown_prepare	= exynos_cpu_powerdown_prepare,
+	.wfi_alternative	= exynos_wfi_alternative,
 	.cluster_powerdown_prepare = exynos_cluster_powerdown_prepare,
 	.cpu_cache_disable	= exynos_cpu_cache_disable,
 	.cluster_cache_disable	= exynos_cluster_cache_disable,
@@ -205,6 +267,7 @@  static void __naked exynos_pm_power_up_setup(unsigned int affinity_level)
 }
 
 static const struct of_device_id exynos_dt_mcpm_match[] = {
+	{ .compatible = "samsung,exynos5260" },
 	{ .compatible = "samsung,exynos5420" },
 	{ .compatible = "samsung,exynos5800" },
 	{},
@@ -253,11 +316,13 @@  static int __init exynos_mcpm_init(void)
 		return -ENOMEM;
 	}
 
-	/*
-	 * To increase the stability of KFC reset we need to program
-	 * the PMU SPARE3 register
-	 */
-	pmu_raw_writel(EXYNOS5420_SWRESET_KFC_SEL, S5P_PMU_SPARE3);
+	if (soc_is_exynos5420() || soc_is_exynos5800()) {
+		/*
+		 * To increase the stability of KFC reset we need to program
+		 * the PMU SPARE3 register.
+		 */
+		pmu_raw_writel(EXYNOS5420_SWRESET_KFC_SEL, S5P_PMU_SPARE3);
+	}
 
 	ret = mcpm_platform_register(&exynos_power_ops);
 	if (!ret)
@@ -273,24 +338,27 @@  static int __init exynos_mcpm_init(void)
 
 	pr_info("Exynos MCPM support installed\n");
 
-	/*
-	 * On Exynos5420/5800 for the A15 and A7 clusters:
-	 *
-	 * EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN ensures that all the cores
-	 * in a cluster are turned off before turning off the cluster L2.
-	 *
-	 * EXYNOS5420_USE_ARM_CORE_DOWN_STATE ensures that a cores is powered
-	 * off before waking it up.
-	 *
-	 * EXYNOS5420_USE_L2_COMMON_UP_STATE ensures that cluster L2 will be
-	 * turned on before the first man is powered up.
-	 */
-	for (i = 0; i < EXYNOS5420_NR_CLUSTERS; i++) {
-		value = pmu_raw_readl(EXYNOS_COMMON_OPTION(i));
-		value |= EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN |
-			 EXYNOS5420_USE_ARM_CORE_DOWN_STATE    |
-			 EXYNOS5420_USE_L2_COMMON_UP_STATE;
-		pmu_raw_writel(value, EXYNOS_COMMON_OPTION(i));
+	if (soc_is_exynos5420() || soc_is_exynos5800()) {
+		/*
+		 * On Exynos5420/5800 for the A15 and A7 clusters:
+		 *
+		 * EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN ensures that all the
+		 * cores in a cluster are turned off before turning off the
+		 * cluster L2.
+		 *
+		 * EXYNOS5420_USE_ARM_CORE_DOWN_STATE ensures that a cores is
+		 * powered off before waking it up.
+		 *
+		 * EXYNOS5420_USE_L2_COMMON_UP_STATE ensures that cluster L2
+		 * will be turned on before the first man is powered up.
+		 */
+		for (i = 0; i < EXYNOS5420_NR_CLUSTERS; i++) {
+			value = pmu_raw_readl(EXYNOS_COMMON_OPTION(i));
+			value |= EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN |
+				EXYNOS5420_USE_ARM_CORE_DOWN_STATE    |
+				EXYNOS5420_USE_L2_COMMON_UP_STATE;
+			pmu_raw_writel(value, EXYNOS_COMMON_OPTION(i));
+		}
 	}
 
 	exynos_mcpm_setup_entry_point();
diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c
index b6da7edbbd2f..07c21b2afc11 100644
--- a/arch/arm/mach-exynos/platsmp.c
+++ b/arch/arm/mach-exynos/platsmp.c
@@ -100,6 +100,23 @@  void exynos_cpu_power_down(int cpu)
 			return;
 	}
 
+	if (cpu == 4 && (soc_is_exynos5260())) {
+		/*
+		 * Bypass power down for CPU4 (KFC0) during suspend. Check for
+		 * the SYS_PWR_REG value to decide if we are suspending
+		 * the system.
+		 */
+		int val = pmu_raw_readl(EXYNOS5260_KFC_CORE0_SYS_PWR_REG);
+
+		if ((val & 0xf) == 8)
+			return;
+	}
+
+	if (soc_is_exynos5260()) {
+		pmu_raw_writel(0, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
+		return;
+	}
+
 	core_conf = pmu_raw_readl(EXYNOS_ARM_CORE_CONFIGURATION(cpu));
 	core_conf &= ~S5P_CORE_LOCAL_PWR_EN;
 	pmu_raw_writel(core_conf, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
@@ -117,6 +134,9 @@  void exynos_cpu_power_up(int cpu)
 
 	if (soc_is_exynos3250())
 		core_conf |= S5P_CORE_AUTOWAKEUP_EN;
+	else if (soc_is_exynos5260())
+		core_conf = EXYNOS5260_CONFIGURATION_LOCAL_POWER_CFG |
+			EXYNOS5260_CONFIGURATION_INITIATE_WAKEUP_FROM_LOWPOWER;
 
 	pmu_raw_writel(core_conf,
 			EXYNOS_ARM_CORE_CONFIGURATION(cpu));
@@ -129,8 +149,17 @@  void exynos_cpu_power_up(int cpu)
  */
 int exynos_cpu_power_state(int cpu)
 {
-	return (pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(cpu)) &
-			S5P_CORE_LOCAL_PWR_EN);
+	u32 status = pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(cpu));
+
+	if (soc_is_exynos5260()) {
+		if ((status & EXYNOS5260_STATUS_STATES) ==
+		    EXYNOS5260_STATUS_STATES_NORMAL)
+			return S5P_CORE_LOCAL_PWR_EN;
+		else
+			return 0;
+	}
+
+	return status & S5P_CORE_LOCAL_PWR_EN;
 }
 
 /**
@@ -197,7 +226,7 @@  static inline void __iomem *cpu_boot_reg(int cpu)
 	boot_reg = cpu_boot_reg_base();
 	if (!boot_reg)
 		return IOMEM_ERR_PTR(-ENODEV);
-	if (soc_is_exynos4412())
+	if (soc_is_exynos4412() || soc_is_exynos5260())
 		boot_reg += 4*cpu;
 	else if (soc_is_exynos5420() || soc_is_exynos5800())
 		boot_reg += 4;
@@ -371,7 +400,7 @@  static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
 
 		call_firmware_op(cpu_boot, core_id);
 
-		if (soc_is_exynos3250())
+		if (soc_is_exynos3250() /* || soc_is_exynos5260() */)
 			dsb_sev();
 		else
 			arch_send_wakeup_ipi_mask(cpumask_of(cpu));
diff --git a/arch/arm/mach-exynos/suspend.c b/arch/arm/mach-exynos/suspend.c
index 0850505ac78b..174b8064a655 100644
--- a/arch/arm/mach-exynos/suspend.c
+++ b/arch/arm/mach-exynos/suspend.c
@@ -230,6 +230,7 @@  EXYNOS_PMU_IRQ(exynos3250_pmu_irq, "samsung,exynos3250-pmu");
 EXYNOS_PMU_IRQ(exynos4210_pmu_irq, "samsung,exynos4210-pmu");
 EXYNOS_PMU_IRQ(exynos4412_pmu_irq, "samsung,exynos4412-pmu");
 EXYNOS_PMU_IRQ(exynos5250_pmu_irq, "samsung,exynos5250-pmu");
+EXYNOS_PMU_IRQ(exynos5260_pmu_irq, "samsung,exynos5260-pmu");
 EXYNOS_PMU_IRQ(exynos5420_pmu_irq, "samsung,exynos5420-pmu");
 
 static int exynos_cpu_do_idle(void)
@@ -258,6 +259,24 @@  static int exynos3250_cpu_suspend(unsigned long arg)
 	return exynos_cpu_do_idle();
 }
 
+static int exynos5260_cpu_suspend(unsigned long arg)
+{
+	/* MCPM works with HW CPU identifiers */
+	unsigned int mpidr = read_cpuid_mpidr();
+	unsigned int cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+	unsigned int cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+
+	if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) {
+		mcpm_set_entry_vector(cpu, cluster, exynos_cpu_resume);
+		mcpm_cpu_suspend();
+	}
+
+	pr_info("Failed to suspend the system\n");
+
+	/* return value != 0 means failure */
+	return 1;
+}
+
 static int exynos5420_cpu_suspend(unsigned long arg)
 {
 	/* MCPM works with HW CPU identifiers */
@@ -324,6 +343,17 @@  static void exynos3250_pm_prepare(void)
 	pmu_raw_writel(__pa_symbol(exynos_cpu_resume), S5P_INFORM0);
 }
 
+static void exynos5260_pm_prepare(void)
+{
+	/* Set wake-up mask registers */
+	pmu_raw_writel(exynos_irqwake_intmask & ~(1 << 31),
+		       EXYNOS5260_WAKEUP_MASK1);
+	/* EXYNOS5260_EINT_WAKEUP_MASK is set by pinctrl */
+
+	/* Set value of power down register for sleep mode */
+	exynos_sys_powerdown_conf(SYS_SLEEP);
+}
+
 static void exynos5420_pm_prepare(void)
 {
 	unsigned int tmp;
@@ -369,14 +399,18 @@  static void exynos5420_pm_prepare(void)
 	pmu_raw_writel(tmp, EXYNOS5420_PSGEN_OPTION);
 }
 
-
 static int exynos_pm_suspend(void)
 {
+	int val;
 	exynos_pm_central_suspend();
 
 	/* Setting SEQ_OPTION register */
-	pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
-		       S5P_CENTRAL_SEQ_OPTION);
+	if (soc_is_exynos5260())
+		val = EXYNOS5260_USE_STANDBYWFI_KFC_CORE0 |
+		      EXYNOS5260_USE_PROLOGNED_LOGIC_RESET;
+	else
+		val = S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0;
+	pmu_raw_writel(val, S5P_CENTRAL_SEQ_OPTION);
 
 	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
 		exynos_cpu_save_register();
@@ -384,6 +418,17 @@  static int exynos_pm_suspend(void)
 	return 0;
 }
 
+static int exynos5260_pm_suspend(void)
+{
+	pmu_raw_writel(EXYNOS5260_USE_STANDBYWFI_KFC_CORE0 |
+		       EXYNOS5260_USE_PROLOGNED_LOGIC_RESET,
+		       S5P_CENTRAL_SEQ_OPTION);
+
+	exynos_pm_central_suspend();
+
+	return 0;
+}
+
 static int exynos5420_pm_suspend(void)
 {
 	u32 this_cluster;
@@ -442,6 +487,19 @@  static void exynos3250_pm_resume(void)
 	pmu_raw_writel(0x0, S5P_INFORM1);
 }
 
+static void exynos5260_pm_resume(void)
+{
+	pmu_raw_writel(EXYNOS5260_USE_STANDBY_WFI_ALL |
+		       EXYNOS5260_USE_PROLOGNED_LOGIC_RESET,
+			S5P_CENTRAL_SEQ_OPTION);
+
+	if (exynos_pm_central_resume())
+		goto early_wakeup;
+
+early_wakeup:
+	return;
+}
+
 static void exynos5420_prepare_pm_resume(void)
 {
 	if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM))
@@ -593,6 +651,15 @@  static const struct exynos_pm_data exynos5250_pm_data = {
 	.cpu_suspend	= exynos_cpu_suspend,
 };
 
+static const struct exynos_pm_data exynos5260_pm_data = {
+	.wkup_irq	= exynos3250_wkup_irq,
+	.wake_disable_mask = ((0xFF << 8) | (0x1F << 1)),
+	.pm_suspend	= exynos5260_pm_suspend,
+	.pm_resume	= exynos5260_pm_resume,
+	.pm_prepare	= exynos5260_pm_prepare,
+	.cpu_suspend	= exynos5260_cpu_suspend,
+};
+
 static const struct exynos_pm_data exynos5420_pm_data = {
 	.wkup_irq	= exynos5250_wkup_irq,
 	.wake_disable_mask = (0x7F << 7) | (0x1F << 1),
@@ -617,6 +684,9 @@  static const struct of_device_id exynos_pmu_of_device_ids[] __initconst = {
 		.compatible = "samsung,exynos5250-pmu",
 		.data = &exynos5250_pm_data,
 	}, {
+		.compatible = "samsung,exynos5260-pmu",
+		.data = &exynos5260_pm_data,
+	}, {
 		.compatible = "samsung,exynos5420-pmu",
 		.data = &exynos5420_pm_data,
 	},
@@ -644,10 +714,12 @@  void __init exynos_pm_init(void)
 
 	pm_data = (const struct exynos_pm_data *) match->data;
 
-	/* All wakeup disable */
-	tmp = pmu_raw_readl(S5P_WAKEUP_MASK);
-	tmp |= pm_data->wake_disable_mask;
-	pmu_raw_writel(tmp, S5P_WAKEUP_MASK);
+	if (!soc_is_exynos5260()) {
+		/* All wakeup disable */
+		tmp = pmu_raw_readl(S5P_WAKEUP_MASK);
+		tmp |= pm_data->wake_disable_mask;
+		pmu_raw_writel(tmp, S5P_WAKEUP_MASK);
+	}
 
 	exynos_pm_syscore_ops.suspend	= pm_data->pm_suspend;
 	exynos_pm_syscore_ops.resume	= pm_data->pm_resume;