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
5154static void __iomem * at91_shdwc_base ;
5255static struct clk * sclk ;
56+ static void __iomem * mpddrc_base ;
5357
5458static 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+
76103static 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)
124151static 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
156202static 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+
166218static const struct of_device_id at91_poweroff_of_match [] = {
167219 { .compatible = "atmel,at91sam9260-shdwc" , },
168220 { .compatible = "atmel,at91sam9rl-shdwc" , },
0 commit comments