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