| From 0b0408745e7ff24757cbfd571d69026c0ddb803c Mon Sep 17 00:00:00 2001 |
| From: Alexandre Belloni <alexandre.belloni@free-electrons.com> |
| Date: Tue, 25 Oct 2016 11:37:59 +0200 |
| Subject: power: reset: at91-poweroff: timely shutdown LPDDR memories |
| |
| From: Alexandre Belloni <alexandre.belloni@free-electrons.com> |
| |
| 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 | 54 +++++++++++++++++++++++++++++++++++- |
| 1 file changed, 53 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/power/reset/at91-poweroff.c |
| +++ b/drivers/power/reset/at91-poweroff.c |
| @@ -14,9 +14,12 @@ |
| #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_ |
| 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(st |
| |
| 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(s |
| 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", }, |