aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAbhilash Kesavan <a.kesavan@samsung.com>2014-07-04 16:50:58 -0400
committerKukjin Kim <kgene.kim@samsung.com>2014-07-22 19:20:21 -0400
commit20fe6f98fae6968b9d6067d49d9ecae9d9593b37 (patch)
tree3e2a7d303e99f7b0a7fd5442cf6d569803650a9a
parent5f534d10d223636b3cd214376471fcfef71baea2 (diff)
ARM: EXYNOS: Support cluster power off on exynos5420/5800
Turning off a cluster when all 4 cores of the cluster are powered off saves power significantly. Powering off the A15 L2 alone gives around 100mW in savings. Add support for powering off the A15/A7 clusters on exynos5420/5800. The patch enables specific register bits which ensure that: - cluster L2 will be turned on before the first man is powered up. - last man will be turned off before the cluster L2 is turned off. - core is powered down before powering it up. Remove the exynos_cluster_power_control function completely as we can rely on the above mentioned bits rather than polling the cluster power status register. Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com> Acked-by: Nicolas Pitre <nico@linaro.org> Tested-by: Kevin Hilman <khilman@linaro.org> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
-rw-r--r--arch/arm/mach-exynos/mcpm-exynos.c66
-rw-r--r--arch/arm/mach-exynos/regs-pmu.h2
2 files changed, 33 insertions, 35 deletions
diff --git a/arch/arm/mach-exynos/mcpm-exynos.c b/arch/arm/mach-exynos/mcpm-exynos.c
index 13a210865c6f..9315ba91c1fb 100644
--- a/arch/arm/mach-exynos/mcpm-exynos.c
+++ b/arch/arm/mach-exynos/mcpm-exynos.c
@@ -26,6 +26,10 @@
26#define EXYNOS5420_CPUS_PER_CLUSTER 4 26#define EXYNOS5420_CPUS_PER_CLUSTER 4
27#define EXYNOS5420_NR_CLUSTERS 2 27#define EXYNOS5420_NR_CLUSTERS 2
28 28
29#define EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN BIT(9)
30#define EXYNOS5420_USE_ARM_CORE_DOWN_STATE BIT(29)
31#define EXYNOS5420_USE_L2_COMMON_UP_STATE BIT(30)
32
29/* 33/*
30 * The common v7_exit_coherency_flush API could not be used because of the 34 * The common v7_exit_coherency_flush API could not be used because of the
31 * Erratum 799270 workaround. This macro is the same as the common one (in 35 * Erratum 799270 workaround. This macro is the same as the common one (in
@@ -73,36 +77,9 @@ cpu_use_count[EXYNOS5420_CPUS_PER_CLUSTER][EXYNOS5420_NR_CLUSTERS];
73 77
74#define exynos_cluster_unused(cluster) !exynos_cluster_usecnt(cluster) 78#define exynos_cluster_unused(cluster) !exynos_cluster_usecnt(cluster)
75 79
76static int exynos_cluster_power_control(unsigned int cluster, int enable)
77{
78 unsigned int tries = 100;
79 unsigned int val;
80
81 if (enable) {
82 exynos_cluster_power_up(cluster);
83 val = S5P_CORE_LOCAL_PWR_EN;
84 } else {
85 exynos_cluster_power_down(cluster);
86 val = 0;
87 }
88
89 /* Wait until cluster power control is applied */
90 while (tries--) {
91 if (exynos_cluster_power_state(cluster) == val)
92 return 0;
93
94 cpu_relax();
95 }
96 pr_debug("timed out waiting for cluster %u to power %s\n", cluster,
97 enable ? "on" : "off");
98
99 return -ETIMEDOUT;
100}
101
102static int exynos_power_up(unsigned int cpu, unsigned int cluster) 80static int exynos_power_up(unsigned int cpu, unsigned int cluster)
103{ 81{
104 unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER); 82 unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
105 int err = 0;
106 83
107 pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); 84 pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
108 if (cpu >= EXYNOS5420_CPUS_PER_CLUSTER || 85 if (cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
@@ -126,12 +103,9 @@ static int exynos_power_up(unsigned int cpu, unsigned int cluster)
126 * cores. 103 * cores.
127 */ 104 */
128 if (was_cluster_down) 105 if (was_cluster_down)
129 err = exynos_cluster_power_control(cluster, 1); 106 exynos_cluster_power_up(cluster);
130 107
131 if (!err) 108 exynos_cpu_power_up(cpunr);
132 exynos_cpu_power_up(cpunr);
133 else
134 exynos_cluster_power_control(cluster, 0);
135 } else if (cpu_use_count[cpu][cluster] != 2) { 109 } else if (cpu_use_count[cpu][cluster] != 2) {
136 /* 110 /*
137 * The only possible values are: 111 * The only possible values are:
@@ -147,7 +121,7 @@ static int exynos_power_up(unsigned int cpu, unsigned int cluster)
147 arch_spin_unlock(&exynos_mcpm_lock); 121 arch_spin_unlock(&exynos_mcpm_lock);
148 local_irq_enable(); 122 local_irq_enable();
149 123
150 return err; 124 return 0;
151} 125}
152 126
153/* 127/*
@@ -178,9 +152,10 @@ static void exynos_power_down(void)
178 if (cpu_use_count[cpu][cluster] == 0) { 152 if (cpu_use_count[cpu][cluster] == 0) {
179 exynos_cpu_power_down(cpunr); 153 exynos_cpu_power_down(cpunr);
180 154
181 if (exynos_cluster_unused(cluster)) 155 if (exynos_cluster_unused(cluster)) {
182 /* TODO: Turn off the cluster here to save power. */ 156 exynos_cluster_power_down(cluster);
183 last_man = true; 157 last_man = true;
158 }
184 } else if (cpu_use_count[cpu][cluster] == 1) { 159 } else if (cpu_use_count[cpu][cluster] == 1) {
185 /* 160 /*
186 * A power_up request went ahead of us. 161 * A power_up request went ahead of us.
@@ -335,6 +310,7 @@ static int __init exynos_mcpm_init(void)
335{ 310{
336 struct device_node *node; 311 struct device_node *node;
337 void __iomem *ns_sram_base_addr; 312 void __iomem *ns_sram_base_addr;
313 unsigned int value, i;
338 int ret; 314 int ret;
339 315
340 node = of_find_matching_node(NULL, exynos_dt_mcpm_match); 316 node = of_find_matching_node(NULL, exynos_dt_mcpm_match);
@@ -378,6 +354,26 @@ static int __init exynos_mcpm_init(void)
378 pr_info("Exynos MCPM support installed\n"); 354 pr_info("Exynos MCPM support installed\n");
379 355
380 /* 356 /*
357 * On Exynos5420/5800 for the A15 and A7 clusters:
358 *
359 * EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN ensures that all the cores
360 * in a cluster are turned off before turning off the cluster L2.
361 *
362 * EXYNOS5420_USE_ARM_CORE_DOWN_STATE ensures that a cores is powered
363 * off before waking it up.
364 *
365 * EXYNOS5420_USE_L2_COMMON_UP_STATE ensures that cluster L2 will be
366 * turned on before the first man is powered up.
367 */
368 for (i = 0; i < EXYNOS5420_NR_CLUSTERS; i++) {
369 value = __raw_readl(EXYNOS_COMMON_OPTION(i));
370 value |= EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN |
371 EXYNOS5420_USE_ARM_CORE_DOWN_STATE |
372 EXYNOS5420_USE_L2_COMMON_UP_STATE;
373 __raw_writel(value, EXYNOS_COMMON_OPTION(i));
374 }
375
376 /*
381 * U-Boot SPL is hardcoded to jump to the start of ns_sram_base_addr 377 * U-Boot SPL is hardcoded to jump to the start of ns_sram_base_addr
382 * as part of secondary_cpu_start(). Let's redirect it to the 378 * as part of secondary_cpu_start(). Let's redirect it to the
383 * mcpm_entry_point(). 379 * mcpm_entry_point().
diff --git a/arch/arm/mach-exynos/regs-pmu.h b/arch/arm/mach-exynos/regs-pmu.h
index c45a2dc53e84..e5e298c0fd01 100644
--- a/arch/arm/mach-exynos/regs-pmu.h
+++ b/arch/arm/mach-exynos/regs-pmu.h
@@ -116,6 +116,8 @@
116 (EXYNOS_ARM_COMMON_CONFIGURATION + (0x80 * (_nr))) 116 (EXYNOS_ARM_COMMON_CONFIGURATION + (0x80 * (_nr)))
117#define EXYNOS_COMMON_STATUS(_nr) \ 117#define EXYNOS_COMMON_STATUS(_nr) \
118 (EXYNOS_COMMON_CONFIGURATION(_nr) + 0x4) 118 (EXYNOS_COMMON_CONFIGURATION(_nr) + 0x4)
119#define EXYNOS_COMMON_OPTION(_nr) \
120 (EXYNOS_COMMON_CONFIGURATION(_nr) + 0x8)
119 121
120#define S5P_PAD_RET_MAUDIO_OPTION S5P_PMUREG(0x3028) 122#define S5P_PAD_RET_MAUDIO_OPTION S5P_PMUREG(0x3028)
121#define S5P_PAD_RET_GPIO_OPTION S5P_PMUREG(0x3108) 123#define S5P_PAD_RET_GPIO_OPTION S5P_PMUREG(0x3108)