aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorGregory CLEMENT <gregory.clement@free-electrons.com>2014-04-14 11:10:11 -0400
committerJason Cooper <jason@lakedaemon.net>2014-05-08 12:18:58 -0400
commitc3e04cabb135625df8ff4b71ef4130f0ccbcc669 (patch)
tree149c7c433c9fa54a0a4612f8a64bafe26e04fc11 /arch/arm
parentf713c7e7421d6945c977c8d8813e8089f925de41 (diff)
ARM: mvebu: Add the PMSU related part of the cpu idle functions
The cpu idle support will need to access to Power Management Service Unit. This commit adds the architecture related functions that will be used in the idle path of the cpuidle driver. Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com> Link: https://lkml.kernel.org/r/1397488214-20685-9-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Jason Cooper <jason@lakedaemon.net>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-mvebu/pmsu.c132
1 files changed, 131 insertions, 1 deletions
diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c
index 7ccf14a065b1..78cf0279e1af 100644
--- a/arch/arm/mach-mvebu/pmsu.c
+++ b/arch/arm/mach-mvebu/pmsu.c
@@ -24,9 +24,12 @@
24#include <linux/io.h> 24#include <linux/io.h>
25#include <linux/smp.h> 25#include <linux/smp.h>
26#include <linux/resource.h> 26#include <linux/resource.h>
27#include <asm/cacheflush.h>
28#include <asm/cp15.h>
27#include <asm/smp_plat.h> 29#include <asm/smp_plat.h>
30#include <asm/suspend.h>
31#include <asm/tlbflush.h>
28#include "common.h" 32#include "common.h"
29#include "pmsu.h"
30 33
31static void __iomem *pmsu_mp_base; 34static void __iomem *pmsu_mp_base;
32 35
@@ -34,12 +37,33 @@ static void __iomem *pmsu_mp_base;
34#define PMSU_REG_SIZE 0x1000 37#define PMSU_REG_SIZE 0x1000
35 38
36/* PMSU MP registers */ 39/* PMSU MP registers */
40#define PMSU_CONTROL_AND_CONFIG(cpu) ((cpu * 0x100) + 0x104)
41#define PMSU_CONTROL_AND_CONFIG_DFS_REQ BIT(18)
42#define PMSU_CONTROL_AND_CONFIG_PWDDN_REQ BIT(16)
43#define PMSU_CONTROL_AND_CONFIG_L2_PWDDN BIT(20)
44
45#define PMSU_CPU_POWER_DOWN_CONTROL(cpu) ((cpu * 0x100) + 0x108)
46
47#define PMSU_CPU_POWER_DOWN_DIS_SNP_Q_SKIP BIT(0)
48
49#define PMSU_STATUS_AND_MASK(cpu) ((cpu * 0x100) + 0x10c)
50#define PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT BIT(16)
51#define PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT BIT(17)
52#define PMSU_STATUS_AND_MASK_IRQ_WAKEUP BIT(20)
53#define PMSU_STATUS_AND_MASK_FIQ_WAKEUP BIT(21)
54#define PMSU_STATUS_AND_MASK_DBG_WAKEUP BIT(22)
55#define PMSU_STATUS_AND_MASK_IRQ_MASK BIT(24)
56#define PMSU_STATUS_AND_MASK_FIQ_MASK BIT(25)
57
37#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124) 58#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124)
38 59
39/* PMSU fabric registers */ 60/* PMSU fabric registers */
40#define L2C_NFABRIC_PM_CTL 0x4 61#define L2C_NFABRIC_PM_CTL 0x4
41#define L2C_NFABRIC_PM_CTL_PWR_DOWN BIT(20) 62#define L2C_NFABRIC_PM_CTL_PWR_DOWN BIT(20)
42 63
64extern void ll_disable_coherency(void);
65extern void ll_enable_coherency(void);
66
43static struct of_device_id of_pmsu_table[] = { 67static struct of_device_id of_pmsu_table[] = {
44 { .compatible = "marvell,armada-370-pmsu", }, 68 { .compatible = "marvell,armada-370-pmsu", },
45 { .compatible = "marvell,armada-370-xp-pmsu", }, 69 { .compatible = "marvell,armada-370-xp-pmsu", },
@@ -110,4 +134,110 @@ static void armada_370_xp_pmsu_enable_l2_powerdown_onidle(void)
110 writel(reg, pmsu_mp_base + L2C_NFABRIC_PM_CTL); 134 writel(reg, pmsu_mp_base + L2C_NFABRIC_PM_CTL);
111} 135}
112 136
137static void armada_370_xp_cpu_resume(void)
138{
139 asm volatile("bl ll_add_cpu_to_smp_group\n\t"
140 "bl ll_enable_coherency\n\t"
141 "b cpu_resume\n\t");
142}
143
144/* No locking is needed because we only access per-CPU registers */
145void armada_370_xp_pmsu_idle_prepare(bool deepidle)
146{
147 unsigned int hw_cpu = cpu_logical_map(smp_processor_id());
148 u32 reg;
149
150 if (pmsu_mp_base == NULL)
151 return;
152
153 /*
154 * Adjust the PMSU configuration to wait for WFI signal, enable
155 * IRQ and FIQ as wakeup events, set wait for snoop queue empty
156 * indication and mask IRQ and FIQ from CPU
157 */
158 reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu));
159 reg |= PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT |
160 PMSU_STATUS_AND_MASK_IRQ_WAKEUP |
161 PMSU_STATUS_AND_MASK_FIQ_WAKEUP |
162 PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT |
163 PMSU_STATUS_AND_MASK_IRQ_MASK |
164 PMSU_STATUS_AND_MASK_FIQ_MASK;
165 writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu));
166
167 reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu));
168 /* ask HW to power down the L2 Cache if needed */
169 if (deepidle)
170 reg |= PMSU_CONTROL_AND_CONFIG_L2_PWDDN;
171
172 /* request power down */
173 reg |= PMSU_CONTROL_AND_CONFIG_PWDDN_REQ;
174 writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu));
175
176 /* Disable snoop disable by HW - SW is taking care of it */
177 reg = readl(pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu));
178 reg |= PMSU_CPU_POWER_DOWN_DIS_SNP_Q_SKIP;
179 writel(reg, pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu));
180}
181
182static noinline int do_armada_370_xp_cpu_suspend(unsigned long deepidle)
183{
184 armada_370_xp_pmsu_idle_prepare(deepidle);
185
186 v7_exit_coherency_flush(all);
187
188 ll_disable_coherency();
189
190 dsb();
191
192 wfi();
193
194 /* If we are here, wfi failed. As processors run out of
195 * coherency for some time, tlbs might be stale, so flush them
196 */
197 local_flush_tlb_all();
198
199 ll_enable_coherency();
200
201 /* Test the CR_C bit and set it if it was cleared */
202 asm volatile(
203 "mrc p15, 0, %0, c1, c0, 0 \n\t"
204 "tst %0, #(1 << 2) \n\t"
205 "orreq %0, %0, #(1 << 2) \n\t"
206 "mcreq p15, 0, %0, c1, c0, 0 \n\t"
207 "isb "
208 : : "r" (0));
209
210 pr_warn("Failed to suspend the system\n");
211
212 return 0;
213}
214
215static int armada_370_xp_cpu_suspend(unsigned long deepidle)
216{
217 return cpu_suspend(deepidle, do_armada_370_xp_cpu_suspend);
218}
219
220/* No locking is needed because we only access per-CPU registers */
221static noinline void armada_370_xp_pmsu_idle_restore(void)
222{
223 unsigned int hw_cpu = cpu_logical_map(smp_processor_id());
224 u32 reg;
225
226 if (pmsu_mp_base == NULL)
227 return;
228
229 /* cancel ask HW to power down the L2 Cache if possible */
230 reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu));
231 reg &= ~PMSU_CONTROL_AND_CONFIG_L2_PWDDN;
232 writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu));
233
234 /* cancel Enable wakeup events and mask interrupts */
235 reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu));
236 reg &= ~(PMSU_STATUS_AND_MASK_IRQ_WAKEUP | PMSU_STATUS_AND_MASK_FIQ_WAKEUP);
237 reg &= ~PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT;
238 reg &= ~PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT;
239 reg &= ~(PMSU_STATUS_AND_MASK_IRQ_MASK | PMSU_STATUS_AND_MASK_FIQ_MASK);
240 writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu));
241}
242
113early_initcall(armada_370_xp_pmsu_init); 243early_initcall(armada_370_xp_pmsu_init);