@@ -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 {
@@ -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.
@@ -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*/ },
};
@@ -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();
@@ -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));
@@ -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;
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(-)