[linux-yocto] [PATCH 19/39] arm/axxia: improved robustness for DDR retention
Charlie Paul
cpaul.windriver at gmail.com
Fri Apr 11 14:13:20 PDT 2014
From: Gary McGee <gary.mcgee at lsi.com>
- add new ncp_ddr_shutdown() function using private i/o functions.
This eliminates unwanted code that may be introduced by use of
the standard i/o macros/functions.
- do explicit memory copy to preload ncp_ddr_shutdown() into L2 cache
- initialize ELM sysmem capture to provide post-mortem debug capability
- fix VP engine quiesce to account for engines with different CMSI
adaptation layers (CAAL vs. CNAL).
Signed-off-by: Gary McGee <gary.mcgee at lsi.com>
---
arch/arm/mach-axxia/Makefile | 2 +-
arch/arm/mach-axxia/ddr_retention.c | 94 ++++------
arch/arm/mach-axxia/ddr_shutdown.c | 332 +++++++++++++++++++++++++++++++++++
3 files changed, 365 insertions(+), 63 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 7dbad2a..8e7dec4 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..7dab78f 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,53 +228,24 @@ 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)
@@ -283,6 +253,8 @@ initiate_retention_reset(void)
unsigned long ctl_244 = 0;
unsigned long value;
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..33b889a
--- /dev/null
+++ b/arch/arm/mach-axxia/ddr_shutdown.c
@@ -0,0 +1,332 @@
+
+#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");
+}
--
1.7.9.5
More information about the linux-yocto
mailing list