OSDN Git Service

power: reset: at91-poweroff: timely shutdown LPDDR memories
authorAlexandre Belloni <alexandre.belloni@free-electrons.com>
Tue, 25 Oct 2016 09:37:59 +0000 (11:37 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 8 Apr 2017 07:53:32 +0000 (09:53 +0200)
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>
drivers/power/reset/at91-poweroff.c

index e9e24df..2579f02 100644 (file)
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/platform_device.h>
 #include <linux/printk.h>
 
+#include <soc/at91/at91sam9_ddrsdr.h>
+
 #define AT91_SHDW_CR   0x00            /* Shut Down Control Register */
 #define AT91_SHDW_SHDW         BIT(0)                  /* Shut Down command */
 #define AT91_SHDW_KEY          (0xa5 << 24)            /* KEY Password */
@@ -50,6 +53,7 @@ static const char *shdwc_wakeup_modes[] = {
 
 static void __iomem *at91_shdwc_base;
 static struct clk *sclk;
+static void __iomem *mpddrc_base;
 
 static void __init at91_wakeup_status(void)
 {
@@ -73,6 +77,29 @@ static void at91_poweroff(void)
        writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
 }
 
+static void at91_lpddr_poweroff(void)
+{
+       asm volatile(
+               /* Align to cache lines */
+               ".balign 32\n\t"
+
+               /* Ensure AT91_SHDW_CR is in the TLB by reading it */
+               "       ldr     r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
+
+               /* Power down SDRAM0 */
+               "       str     %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
+               /* Shutdown CPU */
+               "       str     %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
+
+               "       b       .\n\t"
+               :
+               : "r" (mpddrc_base),
+                 "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
+                 "r" (at91_shdwc_base),
+                 "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
+               : "r0");
+}
+
 static int at91_poweroff_get_wakeup_mode(struct device_node *np)
 {
        const char *pm;
@@ -124,6 +151,8 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
 static int __init at91_poweroff_probe(struct platform_device *pdev)
 {
        struct resource *res;
+       struct device_node *np;
+       u32 ddr_type;
        int ret;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -150,12 +179,30 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
 
        pm_power_off = at91_poweroff;
 
+       np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
+       if (!np)
+               return 0;
+
+       mpddrc_base = of_iomap(np, 0);
+       of_node_put(np);
+
+       if (!mpddrc_base)
+               return 0;
+
+       ddr_type = readl(mpddrc_base + AT91_DDRSDRC_MDR) & AT91_DDRSDRC_MD;
+       if ((ddr_type == AT91_DDRSDRC_MD_LPDDR2) ||
+           (ddr_type == AT91_DDRSDRC_MD_LPDDR3))
+               pm_power_off = at91_lpddr_poweroff;
+       else
+               iounmap(mpddrc_base);
+
        return 0;
 }
 
 static int __exit at91_poweroff_remove(struct platform_device *pdev)
 {
-       if (pm_power_off == at91_poweroff)
+       if (pm_power_off == at91_poweroff ||
+           pm_power_off == at91_lpddr_poweroff)
                pm_power_off = NULL;
 
        clk_disable_unprepare(sclk);
@@ -163,6 +210,11 @@ static int __exit at91_poweroff_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id at91_ramc_of_match[] = {
+       { .compatible = "atmel,sama5d3-ddramc", },
+       { /* sentinel */ }
+};
+
 static const struct of_device_id at91_poweroff_of_match[] = {
        { .compatible = "atmel,at91sam9260-shdwc", },
        { .compatible = "atmel,at91sam9rl-shdwc", },