[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