[linux-yocto] [PATCH 05/15] arch/arm/mach-axxia: Support for DDR Retention Resets

Charlie Paul cpaul.windriver at gmail.com
Mon Jan 20 09:56:12 PST 2014


From: John Jacques <john.jacques at lsi.com>

The Axxia hardware makes it possible to reset the system without
losing the contents of memory.  This commit adds the ability to
initiate a reset in such a way that memory contents are not
lost.

Cleanup of the DDR Retention feature for Linux 3.10.

Shutdown devices, and disable hotplug before shuting down
the cores.

Signed-off-by: John Jacques <john.jacques at lsi.com>
---
 arch/arm/mach-axxia/Makefile           |    1 +
 arch/arm/mach-axxia/axxia.c            |    3 +
 arch/arm/mach-axxia/ddr_retention.c    |  290 ++++++++++++-------
 arch/arm/mach-axxia/include/mach/ncr.h |   44 +++
 arch/arm/mach-axxia/ncr.c              |  486 ++++++++++++++++++++++++++++++++
 drivers/misc/Kconfig                   |    2 +-
 drivers/misc/lsi-smmon.c               |    2 +-
 drivers/net/ethernet/lsi/lsi_acp_net.c |    9 +-
 8 files changed, 725 insertions(+), 112 deletions(-)
 create mode 100644 arch/arm/mach-axxia/include/mach/ncr.h
 create mode 100644 arch/arm/mach-axxia/ncr.c

diff --git a/arch/arm/mach-axxia/Makefile b/arch/arm/mach-axxia/Makefile
index e49adf1..79053c8 100644
--- a/arch/arm/mach-axxia/Makefile
+++ b/arch/arm/mach-axxia/Makefile
@@ -5,6 +5,7 @@ obj-y					+= wrappers.o
 obj-y					+= axxia.o
 obj-y					+= clock.o
 obj-y                                   += io.o
+obj-y					+= ncr.o
 obj-y					+= timers.o
 obj-y					+= pci.o
 obj-y					+= ddr_retention.o
diff --git a/arch/arm/mach-axxia/axxia.c b/arch/arm/mach-axxia/axxia.c
index ef87fb2..421b050 100644
--- a/arch/arm/mach-axxia/axxia.c
+++ b/arch/arm/mach-axxia/axxia.c
@@ -49,6 +49,7 @@
 #include <mach/timers.h>
 #include <mach/axxia-gic.h>
 #include <linux/irqchip/arm-gic.h>
+#include <mach/ncr.h>
 #include "axxia.h"
 #include "pci.h"
 #include "i2c.h"
@@ -325,6 +326,8 @@ void __init axxia_dt_init(void)
 			     axxia_auxdata_lookup, NULL);
 	pm_power_off = NULL; /* TBD */
 
+	ncr_init();
+
 	spi_register_board_info(spi_devs, ARRAY_SIZE(spi_devs));
 
 	/*
diff --git a/arch/arm/mach-axxia/ddr_retention.c b/arch/arm/mach-axxia/ddr_retention.c
index ca426c2..e409774 100644
--- a/arch/arm/mach-axxia/ddr_retention.c
+++ b/arch/arm/mach-axxia/ddr_retention.c
@@ -21,69 +21,124 @@
  */
 
 #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 <linux/delay.h>
 
+#include <linux/of.h>
 #include <asm/io.h>
 #include <asm/cacheflush.h>
-#include <../../../drivers/misc/lsi-ncr.h>
+#include <mach/ncr.h>
 
-static void __iomem *nca_address;
-static void __iomem *apb_base;
-
-static inline void kill_time(int cnt)
-{
-	while (cnt--)
-		;
-}
+static void __iomem *nca;
+static void __iomem *apb;
+static void __iomem *dickens;
 
 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(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)
+/*
+  ------------------------------------------------------------------------------
+  flush_l3
+
+  This is NOT a general function to flush the L3 cache.  There are a number of
+  assumptions that are not usually true...
+
+  1) All other cores are " quiesced".
+  2) There is no need to worry about preemption or interrupts.
+*/
+
+static void
+flush_l3(void)
+{
+
+	unsigned long hnf_offsets[] = {
+		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+	};
+
+	int i;
+	unsigned long status;
+	int retries;
+
+	for (i = 0; i < (sizeof(hnf_offsets) / sizeof(unsigned long)); ++i)
+		writel(0x0, dickens + (0x10000 * hnf_offsets[i]) + 0x10);
+
+	for (i = 0; i < (sizeof(hnf_offsets) / sizeof(unsigned long)); ++i) {
+		retries = 10000;
+
+		do {
+			status = readl(dickens +
+				       (0x10000 * hnf_offsets[i]) + 0x18);
+			udelay(1);
+		} while ((0 < --retries) && (0x0 != (status & 0xf)));
+
+		if (0 == retries)
+			BUG();
+	}
+
+	for (i = 0; i < (sizeof(hnf_offsets) / sizeof(unsigned long)); ++i)
+		writel(0x3, dickens + (0x10000 * hnf_offsets[i]) + 0x10);
+
+	for (i = 0; i < (sizeof(hnf_offsets) / sizeof(unsigned long)); ++i) {
+		retries = 10000;
+
+		do {
+			status = readl(dickens +
+				       (0x10000 * hnf_offsets[i]) + 0x18);
+			udelay(1);
+		} while ((0 < --retries) && (0xc != (status & 0xf)));
+
+		if (0 == retries)
+			BUG();
+	}
+
+	asm volatile ("dsb" : : : "memory");
+	asm volatile ("dmb" : : : "memory");
+
+	return;
+}
+
+static void
+quiesce_vp_engine(void)
 {
-	unsigned long *pCnalRegions = ncp_caal_regions_acp55xx;
-	unsigned long *pRegion;
+	unsigned long   *pCnalRegions = ncp_caal_regions_acp55xx;
+	unsigned long     *pRegion;
 	unsigned ort, owt;
-	unsigned long buf = 0;
-	unsigned short node, target;
-	int loop;
+	unsigned long      buf = 0;
+	unsigned short     node, target;
+	int      loop;
 
 	pr_info("quiescing VP engines...\n");
 	pRegion = pCnalRegions;
-	while (*pRegion != NCP_REGION_ID(0xff, 0xff)) {
 
+	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);
@@ -92,6 +147,7 @@ static void quiesce_vp_engine(void)
 
 	pRegion = pCnalRegions;
 	loop = 0;
+
 	while (*pRegion != NCP_REGION_ID(0xff, 0xff)) {
 		node = (*pRegion & 0xffff0000) >> 16;
 		target = *pRegion & 0x0000ffff;
@@ -102,12 +158,12 @@ static void quiesce_vp_engine(void)
 		if ((ort == 0) && (owt == 0)) {
 			/* this engine has been quiesced, move on to the next */
 			pr_info("quiesced region 0x%02x.0x%02x\n",
-				node, target);
+					node, target);
 			pRegion++;
 		} else {
 			if (loop++ > 10000) {
 				pr_info("Unable to quiesce region 0x%02x.0x%02x ort=0x%x, owt=0x%x\n",
-					node, target, ort, owt);
+				       node, target, ort, owt);
 				pRegion++;
 				loop = 0;
 				continue;
@@ -118,13 +174,15 @@ static void quiesce_vp_engine(void)
 	return;
 }
 
-static inline void ncp_ddr_shutdown(void)
+static inline void
+ncp_ddr_shutdown(void)
 {
 	unsigned long value;
 	int loop = 1;
-	unsigned long cdr2[2] = { 0x00002200, 0x00000f00 };
+	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
@@ -132,76 +190,88 @@ static inline void ncp_ddr_shutdown(void)
 
 	for (smId = 0; smId < 2; smId++) {
 		/* CDR2 - Node.target */
-		ncr_register_write(cdr2[smId],
-				   (unsigned *)(nca_address + 0xf8));
+		ncr_register_write(cdr2[smId], (unsigned *) (nca + 0xf8));
 		/* CDR0 - */
-		ncr_register_write(0x80050003,
-				   (unsigned *)(nca_address + 0xf0));
+		ncr_register_write(0x80050003, (unsigned *) (nca + 0xf0));
 		do {
-			kill_time(100000);
-			value =
-			    ncr_register_read((unsigned *)(nca_address + 0xf0));
+			value = ncr_register_read((unsigned *)
+						  (nca + 0xf0));
 		} while ((0x80000000UL & value));
 	}
 
 	/* check interrupt status for completion */
 	/* CDR1 - word offset 0x104 (byte offset 0x410) */
-	ncr_register_write(0x00000104, (unsigned *)(nca_address + 0xf4));
+	ncr_register_write(0x00000104, (unsigned *) (nca + 0xf4));
 
 	for (smId = 0; smId < 2; smId++) {
 		/* CDR2 - Node.target */
-		ncr_register_write(cdr2[smId],
-				   (unsigned *)(nca_address + 0xf8));
+		ncr_register_write(cdr2[smId], (unsigned *) (nca + 0xf8));
 		do {
-			ncr_register_write(loop,
-					   (unsigned *)(nca_address + 0x11f0));
+			ncr_register_write(loop, (unsigned *)
+					   (nca + 0x11f0));
 
 			/* issue config ring read */
-			ncr_register_write(0x80040003,
-					   (unsigned *)(nca_address + 0xf0));
+			ncr_register_write(0x80040003, (unsigned *)
+					   (nca + 0xf0));
 			do {
-				kill_time(100000);
-				value =
-				    ncr_register_read((unsigned *)(nca_address +
-								   0xf0));
+				value = ncr_register_read((unsigned *)
+							  (nca + 0xf0));
 			} while ((0x80000000UL & value));
 
-			value =
-			    ncr_register_read((unsigned *)(nca_address +
-							   0x1000));
-			ncr_register_write(value,
-					   (unsigned *)(nca_address + 0x1200));
+			value = ncr_register_read((unsigned *)
+						  (nca + 0x1000));
+			ncr_register_write(value, (unsigned *)
+					   (nca + 0x1200));
 
 			loop++;
 		} while ((value & 0x0200) == 0);
 	}
 
-	/* indicate DDR retention reset */
-	writel(0x00000001, apb_base + 0x300dc);	/* set persist_scratch bit 0 */
+	/*
+	  Indicate DDR Retention Reset
+	*/
+
+	/* set bit 0 of persist_scratch */
+	writel(0x00000001, apb + 0x300dc);
 
-	/* issue chip reset */
-	writel(0x00000040, apb_base + 0x31004);	/* Internal Boot, 0xffff0000
-						   Target */
-	writel(0x80000000, apb_base + 0x3180c);	/* Set ResetReadDone */
-	writel(0x00080802, apb_base + 0x31008);	/* Chip Reset */
+	/*
+	  Issue Chip Reset
+	*/
+
+	/* Intrnl Boot, 0xffff0000 Target */
+	writel(0x00000040, apb + 0x31004);
+	/* Set ResetReadDone */
+	writel(0x80000000, apb + 0x3180c);
+	/* Chip Reset */
+	writel(0x00080802, apb + 0x31008);
+
+	return;
 }
 
-void initiate_retention_reset(void)
+void
+initiate_retention_reset(void)
 {
 	unsigned long ctl_244 = 0;
 	unsigned long value;
+	unsigned long delay;
 
-	if (NULL == nca_address)
-		nca_address = ioremap(0x002020100000ULL, 0x20000);
+	if (NULL == nca || NULL == apb || NULL == dickens)
+		BUG();
 
-	/* send stop message to other CPUs */
-	local_irq_disable();
+	system_state = SYSTEM_RESTART;
 	asm volatile ("dsb" : : : "memory");
 	asm volatile ("dmb" : : : "memory");
-	system_state = SYSTEM_RESTART;
+	usermodehelper_disable();
+	device_shutdown();
+	cpu_hotplug_disable();
+	syscore_shutdown();
 	smp_send_stop();
 
-	kill_time(1000000);
+	for (delay = 0; delay < 10000; ++delay)
+		udelay(1000);
+
+	flush_cache_all();
+	flush_l3();
 
 	/* TODO - quiesce VP engines */
 	quiesce_vp_engine();
@@ -213,8 +283,7 @@ void initiate_retention_reset(void)
 	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 */
+	writel(0x000000ab, apb + 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 */
@@ -224,14 +293,15 @@ void initiate_retention_reset(void)
 	/*
 	 * set up for CRBW operation
 	 */
+
 	/* write register value into CDAR[0] */
-	ncr_register_write(ctl_244, (unsigned *)(nca_address + 0x1000));
+	ncr_register_write(ctl_244, (unsigned *) (nca + 0x1000));
 
 	/* CDR2 - Node.target = 34.0 */
-	ncr_register_write(0x00002200, (unsigned *)(nca_address + 0xf8));
+	ncr_register_write(0x00002200, (unsigned *) (nca + 0xf8));
 
 	/* CDR1 - word offset 0xf4 (byte offset 0x3d0) */
-	ncr_register_write(0x000000f4, (unsigned *)(nca_address + 0xf4));
+	ncr_register_write(0x000000f4, (unsigned *) (nca + 0xf4));
 
 	/*
 	 * issue instruction barrier
@@ -242,34 +312,38 @@ void initiate_retention_reset(void)
 	prefetch(ncp_ddr_shutdown);
 
 	ncp_ddr_shutdown();
+
+	return;
 }
+EXPORT_SYMBOL(initiate_retention_reset);
 
-static ssize_t axxia_ddr_retention_trigger(struct file *file,
-					   const char __user *buf,
-					   size_t count, loff_t *ppos)
+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,
+static const struct file_operations proc_ops = {
+	.write      = axxia_ddr_retention_trigger,
+	.llseek     = noop_llseek,
 };
 
-void axxia_ddr_retention_init(void)
+#define PROC_PATH "driver/axxia_ddr_retention_reset"
+
+void
+axxia_ddr_retention_init(void)
 {
-	if (!proc_create("driver/axxia_ddr_retention_reset", S_IWUSR, NULL,
-			 &axxia_ddr_retention_proc_ops))
-		pr_info("Failed to register DDR retention proc interface\n");
-}
-EXPORT_SYMBOL(initiate_retention_reset);
+	if (!of_find_compatible_node(NULL, NULL, "lsi,axm5516"))
+		return;
 
-#else
+	if (!proc_create(PROC_PATH, S_IWUSR, NULL, &proc_ops)) {
+		pr_err("Failed to register DDR retention proc interface\n");
+		return;
+	}
 
-void axxia_ddr_retention_init(void)
-{
-	return;
+	apb = ioremap(0x2010000000, 0x40000);
+	nca = ioremap(0x002020100000ULL, 0x20000);
+	dickens = ioremap(0x2000000000, 0x1000000);
 }
-
-#endif /* CONFIG_ARCH_AXXIA_SIM */
diff --git a/arch/arm/mach-axxia/include/mach/ncr.h b/arch/arm/mach-axxia/include/mach/ncr.h
new file mode 100644
index 0000000..926d366
--- /dev/null
+++ b/arch/arm/mach-axxia/include/mach/ncr.h
@@ -0,0 +1,44 @@
+/*
+ * arch/arm/mach-axxia/include/mach/ncr.h
+ *
+ * Copyright (C) 2010 LSI
+ *
+ * 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
+ */
+
+#ifndef __ARCH_ARM_MACH_AXXIA_NCR_H
+#define __ARCH_ARM_MACH_AXXIA_NCR_H
+
+#ifndef NCP_REGION_ID
+#define NCP_REGION_ID(node, target) \
+((unsigned long) ((((node) & 0xffff) << 16) | ((target) & 0xffff)))
+#endif
+
+#ifndef NCP_NODE_ID
+#define NCP_NODE_ID(region) (((region) >> 16) & 0xffff)
+#endif
+
+#ifndef NCP_TARGET_ID
+#define NCP_TARGET_ID(region) ((region) & 0xffff)
+#endif
+
+unsigned long ncr_register_read(unsigned *);
+void ncr_register_write(const unsigned, unsigned *);
+int ncr_read(unsigned long, unsigned long, int, void *);
+int ncr_write(unsigned long, unsigned long, int, void *);
+int ncr_init(void);
+void ncr_exit(void);
+
+#endif /* __ARCH_ARM_MACH_AXXIA_NCR_H */
diff --git a/arch/arm/mach-axxia/ncr.c b/arch/arm/mach-axxia/ncr.c
new file mode 100644
index 0000000..58b959a
--- /dev/null
+++ b/arch/arm/mach-axxia/ncr.c
@@ -0,0 +1,486 @@
+/*
+ *  Copyright (C) 2009 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>
+
+#include <asm/io.h>
+
+#include <mach/ncr.h>
+
+#ifdef CONFIG_ARCH_AXXIA
+#define NCA_PHYS_ADDRESS         0x002020100000ULL
+#define APB2SER_PHY_PHYS_ADDRESS 0x002010000000ULL
+#else
+#define NCA_PHYS_ADDRESS         0x002000520000ULL
+#endif
+
+static void __iomem *nca_address;
+#ifdef APB2SER_PHY_PHYS_ADDRESS
+static void __iomem *apb2ser0_address;
+#endif /* APB2SER_PHY_PHYS_ADDRESS */
+
+#define WFC_TIMEOUT (400000)
+
+#define LOCK_DOMAIN 0
+
+typedef union {
+	unsigned long raw;
+	struct {
+#ifdef __BIG_ENDIAN
+		unsigned long start_done:1;
+		unsigned long unused:6;
+		unsigned long local_bit:1;
+		unsigned long status:2;
+		unsigned long byte_swap_enable:1;
+		unsigned long cfg_cmpl_int_enable:1;
+		unsigned long cmd_type:4;
+		unsigned long dbs:16;
+#else
+	unsigned long dbs:16;
+	unsigned long cmd_type:4;
+	unsigned long cfg_cmpl_int_enable:1;
+	unsigned long byte_swap_enable:1;
+	unsigned long status:2;
+	unsigned long local_bit:1;
+	unsigned long unused:6;
+	unsigned long start_done:1;
+#endif
+} __attribute__ ((packed)) bits;
+} __attribute__ ((packed)) command_data_register_0_t;
+
+typedef union {
+	unsigned long raw;
+	struct {
+		unsigned long target_address:32;
+	} __attribute__ ((packed)) bits;
+} __attribute__ ((packed)) command_data_register_1_t;
+
+typedef union {
+	unsigned long raw;
+	struct {
+#ifdef __BIG_ENDIAN
+		unsigned long unused:16;
+		unsigned long target_node_id:8;
+		unsigned long target_id_address_upper:8;
+#else
+		unsigned long target_id_address_upper:8;
+		unsigned long target_node_id:8;
+		unsigned long unused:16;
+#endif
+	} __attribute__ ((packed)) bits;
+} __attribute__ ((packed)) command_data_register_2_t;
+
+#ifdef CONFIG_ARM
+
+/*
+  ----------------------------------------------------------------------
+  ncr_register_read
+*/
+
+unsigned long
+ncr_register_read(unsigned *address)
+{
+	unsigned long value;
+
+	value = ioread32be(address);
+
+	return value;
+}
+
+/*
+  ----------------------------------------------------------------------
+  ncr_register_write
+*/
+
+void
+ncr_register_write(const unsigned value, unsigned *address)
+{
+	iowrite32be(value, address);
+
+	return;
+}
+
+#else
+
+/*
+  ----------------------------------------------------------------------
+  ncr_register_read
+*/
+
+unsigned long
+ncr_register_read(unsigned *address)
+{
+	unsigned long value;
+
+	value = in_be32((unsigned *)address);
+
+	return value;
+}
+
+/*
+  ----------------------------------------------------------------------
+  ncr_register_write
+*/
+
+void
+ncr_register_write(const unsigned value, unsigned *address)
+{
+	out_be32(address, value);
+
+	return;
+}
+
+#endif
+
+/*
+  ------------------------------------------------------------------------------
+  ncr_lock
+*/
+
+static int
+ncr_lock(int domain)
+{
+	unsigned long offset;
+	unsigned long value;
+	int loops = 10000;
+
+	offset = (0xff80 + (domain * 4));
+
+	do {
+		value = ncr_register_read((unsigned *)(nca_address + offset));
+	} while ((0 != value) && (0 < --loops));
+
+	if (0 == loops)
+		return -1;
+
+	return 0;
+}
+
+/*
+  ------------------------------------------------------------------------------
+  ncr_unlock
+*/
+
+static void
+ncr_unlock(int domain)
+{
+	unsigned long offset;
+
+	offset = (0xff80 + (domain * 4));
+	ncr_register_write(0, (unsigned *)(nca_address + offset));
+
+	return;
+}
+
+/*
+  ======================================================================
+  ======================================================================
+  Public Interface
+  ======================================================================
+  ======================================================================
+*/
+
+/*
+  ----------------------------------------------------------------------
+  ncr_read
+*/
+
+int
+ncr_read(unsigned long region, unsigned long address, int number,
+	 void *buffer)
+{
+	command_data_register_0_t cdr0;
+	command_data_register_1_t cdr1;
+	command_data_register_2_t cdr2;
+	int wfc_timeout = WFC_TIMEOUT;
+
+	if (NULL == nca_address)
+		return -1;
+
+#ifdef APB2SER_PHY_PHYS_ADDRESS
+	if (NULL == apb2ser0_address)
+		return -1;
+#endif /* APB2SER_PHY_PHYS_ADDRESS */
+
+	if (0 != ncr_lock(LOCK_DOMAIN))
+		return -1;
+
+	if (NCP_NODE_ID(region) != 0x0153) {
+		/*
+		Set up the read command.
+		*/
+
+		cdr2.raw = 0;
+		cdr2.bits.target_node_id = NCP_NODE_ID(region);
+		cdr2.bits.target_id_address_upper = NCP_TARGET_ID(region);
+		ncr_register_write(cdr2.raw, (unsigned *) (nca_address + 0xf8));
+
+		cdr1.raw = 0;
+		cdr1.bits.target_address = (address >> 2);
+		ncr_register_write(cdr1.raw, (unsigned *) (nca_address + 0xf4));
+
+		cdr0.raw = 0;
+		cdr0.bits.start_done = 1;
+
+		if (0xff == cdr2.bits.target_id_address_upper)
+			cdr0.bits.local_bit = 1;
+
+		cdr0.bits.cmd_type = 4;
+		/* TODO: Verify number... */
+		cdr0.bits.dbs = (number - 1);
+		ncr_register_write(cdr0.raw, (unsigned *) (nca_address + 0xf0));
+		mb();
+
+		/*
+		Wait for completion.
+		*/
+
+		do {
+			--wfc_timeout;
+		} while ((0x80000000UL ==
+				ncr_register_read((unsigned *)(nca_address + 0xf0))) &&
+				0 < wfc_timeout);
+
+		if (0 == wfc_timeout) {
+			ncr_unlock(LOCK_DOMAIN);
+			return -1;
+		}
+
+		/*
+		Copy data words to the buffer.
+		*/
+
+		address = (unsigned long)(nca_address + 0x1000);
+		while (4 <= number) {
+			*((unsigned long *) buffer) =
+				ncr_register_read((unsigned *) address);
+			address += 4;
+			number -= 4;
+		}
+
+		if (0 < number) {
+			unsigned long temp =
+				ncr_register_read((unsigned *) address);
+			memcpy((void *) buffer, &temp, number);
+		}
+	} else {
+#ifdef APB2SER_PHY_PHYS_ADDRESS
+		void __iomem *targ_address = apb2ser0_address +
+				(address & (~0x3));
+		/*
+		Copy data words to the buffer.
+		*/
+
+		while (4 <= number) {
+			*((unsigned long *) buffer) =
+				*((unsigned long *) targ_address);
+			targ_address += 4;
+			number -= 4;
+		}
+#else
+		ncr_unlock(LOCK_DOMAIN);
+		return -1;
+#endif /* APB2SER_PHY_PHYS_ADDRESS */
+	}
+
+	ncr_unlock(LOCK_DOMAIN);
+
+	return 0;
+}
+EXPORT_SYMBOL(ncr_read);
+
+/*
+  ----------------------------------------------------------------------
+  ncr_write
+*/
+
+int
+ncr_write(unsigned long region, unsigned long address, int number,
+	  void *buffer)
+{
+	command_data_register_0_t cdr0;
+	command_data_register_1_t cdr1;
+	command_data_register_2_t cdr2;
+	unsigned long data_word_base;
+	int dbs = (number - 1);
+	int wfc_timeout = WFC_TIMEOUT;
+
+	if (NULL == nca_address)
+		return -1;
+
+#ifdef APB2SER_PHY_PHYS_ADDRESS
+	if (NULL == apb2ser0_address)
+		return -1;
+#endif /* APB2SER_PHY_PHYS_ADDRESS */
+
+	if (0 != ncr_lock(LOCK_DOMAIN))
+		return -1;
+
+	if (NCP_NODE_ID(region) != 0x0153) {
+		/*
+		  Set up the write.
+		*/
+
+		cdr2.raw = 0;
+		cdr2.bits.target_node_id = NCP_NODE_ID(region);
+		cdr2.bits.target_id_address_upper = NCP_TARGET_ID(region);
+		ncr_register_write(cdr2.raw, (unsigned *) (nca_address + 0xf8));
+
+		cdr1.raw = 0;
+		cdr1.bits.target_address = (address >> 2);
+		ncr_register_write(cdr1.raw, (unsigned *) (nca_address + 0xf4));
+
+		/*
+		  Copy from buffer to the data words.
+		*/
+
+		data_word_base = (unsigned long)(nca_address + 0x1000);
+
+		while (4 <= number) {
+			ncr_register_write(*((unsigned long *) buffer),
+					(unsigned *) data_word_base);
+			data_word_base += 4;
+			buffer += 4;
+			number -= 4;
+		}
+
+		if (0 < number) {
+			unsigned long temp = 0;
+
+			memcpy((void *) &temp, (void *) buffer, number);
+			ncr_register_write(temp, (unsigned *) data_word_base);
+			data_word_base += number;
+			buffer += number;
+			number = 0;
+		}
+
+		cdr0.raw = 0;
+		cdr0.bits.start_done = 1;
+
+		if (0xff == cdr2.bits.target_id_address_upper)
+			cdr0.bits.local_bit = 1;
+
+		cdr0.bits.cmd_type = 5;
+		/* TODO: Verify number... */
+		cdr0.bits.dbs = dbs;
+		ncr_register_write(cdr0.raw, (unsigned *) (nca_address + 0xf0));
+		mb();
+
+		/*
+		Wait for completion.
+		*/
+
+		do {
+			--wfc_timeout;
+		} while ((0x80000000UL ==
+				ncr_register_read((unsigned *)(nca_address + 0xf0))) &&
+				0 < wfc_timeout);
+
+		if (0 == wfc_timeout) {
+			ncr_unlock(LOCK_DOMAIN);
+			return -1;
+		}
+
+		/*
+		Check status.
+		*/
+
+		if (0x3 != ((ncr_register_read((unsigned *) (nca_address + 0xf0)) &
+						0x00c00000) >> 22)) {
+			unsigned long status;
+
+			status = ncr_register_read((unsigned *)(nca_address +
+								0xe4));
+			ncr_unlock(LOCK_DOMAIN);
+
+			return status;
+		}
+	} else {
+#ifdef APB2SER_PHY_PHYS_ADDRESS
+		void __iomem *targ_address = apb2ser0_address +
+					     (address & (~0x3));
+		/*
+		  Copy from buffer to the data words.
+		*/
+
+		while (4 <= number) {
+			*((unsigned long *) targ_address) =
+				*((unsigned long *) buffer);
+			targ_address += 4;
+			number -= 4;
+		}
+#else
+		ncr_unlock(LOCK_DOMAIN);
+		return -1;
+#endif /* APB2SER_PHY_PHYS_ADDRESS */
+	}
+
+	ncr_unlock(LOCK_DOMAIN);
+
+	return 0;
+}
+EXPORT_SYMBOL(ncr_write);
+
+/*
+  ----------------------------------------------------------------------
+  ncr_init
+*/
+
+int
+ncr_init(void)
+{
+	nca_address = ioremap(NCA_PHYS_ADDRESS, 0x20000);
+
+#ifdef APB2SER_PHY_PHYS_ADDRESS
+	apb2ser0_address = ioremap(APB2SER_PHY_PHYS_ADDRESS, 0x10000);
+#endif /* APB2SER_PHY_PHYS_ADDRESS */
+
+	pr_info("ncr: available\n");
+
+	return 0;
+}
+EXPORT_SYMBOL(ncr_init);
+
+/*
+  ----------------------------------------------------------------------
+  ncr_exit
+*/
+
+void __exit
+ncr_exit(void)
+{
+	/* Unmap the NCA. */
+	if (NULL != nca_address)
+		iounmap(nca_address);
+
+#ifdef APB2SER_PHY_PHYS_ADDRESS
+	/* Unmap the APB2SER0 PHY. */
+	if (NULL != apb2ser0_address)
+		iounmap(apb2ser0_address);
+#endif /* APB2SER_PHY_PHYS_ADDRESS */
+
+	return;
+}
+EXPORT_SYMBOL(ncr_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Register Ring access for LSI's ACP board");
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 62457bc..6272797 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -537,7 +537,7 @@ config LSI_MTC
 
 config LSI_NCR
 	tristate "LSI NCR Access"
-	depends on ARCH_AXXIA || ACP
+	depends on ACP
 	help
 	  Provides access to the LSI Axxia NCR bus.
 
diff --git a/drivers/misc/lsi-smmon.c b/drivers/misc/lsi-smmon.c
index 0c0db5d..5ab644b 100644
--- a/drivers/misc/lsi-smmon.c
+++ b/drivers/misc/lsi-smmon.c
@@ -17,7 +17,7 @@
 #include <linux/spinlock.h>
 #include <linux/slab.h>
 
-#include "lsi-ncr.h"
+#include <mach/ncr.h>
 
 #ifndef CONFIG_ARCH_AXXIA
 #error "Only AXM55xx is Supported At Present!"
diff --git a/drivers/net/ethernet/lsi/lsi_acp_net.c b/drivers/net/ethernet/lsi/lsi_acp_net.c
index 03bb287..8924b19 100644
--- a/drivers/net/ethernet/lsi/lsi_acp_net.c
+++ b/drivers/net/ethernet/lsi/lsi_acp_net.c
@@ -82,13 +82,18 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/dma-mapping.h>
-
 #include <linux/uaccess.h>
 #include <linux/io.h>
+
 #include <asm/dma.h>
 
-#include "lsi_acp_net.h"
+#ifdef CONFIG_AXXIA
+#include <mach/ncr.h>
+#else
 #include "../../../misc/lsi-ncr.h"
+#endif
+
+#include "lsi_acp_net.h"
 
 /* Define to disable full duplex mode on Amarillo boards */
 #undef AMARILLO_WA
-- 
1.7.9.5



More information about the linux-yocto mailing list