diff options
Diffstat (limited to 'drivers/power/reset/at91-poweroff.c')
-rw-r--r-- | drivers/power/reset/at91-poweroff.c | 54 |
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 | ||
51 | static void __iomem *at91_shdwc_base; | 54 | static void __iomem *at91_shdwc_base; |
52 | static struct clk *sclk; | 55 | static struct clk *sclk; |
56 | static void __iomem *mpddrc_base; | ||
53 | 57 | ||
54 | static void __init at91_wakeup_status(void) | 58 | static 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 | ||
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 | |||
76 | static int at91_poweroff_get_wakeup_mode(struct device_node *np) | 103 | static 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) | |||
124 | static int __init at91_poweroff_probe(struct platform_device *pdev) | 151 | static 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 | ||
156 | static int __exit at91_poweroff_remove(struct platform_device *pdev) | 202 | static 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 | ||
213 | static const struct of_device_id at91_ramc_of_match[] = { | ||
214 | { .compatible = "atmel,sama5d3-ddramc", }, | ||
215 | { /* sentinel */ } | ||
216 | }; | ||
217 | |||
166 | static const struct of_device_id at91_poweroff_of_match[] = { | 218 | static 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", }, |