From: Gary McGee <gary.mc...@lsi.com>

Signed-off-by: Gary McGee <gary.mc...@lsi.com>
---
 arch/arm/mach-axxia/Makefile        |   1 +
 arch/arm/mach-axxia/axxia.c         |   4 +
 arch/arm/mach-axxia/ddr_retention.c | 282 ++++++++++++++++++++++++++++++++++++
 3 files changed, 287 insertions(+)
 create mode 100644 arch/arm/mach-axxia/ddr_retention.c

diff --git a/arch/arm/mach-axxia/Makefile b/arch/arm/mach-axxia/Makefile
index ac05192..0459a1f 100644
--- a/arch/arm/mach-axxia/Makefile
+++ b/arch/arm/mach-axxia/Makefile
@@ -7,6 +7,7 @@ obj-y                                   += clock.o
 obj-y                                   += io.o
 obj-y                                  += timers.o
 obj-y                                  += pci.o
+obj-y                                  += ddr_retention.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/axxia.c b/arch/arm/mach-axxia/axxia.c
index a4676d3..9641d0c 100644
--- a/arch/arm/mach-axxia/axxia.c
+++ b/arch/arm/mach-axxia/axxia.c
@@ -52,6 +52,8 @@
 #include "pci.h"
 #include "i2c.h"
 
+extern void axxia_ddr_retention_init(void);
+
 static const char *axxia_dt_match[] __initconst = {
        "lsi,axm5516",          /* AXM5516 */
        NULL
@@ -225,6 +227,8 @@ void __init axxia_dt_init(void)
        }
 
        axxia_pcie_init();
+
+    axxia_ddr_retention_init();
 }
 
 static void axxia_restart(char str, const char *cmd)
diff --git a/arch/arm/mach-axxia/ddr_retention.c 
b/arch/arm/mach-axxia/ddr_retention.c
new file mode 100644
index 0000000..988d361
--- /dev/null
+++ b/arch/arm/mach-axxia/ddr_retention.c
@@ -0,0 +1,282 @@
+/*
+ *  Copyright (C) 2013 LSI Corporation
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+
+#ifndef CONFIG_ARCH_AXXIA_SIM
+
+#include <linux/cpu.h>
+#include <linux/reboot.h>
+#include <linux/syscore_ops.h>
+
+#include <linux/proc_fs.h>
+
+#include <asm/io.h>
+#include <asm/cacheflush.h>
+#include <../../../drivers/misc/lsi-ncr.h>
+
+extern void flush_l3(void);
+static void __iomem *nca_address;
+static void __iomem *apb_base;
+
+static inline void kill_time(int cnt)
+{
+       while (cnt--)
+               ;
+}
+
+unsigned long ncp_caal_regions_acp55xx[] = {
+       NCP_REGION_ID(0x0b, 0x05),      /* SPPV2   */
+       NCP_REGION_ID(0x0c, 0x05),      /* SED     */
+       NCP_REGION_ID(0x0e, 0x05),      /* DPI_HFA */
+       NCP_REGION_ID(0x14, 0x05),      /* MTM     */
+       NCP_REGION_ID(0x14, 0x0a),      /* MTM2    */
+       NCP_REGION_ID(0x15, 0x00),      /* MME     */
+       NCP_REGION_ID(0x16, 0x05),      /* NCAV2   */
+       NCP_REGION_ID(0x16, 0x10),      /* NCAV22  */
+       NCP_REGION_ID(0x17, 0x05),      /* EIOAM1  */
+       NCP_REGION_ID(0x19, 0x05),      /* TMGR    */
+       NCP_REGION_ID(0x1a, 0x05),      /* MPPY    */
+       NCP_REGION_ID(0x1a, 0x23),      /* MPPY2   */
+       NCP_REGION_ID(0x1a, 0x21),      /* MPPY3   */
+       NCP_REGION_ID(0x1b, 0x05),      /* PIC     */
+       NCP_REGION_ID(0x1c, 0x05),      /* PAB     */
+       NCP_REGION_ID(0x1f, 0x05),      /* EIOAM0  */
+       NCP_REGION_ID(0x31, 0x05),      /* ISB     */
+       NCP_REGION_ID(0x28, 0x05),      /* EIOASM0 */
+       NCP_REGION_ID(0x29, 0x05),      /* EIOASM1 */
+       NCP_REGION_ID(0x2a, 0x05),      /* EIOAS2  */
+       NCP_REGION_ID(0x2b, 0x05),      /* EIOAS3  */
+       NCP_REGION_ID(0x2c, 0x05),      /* EIOAS4  */
+       NCP_REGION_ID(0x2d, 0x05),      /* EIOAS5  */
+       NCP_REGION_ID(0x32, 0x05),      /* ISBS    */
+       NCP_REGION_ID(0xff, 0xff)
+};
+
+static void quiesce_vp_engine(void)
+{
+       unsigned long *pCnalRegions = ncp_caal_regions_acp55xx;
+       unsigned long *pRegion;
+       unsigned ort, owt;
+       unsigned long buf = 0;
+       unsigned short node, target;
+       int loop;
+
+       printk(KERN_INFO "quiescing VP engines...\n");
+       pRegion = pCnalRegions;
+       while (*pRegion != NCP_REGION_ID(0xff, 0xff)) {
+
+               /* set read/write transaction limits to zero */
+               ncr_write(*pRegion, 0x8, 4, &buf);
+               ncr_write(*pRegion, 0xc, 4, &buf);
+               pRegion++;
+       }
+
+       pRegion = pCnalRegions;
+       loop = 0;
+       while (*pRegion != NCP_REGION_ID(0xff, 0xff)) {
+               node = (*pRegion & 0xffff0000) >> 16;
+               target = *pRegion & 0x0000ffff;
+               /* read the number of outstanding read/write transactions */
+               ncr_read(*pRegion, 0xf8, 4, &ort);
+               ncr_read(*pRegion, 0xfc, 4, &owt);
+
+               if ((ort == 0) && (owt == 0)) {
+                       /* this engine has been quiesced, move on to the next */
+                       printk(KERN_INFO "quiesced region 0x%02x.0x%02x\n",
+                               node, target);
+                       pRegion++;
+               } else {
+                       if (loop++ > 10000) {
+                               printk(KERN_INFO
+                                       "Unable to quiesce region 0x%02x.0x%02x 
ort=0x%x, owt=0x%x\n",
+                                    node, target, ort, owt);
+                               pRegion++;
+                               loop = 0;
+                               continue;
+                       }
+               }
+       }
+
+       return;
+}
+
+static inline void ncp_ddr_shutdown(void)
+{
+       unsigned long value;
+       int loop = 1;
+       unsigned long cdr2[2] = { 0x00002200, 0x00000f00 };
+       int smId;
+
+       /*
+        * Most of the PIO command has already been set up.
+        * issue config ring write - enter DDR self-refresh mode
+        */
+
+       for (smId = 0; smId < 2; smId++) {
+               /* CDR2 - Node.target */
+               ncr_register_write(cdr2[smId],
+                                  (unsigned *)(nca_address + 0xf8));
+               /* CDR0 - */
+               ncr_register_write(0x80050003,
+                                  (unsigned *)(nca_address + 0xf0));
+               do {
+                       kill_time(100000);
+                       value =
+                           ncr_register_read((unsigned *)(nca_address + 0xf0));
+               } while ((0x80000000UL & value));
+       }
+
+       /* check interrupt status for completion */
+       /* CDR1 - word offset 0x104 (byte offset 0x410) */
+       ncr_register_write(0x00000104, (unsigned *)(nca_address + 0xf4));
+
+       for (smId = 0; smId < 2; smId++) {
+               /* CDR2 - Node.target */
+               ncr_register_write(cdr2[smId],
+                                  (unsigned *)(nca_address + 0xf8));
+               do {
+                       ncr_register_write(loop,
+                                          (unsigned *)(nca_address + 0x11f0));
+
+                       /* issue config ring read */
+                       ncr_register_write(0x80040003,
+                                          (unsigned *)(nca_address + 0xf0));
+                       do {
+                               kill_time(100000);
+                               value =
+                                   ncr_register_read((unsigned *)(nca_address +
+                                                                  0xf0));
+                       } while ((0x80000000UL & value));
+
+                       value =
+                           ncr_register_read((unsigned *)(nca_address +
+                                                          0x1000));
+                       ncr_register_write(value,
+                                          (unsigned *)(nca_address + 0x1200));
+
+                       loop++;
+               } while ((value & 0x0200) == 0);
+       }
+
+       /* indicate DDR retention reset */
+       writel(0x00000001, apb_base + 0x300dc); /* set bit 0 of persist_scratch 
*/
+
+       /* issue chip reset */
+       writel(0x00000040, apb_base + 0x31004); /* Intrnl Boot, 0xffff0000 
Target */
+       writel(0x80000000, apb_base + 0x3180c); /* Set ResetReadDone */
+       writel(0x00080802, apb_base + 0x31008); /* Chip Reset */
+
+}
+
+void initiate_retention_reset(void)
+{
+       unsigned long ctl_244 = 0;
+       unsigned long value;
+
+       if (NULL == nca_address)
+               nca_address = ioremap(0x002020100000ULL, 0x20000);
+
+       /* send stop message to other CPUs */
+       local_irq_disable();
+       asm volatile ("dsb":::"memory");
+       asm volatile ("dmb":::"memory");
+       system_state = SYSTEM_RESTART;
+       smp_send_stop();
+
+       kill_time(1000000);
+
+       /* TODO - quiesce VP engines */
+       quiesce_vp_engine();
+
+       /* disable sysmem interrupts */
+       printk("disabling sysmem interrupts\n");
+       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 */
+       apb_base = ioremap(0x2010000000, 0x40000);
+       writel(0x000000ab, apb_base + 0x31000); /* Access Key */
+
+       /* prepare to put DDR in self refresh power-down mode */
+       /* first read the CTL_244 register and OR in the LP_CMD value */
+       ncr_read(NCP_REGION_ID(34, 0), 0x3d0, 4, &ctl_244);
+       ctl_244 |= 0x000a0000;
+
+       /*
+        * set up for CRBW operation
+        */
+       /* write register value into CDAR[0] */
+       ncr_register_write(ctl_244, (unsigned *)(nca_address + 0x1000));
+
+       /* CDR2 - Node.target = 34.0 */
+       ncr_register_write(0x00002200, (unsigned *)(nca_address + 0xf8));
+
+       /* CDR1 - word offset 0xf4 (byte offset 0x3d0) */
+       ncr_register_write(0x000000f4, (unsigned *)(nca_address + 0xf4));
+
+       /*
+        * issue instruction barrier
+        * this should cause the next few instructions to be fetched
+        * into cache
+        */
+       asm volatile ("dsb":::"memory");
+       prefetch(ncp_ddr_shutdown);
+
+       ncp_ddr_shutdown();
+
+}
+
+static ssize_t axxia_ddr_retention_trigger(struct file *file,
+                                          const char __user *buf,
+                                          size_t count, loff_t *ppos)
+{
+       initiate_retention_reset();
+       return 0;
+}
+
+static const struct file_operations axxia_ddr_retention_proc_ops = {
+       .write = axxia_ddr_retention_trigger,
+       .llseek = noop_llseek,
+};
+
+void axxia_ddr_retention_init(void)
+{
+#ifndef CONFIG_ARCH_AXXIA_SIM
+       if (!proc_create("driver/axxia_ddr_retention_reset", S_IWUSR, NULL,
+                        &axxia_ddr_retention_proc_ops))
+               printk(KERN_INFO
+                       "Failed to register DDR retention proc interface\n");
+#endif
+}
+
+EXPORT_SYMBOL(initiate_retention_reset);
+
+#else
+
+void axxia_ddr_retention_init(void)
+{
+       return;
+}
+
+#endif
-- 
1.8.4.3

_______________________________________________
linux-yocto mailing list
linux-yocto@yoctoproject.org
https://lists.yoctoproject.org/listinfo/linux-yocto

Reply via email to