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

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

Signed-off-by: Gary McGee <gary.mc...@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

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

Reply via email to