Skip to content

Commit 063d30f

Browse files
alexandrebellonigregkh
authored andcommitted
power: reset: at91-poweroff: timely shutdown LPDDR memories
commit 0b0408745e7ff24757cbfd571d69026c0ddb803c upstream. LPDDR memories can only handle up to 400 uncontrolled power off. Ensure the proper power off sequence is used before shutting down the platform. Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> Signed-off-by: Sebastian Reichel <sre@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 42462d2 commit 063d30f

1 file changed

Lines changed: 53 additions & 1 deletion

File tree

drivers/power/reset/at91-poweroff.c

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
#include <linux/io.h>
1515
#include <linux/module.h>
1616
#include <linux/of.h>
17+
#include <linux/of_address.h>
1718
#include <linux/platform_device.h>
1819
#include <linux/printk.h>
1920

21+
#include <soc/at91/at91sam9_ddrsdr.h>
22+
2023
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
2124
#define AT91_SHDW_SHDW BIT(0) /* Shut Down command */
2225
#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */
@@ -50,6 +53,7 @@ static const char *shdwc_wakeup_modes[] = {
5053

5154
static void __iomem *at91_shdwc_base;
5255
static struct clk *sclk;
56+
static void __iomem *mpddrc_base;
5357

5458
static void __init at91_wakeup_status(void)
5559
{
@@ -73,6 +77,29 @@ static void at91_poweroff(void)
7377
writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
7478
}
7579

80+
static void at91_lpddr_poweroff(void)
81+
{
82+
asm volatile(
83+
/* Align to cache lines */
84+
".balign 32\n\t"
85+
86+
/* Ensure AT91_SHDW_CR is in the TLB by reading it */
87+
" ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
88+
89+
/* Power down SDRAM0 */
90+
" str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
91+
/* Shutdown CPU */
92+
" str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
93+
94+
" b .\n\t"
95+
:
96+
: "r" (mpddrc_base),
97+
"r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
98+
"r" (at91_shdwc_base),
99+
"r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
100+
: "r0");
101+
}
102+
76103
static int at91_poweroff_get_wakeup_mode(struct device_node *np)
77104
{
78105
const char *pm;
@@ -124,6 +151,8 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
124151
static int __init at91_poweroff_probe(struct platform_device *pdev)
125152
{
126153
struct resource *res;
154+
struct device_node *np;
155+
u32 ddr_type;
127156
int ret;
128157

129158
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -150,19 +179,42 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
150179

151180
pm_power_off = at91_poweroff;
152181

182+
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
183+
if (!np)
184+
return 0;
185+
186+
mpddrc_base = of_iomap(np, 0);
187+
of_node_put(np);
188+
189+
if (!mpddrc_base)
190+
return 0;
191+
192+
ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD;
193+
if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) ||
194+
(ddr_type == AT91_DDRSDRC_MD_LPDDR3))
195+
pm_power_off = at91_lpddr_poweroff;
196+
else
197+
iounmap(mpddrc_base);
198+
153199
return 0;
154200
}
155201

156202
static int __exit at91_poweroff_remove(struct platform_device *pdev)
157203
{
158-
if (pm_power_off == at91_poweroff)
204+
if (pm_power_off == at91_poweroff ||
205+
pm_power_off == at91_lpddr_poweroff)
159206
pm_power_off = NULL;
160207

161208
clk_disable_unprepare(sclk);
162209

163210
return 0;
164211
}
165212

213+
static const struct of_device_id at91_ramc_of_match[] = {
214+
{ .compatible = "atmel,sama5d3-ddramc", },
215+
{ /* sentinel */ }
216+
};
217+
166218
static const struct of_device_id at91_poweroff_of_match[] = {
167219
{ .compatible = "atmel,at91sam9260-shdwc", },
168220
{ .compatible = "atmel,at91sam9rl-shdwc", },

0 commit comments

Comments
 (0)