[linux-yocto] [PATCH 31/57] arm/axxia: improved robustness for DDR retention
Bruce Ashfield
bruce.ashfield at windriver.com
Tue Mar 18 14:38:05 PDT 2014
On 14-03-18 12:56 AM, Charlie Paul wrote:
> From: Gary McGee <gary.mcgee at lsi.com>
Again, we are missing the "what we changed" part of the commit log .. and
why it works!
Bruce
>
> Signed-off-by: Gary McGee <gary.mcgee at lsi.com>
> ---
> arch/arm/mach-axxia/Makefile | 2 +-
> arch/arm/mach-axxia/ddr_retention.c | 96 ++++------
> arch/arm/mach-axxia/ddr_shutdown.c | 331 +++++++++++++++++++++++++++++++++++
> 3 files changed, 365 insertions(+), 64 deletions(-)
> create mode 100644 arch/arm/mach-axxia/ddr_shutdown.c
>
> diff --git a/arch/arm/mach-axxia/Makefile b/arch/arm/mach-axxia/Makefile
> index 4d41b15..b4b8dd2 100644
> --- a/arch/arm/mach-axxia/Makefile
> +++ b/arch/arm/mach-axxia/Makefile
> @@ -7,7 +7,7 @@ obj-y += io.o
> obj-y += ncr.o
> obj-y += timers.o
> obj-y += pci.o
> -obj-y += ddr_retention.o
> +obj-y += ddr_retention.o ddr_shutdown.o
> obj-$(CONFIG_I2C) += i2c.o
> obj-$(CONFIG_SMP) += platsmp.o headsmp.o
> obj-$(CONFIG_ARCH_AXXIA_GIC) += axxia-gic.o
> diff --git a/arch/arm/mach-axxia/ddr_retention.c b/arch/arm/mach-axxia/ddr_retention.c
> index bcedf33..35dbfcb 100644
> --- a/arch/arm/mach-axxia/ddr_retention.c
> +++ b/arch/arm/mach-axxia/ddr_retention.c
> @@ -36,7 +36,6 @@
> static void __iomem *nca;
> static void __iomem *apb;
> static void __iomem *dickens;
> -static void __iomem *femac;
> static int ddr_retention_enabled;
>
> enum {
> @@ -229,60 +228,33 @@ static inline void cpu_disable_l2_prefetch(void)
> }
>
> static inline void
> -ncp_ddr_shutdown(unsigned long ctl_244)
> +reset_elm_trace(void)
> {
> - unsigned long value;
> -
> - cpu_disable_l2_prefetch();
> -
> - /*
> - * put SDRAM in self-refresh mode
> - */
> - ncr_write(NCP_REGION_ID(34, 0), 0x3d0, 4, &ctl_244);
> - ncr_write(NCP_REGION_ID(15, 0), 0x3d0, 4, &ctl_244);
> -
> - /* check interrupt status for completion */
> - /* CDR1 - word offset 0x104 (byte offset 0x410) */
> - do {
> - ncr_read(NCP_REGION_ID(34, 0), 0x410, 4, &value);
> - } while ((value & 0x0200) == 0);
> + /* reset and disable ELM trace */
> + ncr_register_write(htonl(0x000fff04), (unsigned *) (apb + 0x68000));
> + ncr_register_write(htonl(0x000fff04), (unsigned *) (apb + 0x78000));
>
> - do {
> - ncr_read(NCP_REGION_ID(15, 0), 0x410, 4, &value);
> - } while ((value & 0x0200) == 0);
> -
> - /*
> - Indicate DDR Retention Reset
> - */
> -
> - /* set bit 0 of persist_scratch */
> - writel_relaxed(0x00000001, apb + 0x300dc);
> + /* reset ELM statistics */
> + ncr_register_write(htonl(0x00001), (unsigned *) (apb + 0x60230));
> + ncr_register_write(htonl(0x00001), (unsigned *) (apb + 0x70230));
>
> - /*
> - * Issue Chip Reset
> - */
> + /* enable ELM trace */
> + ncr_register_write(htonl(0x000fff01), (unsigned *) (apb + 0x68000));
> + ncr_register_write(htonl(0x000fff01), (unsigned *) (apb + 0x78000));
> +}
>
> - /* Intrnl Boot, 0xffff0000 Target */
> - writel_relaxed(0x00000040, apb + 0x31004);
> - /* Set ResetReadDone */
> - writel_relaxed(0x80000000, apb + 0x3180c);
> - /* Chip Reset */
> - writel_relaxed(0x00080802, apb + 0x31008);
>
> - wfi();
> - while (1)
> - ;
> - __asm__ __volatile__("nop\n\t");
> +extern void ncp_ddr_shutdown(void *, void *, unsigned long );
>
> - return;
> -}
>
> void
> initiate_retention_reset(void)
> {
> unsigned long ctl_244 = 0;
> unsigned long value;
> - unsigned cpu_id;
> + unsigned cpu_id ;
> + volatile long tmp;
> + volatile long *ptmp;
>
> if (0 == ddr_retention_enabled) {
> pr_info("DDR Retention Reset is Not Enabled\n");
> @@ -292,19 +264,17 @@ initiate_retention_reset(void)
> if (NULL == nca || NULL == apb || NULL == dickens)
> BUG();
>
> - /* kill the femac */
> - writel(0x80000000, (femac+0x2000));
> - value = readl(femac + 0x2000);
> -
> preempt_disable();
> cpu_id = smp_processor_id();
> - udelay(1000);
> +
> /* send stop message to other CPUs */
> local_irq_disable();
> + local_fiq_disable();
> asm volatile ("dsb" : : : "memory");
> asm volatile ("dmb" : : : "memory");
> system_state = SYSTEM_RESTART;
> smp_send_stop();
> + udelay(1000);
>
> flush_cache_all();
> flush_l3();
> @@ -314,11 +284,6 @@ initiate_retention_reset(void)
> quiesce_vp_engine(AXXIA_ENGINE_CNAL);
>
>
> - /* disable sysmem interrupts */
> - value = 0;
> - ncr_write(NCP_REGION_ID(34, 0), 0x414, 4, &value);
> - ncr_write(NCP_REGION_ID(15, 0), 0x414, 4, &value);
> -
> /* unlock reset register for later */
> writel(0x000000ab, apb + 0x31000); /* Access Key */
>
> @@ -327,21 +292,27 @@ initiate_retention_reset(void)
> ncr_read(NCP_REGION_ID(34, 0), 0x3d0, 4, &ctl_244);
> ctl_244 |= 0x000a0000;
>
> -
> - /* put secondary CPUs into reset */
> + /* belts & braces: put secondary CPUs into reset */
> value = ~(1 << cpu_id);
> value &= 0xffff;
> ncr_register_write(htonl(value), (unsigned *) (apb + 0x31030));
>
> - /*
> - * issue instruction barrier
> - * this should cause the next few instructions to be fetched
> - * into cache
> - */
> - asm volatile ("dsb" : : : "memory");
> + /* load entire ddr_shutdown function into L2 cache */
> + ptmp = (long *) ncp_ddr_shutdown;
> + do {
> + tmp += *ptmp++;
> + } while (ptmp < (long*) (ncp_ddr_shutdown + 0x1000));
> +
> asm volatile ("isb" : : : "memory");
>
> - ncp_ddr_shutdown(ctl_244);
> + /* disable L2 prefetching */
> + cpu_disable_l2_prefetch();
> +
> + /* reset ELM DDR access trace buffer */
> + reset_elm_trace();
> +
> + /* call cache resident ddr shutdown function */
> + ncp_ddr_shutdown(nca, apb, ctl_244);
>
> return;
> }
> @@ -376,7 +347,6 @@ axxia_ddr_retention_init(void)
> apb = ioremap(0x2010000000, 0x80000);
> nca = ioremap(0x002020100000ULL, 0x20000);
> dickens = ioremap(0x2000000000, 0x1000000);
> - femac = ioremap(0x2010120000, 0x10000);
> ddr_retention_enabled = 1;
> pr_info("DDR Retention Reset Initialized\n");
> }
> diff --git a/arch/arm/mach-axxia/ddr_shutdown.c b/arch/arm/mach-axxia/ddr_shutdown.c
> new file mode 100644
> index 0000000..6c6c3bb
> --- /dev/null
> +++ b/arch/arm/mach-axxia/ddr_shutdown.c
> @@ -0,0 +1,331 @@
> +
> +#include <asm/io.h>
> +
> +/*
> + * private copies of the ioread/write macros
> + * These are defined with a different barrier
> + * to avoid the outer_sync() call that's part
> + * of the normal barrier.
> + */
> +#define pvt_ioread32be(p) ({ unsigned int __v = be32_to_cpu((__force __be32)__raw_readl(p)); dsb(); __v; })
> +#define pvt_iowrite32be(v,p) ({ dsb(); __raw_writel((__force __u32)cpu_to_be32(v), p); })
> +
> +#define pvt_ioread32(p) ({ unsigned int __v = (__raw_readl(p)); dsb(); __v; })
> +#define pvt_iowrite32(v,p) ({ dsb(); __raw_writel((__force __u32)(v), p); })
> +
> +/* #define DDR_SHUTDOWN_DEBUG */
> +#ifdef DDR_SHUTDOWN_DEBUG
> +#define dbg_write(v, p) pvt_iowrite32be(v,p)
> +#else
> +#define dbg_write(v, p)
> +#endif
> +
> +/*
> + * Wait For Completion timeout
> + * how many loops to wait for the config ring access to complete
> + */
> +#define WFC_TIMEOUT (400000)
> +
> +/*
> + * DDR status timeout
> + * how many times we read the DDR status waiting for self refresh complete
> + */
> +#define DDR_TIMEOUT (1000)
> +
> +void ncp_ddr_shutdown(void *nca, void *apb, unsigned long ctl_244)
> +{
> + unsigned long value;
> + int two_elms = 0;
> + int wfc_loop = 0;
> + int ddr_loop = 0;
> +
> + /* determine if we are in one or two ELM/SMEM mode */
> + value = pvt_ioread32(apb + 0x60004);
> + two_elms = (value & 0x00000200);
> +
> + /*
> + * Issue command to put SMEM0 into self-refresh mode
> + *
> + * ncpWrite 0x22.0.0x3d0
> + */
> + dbg_write(0xaaaa0001, (unsigned *)(nca + 0x1200));
> +
> + /* write register value into CDAR[0] */
> + pvt_iowrite32be(ctl_244, (unsigned *)(nca + 0x1000));
> + /* CDR2 - Node.target */
> + pvt_iowrite32be(0x00002200, (unsigned *)(nca + 0xf8));
> + /* CDR1 - word offset 0xf4 (byte offset 0x3d0) */
> + pvt_iowrite32be(0x000000f4, (unsigned *)(nca + 0xf4));
> + /* CDR0 - write command */
> + pvt_iowrite32be(0x80050003, (unsigned *)(nca + 0xf0));
> + wfc_loop = 0;
> + do {
> + if (wfc_loop++ > WFC_TIMEOUT) {
> + dbg_write(value, (unsigned *)(nca + 0x11fc));
> + dbg_write(0xffff0001, (unsigned *)(nca + 0x1200));
> + goto do_reset;
> + }
> + dbg_write(wfc_loop, (unsigned *)(nca + 0x11f8));
> + value = pvt_ioread32be((unsigned *)
> + (nca + 0xf0));
> + } while ((0x80000000UL & value));
> + dbg_write(0xaaaa0002, (unsigned *)(nca + 0x1200));
> +
> + if (two_elms) {
> + /*
> + * Issue command to put SMEM1 into self-refresh mode
> + *
> + * ncpWrite 0x0f.0.0x3d0
> + */
> + /* CDR2 - Node.target */
> + pvt_iowrite32be(0x00000f00, (unsigned *)(nca + 0xf8));
> + /* CDR0 - write command */
> + pvt_iowrite32be(0x80050003, (unsigned *)(nca + 0xf0));
> + wfc_loop = 0;
> + do {
> + if (wfc_loop++ > WFC_TIMEOUT) {
> + dbg_write(value, (unsigned *)(nca + 0x11fc));
> + dbg_write(0xffff0002,
> + (unsigned *)(nca + 0x1200));
> + goto do_reset;
> + }
> + value = pvt_ioread32be((unsigned *)
> + (nca + 0xf0));
> + } while ((0x80000000UL & value));
> + }
> +
> + dbg_write(0xaaaa0003, (unsigned *)(nca + 0x1200));
> +
> + /*
> + * Poll for SMEM0 refresh-mode command completion
> + */
> + /* CDR1 - word offset 0x104 (byte offset 0x410) */
> + pvt_iowrite32be(0x00000104, (unsigned *)(nca + 0xf4));
> + /* CDR2 - Node.target */
> + pvt_iowrite32be(0x00002200, (unsigned *)(nca + 0xf8));
> + ddr_loop = 0;
> + do {
> + if (ddr_loop++ > DDR_TIMEOUT) {
> + dbg_write(value, (unsigned *)(nca + 0x11fc));
> + dbg_write(0xffff0003, (unsigned *)(nca + 0x1200));
> + goto do_reset;
> + }
> + pvt_iowrite32be(wfc_loop, (unsigned *)
> + (nca + 0x11f0));
> +
> + /* issue config ring read */
> + pvt_iowrite32be(0x80040003, (unsigned *)
> + (nca + 0xf0));
> + wfc_loop = 0;
> + do {
> + if (wfc_loop++ > WFC_TIMEOUT) {
> + dbg_write(value, (unsigned *)(nca + 0x11fc));
> + dbg_write(0xffff0004,
> + (unsigned *)(nca + 0x1200));
> + goto do_reset;
> + }
> + value = pvt_ioread32be((unsigned *)
> + (nca + 0xf0));
> + } while ((0x80000000UL & value));
> +
> + value = pvt_ioread32be((unsigned *)
> + (nca + 0x1000));
> +
> + } while ((value & 0x0200) == 0);
> + dbg_write(0xaaaa0004, (unsigned *)(nca + 0x1200));
> +
> + if (two_elms) {
> + /*
> + * Poll for SMEM1 refresh-mode command completion
> + */
> + /* CDR2 - Node.target */
> + pvt_iowrite32be(0x00000f00, (unsigned *)(nca + 0xf8));
> + ddr_loop = 0;
> + do {
> + if (ddr_loop++ > DDR_TIMEOUT) {
> + dbg_write(value, (unsigned *)(nca + 0x11fc));
> + dbg_write(0xffff0005,
> + (unsigned *)(nca + 0x1200));
> + goto do_reset;
> + }
> +
> + /* issue config ring read */
> + pvt_iowrite32be(0x80040003, (unsigned *)(nca + 0xf0));
> + wfc_loop = 0;
> + do {
> + if (wfc_loop++ > WFC_TIMEOUT) {
> + dbg_write(value,
> + (unsigned *)(nca + 0x11fc));
> + dbg_write(0xffff0006,
> + (unsigned *)(nca + 0x1200));
> + goto do_reset;
> + }
> + value =
> + pvt_ioread32be((unsigned *)(nca + 0xf0));
> + } while ((0x80000000UL & value));
> +
> + value = pvt_ioread32be((unsigned *)
> + (nca + 0x1000));
> + wfc_loop++;
> + } while ((value & 0x0200) == 0);
> + }
> +
> + dbg_write(0xaaaa0005, (unsigned *)(nca + 0x1200));
> +
> + /*
> + * Tell U-Boot to do a DDR retention-reset
> + * (i.e. set bit 0 of persist_scratch register)
> + */
> + pvt_iowrite32(0x00000001, apb + 0x300dc);
> +
> + dbg_write(0xaaaa0006, (unsigned *)(nca + 0x1200));
> + do_reset:
> + /*
> + * Issue Chip reset
> + */
> + /* Intrnl Boot, 0xffff0000 Target */
> + pvt_iowrite32(0x00000040, apb + 0x31004);
> + /* Set ResetReadDone */
> + pvt_iowrite32(0x80000000, apb + 0x3180c);
> + /* Chip Reset */
> + pvt_iowrite32(0x00080802, apb + 0x31008);
> +
> + while(1);
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> +
> + return;
> +}
> +
> +void ncp_ddr_shutdown_dummy(void)
> +{
> + wfi();
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> + __asm__ __volatile__(" nop \n\t");
> +}
>
More information about the linux-yocto
mailing list