aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);