From: Anders Berg <anders.b...@intel.com>

Signed-off-by: Anders Berg <anders.b...@intel.com>
---
 arch/arm/mach-axxia/axxia.c |    1 -
 drivers/misc/lsi-ncr.c      |  407 +++++++++++++++++++++----------------------
 2 files changed, 202 insertions(+), 206 deletions(-)

diff --git a/arch/arm/mach-axxia/axxia.c b/arch/arm/mach-axxia/axxia.c
index c1e472e..e3ddd21 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 fbf101b..d65ec00 100644
--- a/drivers/misc/lsi-ncr.c
+++ b/drivers/misc/lsi-ncr.c
@@ -21,8 +21,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/jiffies.h>
-
 #include <linux/io.h>
 
 #include "lsi-ncr.h"
@@ -41,8 +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 unsigned long flags;
+
+/* 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;
 
 #define LOCK_DOMAIN 0
 
@@ -93,146 +100,167 @@ typedef union {
        } __packed bits;
 } __packed command_data_register_2_t;
 
-#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); })
+unsigned long
+ncr_register_read(unsigned *address)
+{
+       unsigned long value = __raw_readl(address);
+
+       return be32_to_cpu(value);
+}
+
+void
+ncr_register_write(const unsigned value, unsigned *address)
+{
+       __raw_writel(cpu_to_be32(value), address);
+}
 
 /*
   ----------------------------------------------------------------------
-  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 = ioread32be(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
 */
 
-void
-inline ncr_register_write(const unsigned value, unsigned *address)
+static void
+nca_register_write(const unsigned value, unsigned *address)
 {
-       axxia_write32be(value, address);
-       asm volatile ("mcr p15,0,%0,c7,c5,4" : : "r" (0));  /* isb */
+       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);
 }
 
-#else
+/* 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 offset = (0xff80 + (domain * 4));
 
-/*
-  ----------------------------------------------------------------------
-  ncr_register_read
-*/
+       while (nca_register_read((unsigned *)(nca_address + offset)) != 0)
+               cpu_relax();
+}
 
-inline unsigned long
-ncr_register_read(unsigned *address)
+static void
+ncr_amp_unlock(int domain)
 {
-       unsigned long value;
+       unsigned long offset = (0xff80 + (domain * 4));
 
-       value = in_be32((unsigned *)address);
+       nca_register_write(0, (unsigned *)(nca_address + offset));
+}
+#else
+       static void ncr_amp_lock(int domain) {}
+       static void ncr_amp_unlock(int domain) {}
+#endif
 
-       return value;
+/**
+* Used to serialize all access to NCA PIO interface.
+*/
+int
+ncr_lock(int domain)
+{
+       raw_spin_lock_irqsave(&ncr_spin_lock, ncr_spin_flags);
+       ncr_amp_lock(domain);
+       return 0;
+}
+EXPORT_SYMBOL(ncr_lock);
+
+/**
+ * Used to serialize all access to NCA PIO interface.
+ */
+void
+ncr_unlock(int domain)
+{
+       ncr_amp_unlock(domain);
+       raw_spin_unlock_irqrestore(&ncr_spin_lock, ncr_spin_flags);
 }
+EXPORT_SYMBOL(ncr_unlock);
+
 
 /*
-  ----------------------------------------------------------------------
-  ncr_register_write
+  
------------------------------------------------------------------------------
+  ncr_pio_error_dump
 */
 
-inline void
-ncr_register_write(const unsigned value, unsigned *address)
+static void
+ncr_pio_error_dump(char *str)
 {
-       out_be32(address, value);
+       unsigned long cdr0, cdr1, cdr2;
+       unsigned long stat0, stat1;
 
-       return;
-}
+       cdr0 = nca_register_read((unsigned *)(nca_address + 0xf0));
+       cdr1 = nca_register_read((unsigned *)(nca_address + 0xf4));
+       cdr2 = nca_register_read((unsigned *)(nca_address + 0xf8));
 
-#endif
+       stat0 = nca_register_read((unsigned *)(nca_address + 0xe4));
+       stat1 = nca_register_read((unsigned *)(nca_address + 0xe8));
+
+       pr_err("lsi-ncr: %8s failed, error status : 0x%08lx 0x%08lx\n",
+                       str, stat0, stat1);
+       pr_err("lsi-ncr:  CDR0-2: 0x%08lx 0x%08lx 0x%08lx\n",
+                       cdr0, cdr1, cdr2);
 
+}
 /*
   
------------------------------------------------------------------------------
-  ncr_lock
+  ncr_check_pio_status
 */
 
 static int
-ncr_lock(int domain)
+ncr_check_pio_status(char *str)
 {
-       unsigned long offset;
-       unsigned long value;
        unsigned long timeout = jiffies + msecs_to_jiffies(1000);
        command_data_register_0_t cdr0;
 
-       raw_spin_lock_irqsave(&ncr_spin_lock, flags);
-       offset = (0xff80 + (domain * 4));
-
-       do {
-               value = ncr_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, flags);
-               pr_err("ncr_lock() Timeout!\n");
-               BUG();
-
-               return -1;
-       }
        /*
          Make sure any previous commands completed, and check for errors.
        */
-       timeout = jiffies + msecs_to_jiffies(1000);
 
        do {
                cdr0.raw =
-                       ncr_register_read((unsigned *)(nca_address + 0xf0));
-       } while ((0x1 == cdr0.bits.status) &&
-               (time_before(jiffies, timeout)));
-
-       if (!(time_before(jiffies, timeout))) {
-               raw_spin_unlock_irqrestore(&ncr_spin_lock, flags);
-               pr_err("ncr_lock() Previous command didn't complete!\n");
+                               nca_register_read((unsigned *)(nca_address + 
0xf0));
+               } while ((0x1 == cdr0.bits.start_done) &&
+                               (time_before(jiffies, timeout)));
+
+               if (0x1 == cdr0.bits.start_done) {
+                       /* timed out without completing */
+                       pr_err("lsi-ncr: PIO operation timeout cdr0=0x%08lx!\n",
+                                       cdr0.raw);
+                       ncr_pio_error_dump(str);
                BUG();
-
                return -1;
        }
-       if (0x2 == cdr0.bits.status)
-               pr_err("Previous ncr access failed!\n");
-
-       return 0;
-}
-EXPORT_SYMBOL(ncr_lock);
-
-/*
-  
------------------------------------------------------------------------------
-  ncr_unlock
-*/
 
-static void
-ncr_unlock(int domain)
-{
-       unsigned long offset;
-
-       offset = (0xff80 + (domain * 4));
-       ncr_register_write(0, (unsigned *)(nca_address + offset));
-       raw_spin_unlock_irqrestore(&ncr_spin_lock, flags);
+       if (0x3 != cdr0.bits.status) {
+               /* completed with non-success status */
+               ncr_pio_error_dump(str);
+               /* clear CDR0 to allow subsequent commands to complete */
+               nca_register_write(0, (unsigned *) (nca_address + 0xf0));
+       }
 
-       return;
+       return 0;
 }
-EXPORT_SYMBOL(ncr_unlock);
 
 /*
   ======================================================================
@@ -248,26 +276,18 @@ EXPORT_SYMBOL(ncr_unlock);
 */
 
 int
-ncr_read(unsigned long region, unsigned long address, int number,
+ncr_read_nolock(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) && (NCP_NODE_ID(region) != 0x115)) {
+               /* make sure any previous command has completed */
+               if (0 != ncr_check_pio_status("previous"))
+                       return -1;
+
                /*
                Set up the read command.
                */
@@ -275,11 +295,11 @@ ncr_read(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);
-               ncr_register_write(cdr2.raw, (unsigned *) (nca_address + 0xf8));
+               nca_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));
+               nca_register_write(cdr1.raw, (unsigned *) (nca_address + 0xf4));
 
                cdr0.raw = 0;
                cdr0.bits.start_done = 1;
@@ -290,36 +310,16 @@ ncr_read(unsigned long region, unsigned long address, int 
number,
                cdr0.bits.cmd_type = 4;
                /* TODO: Verify number... */
                cdr0.bits.dbs = (number - 1);
-               ncr_register_write(cdr0.raw, (unsigned *) (nca_address + 0xf0));
-               /* Memory Barrier */
+               nca_register_write(cdr0.raw, (unsigned *) (nca_address + 0xf0));
+               /* Memory barrier */
                mb();
 
                /*
                Wait for completion.
                */
 
-               do {
-                       --wfc_timeout;
-                       cdr0.raw =
-                               ncr_register_read((unsigned *)
-                                                 (nca_address + 0xf0));
-               } while (1 == cdr0.bits.start_done && 0 < wfc_timeout);
-
-               if (0 == wfc_timeout) {
-                       ncr_unlock(LOCK_DOMAIN);
-                       pr_err("ncr_read() Timeout!\n");
-                       BUG();
-
+               if (0 != ncr_check_pio_status("read"))
                        return -1;
-               }
-
-               if (0x3 != cdr0.bits.status) {
-                       ncr_unlock(LOCK_DOMAIN);
-                       pr_err("ncr_write() failed: 0x%x\n",
-                              cdr0.bits.status);
-
-                       return -1;
-               }
 
                /*
                Copy data words to the buffer.
@@ -328,7 +328,7 @@ ncr_read(unsigned long region, unsigned long address, int 
number,
                address = (unsigned long)(nca_address + 0x1000);
                while (4 <= number) {
                        *((unsigned long *) buffer) =
-                               ncr_register_read((unsigned *) address);
+                               nca_register_read((unsigned *) address);
                        address += 4;
                        buffer += 4;
                        number -= 4;
@@ -336,11 +336,13 @@ ncr_read(unsigned long region, unsigned long address, int 
number,
 
                if (0 < number) {
                        unsigned long temp =
-                               ncr_register_read((unsigned *) address);
+                               nca_register_read((unsigned *) address);
                        memcpy((void *) buffer, &temp, 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));
@@ -356,10 +358,8 @@ ncr_read(unsigned long region, unsigned long address, int 
number,
                        }
                } else {
                        void __iomem *base;
-                       if (0xffff < address) {
-                               ncr_unlock(LOCK_DOMAIN);
+                       if (0xffff < address)
                                return -1;
-                       }
 
                        switch (NCP_TARGET_ID(region)) {
                        case 0:
@@ -381,7 +381,6 @@ ncr_read(unsigned long region, unsigned long address, int 
number,
                                base = (apb2ser0_address + 0x230);
                                break;
                        default:
-                               ncr_unlock(LOCK_DOMAIN);
                                return -1;
                        }
                        if ((NCP_TARGET_ID(region) == 0x1) ||
@@ -397,10 +396,8 @@ ncr_read(unsigned long region, unsigned long address, int 
number,
                        } while (0 != (*((unsigned long *) buffer) & 0x80000000)
                                        && 0 < wfc_timeout);
 
-                       if (0 == wfc_timeout) {
-                               ncr_unlock(LOCK_DOMAIN);
+                       if (0 == wfc_timeout)
                                return -1;
-                       }
 
                        if ((NCP_TARGET_ID(region) == 0x1) ||
                                (NCP_TARGET_ID(region) == 0x4)) {
@@ -413,15 +410,38 @@ ncr_read(unsigned long region, unsigned long address, int 
number,
 
                }
 #else
-               ncr_unlock(LOCK_DOMAIN);
                return -1;
 #endif /* APB2SER_PHY_PHYS_ADDRESS */
        }
 
-       ncr_unlock(LOCK_DOMAIN);
 
        return 0;
 }
+EXPORT_SYMBOL(ncr_read_nolock);
+
+
+int
+ncr_read(unsigned long region, unsigned long address, int number,
+        void *buffer)
+{
+       int     rc;
+
+       if (NULL == nca_address)
+               return -1;
+
+#ifdef APB2SER_PHY_PHYS_ADDRESS
+       if (NULL == apb2ser0_address)
+               return -1;
+#endif /* APB2SER_PHY_PHYS_ADDRESS */
+
+       ncr_lock(LOCK_DOMAIN);
+
+       rc = ncr_read_nolock(region, address, number, buffer);
+
+       ncr_unlock(LOCK_DOMAIN);
+
+       return rc;
+}
 EXPORT_SYMBOL(ncr_read);
 
 /*
@@ -430,28 +450,20 @@ EXPORT_SYMBOL(ncr_read);
 */
 
 int
-ncr_write(unsigned long region, unsigned long address, int number,
-         void *buffer)
+ncr_write_nolock(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) && (NCP_NODE_ID(region) != 0x115)) {
+               /* make sure any previous command has completed */
+               if (0 != ncr_check_pio_status("previous"))
+                       return -1;
+
                /*
                  Set up the write.
                */
@@ -459,11 +471,11 @@ ncr_write(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);
-               ncr_register_write(cdr2.raw, (unsigned *) (nca_address + 0xf8));
+               nca_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));
+               nca_register_write(cdr1.raw, (unsigned *) (nca_address + 0xf4));
 
                /*
                  Copy from buffer to the data words.
@@ -472,7 +484,7 @@ ncr_write(unsigned long region, unsigned long address, int 
number,
                data_word_base = (unsigned long)(nca_address + 0x1000);
 
                while (4 <= number) {
-                       ncr_register_write(*((unsigned long *) buffer),
+                       nca_register_write(*((unsigned long *) buffer),
                                        (unsigned *) data_word_base);
                        data_word_base += 4;
                        buffer += 4;
@@ -483,7 +495,7 @@ ncr_write(unsigned long region, unsigned long address, int 
number,
                        unsigned long temp = 0;
 
                        memcpy((void *) &temp, (void *) buffer, number);
-                       ncr_register_write(temp, (unsigned *) data_word_base);
+                       nca_register_write(temp, (unsigned *) data_word_base);
                        data_word_base += number;
                        buffer += number;
                        number = 0;
@@ -498,46 +510,20 @@ ncr_write(unsigned long region, unsigned long address, 
int number,
                cdr0.bits.cmd_type = 5;
                /* TODO: Verify number... */
                cdr0.bits.dbs = dbs;
-               ncr_register_write(cdr0.raw, (unsigned *) (nca_address + 0xf0));
-               /* Memory Barrier */
+               nca_register_write(cdr0.raw, (unsigned *) (nca_address + 0xf0));
+               /* Memory barrier */
                mb();
 
                /*
                Wait for completion.
                */
 
-               do {
-                       --wfc_timeout;
-                       cdr0.raw =
-                               ncr_register_read((unsigned *)
-                                                 (nca_address + 0xf0));
-               } while (1 == cdr0.bits.start_done && 0 < wfc_timeout);
-
-               if (0 == wfc_timeout) {
-                       ncr_unlock(LOCK_DOMAIN);
-                       pr_err("ncr_write() Timeout!\n");
-                       BUG();
-
+               if (0 != ncr_check_pio_status("write"))
                        return -1;
-               }
-
-               /*
-               Check status.
-               */
-
-               if ((0x3 != cdr0.bits.status) && (0x00c00000) >> 22) {
-                       unsigned long status;
-
-                       status = ncr_register_read((unsigned *)(nca_address +
-                                                               0xe4));
-                       ncr_unlock(LOCK_DOMAIN);
-                       pr_err("ncr_write() Error: 0x%x 0x%lx\n",
-                              cdr0.bits.status, status);
-
-                       return status;
-               }
        } 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));
@@ -553,10 +539,8 @@ ncr_write(unsigned long region, unsigned long address, int 
number,
                }
        } else {
                void __iomem *base;
-               if (0xffff < address) {
-                       ncr_unlock(LOCK_DOMAIN);
+               if (0xffff < address)
                        return -1;
-               }
 
                switch (NCP_TARGET_ID(region)) {
                case 0:
@@ -578,7 +562,6 @@ ncr_write(unsigned long region, unsigned long address, int 
number,
                        base = (apb2ser0_address + 0x230);
                        break;
                default:
-                       ncr_unlock(LOCK_DOMAIN);
                        return -1;
                }
                if ((NCP_TARGET_ID(region) == 0x1) ||
@@ -596,20 +579,40 @@ ncr_write(unsigned long region, unsigned long address, 
int number,
                        } while (0 != (*((unsigned long *) buffer) & 0x80000000)
                                && 0 < wfc_timeout);
 
-                       if (0 == wfc_timeout) {
-                               ncr_unlock(LOCK_DOMAIN);
+                       if (0 == wfc_timeout)
                                return -1;
-                       }
                }
 #else
-               ncr_unlock(LOCK_DOMAIN);
                return -1;
 #endif /* APB2SER_PHY_PHYS_ADDRESS */
        }
 
+       return 0;
+}
+EXPORT_SYMBOL(ncr_write_nolock);
+
+
+int
+ncr_write(unsigned long region, unsigned long address, int number,
+         void *buffer)
+{
+       int rc = 0;
+
+       if (NULL == nca_address)
+               return -1;
+
+#ifdef APB2SER_PHY_PHYS_ADDRESS
+       if (NULL == apb2ser0_address)
+               return -1;
+#endif /* APB2SER_PHY_PHYS_ADDRESS */
+
+       ncr_lock(LOCK_DOMAIN);
+
+       rc = ncr_write_nolock(region, address, number, buffer);
+
        ncr_unlock(LOCK_DOMAIN);
 
-       return 0;
+       return rc;
 }
 EXPORT_SYMBOL(ncr_write);
 
@@ -618,7 +621,7 @@ EXPORT_SYMBOL(ncr_write);
   ncr_init
 */
 
-int
+static int
 ncr_init(void)
 {
        nca_address = ioremap(NCA_PHYS_ADDRESS, 0x20000);
@@ -631,16 +634,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. */
@@ -652,12 +653,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

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

Reply via email to