* [PATCH v2 0/2] ARM: at91: properly handle LPDDR poweroff
@ 2016-10-19 11:44 Alexandre Belloni
2016-10-19 11:44 ` [PATCH v2 1/2] ARM: at91: define LPDDR types Alexandre Belloni
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Alexandre Belloni @ 2016-10-19 11:44 UTC (permalink / raw)
To: Sebastian Reichel, Dmitry Eremin-Solenikov
Cc: Jean-Jacques Hiblot, linux-pm, Nicolas Ferre, linux-kernel,
Alexandre Belloni, linux-arm-kernel
Hi,
This patch set improves LPDDR support on SoCs using the Atmel MPDDR controller.
LPDDR memories can only handle up to 400 uncontrolled power offs in their
life. The proper power off sequence has to be applied before shutting down the
SoC.
I'm not too happy with the code duplication but this is a design choice
that has been made before because both shutdown controllers are really
different apart from the shutdown itself.
I guess it is still better than slowly killing the LPDDR.
Changes in v2:
- Fix typos
- Add a comment for the dummy read access of AT91_SHDW_CR
- Properly set up pm_power_off in at91_poweroff_probe()
Alexandre Belloni (2):
ARM: at91: define LPDDR types
power/reset: at91-poweroff: timely shutdown LPDDR memories
drivers/power/reset/at91-poweroff.c | 54 +++++++++++++++++++++++++++++++-
drivers/power/reset/at91-sama5d2_shdwc.c | 49 ++++++++++++++++++++++++++++-
include/soc/at91/at91sam9_ddrsdr.h | 3 ++
3 files changed, 104 insertions(+), 2 deletions(-)
--
2.9.3
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v2 1/2] ARM: at91: define LPDDR types 2016-10-19 11:44 [PATCH v2 0/2] ARM: at91: properly handle LPDDR poweroff Alexandre Belloni @ 2016-10-19 11:44 ` Alexandre Belloni 2016-10-19 11:44 ` [PATCH v2 2/2] power/reset: at91-poweroff: timely shutdown LPDDR memories Alexandre Belloni 2016-10-19 14:07 ` [PATCH v2 0/2] ARM: at91: properly handle LPDDR poweroff Geert Uytterhoeven 2 siblings, 0 replies; 5+ messages in thread From: Alexandre Belloni @ 2016-10-19 11:44 UTC (permalink / raw) To: Sebastian Reichel, Dmitry Eremin-Solenikov Cc: Jean-Jacques Hiblot, linux-pm, Nicolas Ferre, linux-kernel, Alexandre Belloni, linux-arm-kernel The Atmel MPDDR controller support LPDDR2 and LPDDR3 memories, add their types. Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> --- include/soc/at91/at91sam9_ddrsdr.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/soc/at91/at91sam9_ddrsdr.h b/include/soc/at91/at91sam9_ddrsdr.h index dc10c52e0e91..393362bdb860 100644 --- a/include/soc/at91/at91sam9_ddrsdr.h +++ b/include/soc/at91/at91sam9_ddrsdr.h @@ -81,6 +81,7 @@ #define AT91_DDRSDRC_LPCB_POWER_DOWN 2 #define AT91_DDRSDRC_LPCB_DEEP_POWER_DOWN 3 #define AT91_DDRSDRC_CLKFR (1 << 2) /* Clock Frozen */ +#define AT91_DDRSDRC_LPDDR2_PWOFF (1 << 3) /* LPDDR Power Off */ #define AT91_DDRSDRC_PASR (7 << 4) /* Partial Array Self Refresh */ #define AT91_DDRSDRC_TCSR (3 << 8) /* Temperature Compensated Self Refresh */ #define AT91_DDRSDRC_DS (3 << 10) /* Drive Strength */ @@ -96,7 +97,9 @@ #define AT91_DDRSDRC_MD_SDR 0 #define AT91_DDRSDRC_MD_LOW_POWER_SDR 1 #define AT91_DDRSDRC_MD_LOW_POWER_DDR 3 +#define AT91_DDRSDRC_MD_LPDDR3 5 #define AT91_DDRSDRC_MD_DDR2 6 /* [SAM9 Only] */ +#define AT91_DDRSDRC_MD_LPDDR2 7 #define AT91_DDRSDRC_DBW (1 << 4) /* Data Bus Width */ #define AT91_DDRSDRC_DBW_32BITS (0 << 4) #define AT91_DDRSDRC_DBW_16BITS (1 << 4) -- 2.9.3 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] power/reset: at91-poweroff: timely shutdown LPDDR memories 2016-10-19 11:44 [PATCH v2 0/2] ARM: at91: properly handle LPDDR poweroff Alexandre Belloni 2016-10-19 11:44 ` [PATCH v2 1/2] ARM: at91: define LPDDR types Alexandre Belloni @ 2016-10-19 11:44 ` Alexandre Belloni 2016-10-19 14:07 ` [PATCH v2 0/2] ARM: at91: properly handle LPDDR poweroff Geert Uytterhoeven 2 siblings, 0 replies; 5+ messages in thread From: Alexandre Belloni @ 2016-10-19 11:44 UTC (permalink / raw) To: Sebastian Reichel, Dmitry Eremin-Solenikov Cc: Jean-Jacques Hiblot, linux-pm, Nicolas Ferre, linux-kernel, Alexandre Belloni, linux-arm-kernel 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> --- drivers/power/reset/at91-poweroff.c | 54 +++++++++++++++++++++++++++++++- drivers/power/reset/at91-sama5d2_shdwc.c | 49 ++++++++++++++++++++++++++++- 2 files changed, 101 insertions(+), 2 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 @@ #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", }, diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 8a5ac9706c9c..90b0b5a70ce5 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -22,9 +22,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 SLOW_CLOCK_FREQ 32768 #define AT91_SHDW_CR 0x00 /* Shut Down Control Register */ @@ -75,6 +78,7 @@ struct shdwc { */ static struct shdwc *at91_shdwc; static struct clk *sclk; +static void __iomem *mpddrc_base; static const unsigned long long sdwc_dbc_period[] = { 0, 3, 32, 512, 4096, 32768, @@ -108,6 +112,29 @@ static void at91_poweroff(void) at91_shdwc->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->at91_shdwc_base), + "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW) + : "r0"); +} + static u32 at91_shdwc_debouncer_value(struct platform_device *pdev, u32 in_period_us) { @@ -212,6 +239,8 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) { struct resource *res; const struct of_device_id *match; + struct device_node *np; + u32 ddr_type; int ret; if (!pdev->dev.of_node) @@ -249,6 +278,23 @@ static int __init at91_shdwc_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; } @@ -256,7 +302,8 @@ static int __exit at91_shdwc_remove(struct platform_device *pdev) { struct shdwc *shdw = platform_get_drvdata(pdev); - if (pm_power_off == at91_poweroff) + if (pm_power_off == at91_poweroff || + pm_power_off == at91_lpddr_poweroff) pm_power_off = NULL; /* Reset values to disable wake-up features */ -- 2.9.3 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 0/2] ARM: at91: properly handle LPDDR poweroff 2016-10-19 11:44 [PATCH v2 0/2] ARM: at91: properly handle LPDDR poweroff Alexandre Belloni 2016-10-19 11:44 ` [PATCH v2 1/2] ARM: at91: define LPDDR types Alexandre Belloni 2016-10-19 11:44 ` [PATCH v2 2/2] power/reset: at91-poweroff: timely shutdown LPDDR memories Alexandre Belloni @ 2016-10-19 14:07 ` Geert Uytterhoeven 2016-10-19 14:30 ` Nicolas Ferre 2 siblings, 1 reply; 5+ messages in thread From: Geert Uytterhoeven @ 2016-10-19 14:07 UTC (permalink / raw) To: Alexandre Belloni Cc: Jean-Jacques Hiblot, Linux PM list, Dmitry Eremin-Solenikov, Nicolas Ferre, Sebastian Reichel, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org On Wed, Oct 19, 2016 at 1:44 PM, Alexandre Belloni <alexandre.belloni@free-electrons.com> wrote: > LPDDR memories can only handle up to 400 uncontrolled power offs in their > life. The proper power off sequence has to be applied before shutting down the > SoC. Interesting. How many boards have been killed during kernelci.org operation? Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 0/2] ARM: at91: properly handle LPDDR poweroff 2016-10-19 14:07 ` [PATCH v2 0/2] ARM: at91: properly handle LPDDR poweroff Geert Uytterhoeven @ 2016-10-19 14:30 ` Nicolas Ferre 0 siblings, 0 replies; 5+ messages in thread From: Nicolas Ferre @ 2016-10-19 14:30 UTC (permalink / raw) To: Geert Uytterhoeven, Alexandre Belloni Cc: Sebastian Reichel, Dmitry Eremin-Solenikov, Jean-Jacques Hiblot, Linux PM list, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Le 19/10/2016 à 16:07, Geert Uytterhoeven a écrit : > On Wed, Oct 19, 2016 at 1:44 PM, Alexandre Belloni > <alexandre.belloni@free-electrons.com> wrote: >> LPDDR memories can only handle up to 400 uncontrolled power offs in their >> life. The proper power off sequence has to be applied before shutting down the >> SoC. > > Interesting. How many boards have been killed during kernelci.org > operation? The reason given above is usually the one that prevent manufacturers from using LPDDR on "evaluation" boards. So all Atmel boards use SDRAM/DDR2/DDR3 not the LP variants. Final products (and internal validation boards, obviously) though use these type of RAM so we must be prepared to handle them. Best regards, -- Nicolas Ferre ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2016-10-19 14:30 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2016-10-19 11:44 [PATCH v2 0/2] ARM: at91: properly handle LPDDR poweroff Alexandre Belloni 2016-10-19 11:44 ` [PATCH v2 1/2] ARM: at91: define LPDDR types Alexandre Belloni 2016-10-19 11:44 ` [PATCH v2 2/2] power/reset: at91-poweroff: timely shutdown LPDDR memories Alexandre Belloni 2016-10-19 14:07 ` [PATCH v2 0/2] ARM: at91: properly handle LPDDR poweroff Geert Uytterhoeven 2016-10-19 14:30 ` Nicolas Ferre
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox