aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/reset/at91-poweroff.c')
-rw-r--r--drivers/power/reset/at91-poweroff.c54
1 files changed, 53 insertions, 1 deletions
diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c
index e9e24df35f26..2579f025b90b 100644
--- a/drivers/power/reset/at91-poweroff.c
+++ b/drivers/power/reset/at91-poweroff.c
@@ -14,9 +14,12 @@
14#include <linux/io.h> 14#include <linux/io.h>
15#include <linux/module.h> 15#include <linux/module.h>
16#include <linux/of.h> 16#include <linux/of.h>
17#include <linux/of_address.h>
17#include <linux/platform_device.h> 18#include <linux/platform_device.h>
18#include <linux/printk.h> 19#include <linux/printk.h>
19 20
21#include <soc/at91/at91sam9_ddrsdr.h>
22
20#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */ 23#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
21#define AT91_SHDW_SHDW BIT(0) /* Shut Down command */ 24#define AT91_SHDW_SHDW BIT(0) /* Shut Down command */
22#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */ 25#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */
@@ -50,6 +53,7 @@ static const char *shdwc_wakeup_modes[] = {
50 53
51static void __iomem *at91_shdwc_base; 54static void __iomem *at91_shdwc_base;
52static struct clk *sclk; 55static struct clk *sclk;
56static void __iomem *mpddrc_base;
53 57
54static void __init at91_wakeup_status(void) 58static void __init at91_wakeup_status(void)
55{ 59{
@@ -73,6 +77,29 @@ static void at91_poweroff(void)
73 writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR); 77 writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
74} 78}
75 79
80static 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
76static int at91_poweroff_get_wakeup_mode(struct device_node *np) 103static int at91_poweroff_get_wakeup_mode(struct device_node *np)
77{ 104{
78 const char *pm; 105 const char *pm;
@@ -124,6 +151,8 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
124static int __init at91_poweroff_probe(struct platform_device *pdev) 151static int __init at91_poweroff_probe(struct platform_device *pdev)
125{ 152{
126 struct resource *res; 153 struct resource *res;
154 struct device_node *np;
155 u32 ddr_type;
127 int ret; 156 int ret;
128 157
129 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 158 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -150,12 +179,30 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
150 179
151 pm_power_off = at91_poweroff; 180 pm_power_off = at91_poweroff;
152 181
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
153 return 0; 199 return 0;
154} 200}
155 201
156static int __exit at91_poweroff_remove(struct platform_device *pdev) 202static int __exit at91_poweroff_remove(struct platform_device *pdev)
157{ 203{
158 if (pm_power_off == at91_poweroff) 204 if (pm_power_off == at91_poweroff ||
205 pm_power_off == at91_lpddr_poweroff)
159 pm_power_off = NULL; 206 pm_power_off = NULL;
160 207
161 clk_disable_unprepare(sclk); 208 clk_disable_unprepare(sclk);
@@ -163,6 +210,11 @@ static int __exit at91_poweroff_remove(struct platform_device *pdev)
163 return 0; 210 return 0;
164} 211}
165 212
213static const struct of_device_id at91_ramc_of_match[] = {
214 { .compatible = "atmel,sama5d3-ddramc", },
215 { /* sentinel */ }
216};
217
166static const struct of_device_id at91_poweroff_of_match[] = { 218static const struct of_device_id at91_poweroff_of_match[] = {
167 { .compatible = "atmel,at91sam9260-shdwc", }, 219 { .compatible = "atmel,at91sam9260-shdwc", },
168 { .compatible = "atmel,at91sam9rl-shdwc", }, 220 { .compatible = "atmel,at91sam9rl-shdwc", },