diff options
-rw-r--r-- | arch/arm/mach-mvebu/pmsu.c | 132 |
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 | ||
31 | static void __iomem *pmsu_mp_base; | 34 | static 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 | ||
64 | extern void ll_disable_coherency(void); | ||
65 | extern void ll_enable_coherency(void); | ||
66 | |||
43 | static struct of_device_id of_pmsu_table[] = { | 67 | static 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 | ||
137 | static 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 */ | ||
145 | void 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 | |||
182 | static 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 | |||
215 | static 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 */ | ||
221 | static 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 | |||
113 | early_initcall(armada_370_xp_pmsu_init); | 243 | early_initcall(armada_370_xp_pmsu_init); |