diff options
author | Abhilash Kesavan <a.kesavan@samsung.com> | 2014-07-04 16:50:58 -0400 |
---|---|---|
committer | Kukjin Kim <kgene.kim@samsung.com> | 2014-07-22 19:20:21 -0400 |
commit | 20fe6f98fae6968b9d6067d49d9ecae9d9593b37 (patch) | |
tree | 3e2a7d303e99f7b0a7fd5442cf6d569803650a9a | |
parent | 5f534d10d223636b3cd214376471fcfef71baea2 (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.c | 66 | ||||
-rw-r--r-- | arch/arm/mach-exynos/regs-pmu.h | 2 |
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 | ||
76 | static 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 | |||
102 | static int exynos_power_up(unsigned int cpu, unsigned int cluster) | 80 | static 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) |