Skip to content

Commit 2efd53c

Browse files
committed
power: reset: at91-poweroff: switch to slow clock before shutdown
The SAMA5D2 NRST input signal is resynchronized with the SLCK clock and it can take up to 2 SLCK cycles (about 90us) for the internal reset to be effective. During this delay, the VDDCORE current consumption may still be high (application-dependent) with the VDDCORE regulator already OFF. Under such conditions, VDDCORE may operate below its operating range leading to potential register corruption. To prevent such situation, it is recommended to decrease significantly the power consumption of the device once the voltage regulator is turned-off. This can be achieved by operating the device at a much lower low frequency. To solve this switch the master clock to slock clock just before writing shutdown command to shutdown controller. Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com> Suggested-by: Patrice Vilchez <patrice.vilchez@microchip.com>
1 parent ece08a0 commit 2efd53c

2 files changed

Lines changed: 66 additions & 8 deletions

File tree

arch/arm/mach-at91/pm_suspend.S

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,14 @@ exit_suspend:
139139
ENDPROC(at91_pm_suspend_in_sram)
140140

141141
ENTRY(at91_backup_mode)
142+
/* Switch the master clock source to slow clock. */
143+
ldr pmc, .pmc_base
144+
ldr tmp1, [pmc, #AT91_PMC_MCKR]
145+
bic tmp1, tmp1, #AT91_PMC_CSS
146+
str tmp1, [pmc, #AT91_PMC_MCKR]
147+
148+
wait_mckrdy
149+
142150
/*BUMEN*/
143151
ldr r0, .sfr
144152
mov tmp1, #0x1

drivers/power/reset/at91-sama5d2_shdwc.c

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020

2121
#include <linux/clk.h>
22+
#include <linux/clk/at91_pmc.h>
2223
#include <linux/io.h>
2324
#include <linux/module.h>
2425
#include <linux/of.h>
@@ -70,6 +71,7 @@ struct shdwc_config {
7071
struct shdwc {
7172
const struct shdwc_config *cfg;
7273
void __iomem *at91_shdwc_base;
74+
void __iomem *pmc_base;
7375
};
7476

7577
/*
@@ -108,6 +110,12 @@ static void __init at91_wakeup_status(struct platform_device *pdev)
108110

109111
static void at91_poweroff(void)
110112
{
113+
/* Switch the master clock source to slow clock. */
114+
writel(readl(at91_shdwc->pmc_base + AT91_PMC_MCKR) & ~AT91_PMC_CSS,
115+
at91_shdwc->pmc_base + AT91_PMC_MCKR);
116+
while (!(readl(at91_shdwc->pmc_base + AT91_PMC_SR) & AT91_PMC_MCKRDY))
117+
;
118+
111119
writel(AT91_SHDW_KEY | AT91_SHDW_SHDW,
112120
at91_shdwc->at91_shdwc_base + AT91_SHDW_CR);
113121
}
@@ -123,6 +131,16 @@ static void at91_lpddr_poweroff(void)
123131

124132
/* Power down SDRAM0 */
125133
" str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
134+
135+
/* Switch the master clock source to slow clock. */
136+
" ldr r6, [%4, #" __stringify(AT91_PMC_MCKR) "]\n\t"
137+
" bic r6, r6, #" __stringify(AT91_PMC_CSS) "\n\t"
138+
" str r6, [%4, #" __stringify(AT91_PMC_MCKR) "]\n\t"
139+
/* Wait for clock switch. */
140+
"1: ldr r6, [%4, #" __stringify(AT91_PMC_SR) "]\n\t"
141+
" tst r6, #" __stringify(AT91_PMC_MCKRDY) "\n\t"
142+
" beq 1b\n\t"
143+
126144
/* Shutdown CPU */
127145
" str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
128146

@@ -131,7 +149,8 @@ static void at91_lpddr_poweroff(void)
131149
: "r" (mpddrc_base),
132150
"r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
133151
"r" (at91_shdwc->at91_shdwc_base),
134-
"r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
152+
"r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW),
153+
"r" (at91_shdwc->pmc_base)
135154
: "r6");
136155
}
137156

@@ -276,26 +295,53 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)
276295

277296
at91_shdwc_dt_configure(pdev);
278297

279-
pm_power_off = at91_poweroff;
298+
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-pmc");
299+
if (!np) {
300+
ret = -ENODEV;
301+
goto clk_disable;
302+
}
303+
304+
at91_shdwc->pmc_base = of_iomap(np, 0);
305+
of_node_put(np);
306+
307+
if (!at91_shdwc->pmc_base) {
308+
ret = -ENOMEM;
309+
goto clk_disable;
310+
}
280311

281312
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
282-
if (!np)
283-
return 0;
313+
if (!np) {
314+
ret = -ENODEV;
315+
goto unmap;
316+
}
284317

285318
mpddrc_base = of_iomap(np, 0);
286319
of_node_put(np);
287320

288-
if (!mpddrc_base)
289-
return 0;
321+
if (!mpddrc_base) {
322+
ret = -ENOMEM;
323+
goto unmap;
324+
}
325+
326+
pm_power_off = at91_poweroff;
290327

291328
ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD;
292329
if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) ||
293-
(ddr_type == AT91_DDRSDRC_MD_LPDDR3))
330+
(ddr_type == AT91_DDRSDRC_MD_LPDDR3)) {
294331
pm_power_off = at91_lpddr_poweroff;
295-
else
332+
} else {
296333
iounmap(mpddrc_base);
334+
mpddrc_base = NULL;
335+
}
297336

298337
return 0;
338+
339+
unmap:
340+
iounmap(at91_shdwc->pmc_base);
341+
clk_disable:
342+
clk_disable_unprepare(sclk);
343+
344+
return ret;
299345
}
300346

301347
static int __exit at91_shdwc_remove(struct platform_device *pdev)
@@ -310,6 +356,10 @@ static int __exit at91_shdwc_remove(struct platform_device *pdev)
310356
writel(0, shdw->at91_shdwc_base + AT91_SHDW_MR);
311357
writel(0, shdw->at91_shdwc_base + AT91_SHDW_WUIR);
312358

359+
if (mpddrc_base)
360+
iounmap(mpddrc_base);
361+
iounmap(shdw->pmc_base);
362+
313363
clk_disable_unprepare(sclk);
314364

315365
return 0;

0 commit comments

Comments
 (0)