[linux-yocto] [PATCH 10/21] misc: lsi-ncr: Only use AMP lock on PPC platforms.

Cristian Bercaru cristian.bercaru at windriver.com
Fri May 15 05:24:51 PDT 2015


From: Anders Berg <anders.berg at intel.com>

Signed-off-by: Anders Berg <anders.berg at intel.com>
---
 arch/arm/mach-axxia/axxia.c |    1 -
 drivers/misc/lsi-ncr.c      |  219 +++++++++++++++----------------------------
 2 files changed, 73 insertions(+), 147 deletions(-)

diff --git a/arch/arm/mach-axxia/axxia.c b/arch/arm/mach-axxia/axxia.c
index 0753d03..7a235cd 100644
--- a/arch/arm/mach-axxia/axxia.c
+++ b/arch/arm/mach-axxia/axxia.c
@@ -211,7 +211,6 @@ void __init axxia_dt_init(void)
 			     axxia_auxdata_lookup, NULL);
 	pm_power_off = NULL; /* TBD */
 
-	ncr_init();
 	axxia_ddr_retention_init();
 
 	axxia_pcie_init();
diff --git a/drivers/misc/lsi-ncr.c b/drivers/misc/lsi-ncr.c
index 276d72c..48244d6 100644
--- a/drivers/misc/lsi-ncr.c
+++ b/drivers/misc/lsi-ncr.c
@@ -21,9 +21,7 @@
  */
 
 #include <linux/module.h>
-#include <linux/jiffies.h>
-
-#include <asm/io.h>
+#include <linux/io.h>
 
 #include "lsi-ncr.h"
 
@@ -41,11 +39,17 @@ static void __iomem *apb2ser0_address;
 
 #define WFC_TIMEOUT (400000)
 
+/* Protect NCA PIO registers from concurrent use. */
 static DEFINE_RAW_SPINLOCK(ncr_spin_lock);
-static DEFINE_RAW_SPINLOCK(nca_access_lock);
+
+/* This lock protect each individual register read/write to the NCA registers
+ * due to a bug in rev 1.0 silicon where the bus interface may hang if the NCA
+ * is subjected to simultaneous requests from multiple masters
+ */
+DEFINE_RAW_SPINLOCK(nca_access_lock);
 EXPORT_SYMBOL(nca_access_lock);
-static unsigned long ncr_spin_flags, axi_access_flags;
 
+static unsigned long ncr_spin_flags;
 
 #define LOCK_DOMAIN 0
 
@@ -96,163 +100,100 @@ typedef union {
 	} __packed bits;
 } __packed command_data_register_2_t;
 
-int
-ncr_read_nolock(unsigned long region, unsigned long address, int number,
-void *buffer);
-
-#ifdef CONFIG_ARM
-
-/*
- * like iowrite32be but without the barrier.
- * The iowmb barrier in the standard macro includes a outer_cache_sync
- * which we don't want for Axxia register IO.
- */
-#define axxia_write32be(v, p) \
-	({  __raw_writel((__force __u32)cpu_to_be32(v), p); })
 
-/*
-  ----------------------------------------------------------------------
-  ncr_register_read
-*/
-
-inline unsigned long
+unsigned long
 ncr_register_read(unsigned *address)
 {
-	unsigned long value;
-
-	value = ioread32be(address);
+	unsigned long value = __raw_readl(address);
 
-	return value;
+	return be32_to_cpu(value);
 }
 
-/*
-  ----------------------------------------------------------------------
-  ncr_register_write
-*/
-
 void
-inline ncr_register_write(const unsigned value, unsigned *address)
+ncr_register_write(const unsigned value, unsigned *address)
 {
-	axxia_write32be(value, address);
-	asm volatile ("mcr p15,0,%0,c7,c5,4" : : "r" (0));  /* isb */
-
-	return;
+	__raw_writel(cpu_to_be32(value), address);
 }
 
-#else
-
 /*
   ----------------------------------------------------------------------
-  ncr_register_read
+  nca_register_read
 */
 
-inline unsigned long
-ncr_register_read(unsigned *address)
+static unsigned long
+nca_register_read(unsigned *address)
 {
-	unsigned long value;
+	unsigned long value, flags;
 
-	value = in_be32((unsigned *)address);
+	raw_spin_lock_irqsave(&nca_access_lock, flags);
+	value = ncr_register_read(address);
+	raw_spin_unlock_irqrestore(&nca_access_lock, flags);
 
 	return value;
 }
 
 /*
   ----------------------------------------------------------------------
-  ncr_register_write
+  nca_register_write
 */
 
-inline void
-ncr_register_write(const unsigned value, unsigned *address)
+static void
+nca_register_write(const unsigned value, unsigned *address)
 {
-	out_be32(address, value);
+	unsigned long flags;
 
-	return;
+	raw_spin_lock_irqsave(&nca_access_lock, flags);
+	ncr_register_write(value, address);
+	raw_spin_unlock_irqrestore(&nca_access_lock, flags);
 }
 
-#endif
-
-/*
-  ----------------------------------------------------------------------
-  nca_register_read
-*/
-
-inline unsigned long
-nca_register_read(unsigned *address)
+/* These are only needed on platforms there AMP mode of operation is supported
+ * (currently only on PowerPC based Axxia platforms). In AMP mode, multiple OS
+ * instances may be accessing the NCA registers, thus requiring a hardware
+ * based spinlock like this.
+ */
+#ifdef CONFIG_PPC32
+static void
+ncr_amp_lock(int domain)
 {
-	unsigned long value;
-
-	raw_spin_lock_irqsave(&nca_access_lock,
-		axi_access_flags);
-	value = ncr_register_read(address);
-	raw_spin_unlock_irqrestore(&nca_access_lock,
-		axi_access_flags);
+	unsigned long offset = (0xff80 + (domain * 4));
 
-	return value;
+	while (nca_register_read((unsigned *)(nca_address + offset)) != 0)
+		cpu_relax();
 }
 
-/*
-  ----------------------------------------------------------------------
-  nca_register_write
-*/
-
-void
-inline nca_register_write(const unsigned value, unsigned *address)
+static void
+ncr_amp_unlock(int domain)
 {
-	raw_spin_lock_irqsave(&nca_access_lock,
-		axi_access_flags);
-	ncr_register_write(value, address);
-	raw_spin_unlock_irqrestore(&nca_access_lock,
-		axi_access_flags);
+	unsigned long offset = (0xff80 + (domain * 4));
 
-	return;
+	nca_register_write(0, (unsigned *)(nca_address + offset));
 }
+#else
+static void ncr_amp_lock(int domain) {}
+static void ncr_amp_unlock(int domain) {}
+#endif
 
-/*
-  ------------------------------------------------------------------------------
-  ncr_lock
-*/
-
-static int
+/**
+ * Used to serialize all access to NCA PIO interface.
+ */
+int
 ncr_lock(int domain)
 {
-	unsigned long offset;
-	unsigned long value;
-	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
-
 	raw_spin_lock_irqsave(&ncr_spin_lock, ncr_spin_flags);
-	offset = (0xff80 + (domain * 4));
-
-	do {
-		value = nca_register_read((unsigned *)(nca_address + offset));
-	} while ((0 != value) && (time_before(jiffies, timeout)));
-
-	if (!(time_before(jiffies, timeout))) {
-		raw_spin_unlock_irqrestore(&ncr_spin_lock, ncr_spin_flags);
-		pr_err("ncr_lock() Timeout!\n");
-		BUG();
-
-		return -1;
-	}
-
+	ncr_amp_lock(domain);
 	return 0;
 }
 EXPORT_SYMBOL(ncr_lock);
 
-/*
-  ------------------------------------------------------------------------------
-  ncr_unlock
-*/
-
-static void
+/**
+ * Used to serialize all access to NCA PIO interface.
+ */
+void
 ncr_unlock(int domain)
 {
-	unsigned long offset;
-
-	offset = (0xff80 + (domain * 4));
-	nca_register_write(0, (unsigned *)(nca_address + offset));
+	ncr_amp_unlock(domain);
 	raw_spin_unlock_irqrestore(&ncr_spin_lock, ncr_spin_flags);
-
-	return;
 }
 EXPORT_SYMBOL(ncr_unlock);
 
@@ -307,7 +248,6 @@ ncr_check_pio_status(char *str)
 		pr_err("lsi-ncr: PIO operation timeout cdr0=0x%08lx!\n",
 		       cdr0.raw);
 		ncr_pio_error_dump(str);
-		ncr_unlock(LOCK_DOMAIN);
 		BUG();
 		return -1;
 	}
@@ -342,7 +282,6 @@ ncr_read_nolock(unsigned long region, unsigned long address, int number,
 	command_data_register_0_t cdr0;
 	command_data_register_1_t cdr1;
 	command_data_register_2_t cdr2;
-	int wfc_timeout = WFC_TIMEOUT;
 
 	if ((NCP_NODE_ID(region) != 0x0153) && (NCP_NODE_ID(region) != 0x115)) {
 		/* make sure any previous command has completed */
@@ -400,6 +339,8 @@ ncr_read_nolock(unsigned long region, unsigned long address, int number,
 		}
 	} else {
 #ifdef APB2SER_PHY_PHYS_ADDRESS
+		int wfc_timeout = WFC_TIMEOUT;
+
 		if (NCP_NODE_ID(region) != 0x115) {
 			void __iomem *targ_address = apb2ser0_address +
 				(address & (~0x3));
@@ -415,9 +356,8 @@ ncr_read_nolock(unsigned long region, unsigned long address, int number,
 			}
 		} else {
 			void __iomem *base;
-			if (0xffff < address) {
+			if (0xffff < address)
 				return -1;
-			}
 
 			switch (NCP_TARGET_ID(region)) {
 			case 0:
@@ -454,9 +394,8 @@ ncr_read_nolock(unsigned long region, unsigned long address, int number,
 			} while (0 != (*((unsigned long *) buffer) & 0x80000000)
 					&& 0 < wfc_timeout);
 
-			if (0 == wfc_timeout) {
+			if (0 == wfc_timeout)
 				return -1;
-			}
 
 			if ((NCP_TARGET_ID(region) == 0x1) ||
 				(NCP_TARGET_ID(region) == 0x4)) {
@@ -481,9 +420,8 @@ EXPORT_SYMBOL(ncr_read_nolock);
 
 int
 ncr_read(unsigned long region, unsigned long address, int number,
-	void *buffer)
+	 void *buffer)
 {
-
 	int	rc;
 
 	if (NULL == nca_address)
@@ -494,9 +432,7 @@ ncr_read(unsigned long region, unsigned long address, int number,
 		return -1;
 #endif /* APB2SER_PHY_PHYS_ADDRESS */
 
-	if (0 != ncr_lock(LOCK_DOMAIN))
-		return -1;
-
+	ncr_lock(LOCK_DOMAIN);
 
 	rc = ncr_read_nolock(region, address, number, buffer);
 
@@ -513,14 +449,13 @@ EXPORT_SYMBOL(ncr_read);
 
 int
 ncr_write_nolock(unsigned long region, unsigned long address, int number,
-	  void *buffer)
+		 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 ((NCP_NODE_ID(region) != 0x0153) && (NCP_NODE_ID(region) != 0x115)) {
 		/* make sure any previous command has completed */
@@ -585,6 +520,8 @@ ncr_write_nolock(unsigned long region, unsigned long address, int number,
 
 	} else {
 #ifdef APB2SER_PHY_PHYS_ADDRESS
+	int wfc_timeout = WFC_TIMEOUT;
+
 	if (NCP_NODE_ID(region) != 0x115) {
 		void __iomem *targ_address = apb2ser0_address +
 					     (address & (~0x3));
@@ -600,9 +537,8 @@ ncr_write_nolock(unsigned long region, unsigned long address, int number,
 		}
 	} else {
 		void __iomem *base;
-		if (0xffff < address) {
+		if (0xffff < address)
 			return -1;
-		}
 
 		switch (NCP_TARGET_ID(region)) {
 		case 0:
@@ -641,9 +577,8 @@ ncr_write_nolock(unsigned long region, unsigned long address, int number,
 			} while (0 != (*((unsigned long *) buffer) & 0x80000000)
 				&& 0 < wfc_timeout);
 
-			if (0 == wfc_timeout) {
+			if (0 == wfc_timeout)
 				return -1;
-			}
 		}
 #else
 		return -1;
@@ -669,8 +604,7 @@ ncr_write(unsigned long region, unsigned long address, int number,
 		return -1;
 #endif /* APB2SER_PHY_PHYS_ADDRESS */
 
-	if (0 != ncr_lock(LOCK_DOMAIN))
-		return -1;
+	ncr_lock(LOCK_DOMAIN);
 
 	rc = ncr_write_nolock(region, address, number, buffer);
 
@@ -678,7 +612,6 @@ ncr_write(unsigned long region, unsigned long address, int number,
 
 	return rc;
 }
-
 EXPORT_SYMBOL(ncr_write);
 
 /*
@@ -686,7 +619,7 @@ EXPORT_SYMBOL(ncr_write);
   ncr_init
 */
 
-int
+static int
 ncr_init(void)
 {
 	nca_address = ioremap(NCA_PHYS_ADDRESS, 0x20000);
@@ -699,16 +632,14 @@ ncr_init(void)
 
 	return 0;
 }
-
-
-module_init(ncr_init);
+core_initcall(ncr_init);
 
 /*
   ----------------------------------------------------------------------
   ncr_exit
 */
 
-void __exit
+static void __exit
 ncr_exit(void)
 {
 	/* Unmap the NCA. */
@@ -720,12 +651,8 @@ ncr_exit(void)
 	if (NULL != apb2ser0_address)
 		iounmap(apb2ser0_address);
 #endif /* APB2SER_PHY_PHYS_ADDRESS */
-
-	return;
 }
-
-
-module_exit(ncr_exit);
+__exitcall(ncr_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Register Ring access for LSI's ACP board");
-- 
1.7.9.5



More information about the linux-yocto mailing list