[linux-yocto] [PATCH 1/2] drivers/misc/lsi-ncr.c: Remove Low-Level Spinlock

Cristian Bercaru cristian.bercaru at windriver.com
Fri Nov 13 10:29:15 PST 2015


From: Gary McGee <gary.mcgee at intel.com>

Remove low-level spinlock from unlocked access methods, and make it
conditional, based on chip revision.

Signed-off-by: Gary McGee <gary.mcgee at intel.com>
---
 drivers/misc/lsi-ncr.c |  190 +++++++++++++++++++++++++++++++++++-------------
 1 file changed, 139 insertions(+), 51 deletions(-)

diff --git a/drivers/misc/lsi-ncr.c b/drivers/misc/lsi-ncr.c
index a0f0704..6a4a208 100644
--- a/drivers/misc/lsi-ncr.c
+++ b/drivers/misc/lsi-ncr.c
@@ -41,13 +41,33 @@ static void __iomem *apb2ser0_address;
 
 #define WFC_TIMEOUT (400000)
 
-/* Protect NCA PIO registers from concurrent use. */
+/*
+ * We provide both 'normal' and 'nolock' versions of the
+ * ncr_read/write functions. For normal operation we use
+ * locking to provide thread-safe operation.
+ * There are two levels of locking.
+ *
+ * 1. ncr_spin_lock -
+ *      This is a high-level lock that protects the NCA PIO
+ *      registers from concurrent use. The NCA PIO mechanism
+ *      only supports a single thread of execution.
+ *
+ * 2. nca_access_lock -
+ *       This is a low-level lock that protects each individual
+ *       register read/write to the NCA registers. This is a
+ *       workaround for a bug in rev 1.0 silicon where the bus
+ *       interface may hang if the NCA is subjected to simultaneous
+ *       requests from multiple masters.
+ *
+ * The 'nolock' versions of ncr_read/write should only be used in
+ * special cases where the caller can guarantee there will be no
+ * other threads of execution.
+ */
+
+/* Lock #1 : Protect NCA PIO registers from concurrent use. */
 static DEFINE_RAW_SPINLOCK(ncr_spin_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
- */
+/* Lock #2 : Protect each individual NCA register access. */
 DEFINE_RAW_SPINLOCK(nca_access_lock);
 EXPORT_SYMBOL(nca_access_lock);
 
@@ -120,6 +140,13 @@ union command_data_register_2 {
 	} __packed bits;
 } __packed;
 
+
+/*
+ * ncr_register_read/write
+ *   low-level access functions to Axxia registers,
+ *   with checking to ensure device is not currently
+ *   held in reset.
+ */
 unsigned long
 ncr_register_read(unsigned *address)
 {
@@ -139,12 +166,14 @@ ncr_register_write(const unsigned value, unsigned *address)
 }
 
 /*
-  ----------------------------------------------------------------------
-  nca_register_read
-*/
-
+ * ncr_register_read/write_lock
+ *   access functions for Axxia NCA block.
+ *   These functions protect the register access with a spinlock.
+ *   This is needed to workaround an AXM55xx v1.0 h/w bug.
+ *
+ */
 static unsigned long
-nca_register_read(unsigned *address)
+ncr_register_read_lock(unsigned *address)
 {
 	unsigned long value, flags;
 
@@ -155,13 +184,8 @@ nca_register_read(unsigned *address)
 	return value;
 }
 
-/*
-  ----------------------------------------------------------------------
-  nca_register_write
-*/
-
 static void
-nca_register_write(const unsigned value, unsigned *address)
+ncr_register_write_lock(const unsigned value, unsigned *address)
 {
 	unsigned long flags;
 
@@ -170,6 +194,28 @@ nca_register_write(const unsigned value, unsigned *address)
 	raw_spin_unlock_irqrestore(&nca_access_lock, flags);
 }
 
+/*
+ * define two sets of function pointers for low-level register
+ * access - one with locking and one without.
+ */
+struct ncr_io_fns {
+	unsigned long (*rd) (unsigned *address);
+	void (*wr) (const unsigned value, unsigned *address);
+};
+
+struct ncr_io_fns ncr_io_fn_lock = {
+	ncr_register_read_lock,
+	ncr_register_write_lock
+};
+
+struct ncr_io_fns ncr_io_fn_nolock = {
+	ncr_register_read,
+	ncr_register_write
+};
+
+struct ncr_io_fns *default_io_fn;
+
+
 /* 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
@@ -181,7 +227,7 @@ ncr_amp_lock(int domain)
 {
 	unsigned long offset = (0xff80 + (domain * 4));
 
-	while (nca_register_read((unsigned *)(nca_address + offset)) != 0)
+	while (ncr_register_read_lock((unsigned *)(nca_address + offset)) != 0)
 		cpu_relax();
 }
 
@@ -190,7 +236,7 @@ ncr_amp_unlock(int domain)
 {
 	unsigned long offset = (0xff80 + (domain * 4));
 
-	nca_register_write(0, (unsigned *)(nca_address + offset));
+	ncr_register_write_lock(0, (unsigned *)(nca_address + offset));
 }
 #else
 static void ncr_amp_lock(int domain) {}
@@ -227,17 +273,17 @@ EXPORT_SYMBOL(ncr_unlock);
 */
 
 static void
-ncr_pio_error_dump(char *str)
+ncr_pio_error_dump(struct ncr_io_fns *io_fn, char *str)
 {
 	unsigned long cdr0, cdr1, cdr2;
 	unsigned long stat0, stat1;
 
-	cdr0 = nca_register_read((unsigned *)(nca_address + 0xf0));
-	cdr1 = nca_register_read((unsigned *)(nca_address + 0xf4));
-	cdr2 = nca_register_read((unsigned *)(nca_address + 0xf8));
+	cdr0 = io_fn->rd((unsigned *)(nca_address + 0xf0));
+	cdr1 = io_fn->rd((unsigned *)(nca_address + 0xf4));
+	cdr2 = io_fn->rd((unsigned *)(nca_address + 0xf8));
 
-	stat0 = nca_register_read((unsigned *)(nca_address + 0xe4));
-	stat1 = nca_register_read((unsigned *)(nca_address + 0xe8));
+	stat0 = io_fn->rd((unsigned *)(nca_address + 0xe4));
+	stat1 = io_fn->rd((unsigned *)(nca_address + 0xe8));
 
 	pr_err("lsi-ncr: %8s failed, error status : 0x%08lx 0x%08lx\n",
 	       str, stat0, stat1);
@@ -251,7 +297,7 @@ ncr_pio_error_dump(char *str)
 */
 
 static int
-ncr_check_pio_status(char *str)
+ncr_check_pio_status(struct ncr_io_fns *io_fn, char *str)
 {
 	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
 	union command_data_register_0 cdr0;
@@ -262,7 +308,7 @@ ncr_check_pio_status(char *str)
 
 	do {
 		cdr0.raw =
-			nca_register_read((unsigned *)(nca_address + 0xf0));
+			io_fn->rd((unsigned *)(nca_address + 0xf0));
 	} while ((0x1 == cdr0.bits.start_done) &&
 		 (time_before(jiffies, timeout)));
 
@@ -270,16 +316,16 @@ ncr_check_pio_status(char *str)
 		/* timed out without completing */
 		pr_err("lsi-ncr: PIO operation timeout cdr0=0x%08lx!\n",
 		       cdr0.raw);
-		ncr_pio_error_dump(str);
+		ncr_pio_error_dump(io_fn, str);
 		BUG();
 		return -1;
 	}
 
 	if (cdr0.raw && (0x3 != cdr0.bits.status)) {
 		/* completed with non-success status */
-		ncr_pio_error_dump(str);
+		ncr_pio_error_dump(io_fn, str);
 		/* clear CDR0 to allow subsequent commands to complete */
-		nca_register_write(0, (unsigned *) (nca_address + 0xf0));
+		io_fn->wr(0, (unsigned *) (nca_address + 0xf0));
 
 		/*
 		 * we now treat any config ring error as a BUG().
@@ -310,9 +356,10 @@ ncr_check_pio_status(char *str)
   ncr_read
 */
 
-int
-ncr_read_nolock(unsigned long region, unsigned long address, int number,
-		void *buffer)
+static int
+__ncr_read(struct ncr_io_fns *io_fn,
+	unsigned long region, unsigned long address, int number,
+	void *buffer)
 {
 	union command_data_register_0 cdr0;
 	union command_data_register_1 cdr1;
@@ -320,7 +367,7 @@ ncr_read_nolock(unsigned long region, unsigned long address, int number,
 
 	if ((NCP_NODE_ID(region) != 0x0153) && (NCP_NODE_ID(region) != 0x115)) {
 		/* make sure any previous command has completed */
-		if (0 != ncr_check_pio_status("previous"))
+		if (0 != ncr_check_pio_status(io_fn, "previous"))
 			return -1;
 
 		/*
@@ -330,11 +377,11 @@ ncr_read_nolock(unsigned long region, unsigned long address, int number,
 		cdr2.raw = 0;
 		cdr2.bits.target_node_id = NCP_NODE_ID(region);
 		cdr2.bits.target_id_address_upper = NCP_TARGET_ID(region);
-		nca_register_write(cdr2.raw, (unsigned *) (nca_address + 0xf8));
+		io_fn->wr(cdr2.raw, (unsigned *) (nca_address + 0xf8));
 
 		cdr1.raw = 0;
 		cdr1.bits.target_address = (address >> 2);
-		nca_register_write(cdr1.raw, (unsigned *) (nca_address + 0xf4));
+		io_fn->wr(cdr1.raw, (unsigned *) (nca_address + 0xf4));
 
 		cdr0.raw = 0;
 		cdr0.bits.start_done = 1;
@@ -345,13 +392,13 @@ ncr_read_nolock(unsigned long region, unsigned long address, int number,
 		cdr0.bits.cmd_type = 4;
 		/* TODO: Verify number... */
 		cdr0.bits.dbs = (number - 1);
-		nca_register_write(cdr0.raw, (unsigned *) (nca_address + 0xf0));
+		io_fn->wr(cdr0.raw, (unsigned *) (nca_address + 0xf0));
 		mb();
 
 		/*
 		  Wait for completion.
 		*/
-		if (0 != ncr_check_pio_status("read"))
+		if (0 != ncr_check_pio_status(io_fn, "read"))
 			return -1;
 
 		/*
@@ -361,7 +408,7 @@ ncr_read_nolock(unsigned long region, unsigned long address, int number,
 		address = (unsigned long)(nca_address + 0x1000);
 		while (4 <= number) {
 			*((unsigned long *) buffer) =
-				nca_register_read((unsigned *) address);
+				io_fn->rd((unsigned *) address);
 			address += 4;
 			buffer += 4;
 			number -= 4;
@@ -369,7 +416,7 @@ ncr_read_nolock(unsigned long region, unsigned long address, int number,
 
 		if (0 < number) {
 			unsigned long temp =
-				nca_register_read((unsigned *) address);
+				io_fn->rd((unsigned *) address);
 			memcpy((void *) buffer, &temp, number);
 		}
 	} else {
@@ -450,6 +497,15 @@ ncr_read_nolock(unsigned long region, unsigned long address, int number,
 
 	return 0;
 }
+
+
+int
+ncr_read_nolock(unsigned long region, unsigned long address,
+		 int number, void *buffer)
+{
+	return __ncr_read(&ncr_io_fn_nolock,
+		region, address, number, buffer);
+}
 EXPORT_SYMBOL(ncr_read_nolock);
 
 
@@ -469,7 +525,8 @@ ncr_read(unsigned long region, unsigned long address, int number,
 
 	ncr_lock(LOCK_DOMAIN);
 
-	rc = ncr_read_nolock(region, address, number, buffer);
+	/* call __ncr_read with chip version dependent io_fn */
+	rc = __ncr_read(default_io_fn, region, address, number, buffer);
 
 	ncr_unlock(LOCK_DOMAIN);
 
@@ -482,9 +539,10 @@ EXPORT_SYMBOL(ncr_read);
   ncr_write
 */
 
-int
-ncr_write_nolock(unsigned long region, unsigned long address, int number,
-		 void *buffer)
+static int
+__ncr_write(struct ncr_io_fns *io_fn,
+	unsigned long region, unsigned long address, int number,
+	void *buffer)
 {
 	union command_data_register_0 cdr0;
 	union command_data_register_1 cdr1;
@@ -494,7 +552,7 @@ ncr_write_nolock(unsigned long region, unsigned long address, int number,
 
 	if ((NCP_NODE_ID(region) != 0x0153) && (NCP_NODE_ID(region) != 0x115)) {
 		/* make sure any previous command has completed */
-		if (0 != ncr_check_pio_status("previous"))
+		if (0 != ncr_check_pio_status(io_fn, "previous"))
 			return -1;
 
 		/*
@@ -504,11 +562,11 @@ ncr_write_nolock(unsigned long region, unsigned long address, int number,
 		cdr2.raw = 0;
 		cdr2.bits.target_node_id = NCP_NODE_ID(region);
 		cdr2.bits.target_id_address_upper = NCP_TARGET_ID(region);
-		nca_register_write(cdr2.raw, (unsigned *) (nca_address + 0xf8));
+		io_fn->wr(cdr2.raw, (unsigned *) (nca_address + 0xf8));
 
 		cdr1.raw = 0;
 		cdr1.bits.target_address = (address >> 2);
-		nca_register_write(cdr1.raw, (unsigned *) (nca_address + 0xf4));
+		io_fn->wr(cdr1.raw, (unsigned *) (nca_address + 0xf4));
 
 		/*
 		  Copy from buffer to the data words.
@@ -517,7 +575,7 @@ ncr_write_nolock(unsigned long region, unsigned long address, int number,
 		data_word_base = (unsigned long)(nca_address + 0x1000);
 
 		while (4 <= number) {
-			nca_register_write(*((unsigned long *) buffer),
+			io_fn->wr(*((unsigned long *) buffer),
 					   (unsigned *) data_word_base);
 			data_word_base += 4;
 			buffer += 4;
@@ -528,7 +586,7 @@ ncr_write_nolock(unsigned long region, unsigned long address, int number,
 			unsigned long temp = 0;
 
 			memcpy((void *) &temp, (void *) buffer, number);
-			nca_register_write(temp, (unsigned *) data_word_base);
+			io_fn->wr(temp, (unsigned *) data_word_base);
 			data_word_base += number;
 			buffer += number;
 			number = 0;
@@ -543,14 +601,14 @@ ncr_write_nolock(unsigned long region, unsigned long address, int number,
 		cdr0.bits.cmd_type = 5;
 		/* TODO: Verify number... */
 		cdr0.bits.dbs = dbs;
-		nca_register_write(cdr0.raw, (unsigned *) (nca_address + 0xf0));
+		io_fn->wr(cdr0.raw, (unsigned *) (nca_address + 0xf0));
 		mb();
 
 		/*
 		  Wait for completion.
 		*/
 
-		if (0 != ncr_check_pio_status("write"))
+		if (0 != ncr_check_pio_status(io_fn, "write"))
 			return -1;
 
 	} else {
@@ -622,6 +680,16 @@ ncr_write_nolock(unsigned long region, unsigned long address, int number,
 
 	return 0;
 }
+
+
+int
+ncr_write_nolock(unsigned long region, unsigned long address, int number,
+	  void *buffer)
+{
+	/* call the __ncr_write function with nolock io_fn */
+	return __ncr_write(&ncr_io_fn_nolock,
+			region, address, number, buffer);
+}
 EXPORT_SYMBOL(ncr_write_nolock);
 
 
@@ -639,10 +707,13 @@ ncr_write(unsigned long region, unsigned long address, int number,
 		return -1;
 #endif /* APB2SER_PHY_PHYS_ADDRESS */
 
+	/* grab the ncr_lock */
 	ncr_lock(LOCK_DOMAIN);
 
-	rc = ncr_write_nolock(region, address, number, buffer);
+	/* call the __ncr_write function with chip-version dependent io_fn */
+	rc = __ncr_write(default_io_fn, region, address, number, buffer);
 
+	/* free the ncr_lock */
 	ncr_unlock(LOCK_DOMAIN);
 
 	return rc;
@@ -657,6 +728,23 @@ EXPORT_SYMBOL(ncr_write);
 static int
 ncr_init(void)
 {
+
+	/*
+	 * read chip type/revision to determine if low-level locking
+	 * is required and select the appropriate io_fns.
+	 */
+	u32 pfuse = readl(syscon + 0x34);
+	u32 chip_type = pfuse & 0x1f;
+	u32 chip_ver  = (pfuse >> 8) & 0x7;
+
+	if ((chip_type == 0 || chip_type == 9) && (chip_ver == 0)) {
+		/* AXM5516v1.0 needs low-level locking */
+		default_io_fn = &ncr_io_fn_lock;
+	} else {
+		/* no low-level locking needed */
+		default_io_fn = &ncr_io_fn_nolock;
+	}
+
 	nca_address = ioremap(NCA_PHYS_ADDRESS, 0x20000);
 
 #ifdef APB2SER_PHY_PHYS_ADDRESS
-- 
1.7.9.5



More information about the linux-yocto mailing list