diff options
author | Sebastian Reichel <sebastian.reichel@collabora.com> | 2018-09-16 06:37:11 -0400 |
---|---|---|
committer | Sebastian Reichel <sebastian.reichel@collabora.com> | 2018-09-16 06:37:11 -0400 |
commit | 2e04dd441a2ecc049505b2cbfabc355a48115b59 (patch) | |
tree | 7ed3e80c9cbee2dfd1e0a5e614380242af2179da /drivers/power | |
parent | b3e1b276a429e80824f9a1384eb656af3c1daf59 (diff) | |
parent | 9f1e44774be578fb92776add95f1fcaf8284d692 (diff) |
Merge tag 'psy-at91-poweroff-immutable-for-v4.20-signed' into psy-next
Immutable branch for mach-at91 and power-supply for v4.20
Immutable branch between mach-at91 and power-supply for driver
changes in at91-poweroff.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/reset/at91-sama5d2_shdwc.c | 119 |
1 files changed, 80 insertions, 39 deletions
diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 0206cce328b3..2b686c55b717 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c | |||
@@ -19,6 +19,7 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include <linux/clk.h> | 21 | #include <linux/clk.h> |
22 | #include <linux/clk/at91_pmc.h> | ||
22 | #include <linux/io.h> | 23 | #include <linux/io.h> |
23 | #include <linux/module.h> | 24 | #include <linux/module.h> |
24 | #include <linux/of.h> | 25 | #include <linux/of.h> |
@@ -69,7 +70,10 @@ struct shdwc_config { | |||
69 | 70 | ||
70 | struct shdwc { | 71 | struct shdwc { |
71 | const struct shdwc_config *cfg; | 72 | const struct shdwc_config *cfg; |
72 | void __iomem *at91_shdwc_base; | 73 | struct clk *sclk; |
74 | void __iomem *shdwc_base; | ||
75 | void __iomem *mpddrc_base; | ||
76 | void __iomem *pmc_base; | ||
73 | }; | 77 | }; |
74 | 78 | ||
75 | /* | 79 | /* |
@@ -77,8 +81,6 @@ struct shdwc { | |||
77 | * since pm_power_off itself is global. | 81 | * since pm_power_off itself is global. |
78 | */ | 82 | */ |
79 | static struct shdwc *at91_shdwc; | 83 | static struct shdwc *at91_shdwc; |
80 | static struct clk *sclk; | ||
81 | static void __iomem *mpddrc_base; | ||
82 | 84 | ||
83 | static const unsigned long long sdwc_dbc_period[] = { | 85 | static const unsigned long long sdwc_dbc_period[] = { |
84 | 0, 3, 32, 512, 4096, 32768, | 86 | 0, 3, 32, 512, 4096, 32768, |
@@ -90,7 +92,7 @@ static void __init at91_wakeup_status(struct platform_device *pdev) | |||
90 | u32 reg; | 92 | u32 reg; |
91 | char *reason = "unknown"; | 93 | char *reason = "unknown"; |
92 | 94 | ||
93 | reg = readl(shdw->at91_shdwc_base + AT91_SHDW_SR); | 95 | reg = readl(shdw->shdwc_base + AT91_SHDW_SR); |
94 | 96 | ||
95 | dev_dbg(&pdev->dev, "%s: status = %#x\n", __func__, reg); | 97 | dev_dbg(&pdev->dev, "%s: status = %#x\n", __func__, reg); |
96 | 98 | ||
@@ -108,12 +110,6 @@ static void __init at91_wakeup_status(struct platform_device *pdev) | |||
108 | 110 | ||
109 | static void at91_poweroff(void) | 111 | static void at91_poweroff(void) |
110 | { | 112 | { |
111 | writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, | ||
112 | at91_shdwc->at91_shdwc_base + AT91_SHDW_CR); | ||
113 | } | ||
114 | |||
115 | static void at91_lpddr_poweroff(void) | ||
116 | { | ||
117 | asm volatile( | 113 | asm volatile( |
118 | /* Align to cache lines */ | 114 | /* Align to cache lines */ |
119 | ".balign 32\n\t" | 115 | ".balign 32\n\t" |
@@ -122,16 +118,29 @@ static void at91_lpddr_poweroff(void) | |||
122 | " ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" | 118 | " ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" |
123 | 119 | ||
124 | /* Power down SDRAM0 */ | 120 | /* Power down SDRAM0 */ |
121 | " tst %0, #0\n\t" | ||
122 | " beq 1f\n\t" | ||
125 | " str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" | 123 | " str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" |
124 | |||
125 | /* Switch the master clock source to slow clock. */ | ||
126 | "1: ldr r6, [%4, #" __stringify(AT91_PMC_MCKR) "]\n\t" | ||
127 | " bic r6, r6, #" __stringify(AT91_PMC_CSS) "\n\t" | ||
128 | " str r6, [%4, #" __stringify(AT91_PMC_MCKR) "]\n\t" | ||
129 | /* Wait for clock switch. */ | ||
130 | "2: ldr r6, [%4, #" __stringify(AT91_PMC_SR) "]\n\t" | ||
131 | " tst r6, #" __stringify(AT91_PMC_MCKRDY) "\n\t" | ||
132 | " beq 2b\n\t" | ||
133 | |||
126 | /* Shutdown CPU */ | 134 | /* Shutdown CPU */ |
127 | " str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" | 135 | " str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t" |
128 | 136 | ||
129 | " b .\n\t" | 137 | " b .\n\t" |
130 | : | 138 | : |
131 | : "r" (mpddrc_base), | 139 | : "r" (at91_shdwc->mpddrc_base), |
132 | "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF), | 140 | "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF), |
133 | "r" (at91_shdwc->at91_shdwc_base), | 141 | "r" (at91_shdwc->shdwc_base), |
134 | "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW) | 142 | "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW), |
143 | "r" (at91_shdwc->pmc_base) | ||
135 | : "r6"); | 144 | : "r6"); |
136 | } | 145 | } |
137 | 146 | ||
@@ -213,10 +222,10 @@ static void at91_shdwc_dt_configure(struct platform_device *pdev) | |||
213 | mode |= SHDW_RTCWKEN(shdw->cfg); | 222 | mode |= SHDW_RTCWKEN(shdw->cfg); |
214 | 223 | ||
215 | dev_dbg(&pdev->dev, "%s: mode = %#x\n", __func__, mode); | 224 | dev_dbg(&pdev->dev, "%s: mode = %#x\n", __func__, mode); |
216 | writel(mode, shdw->at91_shdwc_base + AT91_SHDW_MR); | 225 | writel(mode, shdw->shdwc_base + AT91_SHDW_MR); |
217 | 226 | ||
218 | input = at91_shdwc_get_wakeup_input(pdev, np); | 227 | input = at91_shdwc_get_wakeup_input(pdev, np); |
219 | writel(input, shdw->at91_shdwc_base + AT91_SHDW_WUIR); | 228 | writel(input, shdw->shdwc_base + AT91_SHDW_WUIR); |
220 | } | 229 | } |
221 | 230 | ||
222 | static const struct shdwc_config sama5d2_shdwc_config = { | 231 | static const struct shdwc_config sama5d2_shdwc_config = { |
@@ -246,6 +255,9 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) | |||
246 | if (!pdev->dev.of_node) | 255 | if (!pdev->dev.of_node) |
247 | return -ENODEV; | 256 | return -ENODEV; |
248 | 257 | ||
258 | if (at91_shdwc) | ||
259 | return -EBUSY; | ||
260 | |||
249 | at91_shdwc = devm_kzalloc(&pdev->dev, sizeof(*at91_shdwc), GFP_KERNEL); | 261 | at91_shdwc = devm_kzalloc(&pdev->dev, sizeof(*at91_shdwc), GFP_KERNEL); |
250 | if (!at91_shdwc) | 262 | if (!at91_shdwc) |
251 | return -ENOMEM; | 263 | return -ENOMEM; |
@@ -253,20 +265,20 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) | |||
253 | platform_set_drvdata(pdev, at91_shdwc); | 265 | platform_set_drvdata(pdev, at91_shdwc); |
254 | 266 | ||
255 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 267 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
256 | at91_shdwc->at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res); | 268 | at91_shdwc->shdwc_base = devm_ioremap_resource(&pdev->dev, res); |
257 | if (IS_ERR(at91_shdwc->at91_shdwc_base)) { | 269 | if (IS_ERR(at91_shdwc->shdwc_base)) { |
258 | dev_err(&pdev->dev, "Could not map reset controller address\n"); | 270 | dev_err(&pdev->dev, "Could not map reset controller address\n"); |
259 | return PTR_ERR(at91_shdwc->at91_shdwc_base); | 271 | return PTR_ERR(at91_shdwc->shdwc_base); |
260 | } | 272 | } |
261 | 273 | ||
262 | match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node); | 274 | match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node); |
263 | at91_shdwc->cfg = match->data; | 275 | at91_shdwc->cfg = match->data; |
264 | 276 | ||
265 | sclk = devm_clk_get(&pdev->dev, NULL); | 277 | at91_shdwc->sclk = devm_clk_get(&pdev->dev, NULL); |
266 | if (IS_ERR(sclk)) | 278 | if (IS_ERR(at91_shdwc->sclk)) |
267 | return PTR_ERR(sclk); | 279 | return PTR_ERR(at91_shdwc->sclk); |
268 | 280 | ||
269 | ret = clk_prepare_enable(sclk); | 281 | ret = clk_prepare_enable(at91_shdwc->sclk); |
270 | if (ret) { | 282 | if (ret) { |
271 | dev_err(&pdev->dev, "Could not enable slow clock\n"); | 283 | dev_err(&pdev->dev, "Could not enable slow clock\n"); |
272 | return ret; | 284 | return ret; |
@@ -276,41 +288,70 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) | |||
276 | 288 | ||
277 | at91_shdwc_dt_configure(pdev); | 289 | at91_shdwc_dt_configure(pdev); |
278 | 290 | ||
279 | pm_power_off = at91_poweroff; | 291 | np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-pmc"); |
292 | if (!np) { | ||
293 | ret = -ENODEV; | ||
294 | goto clk_disable; | ||
295 | } | ||
296 | |||
297 | at91_shdwc->pmc_base = of_iomap(np, 0); | ||
298 | of_node_put(np); | ||
299 | |||
300 | if (!at91_shdwc->pmc_base) { | ||
301 | ret = -ENOMEM; | ||
302 | goto clk_disable; | ||
303 | } | ||
280 | 304 | ||
281 | np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc"); | 305 | np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc"); |
282 | if (!np) | 306 | if (!np) { |
283 | return 0; | 307 | ret = -ENODEV; |
308 | goto unmap; | ||
309 | } | ||
284 | 310 | ||
285 | mpddrc_base = of_iomap(np, 0); | 311 | at91_shdwc->mpddrc_base = of_iomap(np, 0); |
286 | of_node_put(np); | 312 | of_node_put(np); |
287 | 313 | ||
288 | if (!mpddrc_base) | 314 | if (!at91_shdwc->mpddrc_base) { |
289 | return 0; | 315 | ret = -ENOMEM; |
316 | goto unmap; | ||
317 | } | ||
290 | 318 | ||
291 | ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD; | 319 | pm_power_off = at91_poweroff; |
292 | if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) || | 320 | |
293 | (ddr_type == AT91_DDRSDRC_MD_LPDDR3)) | 321 | ddr_type = readl(at91_shdwc->mpddrc_base + AT91_DDRSDRC_MDR) & |
294 | pm_power_off = at91_lpddr_poweroff; | 322 | AT91_DDRSDRC_MD; |
295 | else | 323 | if (ddr_type != AT91_DDRSDRC_MD_LPDDR2 && |
296 | iounmap(mpddrc_base); | 324 | ddr_type != AT91_DDRSDRC_MD_LPDDR3) { |
325 | iounmap(at91_shdwc->mpddrc_base); | ||
326 | at91_shdwc->mpddrc_base = NULL; | ||
327 | } | ||
297 | 328 | ||
298 | return 0; | 329 | return 0; |
330 | |||
331 | unmap: | ||
332 | iounmap(at91_shdwc->pmc_base); | ||
333 | clk_disable: | ||
334 | clk_disable_unprepare(at91_shdwc->sclk); | ||
335 | |||
336 | return ret; | ||
299 | } | 337 | } |
300 | 338 | ||
301 | static int __exit at91_shdwc_remove(struct platform_device *pdev) | 339 | static int __exit at91_shdwc_remove(struct platform_device *pdev) |
302 | { | 340 | { |
303 | struct shdwc *shdw = platform_get_drvdata(pdev); | 341 | struct shdwc *shdw = platform_get_drvdata(pdev); |
304 | 342 | ||
305 | if (pm_power_off == at91_poweroff || | 343 | if (pm_power_off == at91_poweroff) |
306 | pm_power_off == at91_lpddr_poweroff) | ||
307 | pm_power_off = NULL; | 344 | pm_power_off = NULL; |
308 | 345 | ||
309 | /* Reset values to disable wake-up features */ | 346 | /* Reset values to disable wake-up features */ |
310 | writel(0, shdw->at91_shdwc_base + AT91_SHDW_MR); | 347 | writel(0, shdw->shdwc_base + AT91_SHDW_MR); |
311 | writel(0, shdw->at91_shdwc_base + AT91_SHDW_WUIR); | 348 | writel(0, shdw->shdwc_base + AT91_SHDW_WUIR); |
349 | |||
350 | if (shdw->mpddrc_base) | ||
351 | iounmap(shdw->mpddrc_base); | ||
352 | iounmap(shdw->pmc_base); | ||
312 | 353 | ||
313 | clk_disable_unprepare(sclk); | 354 | clk_disable_unprepare(shdw->sclk); |
314 | 355 | ||
315 | return 0; | 356 | return 0; |
316 | } | 357 | } |