From 80c394fab89649585089f5bd5013f2d99e5756ef Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 11 Jun 2015 13:27:51 +0200 Subject: cxl: Add explicit precision specifiers C99 says that a precision given as simply '.' with no following digits or * should be interpreted as 0. The kernel's printf implementation, however, treats this case as if the precision was omitted. C99 also says that if both the precision and value are 0, no digits should be printed. Even if the kernel followed C99 to the letter, I don't think that would be particularly useful in these cases. For consistency with most other format strings in the file, use an explicit precision of 16 and add a 0x prefix. Signed-off-by: Rasmus Villemoes Signed-off-by: Michael Ellerman --- drivers/misc/cxl/irq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c index 680cd263436d..2b64bb43b304 100644 --- a/drivers/misc/cxl/irq.c +++ b/drivers/misc/cxl/irq.c @@ -147,7 +147,7 @@ static irqreturn_t cxl_irq(int irq, void *data, struct cxl_irq_info *irq_info) if (dsisr & CXL_PSL_DSISR_An_PE) return handle_psl_slice_error(ctx, dsisr, irq_info->errstat); if (dsisr & CXL_PSL_DSISR_An_AE) { - pr_devel("CXL interrupt: AFU Error %.llx\n", irq_info->afu_err); + pr_devel("CXL interrupt: AFU Error 0x%.16llx\n", irq_info->afu_err); if (ctx->pending_afu_err) { /* @@ -158,7 +158,7 @@ static irqreturn_t cxl_irq(int irq, void *data, struct cxl_irq_info *irq_info) * probably best that we log them somewhere: */ dev_err_ratelimited(&ctx->afu->dev, "CXL AFU Error " - "undelivered to pe %i: %.llx\n", + "undelivered to pe %i: 0x%.16llx\n", ctx->pe, irq_info->afu_err); } else { spin_lock(&ctx->lock); -- cgit v1.2.3 From de369538436ae0caf784c69187ad0e53152b7ddf Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Thu, 11 Jun 2015 13:27:52 +0200 Subject: cxl: use more common format specifier A precision of 16 (%.16llx) has the same effect as a field width of 16 along with passing the 0 flag (%016llx), but the latter is much more common in the kernel tree. Update cxl to use that. Signed-off-by: Rasmus Villemoes Acked-by: Ian Munsie Signed-off-by: Michael Ellerman --- drivers/misc/cxl/irq.c | 32 ++++++++++++++++---------------- drivers/misc/cxl/native.c | 10 +++++----- drivers/misc/cxl/pci.c | 10 +++++----- drivers/misc/cxl/trace.h | 10 +++++----- 4 files changed, 31 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c index 2b64bb43b304..abfce494ca6b 100644 --- a/drivers/misc/cxl/irq.c +++ b/drivers/misc/cxl/irq.c @@ -30,12 +30,12 @@ static irqreturn_t handle_psl_slice_error(struct cxl_context *ctx, u64 dsisr, u6 serr = cxl_p1n_read(ctx->afu, CXL_PSL_SERR_An); afu_debug = cxl_p1n_read(ctx->afu, CXL_AFU_DEBUG_An); - dev_crit(&ctx->afu->dev, "PSL ERROR STATUS: 0x%.16llx\n", errstat); - dev_crit(&ctx->afu->dev, "PSL_FIR1: 0x%.16llx\n", fir1); - dev_crit(&ctx->afu->dev, "PSL_FIR2: 0x%.16llx\n", fir2); - dev_crit(&ctx->afu->dev, "PSL_SERR_An: 0x%.16llx\n", serr); - dev_crit(&ctx->afu->dev, "PSL_FIR_SLICE_An: 0x%.16llx\n", fir_slice); - dev_crit(&ctx->afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%.16llx\n", afu_debug); + dev_crit(&ctx->afu->dev, "PSL ERROR STATUS: 0x%016llx\n", errstat); + dev_crit(&ctx->afu->dev, "PSL_FIR1: 0x%016llx\n", fir1); + dev_crit(&ctx->afu->dev, "PSL_FIR2: 0x%016llx\n", fir2); + dev_crit(&ctx->afu->dev, "PSL_SERR_An: 0x%016llx\n", serr); + dev_crit(&ctx->afu->dev, "PSL_FIR_SLICE_An: 0x%016llx\n", fir_slice); + dev_crit(&ctx->afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%016llx\n", afu_debug); dev_crit(&ctx->afu->dev, "STOPPING CXL TRACE\n"); cxl_stop_trace(ctx->afu->adapter); @@ -54,10 +54,10 @@ irqreturn_t cxl_slice_irq_err(int irq, void *data) fir_slice = cxl_p1n_read(afu, CXL_PSL_FIR_SLICE_An); errstat = cxl_p2n_read(afu, CXL_PSL_ErrStat_An); afu_debug = cxl_p1n_read(afu, CXL_AFU_DEBUG_An); - dev_crit(&afu->dev, "PSL_SERR_An: 0x%.16llx\n", serr); - dev_crit(&afu->dev, "PSL_FIR_SLICE_An: 0x%.16llx\n", fir_slice); - dev_crit(&afu->dev, "CXL_PSL_ErrStat_An: 0x%.16llx\n", errstat); - dev_crit(&afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%.16llx\n", afu_debug); + dev_crit(&afu->dev, "PSL_SERR_An: 0x%016llx\n", serr); + dev_crit(&afu->dev, "PSL_FIR_SLICE_An: 0x%016llx\n", fir_slice); + dev_crit(&afu->dev, "CXL_PSL_ErrStat_An: 0x%016llx\n", errstat); + dev_crit(&afu->dev, "CXL_PSL_AFU_DEBUG_An: 0x%016llx\n", afu_debug); cxl_p1n_write(afu, CXL_PSL_SERR_An, serr); @@ -72,7 +72,7 @@ static irqreturn_t cxl_irq_err(int irq, void *data) WARN(1, "CXL ERROR interrupt %i\n", irq); err_ivte = cxl_p1_read(adapter, CXL_PSL_ErrIVTE); - dev_crit(&adapter->dev, "PSL_ErrIVTE: 0x%.16llx\n", err_ivte); + dev_crit(&adapter->dev, "PSL_ErrIVTE: 0x%016llx\n", err_ivte); dev_crit(&adapter->dev, "STOPPING CXL TRACE\n"); cxl_stop_trace(adapter); @@ -80,7 +80,7 @@ static irqreturn_t cxl_irq_err(int irq, void *data) fir1 = cxl_p1_read(adapter, CXL_PSL_FIR1); fir2 = cxl_p1_read(adapter, CXL_PSL_FIR2); - dev_crit(&adapter->dev, "PSL_FIR1: 0x%.16llx\nPSL_FIR2: 0x%.16llx\n", fir1, fir2); + dev_crit(&adapter->dev, "PSL_FIR1: 0x%016llx\nPSL_FIR2: 0x%016llx\n", fir1, fir2); return IRQ_HANDLED; } @@ -147,7 +147,7 @@ static irqreturn_t cxl_irq(int irq, void *data, struct cxl_irq_info *irq_info) if (dsisr & CXL_PSL_DSISR_An_PE) return handle_psl_slice_error(ctx, dsisr, irq_info->errstat); if (dsisr & CXL_PSL_DSISR_An_AE) { - pr_devel("CXL interrupt: AFU Error 0x%.16llx\n", irq_info->afu_err); + pr_devel("CXL interrupt: AFU Error 0x%016llx\n", irq_info->afu_err); if (ctx->pending_afu_err) { /* @@ -158,7 +158,7 @@ static irqreturn_t cxl_irq(int irq, void *data, struct cxl_irq_info *irq_info) * probably best that we log them somewhere: */ dev_err_ratelimited(&ctx->afu->dev, "CXL AFU Error " - "undelivered to pe %i: 0x%.16llx\n", + "undelivered to pe %i: 0x%016llx\n", ctx->pe, irq_info->afu_err); } else { spin_lock(&ctx->lock); @@ -211,8 +211,8 @@ static irqreturn_t cxl_irq_multiplexed(int irq, void *data) } rcu_read_unlock(); - WARN(1, "Unable to demultiplex CXL PSL IRQ for PE %i DSISR %.16llx DAR" - " %.16llx\n(Possible AFU HW issue - was a term/remove acked" + WARN(1, "Unable to demultiplex CXL PSL IRQ for PE %i DSISR %016llx DAR" + " %016llx\n(Possible AFU HW issue - was a term/remove acked" " with outstanding transactions?)\n", ph, irq_info.dsisr, irq_info.dar); return fail_psl_irq(afu, &irq_info); diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c index 10567f245818..fc9310dd2367 100644 --- a/drivers/misc/cxl/native.c +++ b/drivers/misc/cxl/native.c @@ -41,7 +41,7 @@ static int afu_control(struct cxl_afu *afu, u64 command, rc = -EBUSY; goto out; } - pr_devel_ratelimited("AFU control... (0x%.16llx)\n", + pr_devel_ratelimited("AFU control... (0x%016llx)\n", AFU_Cntl | command); cpu_relax(); AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An); @@ -120,13 +120,13 @@ int cxl_psl_purge(struct cxl_afu *afu) goto out; } dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An); - pr_devel_ratelimited("PSL purging... PSL_CNTL: 0x%.16llx PSL_DSISR: 0x%.16llx\n", PSL_CNTL, dsisr); + pr_devel_ratelimited("PSL purging... PSL_CNTL: 0x%016llx PSL_DSISR: 0x%016llx\n", PSL_CNTL, dsisr); if (dsisr & CXL_PSL_DSISR_TRANS) { dar = cxl_p2n_read(afu, CXL_PSL_DAR_An); - dev_notice(&afu->dev, "PSL purge terminating pending translation, DSISR: 0x%.16llx, DAR: 0x%.16llx\n", dsisr, dar); + dev_notice(&afu->dev, "PSL purge terminating pending translation, DSISR: 0x%016llx, DAR: 0x%016llx\n", dsisr, dar); cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_AE); } else if (dsisr) { - dev_notice(&afu->dev, "PSL purge acknowledging pending non-translation fault, DSISR: 0x%.16llx\n", dsisr); + dev_notice(&afu->dev, "PSL purge acknowledging pending non-translation fault, DSISR: 0x%016llx\n", dsisr); cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_A); } else { cpu_relax(); @@ -684,7 +684,7 @@ static void recover_psl_err(struct cxl_afu *afu, u64 errstat) { u64 dsisr; - pr_devel("RECOVERING FROM PSL ERROR... (0x%.16llx)\n", errstat); + pr_devel("RECOVERING FROM PSL ERROR... (0x%016llx)\n", errstat); /* Clear PSL_DSISR[PE] */ dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An); diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 32ad09705949..eb05efb74eed 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -656,7 +656,7 @@ static int sanitise_afu_regs(struct cxl_afu *afu) */ reg = cxl_p2n_read(afu, CXL_AFU_Cntl_An); if ((reg & CXL_AFU_Cntl_An_ES_MASK) != CXL_AFU_Cntl_An_ES_Disabled) { - dev_warn(&afu->dev, "WARNING: AFU was not disabled: %#.16llx\n", reg); + dev_warn(&afu->dev, "WARNING: AFU was not disabled: %#016llx\n", reg); if (__cxl_afu_reset(afu)) return -EIO; if (cxl_afu_disable(afu)) @@ -677,7 +677,7 @@ static int sanitise_afu_regs(struct cxl_afu *afu) cxl_p2n_write(afu, CXL_SSTP0_An, 0x0000000000000000); reg = cxl_p2n_read(afu, CXL_PSL_DSISR_An); if (reg) { - dev_warn(&afu->dev, "AFU had pending DSISR: %#.16llx\n", reg); + dev_warn(&afu->dev, "AFU had pending DSISR: %#016llx\n", reg); if (reg & CXL_PSL_DSISR_TRANS) cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_AE); else @@ -686,12 +686,12 @@ static int sanitise_afu_regs(struct cxl_afu *afu) reg = cxl_p1n_read(afu, CXL_PSL_SERR_An); if (reg) { if (reg & ~0xffff) - dev_warn(&afu->dev, "AFU had pending SERR: %#.16llx\n", reg); + dev_warn(&afu->dev, "AFU had pending SERR: %#016llx\n", reg); cxl_p1n_write(afu, CXL_PSL_SERR_An, reg & ~0xffff); } reg = cxl_p2n_read(afu, CXL_PSL_ErrStat_An); if (reg) { - dev_warn(&afu->dev, "AFU had pending error status: %#.16llx\n", reg); + dev_warn(&afu->dev, "AFU had pending error status: %#016llx\n", reg); cxl_p2n_write(afu, CXL_PSL_ErrStat_An, reg); } @@ -893,7 +893,7 @@ static int cxl_map_adapter_regs(struct cxl *adapter, struct pci_dev *dev) if (pci_request_region(dev, 0, "priv 1 regs")) goto err2; - pr_devel("cxl_map_adapter_regs: p1: %#.16llx %#llx, p2: %#.16llx %#llx", + pr_devel("cxl_map_adapter_regs: p1: %#016llx %#llx, p2: %#016llx %#llx", p1_base(dev), p1_size(dev), p2_base(dev), p2_size(dev)); if (!(adapter->p1_mmio = ioremap(p1_base(dev), p1_size(dev)))) diff --git a/drivers/misc/cxl/trace.h b/drivers/misc/cxl/trace.h index ae434d87887e..6e1e2adfba8e 100644 --- a/drivers/misc/cxl/trace.h +++ b/drivers/misc/cxl/trace.h @@ -105,7 +105,7 @@ TRACE_EVENT(cxl_attach, __entry->num_interrupts = num_interrupts; ), - TP_printk("afu%i.%i pid=%i pe=%i wed=0x%.16llx irqs=%i amr=0x%llx", + TP_printk("afu%i.%i pid=%i pe=%i wed=0x%016llx irqs=%i amr=0x%llx", __entry->card, __entry->afu, __entry->pid, @@ -177,7 +177,7 @@ TRACE_EVENT(cxl_psl_irq, __entry->dar = dar; ), - TP_printk("afu%i.%i pe=%i irq=%i dsisr=%s dar=0x%.16llx", + TP_printk("afu%i.%i pe=%i irq=%i dsisr=%s dar=0x%016llx", __entry->card, __entry->afu, __entry->pe, @@ -233,7 +233,7 @@ TRACE_EVENT(cxl_ste_miss, __entry->dar = dar; ), - TP_printk("afu%i.%i pe=%i dar=0x%.16llx", + TP_printk("afu%i.%i pe=%i dar=0x%016llx", __entry->card, __entry->afu, __entry->pe, @@ -264,7 +264,7 @@ TRACE_EVENT(cxl_ste_write, __entry->v = v; ), - TP_printk("afu%i.%i pe=%i SSTE[%i] E=0x%.16llx V=0x%.16llx", + TP_printk("afu%i.%i pe=%i SSTE[%i] E=0x%016llx V=0x%016llx", __entry->card, __entry->afu, __entry->pe, @@ -295,7 +295,7 @@ TRACE_EVENT(cxl_pte_miss, __entry->dar = dar; ), - TP_printk("afu%i.%i pe=%i dsisr=%s dar=0x%.16llx", + TP_printk("afu%i.%i pe=%i dsisr=%s dar=0x%016llx", __entry->card, __entry->afu, __entry->pe, -- cgit v1.2.3 From b2a02ac65e40fb3900d176828a407b44bb33f9f4 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Wed, 8 Jul 2015 17:14:36 +0200 Subject: cxl: Destroy cxl_adapter_idr on module_exit Destroy cxl_adapter_idr on module exit, reclaiming the allocated memory. This was detected by the following semantic patch (written by Luis Rodriguez ) @ defines_module_init @ declarer name module_init, module_exit; declarer name DEFINE_IDR; identifier init; @@ module_init(init); @ defines_module_exit @ identifier exit; @@ module_exit(exit); @ declares_idr depends on defines_module_init && defines_module_exit @ identifier idr; @@ DEFINE_IDR(idr); @ on_exit_calls_destroy depends on declares_idr && defines_module_exit @ identifier declares_idr.idr, defines_module_exit.exit; @@ exit(void) { ... idr_destroy(&idr); ... } @ missing_module_idr_destroy depends on declares_idr && defines_module_exit && !on_exit_calls_destroy @ identifier declares_idr.idr, defines_module_exit.exit; @@ exit(void) { ... +idr_destroy(&idr); } Signed-off-by: Johannes Thumshirn Acked-by: Ian Munsie Signed-off-by: Michael Ellerman --- drivers/misc/cxl/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/misc/cxl/main.c b/drivers/misc/cxl/main.c index 4a164ab8b35a..9fde75ed4fac 100644 --- a/drivers/misc/cxl/main.c +++ b/drivers/misc/cxl/main.c @@ -222,6 +222,7 @@ static void exit_cxl(void) cxl_debugfs_exit(); cxl_file_exit(); unregister_cxl_calls(&cxl_calls); + idr_destroy(&cxl_adapter_idr); } module_init(init_cxl); -- cgit v1.2.3 From bd664f892e3e2b01c79197cad3111d54b7aedf39 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Thu, 9 Jul 2015 09:39:42 +0200 Subject: cxl: Destroy afu->contexts_idr on release of an afu Destroy afu->contexts_idr on release of an afu, reclaiming the allocated memory. Signed-off-by: Johannes Thumshirn Acked-by: Ian Munsie Signed-off-by: Michael Ellerman --- drivers/misc/cxl/pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index eb05efb74eed..1d314f1f95fe 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -551,6 +551,7 @@ static void cxl_release_afu(struct device *dev) pr_devel("cxl_release_afu\n"); + idr_destroy(&afu->contexts_idr); kfree(afu); } -- cgit v1.2.3 From 368857c16c595eb7537cc0846708ddaa57a3a25b Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Wed, 29 Jul 2015 14:07:22 +1000 Subject: cxl: Don't ignore add_process_element() result when attaching context Currently when attaching a context in dedicated mode, we ignore the result of add_process_element(), which could potentially fail. If add_process_element() returns an error, pass it back to the caller. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- drivers/misc/cxl/native.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c index fc9310dd2367..f74ff80266c8 100644 --- a/drivers/misc/cxl/native.c +++ b/drivers/misc/cxl/native.c @@ -492,9 +492,7 @@ static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr) if ((result = cxl_afu_check_and_enable(ctx->afu))) return result; - add_process_element(ctx); - - return 0; + return add_process_element(ctx); } static int deactivate_afu_directed(struct cxl_afu *afu) -- cgit v1.2.3 From fe2b592173ff0274e70dc44d1d28c19bb995aa7c Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Fri, 31 Jul 2015 14:08:58 +0200 Subject: windfarm: decrement client count when unregistering wf_unregister_client() increments the client count when a client unregisters. That is obviously incorrect. Decrement that client count instead. Fixes: 75722d3992f5 ("[PATCH] ppc64: Thermal control for SMU based machines") Signed-off-by: Paul Bolle Signed-off-by: Michael Ellerman --- drivers/macintosh/windfarm_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index 3ee198b65843..cc7ece1712b5 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c @@ -435,7 +435,7 @@ int wf_unregister_client(struct notifier_block *nb) { mutex_lock(&wf_lock); blocking_notifier_chain_unregister(&wf_client_list, nb); - wf_client_count++; + wf_client_count--; if (wf_client_count == 0) wf_stop_thread(); mutex_unlock(&wf_lock); -- cgit v1.2.3 From ca94bbab1a0336f34066f27c503767b8181db5b1 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Fri, 31 Jul 2015 14:12:20 +0200 Subject: windfarm: make wf_critical_overtemp() static wf_critical_overtemp() is exported. But nothing uses that export. That's unsurprising because there's no header that defines it. Stop exporting that function and make it static. Signed-off-by: Paul Bolle Signed-off-by: Michael Ellerman --- drivers/macintosh/windfarm_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index cc7ece1712b5..681e5b4d4b6a 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c @@ -72,7 +72,7 @@ static inline void wf_notify(int event, void *param) blocking_notifier_call_chain(&wf_client_list, event, param); } -int wf_critical_overtemp(void) +static int wf_critical_overtemp(void) { static char * critical_overtemp_path = "/sbin/critical_overtemp"; char *argv[] = { critical_overtemp_path, NULL }; @@ -84,7 +84,6 @@ int wf_critical_overtemp(void) return call_usermodehelper(critical_overtemp_path, argv, envp, UMH_WAIT_EXEC); } -EXPORT_SYMBOL_GPL(wf_critical_overtemp); static int wf_thread_func(void *data) { -- cgit v1.2.3 From a368c29cf105485d2c34fb5d09d2dbe813e483e1 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Fri, 31 Jul 2015 14:14:20 +0200 Subject: windfarm: remove three exported but unused functions wf_find_control(), wf_find_sensor(), and wf_is_overtemp() are exported but unused. Remove these three functions. Signed-off-by: Paul Bolle Signed-off-by: Michael Ellerman --- drivers/macintosh/windfarm.h | 4 ---- drivers/macintosh/windfarm_core.c | 42 --------------------------------------- 2 files changed, 46 deletions(-) (limited to 'drivers') diff --git a/drivers/macintosh/windfarm.h b/drivers/macintosh/windfarm.h index 028cdac2d33d..901c42f71b5a 100644 --- a/drivers/macintosh/windfarm.h +++ b/drivers/macintosh/windfarm.h @@ -53,11 +53,9 @@ struct wf_control { * the kref and wf_unregister_control will decrement it, thus the * object creating/disposing a given control shouldn't assume it * still exists after wf_unregister_control has been called. - * wf_find_control will inc the refcount for you */ extern int wf_register_control(struct wf_control *ct); extern void wf_unregister_control(struct wf_control *ct); -extern struct wf_control * wf_find_control(const char *name); extern int wf_get_control(struct wf_control *ct); extern void wf_put_control(struct wf_control *ct); @@ -117,7 +115,6 @@ struct wf_sensor { /* Same lifetime rules as controls */ extern int wf_register_sensor(struct wf_sensor *sr); extern void wf_unregister_sensor(struct wf_sensor *sr); -extern struct wf_sensor * wf_find_sensor(const char *name); extern int wf_get_sensor(struct wf_sensor *sr); extern void wf_put_sensor(struct wf_sensor *sr); @@ -144,7 +141,6 @@ extern int wf_unregister_client(struct notifier_block *nb); /* Overtemp conditions. Those are refcounted */ extern void wf_set_overtemp(void); extern void wf_clear_overtemp(void); -extern int wf_is_overtemp(void); #define WF_EVENT_NEW_CONTROL 0 /* param is wf_control * */ #define WF_EVENT_NEW_SENSOR 1 /* param is wf_sensor * */ diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index 681e5b4d4b6a..465d770ab0bb 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c @@ -254,24 +254,6 @@ void wf_unregister_control(struct wf_control *ct) } EXPORT_SYMBOL_GPL(wf_unregister_control); -struct wf_control * wf_find_control(const char *name) -{ - struct wf_control *ct; - - mutex_lock(&wf_lock); - list_for_each_entry(ct, &wf_controls, link) { - if (!strcmp(ct->name, name)) { - if (wf_get_control(ct)) - ct = NULL; - mutex_unlock(&wf_lock); - return ct; - } - } - mutex_unlock(&wf_lock); - return NULL; -} -EXPORT_SYMBOL_GPL(wf_find_control); - int wf_get_control(struct wf_control *ct) { if (!try_module_get(ct->ops->owner)) @@ -367,24 +349,6 @@ void wf_unregister_sensor(struct wf_sensor *sr) } EXPORT_SYMBOL_GPL(wf_unregister_sensor); -struct wf_sensor * wf_find_sensor(const char *name) -{ - struct wf_sensor *sr; - - mutex_lock(&wf_lock); - list_for_each_entry(sr, &wf_sensors, link) { - if (!strcmp(sr->name, name)) { - if (wf_get_sensor(sr)) - sr = NULL; - mutex_unlock(&wf_lock); - return sr; - } - } - mutex_unlock(&wf_lock); - return NULL; -} -EXPORT_SYMBOL_GPL(wf_find_sensor); - int wf_get_sensor(struct wf_sensor *sr) { if (!try_module_get(sr->ops->owner)) @@ -473,12 +437,6 @@ void wf_clear_overtemp(void) } EXPORT_SYMBOL_GPL(wf_clear_overtemp); -int wf_is_overtemp(void) -{ - return (wf_overtemp != 0); -} -EXPORT_SYMBOL_GPL(wf_is_overtemp); - static int __init windfarm_core_init(void) { DBG("wf: core loaded\n"); -- cgit v1.2.3 From cf184dc2dd33847f4b211b01d8c7ec0526e6c5e4 Mon Sep 17 00:00:00 2001 From: Jaiprakash Singh Date: Wed, 20 May 2015 21:17:11 -0500 Subject: fsl_ifc: Change IO accessor based on endianness IFC IO accressor are set at run time based on IFC IP registers endianness.IFC node in DTS file contains information about endianness. Signed-off-by: Jaiprakash Singh Signed-off-by: Scott Wood Acked-by: Brian Norris --- drivers/memory/fsl_ifc.c | 43 +++++-- drivers/mtd/nand/fsl_ifc_nand.c | 258 ++++++++++++++++++++-------------------- 2 files changed, 160 insertions(+), 141 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/fsl_ifc.c b/drivers/memory/fsl_ifc.c index 410c39749872..e87459f6d686 100644 --- a/drivers/memory/fsl_ifc.c +++ b/drivers/memory/fsl_ifc.c @@ -62,7 +62,7 @@ int fsl_ifc_find(phys_addr_t addr_base) return -ENODEV; for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) { - u32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr); + u32 cspr = ifc_in32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr); if (cspr & CSPR_V && (cspr & CSPR_BA) == convert_ifc_address(addr_base)) return i; @@ -79,16 +79,16 @@ static int fsl_ifc_ctrl_init(struct fsl_ifc_ctrl *ctrl) /* * Clear all the common status and event registers */ - if (in_be32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER) - out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER); + if (ifc_in32(&ifc->cm_evter_stat) & IFC_CM_EVTER_STAT_CSER) + ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat); /* enable all error and events */ - out_be32(&ifc->cm_evter_en, IFC_CM_EVTER_EN_CSEREN); + ifc_out32(IFC_CM_EVTER_EN_CSEREN, &ifc->cm_evter_en); /* enable all error and event interrupts */ - out_be32(&ifc->cm_evter_intr_en, IFC_CM_EVTER_INTR_EN_CSERIREN); - out_be32(&ifc->cm_erattr0, 0x0); - out_be32(&ifc->cm_erattr1, 0x0); + ifc_out32(IFC_CM_EVTER_INTR_EN_CSERIREN, &ifc->cm_evter_intr_en); + ifc_out32(0x0, &ifc->cm_erattr0); + ifc_out32(0x0, &ifc->cm_erattr1); return 0; } @@ -127,9 +127,9 @@ static u32 check_nand_stat(struct fsl_ifc_ctrl *ctrl) spin_lock_irqsave(&nand_irq_lock, flags); - stat = in_be32(&ifc->ifc_nand.nand_evter_stat); + stat = ifc_in32(&ifc->ifc_nand.nand_evter_stat); if (stat) { - out_be32(&ifc->ifc_nand.nand_evter_stat, stat); + ifc_out32(stat, &ifc->ifc_nand.nand_evter_stat); ctrl->nand_stat = stat; wake_up(&ctrl->nand_wait); } @@ -161,16 +161,16 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data) irqreturn_t ret = IRQ_NONE; /* read for chip select error */ - cs_err = in_be32(&ifc->cm_evter_stat); + cs_err = ifc_in32(&ifc->cm_evter_stat); if (cs_err) { dev_err(ctrl->dev, "transaction sent to IFC is not mapped to" "any memory bank 0x%08X\n", cs_err); /* clear the chip select error */ - out_be32(&ifc->cm_evter_stat, IFC_CM_EVTER_STAT_CSER); + ifc_out32(IFC_CM_EVTER_STAT_CSER, &ifc->cm_evter_stat); /* read error attribute registers print the error information */ - status = in_be32(&ifc->cm_erattr0); - err_addr = in_be32(&ifc->cm_erattr1); + status = ifc_in32(&ifc->cm_erattr0); + err_addr = ifc_in32(&ifc->cm_erattr1); if (status & IFC_CM_ERATTR0_ERTYP_READ) dev_err(ctrl->dev, "Read transaction error" @@ -231,6 +231,23 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev) goto err; } + version = ifc_in32(&fsl_ifc_ctrl_dev->regs->ifc_rev) & + FSL_IFC_VERSION_MASK; + banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; + dev_info(&dev->dev, "IFC version %d.%d, %d banks\n", + version >> 24, (version >> 16) & 0xf, banks); + + fsl_ifc_ctrl_dev->version = version; + fsl_ifc_ctrl_dev->banks = banks; + + if (of_property_read_bool(dev->dev.of_node, "little-endian")) { + fsl_ifc_ctrl_dev->little_endian = true; + dev_dbg(&dev->dev, "IFC REGISTERS are LITTLE endian\n"); + } else { + fsl_ifc_ctrl_dev->little_endian = false; + dev_dbg(&dev->dev, "IFC REGISTERS are BIG endian\n"); + } + version = ioread32be(&fsl_ifc_ctrl_dev->regs->ifc_rev) & FSL_IFC_VERSION_MASK; banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8; diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 51394e59901b..a4e27e891153 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -238,8 +238,8 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) ifc_nand_ctrl->page = page_addr; /* Program ROW0/COL0 */ - iowrite32be(page_addr, &ifc->ifc_nand.row0); - iowrite32be((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); + ifc_out32(page_addr, &ifc->ifc_nand.row0); + ifc_out32((oob ? IFC_NAND_COL_MS : 0) | column, &ifc->ifc_nand.col0); buf_num = page_addr & priv->bufnum_mask; @@ -301,19 +301,19 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) int i; /* set the chip select for NAND Transaction */ - iowrite32be(priv->bank << IFC_NAND_CSEL_SHIFT, - &ifc->ifc_nand.nand_csel); + ifc_out32(priv->bank << IFC_NAND_CSEL_SHIFT, + &ifc->ifc_nand.nand_csel); dev_vdbg(priv->dev, "%s: fir0=%08x fcr0=%08x\n", __func__, - ioread32be(&ifc->ifc_nand.nand_fir0), - ioread32be(&ifc->ifc_nand.nand_fcr0)); + ifc_in32(&ifc->ifc_nand.nand_fir0), + ifc_in32(&ifc->ifc_nand.nand_fcr0)); ctrl->nand_stat = 0; /* start read/write seq */ - iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); + ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); /* wait for command complete flag or timeout */ wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, @@ -336,7 +336,7 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) int sector_end = sector + chip->ecc.steps - 1; for (i = sector / 4; i <= sector_end / 4; i++) - eccstat[i] = ioread32be(&ifc->ifc_nand.nand_eccstat[i]); + eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]); for (i = sector; i <= sector_end; i++) { errors = check_read_ecc(mtd, ctrl, eccstat, i); @@ -376,33 +376,33 @@ static void fsl_ifc_do_read(struct nand_chip *chip, /* Program FIR/IFC_NAND_FCR0 for Small/Large page */ if (mtd->writesize > 512) { - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | - (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), - &ifc->ifc_nand.nand_fir0); - iowrite32be(0x0, &ifc->ifc_nand.nand_fir1); - - iowrite32be((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | - (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), - &ifc->ifc_nand.nand_fcr0); + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP4_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); + + ifc_out32((NAND_CMD_READ0 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_READSTART << IFC_NAND_FCR0_CMD1_SHIFT), + &ifc->ifc_nand.nand_fcr0); } else { - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), - &ifc->ifc_nand.nand_fir0); - iowrite32be(0x0, &ifc->ifc_nand.nand_fir1); + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_RBCD << IFC_NAND_FIR0_OP3_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32(0x0, &ifc->ifc_nand.nand_fir1); if (oob) - iowrite32be(NAND_CMD_READOOB << - IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); + ifc_out32(NAND_CMD_READOOB << + IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); else - iowrite32be(NAND_CMD_READ0 << - IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); + ifc_out32(NAND_CMD_READ0 << + IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); } } @@ -422,7 +422,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, switch (command) { /* READ0 read the entire buffer to use hardware ECC. */ case NAND_CMD_READ0: - iowrite32be(0, &ifc->ifc_nand.nand_fbcr); + ifc_out32(0, &ifc->ifc_nand.nand_fbcr); set_addr(mtd, 0, page_addr, 0); ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; @@ -437,7 +437,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* READOOB reads only the OOB because no ECC is performed. */ case NAND_CMD_READOOB: - iowrite32be(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); + ifc_out32(mtd->oobsize - column, &ifc->ifc_nand.nand_fbcr); set_addr(mtd, column, page_addr, 1); ifc_nand_ctrl->read_bytes = mtd->writesize + mtd->oobsize; @@ -453,19 +453,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, if (command == NAND_CMD_PARAM) timing = IFC_FIR_OP_RBCD; - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (timing << IFC_NAND_FIR0_OP2_SHIFT), - &ifc->ifc_nand.nand_fir0); - iowrite32be(command << IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); - iowrite32be(column, &ifc->ifc_nand.row3); + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (timing << IFC_NAND_FIR0_OP2_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32(command << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + ifc_out32(column, &ifc->ifc_nand.row3); /* * although currently it's 8 bytes for READID, we always read * the maximum 256 bytes(for PARAM) */ - iowrite32be(256, &ifc->ifc_nand.nand_fbcr); + ifc_out32(256, &ifc->ifc_nand.nand_fbcr); ifc_nand_ctrl->read_bytes = 256; set_addr(mtd, 0, 0, 0); @@ -480,16 +480,16 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* ERASE2 uses the block and page address from ERASE1 */ case NAND_CMD_ERASE2: - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), - &ifc->ifc_nand.nand_fir0); + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR0_OP2_SHIFT), + &ifc->ifc_nand.nand_fir0); - iowrite32be((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | - (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), - &ifc->ifc_nand.nand_fcr0); + ifc_out32((NAND_CMD_ERASE1 << IFC_NAND_FCR0_CMD0_SHIFT) | + (NAND_CMD_ERASE2 << IFC_NAND_FCR0_CMD1_SHIFT), + &ifc->ifc_nand.nand_fcr0); - iowrite32be(0, &ifc->ifc_nand.nand_fbcr); + ifc_out32(0, &ifc->ifc_nand.nand_fbcr); ifc_nand_ctrl->read_bytes = 0; fsl_ifc_run_command(mtd); return; @@ -506,19 +506,18 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); - iowrite32be( - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | - (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), - &ifc->ifc_nand.nand_fir0); - iowrite32be( - (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | - (IFC_FIR_OP_RDSTAT << - IFC_NAND_FIR1_OP6_SHIFT) | - (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), - &ifc->ifc_nand.nand_fir1); + ifc_out32( + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32( + (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | + (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP6_SHIFT) | + (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), + &ifc->ifc_nand.nand_fir1); } else { nand_fcr0 = ((NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD1_SHIFT) | @@ -527,20 +526,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD3_SHIFT)); - iowrite32be( + ifc_out32( (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP1_SHIFT) | (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP2_SHIFT) | (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT), &ifc->ifc_nand.nand_fir0); - iowrite32be( - (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | - (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | - (IFC_FIR_OP_RDSTAT << - IFC_NAND_FIR1_OP7_SHIFT) | - (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), - &ifc->ifc_nand.nand_fir1); + ifc_out32( + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | + (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | + (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR1_OP7_SHIFT) | + (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), + &ifc->ifc_nand.nand_fir1); if (column >= mtd->writesize) nand_fcr0 |= @@ -555,7 +553,7 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, column -= mtd->writesize; ifc_nand_ctrl->oob = 1; } - iowrite32be(nand_fcr0, &ifc->ifc_nand.nand_fcr0); + ifc_out32(nand_fcr0, &ifc->ifc_nand.nand_fcr0); set_addr(mtd, column, page_addr, ifc_nand_ctrl->oob); return; } @@ -563,24 +561,26 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ case NAND_CMD_PAGEPROG: { if (ifc_nand_ctrl->oob) { - iowrite32be(ifc_nand_ctrl->index - - ifc_nand_ctrl->column, - &ifc->ifc_nand.nand_fbcr); + ifc_out32(ifc_nand_ctrl->index - + ifc_nand_ctrl->column, + &ifc->ifc_nand.nand_fbcr); } else { - iowrite32be(0, &ifc->ifc_nand.nand_fbcr); + ifc_out32(0, &ifc->ifc_nand.nand_fbcr); } fsl_ifc_run_command(mtd); return; } - case NAND_CMD_STATUS: - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), - &ifc->ifc_nand.nand_fir0); - iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); - iowrite32be(1, &ifc->ifc_nand.nand_fbcr); + case NAND_CMD_STATUS: { + void __iomem *addr; + + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP1_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + ifc_out32(1, &ifc->ifc_nand.nand_fbcr); set_addr(mtd, 0, 0, 0); ifc_nand_ctrl->read_bytes = 1; @@ -590,17 +590,19 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, * The chip always seems to report that it is * write-protected, even when it is not. */ + addr = ifc_nand_ctrl->addr; if (chip->options & NAND_BUSWIDTH_16) - setbits16(ifc_nand_ctrl->addr, NAND_STATUS_WP); + ifc_out16(ifc_in16(addr) | (NAND_STATUS_WP), addr); else - setbits8(ifc_nand_ctrl->addr, NAND_STATUS_WP); + ifc_out8(ifc_in8(addr) | (NAND_STATUS_WP), addr); return; + } case NAND_CMD_RESET: - iowrite32be(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, - &ifc->ifc_nand.nand_fir0); - iowrite32be(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); + ifc_out32(IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT, + &ifc->ifc_nand.nand_fir0); + ifc_out32(NAND_CMD_RESET << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); fsl_ifc_run_command(mtd); return; @@ -658,7 +660,7 @@ static uint8_t fsl_ifc_read_byte(struct mtd_info *mtd) */ if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { offset = ifc_nand_ctrl->index++; - return in_8(ifc_nand_ctrl->addr + offset); + return ifc_in8(ifc_nand_ctrl->addr + offset); } dev_err(priv->dev, "%s: beyond end of buffer\n", __func__); @@ -680,7 +682,7 @@ static uint8_t fsl_ifc_read_byte16(struct mtd_info *mtd) * next byte. */ if (ifc_nand_ctrl->index < ifc_nand_ctrl->read_bytes) { - data = in_be16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index); + data = ifc_in16(ifc_nand_ctrl->addr + ifc_nand_ctrl->index); ifc_nand_ctrl->index += 2; return (uint8_t) data; } @@ -726,18 +728,18 @@ static int fsl_ifc_wait(struct mtd_info *mtd, struct nand_chip *chip) u32 nand_fsr; /* Use READ_STATUS command, but wait for the device to be ready */ - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), - &ifc->ifc_nand.nand_fir0); - iowrite32be(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); - iowrite32be(1, &ifc->ifc_nand.nand_fbcr); + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_RDSTAT << IFC_NAND_FIR0_OP1_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32(NAND_CMD_STATUS << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + ifc_out32(1, &ifc->ifc_nand.nand_fbcr); set_addr(mtd, 0, 0, 0); ifc_nand_ctrl->read_bytes = 1; fsl_ifc_run_command(mtd); - nand_fsr = ioread32be(&ifc->ifc_nand.nand_fsr); + nand_fsr = ifc_in32(&ifc->ifc_nand.nand_fsr); /* * The chip always seems to report that it is @@ -829,34 +831,34 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) uint32_t cs = priv->bank; /* Save CSOR and CSOR_ext */ - csor = ioread32be(&ifc->csor_cs[cs].csor); - csor_ext = ioread32be(&ifc->csor_cs[cs].csor_ext); + csor = ifc_in32(&ifc->csor_cs[cs].csor); + csor_ext = ifc_in32(&ifc->csor_cs[cs].csor_ext); /* chage PageSize 8K and SpareSize 1K*/ csor_8k = (csor & ~(CSOR_NAND_PGS_MASK)) | 0x0018C000; - iowrite32be(csor_8k, &ifc->csor_cs[cs].csor); - iowrite32be(0x0000400, &ifc->csor_cs[cs].csor_ext); + ifc_out32(csor_8k, &ifc->csor_cs[cs].csor); + ifc_out32(0x0000400, &ifc->csor_cs[cs].csor_ext); /* READID */ - iowrite32be((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), - &ifc->ifc_nand.nand_fir0); - iowrite32be(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, - &ifc->ifc_nand.nand_fcr0); - iowrite32be(0x0, &ifc->ifc_nand.row3); + ifc_out32((IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_UA << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RB << IFC_NAND_FIR0_OP2_SHIFT), + &ifc->ifc_nand.nand_fir0); + ifc_out32(NAND_CMD_READID << IFC_NAND_FCR0_CMD0_SHIFT, + &ifc->ifc_nand.nand_fcr0); + ifc_out32(0x0, &ifc->ifc_nand.row3); - iowrite32be(0x0, &ifc->ifc_nand.nand_fbcr); + ifc_out32(0x0, &ifc->ifc_nand.nand_fbcr); /* Program ROW0/COL0 */ - iowrite32be(0x0, &ifc->ifc_nand.row0); - iowrite32be(0x0, &ifc->ifc_nand.col0); + ifc_out32(0x0, &ifc->ifc_nand.row0); + ifc_out32(0x0, &ifc->ifc_nand.col0); /* set the chip select for NAND Transaction */ - iowrite32be(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel); + ifc_out32(cs << IFC_NAND_CSEL_SHIFT, &ifc->ifc_nand.nand_csel); /* start read seq */ - iowrite32be(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); + ifc_out32(IFC_NAND_SEQ_STRT_FIR_STRT, &ifc->ifc_nand.nandseq_strt); /* wait for command complete flag or timeout */ wait_event_timeout(ctrl->nand_wait, ctrl->nand_stat, @@ -866,8 +868,8 @@ static void fsl_ifc_sram_init(struct fsl_ifc_mtd *priv) printk(KERN_ERR "fsl-ifc: Failed to Initialise SRAM\n"); /* Restore CSOR and CSOR_ext */ - iowrite32be(csor, &ifc->csor_cs[cs].csor); - iowrite32be(csor_ext, &ifc->csor_cs[cs].csor_ext); + ifc_out32(csor, &ifc->csor_cs[cs].csor); + ifc_out32(csor_ext, &ifc->csor_cs[cs].csor_ext); } static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) @@ -884,7 +886,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* fill in nand_chip structure */ /* set up function call table */ - if ((ioread32be(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16) + if ((ifc_in32(&ifc->cspr_cs[priv->bank].cspr)) & CSPR_PORT_SIZE_16) chip->read_byte = fsl_ifc_read_byte16; else chip->read_byte = fsl_ifc_read_byte; @@ -898,13 +900,13 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr; - iowrite32be(0x0, &ifc->ifc_nand.ncfgr); + ifc_out32(0x0, &ifc->ifc_nand.ncfgr); /* set up nand options */ chip->bbt_options = NAND_BBT_USE_FLASH; chip->options = NAND_NO_SUBPAGE_WRITE; - if (ioread32be(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { + if (ifc_in32(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { chip->read_byte = fsl_ifc_read_byte16; chip->options |= NAND_BUSWIDTH_16; } else { @@ -917,7 +919,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) chip->ecc.read_page = fsl_ifc_read_page; chip->ecc.write_page = fsl_ifc_write_page; - csor = ioread32be(&ifc->csor_cs[priv->bank].csor); + csor = ifc_in32(&ifc->csor_cs[priv->bank].csor); /* Hardware generates ECC per 512 Bytes */ chip->ecc.size = 512; @@ -1006,7 +1008,7 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) static int match_bank(struct fsl_ifc_regs __iomem *ifc, int bank, phys_addr_t addr) { - u32 cspr = ioread32be(&ifc->cspr_cs[bank].cspr); + u32 cspr = ifc_in32(&ifc->cspr_cs[bank].cspr); if (!(cspr & CSPR_V)) return 0; @@ -1092,16 +1094,16 @@ static int fsl_ifc_nand_probe(struct platform_device *dev) dev_set_drvdata(priv->dev, priv); - iowrite32be(IFC_NAND_EVTER_EN_OPC_EN | - IFC_NAND_EVTER_EN_FTOER_EN | - IFC_NAND_EVTER_EN_WPER_EN, - &ifc->ifc_nand.nand_evter_en); + ifc_out32(IFC_NAND_EVTER_EN_OPC_EN | + IFC_NAND_EVTER_EN_FTOER_EN | + IFC_NAND_EVTER_EN_WPER_EN, + &ifc->ifc_nand.nand_evter_en); /* enable NAND Machine Interrupts */ - iowrite32be(IFC_NAND_EVTER_INTR_OPCIR_EN | - IFC_NAND_EVTER_INTR_FTOERIR_EN | - IFC_NAND_EVTER_INTR_WPERIR_EN, - &ifc->ifc_nand.nand_evter_intr_en); + ifc_out32(IFC_NAND_EVTER_INTR_OPCIR_EN | + IFC_NAND_EVTER_INTR_FTOERIR_EN | + IFC_NAND_EVTER_INTR_WPERIR_EN, + &ifc->ifc_nand.nand_evter_intr_en); priv->mtd.name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start); if (!priv->mtd.name) { ret = -ENOMEM; -- cgit v1.2.3 From d3d73f4b38a8ece19846b8fa2519cdc8583f35bb Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 7 Aug 2015 13:18:17 +1000 Subject: cxl: Compile with -Werror It's a good idea, and it brings us in line with the rest of arch/powerpc. Signed-off-by: Daniel Axtens Acked-by: Michael Neuling Signed-off-by: Michael Ellerman --- drivers/misc/cxl/Makefile | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/cxl/Makefile b/drivers/misc/cxl/Makefile index 14e3f8219a11..6f484dfe78f9 100644 --- a/drivers/misc/cxl/Makefile +++ b/drivers/misc/cxl/Makefile @@ -1,3 +1,5 @@ +ccflags-y := -Werror + cxl-y += main.o file.o irq.o fault.o native.o cxl-y += context.o sysfs.o debugfs.o pci.o trace.o cxl-y += vphb.o api.o -- cgit v1.2.3 From 3d6b040e73387570521da4aedfd03d2643acdae7 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 7 Aug 2015 13:18:18 +1000 Subject: cxl: sparse: Make declarations static A few declarations were identified by sparse as needing to be static: drivers/misc/cxl/irq.c:408:6: warning: symbol 'afu_irq_name_free' was not declared. Should it be static? drivers/misc/cxl/irq.c:467:6: warning: symbol 'afu_register_hwirqs' was not declared. Should it be static? drivers/misc/cxl/file.c:254:6: warning: symbol 'afu_compat_ioctl' was not declared. Should it be static? drivers/misc/cxl/file.c:399:30: warning: symbol 'afu_master_fops' was not declared. Should it be static? Make them static. Signed-off-by: Daniel Axtens Acked-by: Michael Neuling Signed-off-by: Michael Ellerman --- drivers/misc/cxl/file.c | 4 ++-- drivers/misc/cxl/irq.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index e3f4b69527a9..c8c8bfa2679b 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -251,7 +251,7 @@ long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return -EINVAL; } -long afu_compat_ioctl(struct file *file, unsigned int cmd, +static long afu_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return afu_ioctl(file, cmd, arg); @@ -396,7 +396,7 @@ const struct file_operations afu_fops = { .mmap = afu_mmap, }; -const struct file_operations afu_master_fops = { +static const struct file_operations afu_master_fops = { .owner = THIS_MODULE, .open = afu_master_open, .poll = afu_poll, diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c index abfce494ca6b..b6b04374c10c 100644 --- a/drivers/misc/cxl/irq.c +++ b/drivers/misc/cxl/irq.c @@ -405,7 +405,7 @@ void cxl_release_psl_irq(struct cxl_afu *afu) kfree(afu->psl_irq_name); } -void afu_irq_name_free(struct cxl_context *ctx) +static void afu_irq_name_free(struct cxl_context *ctx) { struct cxl_irq_name *irq_name, *tmp; @@ -464,7 +464,7 @@ out: return -ENOMEM; } -void afu_register_hwirqs(struct cxl_context *ctx) +static void afu_register_hwirqs(struct cxl_context *ctx) { irq_hw_number_t hwirq; struct cxl_irq_name *irq_name; -- cgit v1.2.3 From 83c3fee7e78f5a937b734f1ba954c5cdcb2be755 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 7 Aug 2015 13:18:20 +1000 Subject: cxl: sparse: Silence iomem warning in debugfs file creation An IO address, tagged with __iomem, is passed to debugfs_create_file as private data. This requires that it be cast to void *. The cast drops the __iomem annotation and so creates a sparse warning: drivers/misc/cxl/debugfs.c:51:57: warning: cast removes address space of expression The address space marker is added back in the file operations (fops_io_u64). Silence the warning with __force. Signed-off-by: Daniel Axtens Acked-by: Michael Neuling Signed-off-by: Michael Ellerman --- drivers/misc/cxl/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/debugfs.c b/drivers/misc/cxl/debugfs.c index 825c412580bc..18df6f44af2a 100644 --- a/drivers/misc/cxl/debugfs.c +++ b/drivers/misc/cxl/debugfs.c @@ -48,7 +48,7 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_io_x64, debugfs_io_u64_get, debugfs_io_u64_set, "0x static struct dentry *debugfs_create_io_x64(const char *name, umode_t mode, struct dentry *parent, u64 __iomem *value) { - return debugfs_create_file(name, mode, parent, (void *)value, &fops_io_x64); + return debugfs_create_file(name, mode, parent, (void __force *)value, &fops_io_x64); } int cxl_debugfs_adapter_add(struct cxl *adapter) -- cgit v1.2.3 From 588b34be20bc3dd7441c108c95039933699467ab Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 14 Aug 2015 17:41:17 +1000 Subject: cxl: Convert MMIO read/write macros to inline functions We're about to make these more complex, so make them functions first. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- drivers/misc/cxl/cxl.h | 51 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index 4fd66cabde1e..6a93bfbcd826 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -537,10 +537,15 @@ static inline void __iomem *_cxl_p1_addr(struct cxl *cxl, cxl_p1_reg_t reg) return cxl->p1_mmio + cxl_reg_off(reg); } -#define cxl_p1_write(cxl, reg, val) \ - out_be64(_cxl_p1_addr(cxl, reg), val) -#define cxl_p1_read(cxl, reg) \ - in_be64(_cxl_p1_addr(cxl, reg)) +static inline void cxl_p1_write(struct cxl *cxl, cxl_p1_reg_t reg, u64 val) +{ + out_be64(_cxl_p1_addr(cxl, reg), val); +} + +static inline u64 cxl_p1_read(struct cxl *cxl, cxl_p1_reg_t reg) +{ + return in_be64(_cxl_p1_addr(cxl, reg)); +} static inline void __iomem *_cxl_p1n_addr(struct cxl_afu *afu, cxl_p1n_reg_t reg) { @@ -548,26 +553,40 @@ static inline void __iomem *_cxl_p1n_addr(struct cxl_afu *afu, cxl_p1n_reg_t reg return afu->p1n_mmio + cxl_reg_off(reg); } -#define cxl_p1n_write(afu, reg, val) \ - out_be64(_cxl_p1n_addr(afu, reg), val) -#define cxl_p1n_read(afu, reg) \ - in_be64(_cxl_p1n_addr(afu, reg)) +static inline void cxl_p1n_write(struct cxl_afu *afu, cxl_p1n_reg_t reg, u64 val) +{ + out_be64(_cxl_p1n_addr(afu, reg), val); +} + +static inline u64 cxl_p1n_read(struct cxl_afu *afu, cxl_p1n_reg_t reg) +{ + return in_be64(_cxl_p1n_addr(afu, reg)); +} static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg) { return afu->p2n_mmio + cxl_reg_off(reg); } -#define cxl_p2n_write(afu, reg, val) \ - out_be64(_cxl_p2n_addr(afu, reg), val) -#define cxl_p2n_read(afu, reg) \ - in_be64(_cxl_p2n_addr(afu, reg)) +static inline void cxl_p2n_write(struct cxl_afu *afu, cxl_p2n_reg_t reg, u64 val) +{ + out_be64(_cxl_p2n_addr(afu, reg), val); +} +static inline u64 cxl_p2n_read(struct cxl_afu *afu, cxl_p2n_reg_t reg) +{ + return in_be64(_cxl_p2n_addr(afu, reg)); +} -#define cxl_afu_cr_read64(afu, cr, off) \ - in_le64((afu)->afu_desc_mmio + (afu)->crs_offset + ((cr) * (afu)->crs_len) + (off)) -#define cxl_afu_cr_read32(afu, cr, off) \ - in_le32((afu)->afu_desc_mmio + (afu)->crs_offset + ((cr) * (afu)->crs_len) + (off)) +static inline u64 cxl_afu_cr_read64(struct cxl_afu *afu, int cr, u64 off) +{ + return in_le64((afu)->afu_desc_mmio + (afu)->crs_offset + ((cr) * (afu)->crs_len) + (off)); +} + +static inline u32 cxl_afu_cr_read32(struct cxl_afu *afu, int cr, u64 off) +{ + return in_le32((afu)->afu_desc_mmio + (afu)->crs_offset + ((cr) * (afu)->crs_len) + (off)); +} u16 cxl_afu_cr_read16(struct cxl_afu *afu, int cr, u64 off); u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off); -- cgit v1.2.3 From 0b3f9c757cabad4b8101c5fcddddd029ed5506a6 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 14 Aug 2015 17:41:18 +1000 Subject: cxl: Drop commands if the PCI channel is not in normal state If the PCI channel has gone down, don't attempt to poke the hardware. We need to guard every time cxl_whatever_(read|write) is called. This is because a call to those functions will dereference an offset into an mmio register, and the mmio mappings get invalidated in the EEH teardown. Check in the read/write functions in the header. We give them the same semantics as usual PCI operations: - a write to a channel that is down is ignored. - a read from a channel that is down returns all fs. Also, we try to access the MMIO space of a vPHB device as part of the PCI disable path. Because that's a read that bypasses most of our usual checks, we handle it explicitly. As far as user visible warnings go: - Check link state in file ops, return -EIO if down. - Be reasonably quiet if there's an error in a teardown path, or when we already know the hardware is going down. - Throw a big WARN if someone tries to start a CXL operation while the card is down. This gives a useful stacktrace for debugging whatever is doing that. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- drivers/misc/cxl/context.c | 6 +++- drivers/misc/cxl/cxl.h | 44 ++++++++++++++++++++++------ drivers/misc/cxl/file.c | 19 ++++++++++++ drivers/misc/cxl/native.c | 72 ++++++++++++++++++++++++++++++++++++++++++++-- drivers/misc/cxl/vphb.c | 26 +++++++++++++++++ 5 files changed, 155 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c index 1287148629c0..615842115848 100644 --- a/drivers/misc/cxl/context.c +++ b/drivers/misc/cxl/context.c @@ -193,7 +193,11 @@ int __detach_context(struct cxl_context *ctx) if (status != STARTED) return -EBUSY; - WARN_ON(cxl_detach_process(ctx)); + /* Only warn if we detached while the link was OK. + * If detach fails when hw is down, we don't care. + */ + WARN_ON(cxl_detach_process(ctx) && + cxl_adapter_link_ok(ctx->afu->adapter)); flush_work(&ctx->fault_work); /* Only needed for dedicated process */ put_pid(ctx->pid); cxl_ctx_put(); diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index 6a93bfbcd826..9b9e89fd02cc 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -531,6 +531,14 @@ struct cxl_process_element { __be32 software_state; } __packed; +static inline bool cxl_adapter_link_ok(struct cxl *cxl) +{ + struct pci_dev *pdev; + + pdev = to_pci_dev(cxl->dev.parent); + return !pci_channel_offline(pdev); +} + static inline void __iomem *_cxl_p1_addr(struct cxl *cxl, cxl_p1_reg_t reg) { WARN_ON(!cpu_has_feature(CPU_FTR_HVMODE)); @@ -539,12 +547,16 @@ static inline void __iomem *_cxl_p1_addr(struct cxl *cxl, cxl_p1_reg_t reg) static inline void cxl_p1_write(struct cxl *cxl, cxl_p1_reg_t reg, u64 val) { - out_be64(_cxl_p1_addr(cxl, reg), val); + if (likely(cxl_adapter_link_ok(cxl))) + out_be64(_cxl_p1_addr(cxl, reg), val); } static inline u64 cxl_p1_read(struct cxl *cxl, cxl_p1_reg_t reg) { - return in_be64(_cxl_p1_addr(cxl, reg)); + if (likely(cxl_adapter_link_ok(cxl))) + return in_be64(_cxl_p1_addr(cxl, reg)); + else + return ~0ULL; } static inline void __iomem *_cxl_p1n_addr(struct cxl_afu *afu, cxl_p1n_reg_t reg) @@ -555,12 +567,16 @@ static inline void __iomem *_cxl_p1n_addr(struct cxl_afu *afu, cxl_p1n_reg_t reg static inline void cxl_p1n_write(struct cxl_afu *afu, cxl_p1n_reg_t reg, u64 val) { - out_be64(_cxl_p1n_addr(afu, reg), val); + if (likely(cxl_adapter_link_ok(afu->adapter))) + out_be64(_cxl_p1n_addr(afu, reg), val); } static inline u64 cxl_p1n_read(struct cxl_afu *afu, cxl_p1n_reg_t reg) { - return in_be64(_cxl_p1n_addr(afu, reg)); + if (likely(cxl_adapter_link_ok(afu->adapter))) + return in_be64(_cxl_p1n_addr(afu, reg)); + else + return ~0ULL; } static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg) @@ -570,22 +586,34 @@ static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg static inline void cxl_p2n_write(struct cxl_afu *afu, cxl_p2n_reg_t reg, u64 val) { - out_be64(_cxl_p2n_addr(afu, reg), val); + if (likely(cxl_adapter_link_ok(afu->adapter))) + out_be64(_cxl_p2n_addr(afu, reg), val); } static inline u64 cxl_p2n_read(struct cxl_afu *afu, cxl_p2n_reg_t reg) { - return in_be64(_cxl_p2n_addr(afu, reg)); + if (likely(cxl_adapter_link_ok(afu->adapter))) + return in_be64(_cxl_p2n_addr(afu, reg)); + else + return ~0ULL; } static inline u64 cxl_afu_cr_read64(struct cxl_afu *afu, int cr, u64 off) { - return in_le64((afu)->afu_desc_mmio + (afu)->crs_offset + ((cr) * (afu)->crs_len) + (off)); + if (likely(cxl_adapter_link_ok(afu->adapter))) + return in_le64((afu)->afu_desc_mmio + (afu)->crs_offset + + ((cr) * (afu)->crs_len) + (off)); + else + return ~0ULL; } static inline u32 cxl_afu_cr_read32(struct cxl_afu *afu, int cr, u64 off) { - return in_le32((afu)->afu_desc_mmio + (afu)->crs_offset + ((cr) * (afu)->crs_len) + (off)); + if (likely(cxl_adapter_link_ok(afu->adapter))) + return in_le32((afu)->afu_desc_mmio + (afu)->crs_offset + + ((cr) * (afu)->crs_len) + (off)); + else + return 0xffffffff; } u16 cxl_afu_cr_read16(struct cxl_afu *afu, int cr, u64 off); u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off); diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index c8c8bfa2679b..57bdb473749f 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -73,6 +73,11 @@ static int __afu_open(struct inode *inode, struct file *file, bool master) if (!afu->current_mode) goto err_put_afu; + if (!cxl_adapter_link_ok(adapter)) { + rc = -EIO; + goto err_put_afu; + } + if (!(ctx = cxl_context_alloc())) { rc = -ENOMEM; goto err_put_afu; @@ -238,6 +243,9 @@ long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (ctx->status == CLOSED) return -EIO; + if (!cxl_adapter_link_ok(ctx->afu->adapter)) + return -EIO; + pr_devel("afu_ioctl\n"); switch (cmd) { case CXL_IOCTL_START_WORK: @@ -265,6 +273,9 @@ int afu_mmap(struct file *file, struct vm_area_struct *vm) if (ctx->status != STARTED) return -EIO; + if (!cxl_adapter_link_ok(ctx->afu->adapter)) + return -EIO; + return cxl_context_iomap(ctx, vm); } @@ -309,6 +320,9 @@ ssize_t afu_read(struct file *file, char __user *buf, size_t count, int rc; DEFINE_WAIT(wait); + if (!cxl_adapter_link_ok(ctx->afu->adapter)) + return -EIO; + if (count < CXL_READ_MIN_SIZE) return -EINVAL; @@ -319,6 +333,11 @@ ssize_t afu_read(struct file *file, char __user *buf, size_t count, if (ctx_event_pending(ctx)) break; + if (!cxl_adapter_link_ok(ctx->afu->adapter)) { + rc = -EIO; + goto out; + } + if (file->f_flags & O_NONBLOCK) { rc = -EAGAIN; goto out; diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c index f74ff80266c8..44568dd68bb9 100644 --- a/drivers/misc/cxl/native.c +++ b/drivers/misc/cxl/native.c @@ -41,6 +41,13 @@ static int afu_control(struct cxl_afu *afu, u64 command, rc = -EBUSY; goto out; } + + if (!cxl_adapter_link_ok(afu->adapter)) { + afu->enabled = enabled; + rc = -EIO; + goto out; + } + pr_devel_ratelimited("AFU control... (0x%016llx)\n", AFU_Cntl | command); cpu_relax(); @@ -85,6 +92,10 @@ int __cxl_afu_reset(struct cxl_afu *afu) int cxl_afu_check_and_enable(struct cxl_afu *afu) { + if (!cxl_adapter_link_ok(afu->adapter)) { + WARN(1, "Refusing to enable afu while link down!\n"); + return -EIO; + } if (afu->enabled) return 0; return afu_enable(afu); @@ -103,6 +114,12 @@ int cxl_psl_purge(struct cxl_afu *afu) pr_devel("PSL purge request\n"); + if (!cxl_adapter_link_ok(afu->adapter)) { + dev_warn(&afu->dev, "PSL Purge called with link down, ignoring\n"); + rc = -EIO; + goto out; + } + if ((AFU_Cntl & CXL_AFU_Cntl_An_ES_MASK) != CXL_AFU_Cntl_An_ES_Disabled) { WARN(1, "psl_purge request while AFU not disabled!\n"); cxl_afu_disable(afu); @@ -119,6 +136,11 @@ int cxl_psl_purge(struct cxl_afu *afu) rc = -EBUSY; goto out; } + if (!cxl_adapter_link_ok(afu->adapter)) { + rc = -EIO; + goto out; + } + dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An); pr_devel_ratelimited("PSL purging... PSL_CNTL: 0x%016llx PSL_DSISR: 0x%016llx\n", PSL_CNTL, dsisr); if (dsisr & CXL_PSL_DSISR_TRANS) { @@ -215,6 +237,8 @@ int cxl_tlb_slb_invalidate(struct cxl *adapter) dev_warn(&adapter->dev, "WARNING: CXL adapter wide TLBIA timed out!\n"); return -EBUSY; } + if (!cxl_adapter_link_ok(adapter)) + return -EIO; cpu_relax(); } @@ -224,6 +248,8 @@ int cxl_tlb_slb_invalidate(struct cxl *adapter) dev_warn(&adapter->dev, "WARNING: CXL adapter wide SLBIA timed out!\n"); return -EBUSY; } + if (!cxl_adapter_link_ok(adapter)) + return -EIO; cpu_relax(); } return 0; @@ -240,6 +266,11 @@ int cxl_afu_slbia(struct cxl_afu *afu) dev_warn(&afu->dev, "WARNING: CXL AFU SLBIA timed out!\n"); return -EBUSY; } + /* If the adapter has gone down, we can assume that we + * will PERST it and that will invalidate everything. + */ + if (!cxl_adapter_link_ok(afu->adapter)) + return -EIO; cpu_relax(); } return 0; @@ -279,6 +310,8 @@ static void slb_invalid(struct cxl_context *ctx) cxl_p1_write(adapter, CXL_PSL_SLBIA, CXL_TLB_SLB_IQ_LPIDPID); while (1) { + if (!cxl_adapter_link_ok(adapter)) + break; slbia = cxl_p1_read(adapter, CXL_PSL_SLBIA); if (!(slbia & CXL_TLB_SLB_P)) break; @@ -308,6 +341,11 @@ static int do_process_element_cmd(struct cxl_context *ctx, rc = -EBUSY; goto out; } + if (!cxl_adapter_link_ok(ctx->afu->adapter)) { + dev_warn(&ctx->afu->dev, "WARNING: Device link down, aborting Process Element Command!\n"); + rc = -EIO; + goto out; + } state = be64_to_cpup(ctx->afu->sw_command_status); if (state == ~0ULL) { pr_err("cxl: Error adding process element to AFU\n"); @@ -355,8 +393,13 @@ static int terminate_process_element(struct cxl_context *ctx) mutex_lock(&ctx->afu->spa_mutex); pr_devel("%s Terminate pe: %i started\n", __func__, ctx->pe); - rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_TERMINATE, - CXL_PE_SOFTWARE_STATE_V | CXL_PE_SOFTWARE_STATE_T); + /* We could be asked to terminate when the hw is down. That + * should always succeed: it's not running if the hw has gone + * away and is being reset. + */ + if (cxl_adapter_link_ok(ctx->afu->adapter)) + rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_TERMINATE, + CXL_PE_SOFTWARE_STATE_V | CXL_PE_SOFTWARE_STATE_T); ctx->elem->software_state = 0; /* Remove Valid bit */ pr_devel("%s Terminate pe: %i finished\n", __func__, ctx->pe); mutex_unlock(&ctx->afu->spa_mutex); @@ -369,7 +412,14 @@ static int remove_process_element(struct cxl_context *ctx) mutex_lock(&ctx->afu->spa_mutex); pr_devel("%s Remove pe: %i started\n", __func__, ctx->pe); - if (!(rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_REMOVE, 0))) + + /* We could be asked to remove when the hw is down. Again, if + * the hw is down, the PE is gone, so we succeed. + */ + if (cxl_adapter_link_ok(ctx->afu->adapter)) + rc = do_process_element_cmd(ctx, CXL_SPA_SW_CMD_REMOVE, 0); + + if (!rc) ctx->pe_inserted = false; slb_invalid(ctx); pr_devel("%s Remove pe: %i finished\n", __func__, ctx->pe); @@ -612,6 +662,11 @@ int cxl_afu_activate_mode(struct cxl_afu *afu, int mode) if (!(mode & afu->modes_supported)) return -EINVAL; + if (!cxl_adapter_link_ok(afu->adapter)) { + WARN(1, "Device link is down, refusing to activate!\n"); + return -EIO; + } + if (mode == CXL_MODE_DIRECTED) return activate_afu_directed(afu); if (mode == CXL_MODE_DEDICATED) @@ -622,6 +677,11 @@ int cxl_afu_activate_mode(struct cxl_afu *afu, int mode) int cxl_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr) { + if (!cxl_adapter_link_ok(ctx->afu->adapter)) { + WARN(1, "Device link is down, refusing to attach process!\n"); + return -EIO; + } + ctx->kernel = kernel; if (ctx->afu->current_mode == CXL_MODE_DIRECTED) return attach_afu_directed(ctx, wed, amr); @@ -666,6 +726,12 @@ int cxl_get_irq(struct cxl_afu *afu, struct cxl_irq_info *info) { u64 pidtid; + /* If the adapter has gone away, we can't get any meaningful + * information. + */ + if (!cxl_adapter_link_ok(afu->adapter)) + return -EIO; + info->dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An); info->dar = cxl_p2n_read(afu, CXL_PSL_DAR_An); info->dsr = cxl_p2n_read(afu, CXL_PSL_DSR_An); diff --git a/drivers/misc/cxl/vphb.c b/drivers/misc/cxl/vphb.c index 2eba002b580b..2930911c1e42 100644 --- a/drivers/misc/cxl/vphb.c +++ b/drivers/misc/cxl/vphb.c @@ -138,6 +138,26 @@ static int cxl_pcie_config_info(struct pci_bus *bus, unsigned int devfn, return 0; } + +static inline bool cxl_config_link_ok(struct pci_bus *bus) +{ + struct pci_controller *phb; + struct cxl_afu *afu; + + /* Config space IO is based on phb->cfg_addr, which is based on + * afu_desc_mmio. This isn't safe to read/write when the link + * goes down, as EEH tears down MMIO space. + * + * Check if the link is OK before proceeding. + */ + + phb = pci_bus_to_host(bus); + if (phb == NULL) + return false; + afu = (struct cxl_afu *)phb->private_data; + return cxl_adapter_link_ok(afu->adapter); +} + static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn, int offset, int len, u32 *val) { @@ -150,6 +170,9 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn, if (rc) return rc; + if (!cxl_config_link_ok(bus)) + return PCIBIOS_DEVICE_NOT_FOUND; + /* Can only read 32 bits */ *val = (in_le32(ioaddr) >> shift) & mask; return PCIBIOS_SUCCESSFUL; @@ -167,6 +190,9 @@ static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn, if (rc) return rc; + if (!cxl_config_link_ok(bus)) + return PCIBIOS_DEVICE_NOT_FOUND; + /* Can only write 32 bits so do read-modify-write */ mask <<= shift; val <<= shift; -- cgit v1.2.3 From 05155772f642a5b0e421adf2017f993759bf7f95 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 14 Aug 2015 17:41:19 +1000 Subject: cxl: Allocate and release the SPA with the AFU Previously the SPA was allocated and freed upon entering and leaving AFU-directed mode. This causes some issues for error recovery - contexts hold a pointer inside the SPA, and they may persist after the AFU has been detached. We would ideally like to allocate the SPA when the AFU is allocated, and release it until the AFU is released. However, we don't know how big the SPA needs to be until we read the AFU descriptor. Therefore, restructure the code: - Allocate the SPA only once, on the first attach. - Release the SPA only when the entire AFU is being released (not detached). Guard the release with a NULL check, so we don't free if it was never allocated (e.g. dedicated mode) Acked-by: Cyril Bur Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- drivers/misc/cxl/cxl.h | 3 +++ drivers/misc/cxl/native.c | 33 ++++++++++++++++++++++----------- drivers/misc/cxl/pci.c | 2 ++ 3 files changed, 27 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index 9b9e89fd02cc..d540542f9931 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -632,6 +632,9 @@ void unregister_cxl_calls(struct cxl_calls *calls); int cxl_alloc_adapter_nr(struct cxl *adapter); void cxl_remove_adapter_nr(struct cxl *adapter); +int cxl_alloc_spa(struct cxl_afu *afu); +void cxl_release_spa(struct cxl_afu *afu); + int cxl_file_init(void); void cxl_file_exit(void); int cxl_register_adapter(struct cxl *adapter); diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c index 44568dd68bb9..b37f2e8004f5 100644 --- a/drivers/misc/cxl/native.c +++ b/drivers/misc/cxl/native.c @@ -183,10 +183,8 @@ static int spa_max_procs(int spa_size) return ((spa_size / 8) - 96) / 17; } -static int alloc_spa(struct cxl_afu *afu) +int cxl_alloc_spa(struct cxl_afu *afu) { - u64 spap; - /* Work out how many pages to allocate */ afu->spa_order = 0; do { @@ -205,6 +203,13 @@ static int alloc_spa(struct cxl_afu *afu) pr_devel("spa pages: %i afu->spa_max_procs: %i afu->num_procs: %i\n", 1<spa_order, afu->spa_max_procs, afu->num_procs); + return 0; +} + +static void attach_spa(struct cxl_afu *afu) +{ + u64 spap; + afu->sw_command_status = (__be64 *)((char *)afu->spa + ((afu->spa_max_procs + 3) * 128)); @@ -213,14 +218,19 @@ static int alloc_spa(struct cxl_afu *afu) spap |= CXL_PSL_SPAP_V; pr_devel("cxl: SPA allocated at 0x%p. Max processes: %i, sw_command_status: 0x%p CXL_PSL_SPAP_An=0x%016llx\n", afu->spa, afu->spa_max_procs, afu->sw_command_status, spap); cxl_p1n_write(afu, CXL_PSL_SPAP_An, spap); - - return 0; } -static void release_spa(struct cxl_afu *afu) +static inline void detach_spa(struct cxl_afu *afu) { cxl_p1n_write(afu, CXL_PSL_SPAP_An, 0); - free_pages((unsigned long) afu->spa, afu->spa_order); +} + +void cxl_release_spa(struct cxl_afu *afu) +{ + if (afu->spa) { + free_pages((unsigned long) afu->spa, afu->spa_order); + afu->spa = NULL; + } } int cxl_tlb_slb_invalidate(struct cxl *adapter) @@ -447,8 +457,11 @@ static int activate_afu_directed(struct cxl_afu *afu) dev_info(&afu->dev, "Activating AFU directed mode\n"); - if (alloc_spa(afu)) - return -ENOMEM; + if (afu->spa == NULL) { + if (cxl_alloc_spa(afu)) + return -ENOMEM; + } + attach_spa(afu); cxl_p1n_write(afu, CXL_PSL_SCNTL_An, CXL_PSL_SCNTL_An_PM_AFU); cxl_p1n_write(afu, CXL_PSL_AMOR_An, 0xFFFFFFFFFFFFFFFFULL); @@ -559,8 +572,6 @@ static int deactivate_afu_directed(struct cxl_afu *afu) cxl_afu_disable(afu); cxl_psl_purge(afu); - release_spa(afu); - return 0; } diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 1d314f1f95fe..dc26cdd653ad 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -552,6 +552,8 @@ static void cxl_release_afu(struct device *dev) pr_devel("cxl_release_afu\n"); idr_destroy(&afu->contexts_idr); + cxl_release_spa(afu); + kfree(afu); } -- cgit v1.2.3 From e640d2fc811ff8cc1451f92490d3ccc2adcad179 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 14 Aug 2015 17:41:20 +1000 Subject: cxl: Make IRQ release idempotent Check if an IRQ is mapped before releasing it. This will simplify future EEH code by allowing unconditional unmapping of IRQs. Acked-by: Cyril Bur Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- drivers/misc/cxl/irq.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c index b6b04374c10c..62823784f68e 100644 --- a/drivers/misc/cxl/irq.c +++ b/drivers/misc/cxl/irq.c @@ -341,6 +341,9 @@ int cxl_register_psl_err_irq(struct cxl *adapter) void cxl_release_psl_err_irq(struct cxl *adapter) { + if (adapter->err_virq != irq_find_mapping(NULL, adapter->err_hwirq)) + return; + cxl_p1_write(adapter, CXL_PSL_ErrIVTE, 0x0000000000000000); cxl_unmap_irq(adapter->err_virq, adapter); cxl_release_one_irq(adapter, adapter->err_hwirq); @@ -374,6 +377,9 @@ int cxl_register_serr_irq(struct cxl_afu *afu) void cxl_release_serr_irq(struct cxl_afu *afu) { + if (afu->serr_virq != irq_find_mapping(NULL, afu->serr_hwirq)) + return; + cxl_p1n_write(afu, CXL_PSL_SERR_An, 0x0000000000000000); cxl_unmap_irq(afu->serr_virq, afu); cxl_release_one_irq(afu->adapter, afu->serr_hwirq); @@ -400,6 +406,9 @@ int cxl_register_psl_irq(struct cxl_afu *afu) void cxl_release_psl_irq(struct cxl_afu *afu) { + if (afu->psl_virq != irq_find_mapping(NULL, afu->psl_hwirq)) + return; + cxl_unmap_irq(afu->psl_virq, afu); cxl_release_one_irq(afu->adapter, afu->psl_hwirq); kfree(afu->psl_irq_name); -- cgit v1.2.3 From 575e6986f028711287fbbf250e5289a089bac8cb Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 14 Aug 2015 17:41:21 +1000 Subject: cxl: Clean up adapter MMIO unmap path. - MMIO pointer unmapping is guarded by a null pointer check. However, iounmap doesn't null the pointer, just invalidate it. Therefore, explicitly null the pointer after unmapping. - afu_desc_mmio also needs to be unmapped. - PCI regions are allocated in cxl_map_adapter_regs. Therefore they should be released in unmap, not elsewhere. Acked-by: Cyril Bur Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- drivers/misc/cxl/pci.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index dc26cdd653ad..1dae1db3166d 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -539,10 +539,18 @@ err: static void cxl_unmap_slice_regs(struct cxl_afu *afu) { - if (afu->p2n_mmio) + if (afu->p2n_mmio) { iounmap(afu->p2n_mmio); - if (afu->p1n_mmio) + afu->p2n_mmio = NULL; + } + if (afu->p1n_mmio) { iounmap(afu->p1n_mmio); + afu->p1n_mmio = NULL; + } + if (afu->afu_desc_mmio) { + iounmap(afu->afu_desc_mmio); + afu->afu_desc_mmio = NULL; + } } static void cxl_release_afu(struct device *dev) @@ -920,10 +928,16 @@ err1: static void cxl_unmap_adapter_regs(struct cxl *adapter) { - if (adapter->p1_mmio) + if (adapter->p1_mmio) { iounmap(adapter->p1_mmio); - if (adapter->p2_mmio) + adapter->p1_mmio = NULL; + pci_release_region(to_pci_dev(adapter->dev.parent), 2); + } + if (adapter->p2_mmio) { iounmap(adapter->p2_mmio); + adapter->p2_mmio = NULL; + pci_release_region(to_pci_dev(adapter->dev.parent), 0); + } } static int cxl_read_vsec(struct cxl *adapter, struct pci_dev *dev) @@ -1132,8 +1146,6 @@ static void cxl_remove_adapter(struct cxl *adapter) device_unregister(&adapter->dev); - pci_release_region(pdev, 0); - pci_release_region(pdev, 2); pci_disable_device(pdev); } -- cgit v1.2.3 From c044c4154247372c86b468644de88a4e48f903e9 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 14 Aug 2015 17:41:22 +1000 Subject: cxl: Refactor adaptor init/teardown Some aspects of initialisation are done only once in the lifetime of an adapter: for example, allocating memory for the adapter, allocating the adapter number, or setting up sysfs/debugfs files. However, we may want to be able to do some parts of the initialisation multiple times: for example, in error recovery we want to be able to tear down and then re-map IO memory and IRQs. Therefore, refactor CXL init/teardown as follows. - Keep the overarching functions 'cxl_init_adapter' and its pair, 'cxl_remove_adapter'. - Move all 'once only' allocation/freeing steps to the existing 'cxl_alloc_adapter' function, and its pair 'cxl_release_adapter' (This involves moving allocation of the adapter number out of cxl_init_adapter.) - Create two new functions: 'cxl_configure_adapter', and its pair 'cxl_deconfigure_adapter'. These two functions 'wire up' the hardware --- they (de)configure resources that do not need to last the entire lifetime of the adapter Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- drivers/misc/cxl/pci.c | 139 ++++++++++++++++++++++++++++++------------------- 1 file changed, 86 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 1dae1db3166d..feec4800e922 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -966,7 +966,6 @@ static int cxl_read_vsec(struct cxl *adapter, struct pci_dev *dev) CXL_READ_VSEC_BASE_IMAGE(dev, vsec, &adapter->base_image); CXL_READ_VSEC_IMAGE_STATE(dev, vsec, &image_state); adapter->user_image_loaded = !!(image_state & CXL_VSEC_USER_IMAGE_LOADED); - adapter->perst_loads_image = true; adapter->perst_select_user = !!(image_state & CXL_VSEC_USER_IMAGE_LOADED); CXL_READ_VSEC_NAFUS(dev, vsec, &adapter->slices); @@ -1026,22 +1025,33 @@ static void cxl_release_adapter(struct device *dev) pr_devel("cxl_release_adapter\n"); + cxl_remove_adapter_nr(adapter); + kfree(adapter); } -static struct cxl *cxl_alloc_adapter(struct pci_dev *dev) +static struct cxl *cxl_alloc_adapter(void) { struct cxl *adapter; if (!(adapter = kzalloc(sizeof(struct cxl), GFP_KERNEL))) return NULL; - adapter->dev.parent = &dev->dev; - adapter->dev.release = cxl_release_adapter; - pci_set_drvdata(dev, adapter); spin_lock_init(&adapter->afu_list_lock); + if (cxl_alloc_adapter_nr(adapter)) + goto err1; + + if (dev_set_name(&adapter->dev, "card%i", adapter->adapter_num)) + goto err2; + return adapter; + +err2: + cxl_remove_adapter_nr(adapter); +err1: + kfree(adapter); + return NULL; } static int sanitise_adapter_regs(struct cxl *adapter) @@ -1050,57 +1060,96 @@ static int sanitise_adapter_regs(struct cxl *adapter) return cxl_tlb_slb_invalidate(adapter); } -static struct cxl *cxl_init_adapter(struct pci_dev *dev) +/* This should contain *only* operations that can safely be done in + * both creation and recovery. + */ +static int cxl_configure_adapter(struct cxl *adapter, struct pci_dev *dev) { - struct cxl *adapter; - bool free = true; int rc; + adapter->dev.parent = &dev->dev; + adapter->dev.release = cxl_release_adapter; + pci_set_drvdata(dev, adapter); - if (!(adapter = cxl_alloc_adapter(dev))) - return ERR_PTR(-ENOMEM); + rc = pci_enable_device(dev); + if (rc) { + dev_err(&dev->dev, "pci_enable_device failed: %i\n", rc); + return rc; + } if ((rc = cxl_read_vsec(adapter, dev))) - goto err1; + return rc; if ((rc = cxl_vsec_looks_ok(adapter, dev))) - goto err1; + return rc; if ((rc = setup_cxl_bars(dev))) - goto err1; + return rc; if ((rc = switch_card_to_cxl(dev))) - goto err1; - - if ((rc = cxl_alloc_adapter_nr(adapter))) - goto err1; - - if ((rc = dev_set_name(&adapter->dev, "card%i", adapter->adapter_num))) - goto err2; + return rc; if ((rc = cxl_update_image_control(adapter))) - goto err2; + return rc; if ((rc = cxl_map_adapter_regs(adapter, dev))) - goto err2; + return rc; if ((rc = sanitise_adapter_regs(adapter))) - goto err2; + goto err; if ((rc = init_implementation_adapter_regs(adapter, dev))) - goto err3; + goto err; if ((rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_CAPI))) - goto err3; + goto err; /* If recovery happened, the last step is to turn on snooping. * In the non-recovery case this has no effect */ - if ((rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_SNOOP_ON))) { - goto err3; - } + if ((rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_SNOOP_ON))) + goto err; if ((rc = cxl_register_psl_err_irq(adapter))) - goto err3; + goto err; + + return 0; + +err: + cxl_unmap_adapter_regs(adapter); + return rc; + +} + +static void cxl_deconfigure_adapter(struct cxl *adapter) +{ + struct pci_dev *pdev = to_pci_dev(adapter->dev.parent); + + cxl_release_psl_err_irq(adapter); + cxl_unmap_adapter_regs(adapter); + + pci_disable_device(pdev); +} + +static struct cxl *cxl_init_adapter(struct pci_dev *dev) +{ + struct cxl *adapter; + int rc; + + adapter = cxl_alloc_adapter(); + if (!adapter) + return ERR_PTR(-ENOMEM); + + /* Set defaults for parameters which need to persist over + * configure/reconfigure + */ + adapter->perst_loads_image = true; + + rc = cxl_configure_adapter(adapter, dev); + if (rc) { + pci_disable_device(dev); + cxl_release_adapter(&adapter->dev); + return ERR_PTR(rc); + } /* Don't care if this one fails: */ cxl_debugfs_adapter_add(adapter); @@ -1118,35 +1167,25 @@ static struct cxl *cxl_init_adapter(struct pci_dev *dev) return adapter; err_put1: - device_unregister(&adapter->dev); - free = false; + /* This should mirror cxl_remove_adapter, except without the + * sysfs parts + */ cxl_debugfs_adapter_remove(adapter); - cxl_release_psl_err_irq(adapter); -err3: - cxl_unmap_adapter_regs(adapter); -err2: - cxl_remove_adapter_nr(adapter); -err1: - if (free) - kfree(adapter); + cxl_deconfigure_adapter(adapter); + device_unregister(&adapter->dev); return ERR_PTR(rc); } static void cxl_remove_adapter(struct cxl *adapter) { - struct pci_dev *pdev = to_pci_dev(adapter->dev.parent); - - pr_devel("cxl_release_adapter\n"); + pr_devel("cxl_remove_adapter\n"); cxl_sysfs_adapter_remove(adapter); cxl_debugfs_adapter_remove(adapter); - cxl_release_psl_err_irq(adapter); - cxl_unmap_adapter_regs(adapter); - cxl_remove_adapter_nr(adapter); - device_unregister(&adapter->dev); + cxl_deconfigure_adapter(adapter); - pci_disable_device(pdev); + device_unregister(&adapter->dev); } static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id) @@ -1160,15 +1199,9 @@ static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id) if (cxl_verbose) dump_cxl_config_space(dev); - if ((rc = pci_enable_device(dev))) { - dev_err(&dev->dev, "pci_enable_device failed: %i\n", rc); - return rc; - } - adapter = cxl_init_adapter(dev); if (IS_ERR(adapter)) { dev_err(&dev->dev, "cxl_init_adapter failed: %li\n", PTR_ERR(adapter)); - pci_disable_device(dev); return PTR_ERR(adapter); } -- cgit v1.2.3 From d76427b0d89524fdb623774a22a005ba5c400134 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 14 Aug 2015 17:41:23 +1000 Subject: cxl: Refactor AFU init/teardown As with an adapter, some aspects of initialisation are done only once in the lifetime of an AFU: for example, allocating memory, or setting up sysfs/debugfs files. However, we may want to be able to do some parts of the initialisation multiple times: for example, in error recovery we want to be able to tear down and then re-map IO memory and IRQs. Therefore, refactor AFU init/teardown as follows. - Create two new functions: 'cxl_configure_afu', and its pair 'cxl_deconfigure_afu'. As with the adapter functions, these (de)configure resources that do not need to last the entire lifetime of the AFU. - Allocating and releasing memory remain the task of 'cxl_alloc_afu' and 'cxl_release_afu'. - Once-only functions that do not involve allocating/releasing memory stay in the overarching 'cxl_init_afu'/'cxl_remove_afu' pair. However, the task of picking an AFU mode and activating it has been broken out. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- drivers/misc/cxl/pci.c | 95 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index feec4800e922..b63d96023c50 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -753,45 +753,70 @@ ssize_t cxl_afu_read_err_buffer(struct cxl_afu *afu, char *buf, return count; } -static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev) +static int cxl_configure_afu(struct cxl_afu *afu, struct cxl *adapter, struct pci_dev *dev) { - struct cxl_afu *afu; - bool free = true; int rc; - if (!(afu = cxl_alloc_afu(adapter, slice))) - return -ENOMEM; - - if ((rc = dev_set_name(&afu->dev, "afu%i.%i", adapter->adapter_num, slice))) - goto err1; - if ((rc = cxl_map_slice_regs(afu, adapter, dev))) - goto err1; + return rc; if ((rc = sanitise_afu_regs(afu))) - goto err2; + goto err1; /* We need to reset the AFU before we can read the AFU descriptor */ if ((rc = __cxl_afu_reset(afu))) - goto err2; + goto err1; if (cxl_verbose) dump_afu_descriptor(afu); if ((rc = cxl_read_afu_descriptor(afu))) - goto err2; + goto err1; if ((rc = cxl_afu_descriptor_looks_ok(afu))) - goto err2; + goto err1; if ((rc = init_implementation_afu_regs(afu))) - goto err2; + goto err1; if ((rc = cxl_register_serr_irq(afu))) - goto err2; + goto err1; if ((rc = cxl_register_psl_irq(afu))) - goto err3; + goto err2; + + return 0; + +err2: + cxl_release_serr_irq(afu); +err1: + cxl_unmap_slice_regs(afu); + return rc; +} + +static void cxl_deconfigure_afu(struct cxl_afu *afu) +{ + cxl_release_psl_irq(afu); + cxl_release_serr_irq(afu); + cxl_unmap_slice_regs(afu); +} + +static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev) +{ + struct cxl_afu *afu; + int rc; + + afu = cxl_alloc_afu(adapter, slice); + if (!afu) + return -ENOMEM; + + rc = dev_set_name(&afu->dev, "afu%i.%i", adapter->adapter_num, slice); + if (rc) + goto err_free; + + rc = cxl_configure_afu(afu, adapter, dev); + if (rc) + goto err_free; /* Don't care if this fails */ cxl_debugfs_afu_add(afu); @@ -806,10 +831,6 @@ static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev) if ((rc = cxl_sysfs_afu_add(afu))) goto err_put1; - - if ((rc = cxl_afu_select_best_mode(afu))) - goto err_put2; - adapter->afu[afu->slice] = afu; if ((rc = cxl_pci_vphb_add(afu))) @@ -817,21 +838,16 @@ static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev) return 0; -err_put2: - cxl_sysfs_afu_remove(afu); err_put1: - device_unregister(&afu->dev); - free = false; + cxl_deconfigure_afu(afu); cxl_debugfs_afu_remove(afu); - cxl_release_psl_irq(afu); -err3: - cxl_release_serr_irq(afu); -err2: - cxl_unmap_slice_regs(afu); -err1: - if (free) - kfree(afu); + device_unregister(&afu->dev); return rc; + +err_free: + kfree(afu); + return rc; + } static void cxl_remove_afu(struct cxl_afu *afu) @@ -851,10 +867,7 @@ static void cxl_remove_afu(struct cxl_afu *afu) cxl_context_detach_all(afu); cxl_afu_deactivate_mode(afu); - cxl_release_psl_irq(afu); - cxl_release_serr_irq(afu); - cxl_unmap_slice_regs(afu); - + cxl_deconfigure_afu(afu); device_unregister(&afu->dev); } @@ -1206,8 +1219,14 @@ static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id) } for (slice = 0; slice < adapter->slices; slice++) { - if ((rc = cxl_init_afu(adapter, slice, dev))) + if ((rc = cxl_init_afu(adapter, slice, dev))) { dev_err(&dev->dev, "AFU %i failed to initialise: %i\n", slice, rc); + continue; + } + + rc = cxl_afu_select_best_mode(adapter->afu[slice]); + if (rc) + dev_err(&dev->dev, "AFU %i failed to start: %i\n", slice, rc); } return 0; -- cgit v1.2.3 From 4e1efb403c1c016ae831bd9988a7d2e5e0af41a0 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 14 Aug 2015 17:41:24 +1000 Subject: cxl: Don't remove AFUs/vPHBs in cxl_reset If the driver doesn't participate in EEH, the AFUs will be removed by cxl_remove, which will be invoked by EEH. If the driver does particpate in EEH, the vPHB needs to stick around so that the it can particpate. In both cases, we shouldn't remove the AFU/vPHB. Reviewed-by: Cyril Bur Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- drivers/misc/cxl/pci.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index b63d96023c50..2b61cb1ee62c 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -880,11 +880,6 @@ int cxl_reset(struct cxl *adapter) dev_info(&dev->dev, "CXL reset\n"); - for (i = 0; i < adapter->slices; i++) { - cxl_pci_vphb_remove(adapter->afu[i]); - cxl_remove_afu(adapter->afu[i]); - } - /* pcie_warm_reset requests a fundamental pci reset which includes a * PERST assert/deassert. PERST triggers a loading of the image * if "user" or "factory" is selected in sysfs */ -- cgit v1.2.3 From 13e68d8bd05c998cae452a4f3400af1e8edd852e Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 14 Aug 2015 17:41:25 +1000 Subject: cxl: Allow the kernel to trust that an image won't change on PERST. Provide a kernel API and a sysfs entry which allow a user to specify that when a card is PERSTed, it's image will stay the same, allowing it to participate in EEH. cxl_reset is used to reflash the card. In that case, we cannot safely assert that the image will not change. Therefore, disallow cxl_reset if the flag is set. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- drivers/misc/cxl/api.c | 7 +++++++ drivers/misc/cxl/cxl.h | 1 + drivers/misc/cxl/pci.c | 7 +++++++ drivers/misc/cxl/sysfs.c | 26 ++++++++++++++++++++++++++ 4 files changed, 41 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c index 729e0851167d..6a768a9ad22f 100644 --- a/drivers/misc/cxl/api.c +++ b/drivers/misc/cxl/api.c @@ -327,3 +327,10 @@ int cxl_afu_reset(struct cxl_context *ctx) return cxl_afu_check_and_enable(afu); } EXPORT_SYMBOL_GPL(cxl_afu_reset); + +void cxl_perst_reloads_same_image(struct cxl_afu *afu, + bool perst_reloads_same_image) +{ + afu->adapter->perst_same_image = perst_reloads_same_image; +} +EXPORT_SYMBOL_GPL(cxl_perst_reloads_same_image); diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index d540542f9931..cda02412b01e 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -493,6 +493,7 @@ struct cxl { bool user_image_loaded; bool perst_loads_image; bool perst_select_user; + bool perst_same_image; }; int cxl_alloc_one_irq(struct cxl *adapter); diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 2b61cb1ee62c..bfbd6478c0c5 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -878,6 +878,12 @@ int cxl_reset(struct cxl *adapter) int i; u32 val; + if (adapter->perst_same_image) { + dev_warn(&dev->dev, + "cxl: refusing to reset/reflash when perst_reloads_same_image is set.\n"); + return -EINVAL; + } + dev_info(&dev->dev, "CXL reset\n"); /* pcie_warm_reset requests a fundamental pci reset which includes a @@ -1151,6 +1157,7 @@ static struct cxl *cxl_init_adapter(struct pci_dev *dev) * configure/reconfigure */ adapter->perst_loads_image = true; + adapter->perst_same_image = false; rc = cxl_configure_adapter(adapter, dev); if (rc) { diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c index 31f38bc71a3d..6619cf1f6e1f 100644 --- a/drivers/misc/cxl/sysfs.c +++ b/drivers/misc/cxl/sysfs.c @@ -112,12 +112,38 @@ static ssize_t load_image_on_perst_store(struct device *device, return count; } +static ssize_t perst_reloads_same_image_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct cxl *adapter = to_cxl_adapter(device); + + return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->perst_same_image); +} + +static ssize_t perst_reloads_same_image_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cxl *adapter = to_cxl_adapter(device); + int rc; + int val; + + rc = sscanf(buf, "%i", &val); + if ((rc != 1) || !(val == 1 || val == 0)) + return -EINVAL; + + adapter->perst_same_image = (val == 1 ? true : false); + return count; +} + static struct device_attribute adapter_attrs[] = { __ATTR_RO(caia_version), __ATTR_RO(psl_revision), __ATTR_RO(base_image), __ATTR_RO(image_loaded), __ATTR_RW(load_image_on_perst), + __ATTR_RW(perst_reloads_same_image), __ATTR(reset, S_IWUSR, NULL, reset_adapter_store), }; -- cgit v1.2.3 From 9e8df8a219635c5af36a49d78c1f69009b780339 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 14 Aug 2015 17:41:26 +1000 Subject: cxl: EEH support EEH (Enhanced Error Handling) allows a driver to recover from the temporary failure of an attached PCI card. Enable basic CXL support for EEH. Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- drivers/misc/cxl/cxl.h | 1 + drivers/misc/cxl/pci.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/misc/cxl/vphb.c | 8 ++ 3 files changed, 262 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index cda02412b01e..6f5386653dae 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -726,6 +726,7 @@ int cxl_psl_purge(struct cxl_afu *afu); void cxl_stop_trace(struct cxl *cxl); int cxl_pci_vphb_add(struct cxl_afu *afu); +void cxl_pci_vphb_reconfigure(struct cxl_afu *afu); void cxl_pci_vphb_remove(struct cxl_afu *afu); extern struct pci_driver cxl_pci_driver; diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index bfbd6478c0c5..03ddb2d8f974 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -24,6 +24,7 @@ #include #include "cxl.h" +#include #define CXL_PCI_VSEC_ID 0x1280 @@ -1252,10 +1253,262 @@ static void cxl_remove(struct pci_dev *dev) cxl_remove_adapter(adapter); } +static pci_ers_result_t cxl_vphb_error_detected(struct cxl_afu *afu, + pci_channel_state_t state) +{ + struct pci_dev *afu_dev; + pci_ers_result_t result = PCI_ERS_RESULT_NEED_RESET; + pci_ers_result_t afu_result = PCI_ERS_RESULT_NEED_RESET; + + /* There should only be one entry, but go through the list + * anyway + */ + list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) { + if (!afu_dev->driver) + continue; + + afu_dev->error_state = state; + + if (afu_dev->driver->err_handler) + afu_result = afu_dev->driver->err_handler->error_detected(afu_dev, + state); + /* Disconnect trumps all, NONE trumps NEED_RESET */ + if (afu_result == PCI_ERS_RESULT_DISCONNECT) + result = PCI_ERS_RESULT_DISCONNECT; + else if ((afu_result == PCI_ERS_RESULT_NONE) && + (result == PCI_ERS_RESULT_NEED_RESET)) + result = PCI_ERS_RESULT_NONE; + } + return result; +} + +static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct cxl *adapter = pci_get_drvdata(pdev); + struct cxl_afu *afu; + pci_ers_result_t result = PCI_ERS_RESULT_NEED_RESET; + int i; + + /* At this point, we could still have an interrupt pending. + * Let's try to get them out of the way before they do + * anything we don't like. + */ + schedule(); + + /* If we're permanently dead, give up. */ + if (state == pci_channel_io_perm_failure) { + /* Tell the AFU drivers; but we don't care what they + * say, we're going away. + */ + for (i = 0; i < adapter->slices; i++) { + afu = adapter->afu[i]; + cxl_vphb_error_detected(afu, state); + } + return PCI_ERS_RESULT_DISCONNECT; + } + + /* Are we reflashing? + * + * If we reflash, we could come back as something entirely + * different, including a non-CAPI card. As such, by default + * we don't participate in the process. We'll be unbound and + * the slot re-probed. (TODO: check EEH doesn't blindly rebind + * us!) + * + * However, this isn't the entire story: for reliablity + * reasons, we usually want to reflash the FPGA on PERST in + * order to get back to a more reliable known-good state. + * + * This causes us a bit of a problem: if we reflash we can't + * trust that we'll come back the same - we could have a new + * image and been PERSTed in order to load that + * image. However, most of the time we actually *will* come + * back the same - for example a regular EEH event. + * + * Therefore, we allow the user to assert that the image is + * indeed the same and that we should continue on into EEH + * anyway. + */ + if (adapter->perst_loads_image && !adapter->perst_same_image) { + /* TODO take the PHB out of CXL mode */ + dev_info(&pdev->dev, "reflashing, so opting out of EEH!\n"); + return PCI_ERS_RESULT_NONE; + } + + /* + * At this point, we want to try to recover. We'll always + * need a complete slot reset: we don't trust any other reset. + * + * Now, we go through each AFU: + * - We send the driver, if bound, an error_detected callback. + * We expect it to clean up, but it can also tell us to give + * up and permanently detach the card. To simplify things, if + * any bound AFU driver doesn't support EEH, we give up on EEH. + * + * - We detach all contexts associated with the AFU. This + * does not free them, but puts them into a CLOSED state + * which causes any the associated files to return useful + * errors to userland. It also unmaps, but does not free, + * any IRQs. + * + * - We clean up our side: releasing and unmapping resources we hold + * so we can wire them up again when the hardware comes back up. + * + * Driver authors should note: + * + * - Any contexts you create in your kernel driver (except + * those associated with anonymous file descriptors) are + * your responsibility to free and recreate. Likewise with + * any attached resources. + * + * - We will take responsibility for re-initialising the + * device context (the one set up for you in + * cxl_pci_enable_device_hook and accessed through + * cxl_get_context). If you've attached IRQs or other + * resources to it, they remains yours to free. + * + * You can call the same functions to release resources as you + * normally would: we make sure that these functions continue + * to work when the hardware is down. + * + * Two examples: + * + * 1) If you normally free all your resources at the end of + * each request, or if you use anonymous FDs, your + * error_detected callback can simply set a flag to tell + * your driver not to start any new calls. You can then + * clear the flag in the resume callback. + * + * 2) If you normally allocate your resources on startup: + * * Set a flag in error_detected as above. + * * Let CXL detach your contexts. + * * In slot_reset, free the old resources and allocate new ones. + * * In resume, clear the flag to allow things to start. + */ + for (i = 0; i < adapter->slices; i++) { + afu = adapter->afu[i]; + + result = cxl_vphb_error_detected(afu, state); + + /* Only continue if everyone agrees on NEED_RESET */ + if (result != PCI_ERS_RESULT_NEED_RESET) + return result; + + cxl_context_detach_all(afu); + cxl_afu_deactivate_mode(afu); + cxl_deconfigure_afu(afu); + } + cxl_deconfigure_adapter(adapter); + + return result; +} + +static pci_ers_result_t cxl_pci_slot_reset(struct pci_dev *pdev) +{ + struct cxl *adapter = pci_get_drvdata(pdev); + struct cxl_afu *afu; + struct cxl_context *ctx; + struct pci_dev *afu_dev; + pci_ers_result_t afu_result = PCI_ERS_RESULT_RECOVERED; + pci_ers_result_t result = PCI_ERS_RESULT_RECOVERED; + int i; + + if (cxl_configure_adapter(adapter, pdev)) + goto err; + + for (i = 0; i < adapter->slices; i++) { + afu = adapter->afu[i]; + + if (cxl_configure_afu(afu, adapter, pdev)) + goto err; + + if (cxl_afu_select_best_mode(afu)) + goto err; + + cxl_pci_vphb_reconfigure(afu); + + list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) { + /* Reset the device context. + * TODO: make this less disruptive + */ + ctx = cxl_get_context(afu_dev); + + if (ctx && cxl_release_context(ctx)) + goto err; + + ctx = cxl_dev_context_init(afu_dev); + if (!ctx) + goto err; + + afu_dev->dev.archdata.cxl_ctx = ctx; + + if (cxl_afu_check_and_enable(afu)) + goto err; + + afu_dev->error_state = pci_channel_io_normal; + + /* If there's a driver attached, allow it to + * chime in on recovery. Drivers should check + * if everything has come back OK, but + * shouldn't start new work until we call + * their resume function. + */ + if (!afu_dev->driver) + continue; + + if (afu_dev->driver->err_handler && + afu_dev->driver->err_handler->slot_reset) + afu_result = afu_dev->driver->err_handler->slot_reset(afu_dev); + + if (afu_result == PCI_ERS_RESULT_DISCONNECT) + result = PCI_ERS_RESULT_DISCONNECT; + } + } + return result; + +err: + /* All the bits that happen in both error_detected and cxl_remove + * should be idempotent, so we don't need to worry about leaving a mix + * of unconfigured and reconfigured resources. + */ + dev_err(&pdev->dev, "EEH recovery failed. Asking to be disconnected.\n"); + return PCI_ERS_RESULT_DISCONNECT; +} + +static void cxl_pci_resume(struct pci_dev *pdev) +{ + struct cxl *adapter = pci_get_drvdata(pdev); + struct cxl_afu *afu; + struct pci_dev *afu_dev; + int i; + + /* Everything is back now. Drivers should restart work now. + * This is not the place to be checking if everything came back up + * properly, because there's no return value: do that in slot_reset. + */ + for (i = 0; i < adapter->slices; i++) { + afu = adapter->afu[i]; + + list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) { + if (afu_dev->driver && afu_dev->driver->err_handler && + afu_dev->driver->err_handler->resume) + afu_dev->driver->err_handler->resume(afu_dev); + } + } +} + +static const struct pci_error_handlers cxl_err_handler = { + .error_detected = cxl_pci_error_detected, + .slot_reset = cxl_pci_slot_reset, + .resume = cxl_pci_resume, +}; + struct pci_driver cxl_pci_driver = { .name = "cxl-pci", .id_table = cxl_pci_tbl, .probe = cxl_probe, .remove = cxl_remove, .shutdown = cxl_remove, + .err_handler = &cxl_err_handler, }; diff --git a/drivers/misc/cxl/vphb.c b/drivers/misc/cxl/vphb.c index 2930911c1e42..6dd16a6d153f 100644 --- a/drivers/misc/cxl/vphb.c +++ b/drivers/misc/cxl/vphb.c @@ -266,6 +266,14 @@ int cxl_pci_vphb_add(struct cxl_afu *afu) return 0; } +void cxl_pci_vphb_reconfigure(struct cxl_afu *afu) +{ + /* When we are reconfigured, the AFU's MMIO space is unmapped + * and remapped. We need to reflect this in the PHB's view of + * the world. + */ + afu->phb->cfg_addr = afu->afu_desc_mmio + afu->crs_offset; +} void cxl_pci_vphb_remove(struct cxl_afu *afu) { -- cgit v1.2.3 From 25901632c9323e5208cca66097a0b8f34c752606 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 14 Aug 2015 17:41:27 +1000 Subject: cxl: Add CONFIG_CXL_EEH symbol CONFIG_CXL_EEH is for CXL's EEH related code. Other drivers can depend on or #ifdef on this symbol to configure PERST behaviour, allowing CXL to participate in the EEH process. Reviewed-by: Cyril Bur Signed-off-by: Daniel Axtens Signed-off-by: Michael Ellerman --- drivers/misc/cxl/Kconfig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/Kconfig b/drivers/misc/cxl/Kconfig index b6db9ebd52c2..8756d06e2bb8 100644 --- a/drivers/misc/cxl/Kconfig +++ b/drivers/misc/cxl/Kconfig @@ -11,11 +11,16 @@ config CXL_KERNEL_API bool default n +config CXL_EEH + bool + default n + config CXL tristate "Support for IBM Coherent Accelerators (CXL)" - depends on PPC_POWERNV && PCI_MSI + depends on PPC_POWERNV && PCI_MSI && EEH select CXL_BASE select CXL_KERNEL_API + select CXL_EEH default m help Select this option to enable driver support for IBM Coherent -- cgit v1.2.3 From 8c7dd08a8ccac3f0f3df6002dc8e86d3ca6c56a8 Mon Sep 17 00:00:00 2001 From: Vaibhav Jain Date: Fri, 14 Aug 2015 12:28:38 +0530 Subject: cxl: Plug irq_bitmap getting leaked in cxl_context This patch plugs the leak of irq_bitmap, allocated as part of initialization of cxl_context struct; during the call to afu_allocate_irqs. The bitmap is now release during the call to function afu_release_irqs. Reported-by: Matthew R. Ochs Signed-off-by: Vaibhav Jain Signed-off-by: Michael Ellerman --- drivers/misc/cxl/irq.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c index 62823784f68e..72bf45ae3806 100644 --- a/drivers/misc/cxl/irq.c +++ b/drivers/misc/cxl/irq.c @@ -520,4 +520,8 @@ void afu_release_irqs(struct cxl_context *ctx, void *cookie) afu_irq_name_free(ctx); cxl_release_irq_ranges(&ctx->irqs, ctx->afu->adapter); + + kfree(ctx->irq_bitmap); + ctx->irq_bitmap = NULL; + ctx->irq_count = 0; } -- cgit v1.2.3 From d9232a3da8683cd9c9854a858bcca968fe5f3bca Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Thu, 23 Jul 2015 16:43:56 +1000 Subject: cxl: Add alternate MMIO error handling userspace programs using cxl currently have to use two strategies for dealing with MMIO errors simultaneously. They have to check every read for a return of all Fs in case the adapter has gone away and the kernel has not yet noticed, and they have to deal with SIGBUS in case the kernel has already noticed, invalidated the mapping and marked the context as failed. In order to simplify things, this patch adds an alternative approach where the kernel will return a page filled with Fs instead of delivering a SIGBUS. This allows userspace to only need to deal with one of these two error paths, and is intended for use in libraries that use cxl transparently and may not be able to safely install a signal handler. This approach will only work if certain constraints are met. Namely, if the application is both reading and writing to an address in the problem state area it cannot assume that a non-FF read is OK, as it may just be reading out a value it has previously written. Further - since only one page is used per context a write to a given offset would be visible when reading the same offset from a different page in the mapping (this only applies within a single context, not between contexts). An application could deal with this by e.g. making sure it also reads from a read-only offset after any reads to a read/write offset. Due to these constraints, this functionality must be explicitly requested by userspace when starting the context by passing in the CXL_START_WORK_ERR_FF flag. Signed-off-by: Ian Munsie Acked-by: Michael Neuling Signed-off-by: Michael Ellerman --- drivers/misc/cxl/context.c | 14 ++++++++++++++ drivers/misc/cxl/cxl.h | 4 +++- drivers/misc/cxl/file.c | 4 +++- 3 files changed, 20 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c index 615842115848..941fda04aa9a 100644 --- a/drivers/misc/cxl/context.c +++ b/drivers/misc/cxl/context.c @@ -126,6 +126,18 @@ static int cxl_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) if (ctx->status != STARTED) { mutex_unlock(&ctx->status_mutex); pr_devel("%s: Context not started, failing problem state access\n", __func__); + if (ctx->mmio_err_ff) { + if (!ctx->ff_page) { + ctx->ff_page = alloc_page(GFP_USER); + if (!ctx->ff_page) + return VM_FAULT_OOM; + memset(page_address(ctx->ff_page), 0xff, PAGE_SIZE); + } + get_page(ctx->ff_page); + vmf->page = ctx->ff_page; + vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); + return 0; + } return VM_FAULT_SIGBUS; } @@ -257,6 +269,8 @@ static void reclaim_ctx(struct rcu_head *rcu) struct cxl_context *ctx = container_of(rcu, struct cxl_context, rcu); free_page((u64)ctx->sstp); + if (ctx->ff_page) + __free_page(ctx->ff_page); ctx->sstp = NULL; kfree(ctx); diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index 6f5386653dae..e7af256f60c5 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -34,7 +34,7 @@ extern uint cxl_verbose; * Bump version each time a user API change is made, whether it is * backwards compatible ot not. */ -#define CXL_API_VERSION 1 +#define CXL_API_VERSION 2 #define CXL_API_VERSION_COMPATIBLE 1 /* @@ -418,6 +418,8 @@ struct cxl_context { /* Used to unmap any mmaps when force detaching */ struct address_space *mapping; struct mutex mapping_lock; + struct page *ff_page; + bool mmio_err_ff; spinlock_t sste_lock; /* Protects segment table entries */ struct cxl_sste *sstp; diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index 57bdb473749f..a30bf285b5bd 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -184,6 +184,8 @@ static long afu_ioctl_start_work(struct cxl_context *ctx, if (work.flags & CXL_START_WORK_AMR) amr = work.amr & mfspr(SPRN_UAMOR); + ctx->mmio_err_ff = !!(work.flags & CXL_START_WORK_ERR_FF); + /* * We grab the PID here and not in the file open to allow for the case * where a process (master, some daemon, etc) has opened the chardev on @@ -538,7 +540,7 @@ int __init cxl_file_init(void) * If these change we really need to update API. Either change some * flags or update API version number CXL_API_VERSION. */ - BUILD_BUG_ON(CXL_API_VERSION != 1); + BUILD_BUG_ON(CXL_API_VERSION != 2); BUILD_BUG_ON(sizeof(struct cxl_ioctl_start_work) != 64); BUILD_BUG_ON(sizeof(struct cxl_event_header) != 8); BUILD_BUG_ON(sizeof(struct cxl_event_afu_interrupt) != 8); -- cgit v1.2.3 From cb0eefcc3271ea1d370476dd29685918b99c5a9f Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 30 Jul 2015 18:18:30 +0200 Subject: macintosh/therm_windtunnel: Export I2C module alias information The I2C core always reports the MODALIAS uevent as "i2c: Signed-off-by: Michael Ellerman --- drivers/macintosh/therm_windtunnel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index 109dcaa15934..97269567ec01 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -408,6 +408,7 @@ static const struct i2c_device_id therm_windtunnel_id[] = { { "therm_adm1030", adm1030 }, { } }; +MODULE_DEVICE_TABLE(i2c, therm_windtunnel_id); static int do_probe(struct i2c_client *cl, const struct i2c_device_id *id) -- cgit v1.2.3 From a1a42b7920ba57f287d742d42f113402fbb7c85e Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 30 Jul 2015 18:18:47 +0200 Subject: macintosh/therm_windtunnel: Export OF module alias information The I2C core always reports the MODALIAS uevent as "i2c: Signed-off-by: Michael Ellerman --- drivers/macintosh/therm_windtunnel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index 97269567ec01..68dcbcb4fc5b 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -460,6 +460,7 @@ static const struct of_device_id therm_of_match[] = {{ .compatible = "adm1030" }, {} }; +MODULE_DEVICE_TABLE(of, therm_of_match); static struct platform_driver therm_of_driver = { .driver = { -- cgit v1.2.3 From 7c26b9cf5347c24272152438cdd9675183804425 Mon Sep 17 00:00:00 2001 From: Andrew Donnellan Date: Wed, 19 Aug 2015 09:27:18 +1000 Subject: cxl: Allow release of contexts which have been OPENED but not STARTED If we open a context but do not start it (either because we do not attempt to start it, or because it fails to start for some reason), we are left with a context in state OPENED. Previously, cxl_release_context() only allowed releasing contexts in state CLOSED, so attempting to release an OPENED context would fail. In particular, this bug causes available contexts to run out after some EEH failures, where drivers attempt to release contexts that have failed to start. Allow releasing contexts in any state with a value lower than STARTED, i.e. OPENED or CLOSED (we can't release a STARTED context as it's currently using the hardware, and we assume that contexts in any new states which may be added in future with a value higher than STARTED are also unsafe to release). Cc: stable@vger.kernel.org Fixes: 6f7f0b3df6d4 ("cxl: Add AFU virtual PHB and kernel API") Signed-off-by: Andrew Donnellan Signed-off-by: Daniel Axtens Acked-by: Ian Munsie Signed-off-by: Michael Ellerman --- drivers/misc/cxl/api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c index 6a768a9ad22f..f49e3e5db58d 100644 --- a/drivers/misc/cxl/api.c +++ b/drivers/misc/cxl/api.c @@ -59,7 +59,7 @@ EXPORT_SYMBOL_GPL(cxl_get_phys_dev); int cxl_release_context(struct cxl_context *ctx) { - if (ctx->status != CLOSED) + if (ctx->status >= STARTED) return -EBUSY; put_device(&ctx->afu->dev); -- cgit v1.2.3 From 84ad6e5cd3e8b365c893f31787864cae5500610b Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Wed, 19 Aug 2015 22:19:54 +0530 Subject: leds/powernv: Add driver for PowerNV platform This patch implements LED driver for PowerNV platform using the existing generic LED class framework. PowerNV platform has below type of LEDs: - System attention Indicates there is a problem with the system that needs attention. - Identify Helps the user locate/identify a particular FRU or resource in the system. - Fault Indicates there is a problem with the FRU or resource at the location with which the indicator is associated. We register classdev structures for all individual LEDs detected on the system through LED specific device tree nodes. Device tree nodes specify what all kind of LEDs present on the same location code. It registers LED classdev structure for each of them. All the system LEDs can be found in the same regular path /sys/class/leds/. We don't use LED colors. We use LED node and led-types property to form LED classdev. Our LEDs have names in this format. : Any positive brightness value would turn on the LED and a zero value would turn off the LED. The driver will return LED_FULL (255) for any turned on LED and LED_OFF (0) for any turned off LED. The platform level implementation of LED get and set state has been achieved through OPAL calls. These calls are made available for the driver by exporting from architecture specific codes. Signed-off-by: Vasant Hegde Signed-off-by: Anshuman Khandual Acked-by: Stewart Smith Tested-by: Stewart Smith Acked-by: Jacek Anaszewski Signed-off-by: Michael Ellerman --- drivers/leds/Kconfig | 11 ++ drivers/leds/Makefile | 1 + drivers/leds/leds-powernv.c | 345 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 357 insertions(+) create mode 100644 drivers/leds/leds-powernv.c (limited to 'drivers') diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 9ad35f72ab4c..f218cc3acc10 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -560,6 +560,17 @@ config LEDS_BLINKM This option enables support for the BlinkM RGB LED connected through I2C. Say Y to enable support for the BlinkM LED. +config LEDS_POWERNV + tristate "LED support for PowerNV Platform" + depends on LEDS_CLASS + depends on PPC_POWERNV + depends on OF + help + This option enables support for the system LEDs present on + PowerNV platforms. Say 'y' to enable this support in kernel. + To compile this driver as a module, choose 'm' here: the module + will be called leds-powernv. + config LEDS_SYSCON bool "LED support for LEDs on system controllers" depends on LEDS_CLASS=y diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 8d6a24a2f513..6a943d16ecab 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o obj-$(CONFIG_LEDS_PM8941_WLED) += leds-pm8941-wled.o obj-$(CONFIG_LEDS_KTD2692) += leds-ktd2692.o +obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/leds-powernv.c b/drivers/leds/leds-powernv.c new file mode 100644 index 000000000000..a2fea192573b --- /dev/null +++ b/drivers/leds/leds-powernv.c @@ -0,0 +1,345 @@ +/* + * PowerNV LED Driver + * + * Copyright IBM Corp. 2015 + * + * Author: Vasant Hegde + * Author: Anshuman Khandual + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include + +/* Map LED type to description. */ +struct led_type_map { + const int type; + const char *desc; +}; +static const struct led_type_map led_type_map[] = { + {OPAL_SLOT_LED_TYPE_ID, POWERNV_LED_TYPE_IDENTIFY}, + {OPAL_SLOT_LED_TYPE_FAULT, POWERNV_LED_TYPE_FAULT}, + {OPAL_SLOT_LED_TYPE_ATTN, POWERNV_LED_TYPE_ATTENTION}, + {-1, NULL}, +}; + +struct powernv_led_common { + /* + * By default unload path resets all the LEDs. But on PowerNV + * platform we want to retain LED state across reboot as these + * are controlled by firmware. Also service processor can modify + * the LEDs independent of OS. Hence avoid resetting LEDs in + * unload path. + */ + bool led_disabled; + + /* Max supported LED type */ + __be64 max_led_type; + + /* glabal lock */ + struct mutex lock; +}; + +/* PowerNV LED data */ +struct powernv_led_data { + struct led_classdev cdev; + char *loc_code; /* LED location code */ + int led_type; /* OPAL_SLOT_LED_TYPE_* */ + + struct powernv_led_common *common; +}; + + +/* Returns OPAL_SLOT_LED_TYPE_* for given led type string */ +static int powernv_get_led_type(const char *led_type_desc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(led_type_map); i++) + if (!strcmp(led_type_map[i].desc, led_type_desc)) + return led_type_map[i].type; + + return -1; +} + +/* + * This commits the state change of the requested LED through an OPAL call. + * This function is called from work queue task context when ever it gets + * scheduled. This function can sleep at opal_async_wait_response call. + */ +static void powernv_led_set(struct powernv_led_data *powernv_led, + enum led_brightness value) +{ + int rc, token; + u64 led_mask, led_value = 0; + __be64 max_type; + struct opal_msg msg; + struct device *dev = powernv_led->cdev.dev; + struct powernv_led_common *powernv_led_common = powernv_led->common; + + /* Prepare for the OPAL call */ + max_type = powernv_led_common->max_led_type; + led_mask = OPAL_SLOT_LED_STATE_ON << powernv_led->led_type; + if (value) + led_value = led_mask; + + /* OPAL async call */ + token = opal_async_get_token_interruptible(); + if (token < 0) { + if (token != -ERESTARTSYS) + dev_err(dev, "%s: Couldn't get OPAL async token\n", + __func__); + return; + } + + rc = opal_leds_set_ind(token, powernv_led->loc_code, + led_mask, led_value, &max_type); + if (rc != OPAL_ASYNC_COMPLETION) { + dev_err(dev, "%s: OPAL set LED call failed for %s [rc=%d]\n", + __func__, powernv_led->loc_code, rc); + goto out_token; + } + + rc = opal_async_wait_response(token, &msg); + if (rc) { + dev_err(dev, + "%s: Failed to wait for the async response [rc=%d]\n", + __func__, rc); + goto out_token; + } + + rc = be64_to_cpu(msg.params[1]); + if (rc != OPAL_SUCCESS) + dev_err(dev, "%s : OAPL async call returned failed [rc=%d]\n", + __func__, rc); + +out_token: + opal_async_release_token(token); +} + +/* + * This function fetches the LED state for a given LED type for + * mentioned LED classdev structure. + */ +static enum led_brightness powernv_led_get(struct powernv_led_data *powernv_led) +{ + int rc; + __be64 mask, value, max_type; + u64 led_mask, led_value; + struct device *dev = powernv_led->cdev.dev; + struct powernv_led_common *powernv_led_common = powernv_led->common; + + /* Fetch all LED status */ + mask = cpu_to_be64(0); + value = cpu_to_be64(0); + max_type = powernv_led_common->max_led_type; + + rc = opal_leds_get_ind(powernv_led->loc_code, + &mask, &value, &max_type); + if (rc != OPAL_SUCCESS && rc != OPAL_PARTIAL) { + dev_err(dev, "%s: OPAL get led call failed [rc=%d]\n", + __func__, rc); + return LED_OFF; + } + + led_mask = be64_to_cpu(mask); + led_value = be64_to_cpu(value); + + /* LED status available */ + if (!((led_mask >> powernv_led->led_type) & OPAL_SLOT_LED_STATE_ON)) { + dev_err(dev, "%s: LED status not available for %s\n", + __func__, powernv_led->cdev.name); + return LED_OFF; + } + + /* LED status value */ + if ((led_value >> powernv_led->led_type) & OPAL_SLOT_LED_STATE_ON) + return LED_FULL; + + return LED_OFF; +} + +/* + * LED classdev 'brightness_get' function. This schedules work + * to update LED state. + */ +static void powernv_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct powernv_led_data *powernv_led = + container_of(led_cdev, struct powernv_led_data, cdev); + struct powernv_led_common *powernv_led_common = powernv_led->common; + + /* Do not modify LED in unload path */ + if (powernv_led_common->led_disabled) + return; + + mutex_lock(&powernv_led_common->lock); + powernv_led_set(powernv_led, value); + mutex_unlock(&powernv_led_common->lock); +} + +/* LED classdev 'brightness_get' function */ +static enum led_brightness powernv_brightness_get(struct led_classdev *led_cdev) +{ + struct powernv_led_data *powernv_led = + container_of(led_cdev, struct powernv_led_data, cdev); + + return powernv_led_get(powernv_led); +} + +/* + * This function registers classdev structure for any given type of LED on + * a given child LED device node. + */ +static int powernv_led_create(struct device *dev, + struct powernv_led_data *powernv_led, + const char *led_type_desc) +{ + int rc; + + /* Make sure LED type is supported */ + powernv_led->led_type = powernv_get_led_type(led_type_desc); + if (powernv_led->led_type == -1) { + dev_warn(dev, "%s: No support for led type : %s\n", + __func__, led_type_desc); + return -EINVAL; + } + + /* Create the name for classdev */ + powernv_led->cdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", + powernv_led->loc_code, + led_type_desc); + if (!powernv_led->cdev.name) { + dev_err(dev, + "%s: Memory allocation failed for classdev name\n", + __func__); + return -ENOMEM; + } + + powernv_led->cdev.brightness_set = powernv_brightness_set; + powernv_led->cdev.brightness_get = powernv_brightness_get; + powernv_led->cdev.brightness = LED_OFF; + powernv_led->cdev.max_brightness = LED_FULL; + + /* Register the classdev */ + rc = devm_led_classdev_register(dev, &powernv_led->cdev); + if (rc) { + dev_err(dev, "%s: Classdev registration failed for %s\n", + __func__, powernv_led->cdev.name); + } + + return rc; +} + +/* Go through LED device tree node and register LED classdev structure */ +static int powernv_led_classdev(struct platform_device *pdev, + struct device_node *led_node, + struct powernv_led_common *powernv_led_common) +{ + const char *cur = NULL; + int rc = -1; + struct property *p; + struct device_node *np; + struct powernv_led_data *powernv_led; + struct device *dev = &pdev->dev; + + for_each_child_of_node(led_node, np) { + p = of_find_property(np, "led-types", NULL); + if (!p) + continue; + + while ((cur = of_prop_next_string(p, cur)) != NULL) { + powernv_led = devm_kzalloc(dev, sizeof(*powernv_led), + GFP_KERNEL); + if (!powernv_led) + return -ENOMEM; + + powernv_led->common = powernv_led_common; + powernv_led->loc_code = (char *)np->name; + + rc = powernv_led_create(dev, powernv_led, cur); + if (rc) + return rc; + } /* while end */ + } + + return rc; +} + +/* Platform driver probe */ +static int powernv_led_probe(struct platform_device *pdev) +{ + struct device_node *led_node; + struct powernv_led_common *powernv_led_common; + struct device *dev = &pdev->dev; + + led_node = of_find_node_by_path("/ibm,opal/leds"); + if (!led_node) { + dev_err(dev, "%s: LED parent device node not found\n", + __func__); + return -EINVAL; + } + + powernv_led_common = devm_kzalloc(dev, sizeof(*powernv_led_common), + GFP_KERNEL); + if (!powernv_led_common) + return -ENOMEM; + + mutex_init(&powernv_led_common->lock); + powernv_led_common->max_led_type = cpu_to_be64(OPAL_SLOT_LED_TYPE_MAX); + + platform_set_drvdata(pdev, powernv_led_common); + + return powernv_led_classdev(pdev, led_node, powernv_led_common); +} + +/* Platform driver remove */ +static int powernv_led_remove(struct platform_device *pdev) +{ + struct powernv_led_common *powernv_led_common; + + /* Disable LED operation */ + powernv_led_common = platform_get_drvdata(pdev); + powernv_led_common->led_disabled = true; + + /* Destroy lock */ + mutex_destroy(&powernv_led_common->lock); + + dev_info(&pdev->dev, "PowerNV led module unregistered\n"); + return 0; +} + +/* Platform driver property match */ +static const struct of_device_id powernv_led_match[] = { + { + .compatible = "ibm,opal-v3-led", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, powernv_led_match); + +static struct platform_driver powernv_led_driver = { + .probe = powernv_led_probe, + .remove = powernv_led_remove, + .driver = { + .name = "powernv-led-driver", + .of_match_table = powernv_led_match, + }, +}; + +module_platform_driver(powernv_led_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PowerNV LED driver"); +MODULE_AUTHOR("Vasant Hegde "); -- cgit v1.2.3 From 480798044eb268a31f6b84bc3b7f99b9989e463d Mon Sep 17 00:00:00 2001 From: Laurent Dufour Date: Fri, 31 Jul 2015 11:29:50 +0200 Subject: powerpc/hvsi: Fix endianness issues in the HVSI driver This patch fixes several endianness issues detected when running the HVSI driver in little endian mode. These issues are raised in little endian mode because the data exchanged in memory between the kernel and the hypervisor has to be in big endian format. This exhibits as errors such as: irq: (null) didn't like hwirq-0x1000a00 to VIRQ16 mapping (rc=-22) hvsi_console_init: couldn't create irq mapping for 0x1000a00 The data structures already have endian annotations, and sparse is generating numerous warnings based on those. This commit fixes all of them. Signed-off-by: Laurent Dufour CC: Greg Kroah-Hartman CC: Jiri Slaby CC: linuxppc-dev@lists.ozlabs.org CC: linux-kernel@vger.kernel.org [mpe: Flesh out change log] Signed-off-by: Michael Ellerman --- drivers/tty/hvc/hvsi.c | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c index 41901997c0d6..a75146f600cb 100644 --- a/drivers/tty/hvc/hvsi.c +++ b/drivers/tty/hvc/hvsi.c @@ -240,9 +240,9 @@ static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, { struct hvsi_control *header = (struct hvsi_control *)packet; - switch (header->verb) { + switch (be16_to_cpu(header->verb)) { case VSV_MODEM_CTL_UPDATE: - if ((header->word & HVSI_TSCD) == 0) { + if ((be32_to_cpu(header->word) & HVSI_TSCD) == 0) { /* CD went away; no more connection */ pr_debug("hvsi%i: CD dropped\n", hp->index); hp->mctrl &= TIOCM_CD; @@ -267,6 +267,7 @@ static void hvsi_recv_control(struct hvsi_struct *hp, uint8_t *packet, static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) { struct hvsi_query_response *resp = (struct hvsi_query_response *)packet; + uint32_t mctrl_word; switch (hp->state) { case HVSI_WAIT_FOR_VER_RESPONSE: @@ -274,9 +275,10 @@ static void hvsi_recv_response(struct hvsi_struct *hp, uint8_t *packet) break; case HVSI_WAIT_FOR_MCTRL_RESPONSE: hp->mctrl = 0; - if (resp->u.mctrl_word & HVSI_TSDTR) + mctrl_word = be32_to_cpu(resp->u.mctrl_word); + if (mctrl_word & HVSI_TSDTR) hp->mctrl |= TIOCM_DTR; - if (resp->u.mctrl_word & HVSI_TSCD) + if (mctrl_word & HVSI_TSCD) hp->mctrl |= TIOCM_CD; __set_state(hp, HVSI_OPEN); break; @@ -295,10 +297,10 @@ static int hvsi_version_respond(struct hvsi_struct *hp, uint16_t query_seqno) packet.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER; packet.hdr.len = sizeof(struct hvsi_query_response); - packet.hdr.seqno = atomic_inc_return(&hp->seqno); - packet.verb = VSV_SEND_VERSION_NUMBER; + packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); + packet.verb = cpu_to_be16(VSV_SEND_VERSION_NUMBER); packet.u.version = HVSI_VERSION; - packet.query_seqno = query_seqno+1; + packet.query_seqno = cpu_to_be16(query_seqno+1); pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); @@ -319,7 +321,7 @@ static void hvsi_recv_query(struct hvsi_struct *hp, uint8_t *packet) switch (hp->state) { case HVSI_WAIT_FOR_VER_QUERY: - hvsi_version_respond(hp, query->hdr.seqno); + hvsi_version_respond(hp, be16_to_cpu(query->hdr.seqno)); __set_state(hp, HVSI_OPEN); break; default: @@ -555,8 +557,8 @@ static int hvsi_query(struct hvsi_struct *hp, uint16_t verb) packet.hdr.type = VS_QUERY_PACKET_HEADER; packet.hdr.len = sizeof(struct hvsi_query); - packet.hdr.seqno = atomic_inc_return(&hp->seqno); - packet.verb = verb; + packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); + packet.verb = cpu_to_be16(verb); pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); @@ -596,14 +598,14 @@ static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl) struct hvsi_control packet __ALIGNED__; int wrote; - packet.hdr.type = VS_CONTROL_PACKET_HEADER, - packet.hdr.seqno = atomic_inc_return(&hp->seqno); + packet.hdr.type = VS_CONTROL_PACKET_HEADER; + packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); packet.hdr.len = sizeof(struct hvsi_control); - packet.verb = VSV_SET_MODEM_CTL; - packet.mask = HVSI_TSDTR; + packet.verb = cpu_to_be16(VSV_SET_MODEM_CTL); + packet.mask = cpu_to_be32(HVSI_TSDTR); if (mctrl & TIOCM_DTR) - packet.word = HVSI_TSDTR; + packet.word = cpu_to_be32(HVSI_TSDTR); pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); @@ -680,7 +682,7 @@ static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count) BUG_ON(count > HVSI_MAX_OUTGOING_DATA); packet.hdr.type = VS_DATA_PACKET_HEADER; - packet.hdr.seqno = atomic_inc_return(&hp->seqno); + packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); packet.hdr.len = count + sizeof(struct hvsi_header); memcpy(&packet.data, buf, count); @@ -697,9 +699,9 @@ static void hvsi_close_protocol(struct hvsi_struct *hp) struct hvsi_control packet __ALIGNED__; packet.hdr.type = VS_CONTROL_PACKET_HEADER; - packet.hdr.seqno = atomic_inc_return(&hp->seqno); + packet.hdr.seqno = cpu_to_be16(atomic_inc_return(&hp->seqno)); packet.hdr.len = 6; - packet.verb = VSV_CLOSE_PROTOCOL; + packet.verb = cpu_to_be16(VSV_CLOSE_PROTOCOL); pr_debug("%s: sending %i bytes\n", __func__, packet.hdr.len); dbg_dump_hex((uint8_t*)&packet, packet.hdr.len); @@ -1180,7 +1182,7 @@ static int __init hvsi_console_init(void) /* search device tree for vty nodes */ for_each_compatible_node(vty, "serial", "hvterm-protocol") { struct hvsi_struct *hp; - const uint32_t *vtermno, *irq; + const __be32 *vtermno, *irq; vtermno = of_get_property(vty, "reg", NULL); irq = of_get_property(vty, "interrupts", NULL); @@ -1202,11 +1204,11 @@ static int __init hvsi_console_init(void) hp->index = hvsi_count; hp->inbuf_end = hp->inbuf; hp->state = HVSI_CLOSED; - hp->vtermno = *vtermno; - hp->virq = irq_create_mapping(NULL, irq[0]); + hp->vtermno = be32_to_cpup(vtermno); + hp->virq = irq_create_mapping(NULL, be32_to_cpup(irq)); if (hp->virq == 0) { printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", - __func__, irq[0]); + __func__, be32_to_cpup(irq)); tty_port_destroy(&hp->port); continue; } -- cgit v1.2.3 From 5d53be7d8c7ccf8eec1ce66c6b3573c01d16b755 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Sat, 22 Aug 2015 09:33:55 +1000 Subject: powerpc/powernv: Fix mis-merge of OPAL support for LEDS driver When I merged the OPAL support for the powernv LEDS driver I missed a hunk. This is slightly modified from the original patch, as the original added code to opal-api.h which is not in the skiboot version, which is discouraged. Instead those values are moved into the driver, which is the only place they are used. Fixes: 8a8d91817aec ("powerpc/powernv: Add OPAL interfaces for accessing and modifying system LED states") Reviewed-by: Vasant Hegde Signed-off-by: Michael Ellerman --- drivers/leds/leds-powernv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/leds/leds-powernv.c b/drivers/leds/leds-powernv.c index a2fea192573b..2c5c5b12ab64 100644 --- a/drivers/leds/leds-powernv.c +++ b/drivers/leds/leds-powernv.c @@ -27,9 +27,9 @@ struct led_type_map { const char *desc; }; static const struct led_type_map led_type_map[] = { - {OPAL_SLOT_LED_TYPE_ID, POWERNV_LED_TYPE_IDENTIFY}, - {OPAL_SLOT_LED_TYPE_FAULT, POWERNV_LED_TYPE_FAULT}, - {OPAL_SLOT_LED_TYPE_ATTN, POWERNV_LED_TYPE_ATTENTION}, + {OPAL_SLOT_LED_TYPE_ID, "identify"}, + {OPAL_SLOT_LED_TYPE_FAULT, "fault"}, + {OPAL_SLOT_LED_TYPE_ATTN, "attention"}, {-1, NULL}, }; -- cgit v1.2.3 From f47f966fbe0aaff4ebbdb83d95acdfa5d3c20477 Mon Sep 17 00:00:00 2001 From: Vaishali Thakkar Date: Sun, 19 Jul 2015 22:53:52 +0530 Subject: cxl: Remove use of macro DEFINE_PCI_DEVICE_TABLE Macro DEFINE_PCI_DEVICE_TABLE is deprecated. So, here use struct pci_device_id instead of DEFINE_PCI_DEVICE_TABLE with the goal of getting rid of this macro completely. The Coccinelle semantic patch that performs this transformation is as follows: @@ identifier a; declarer name DEFINE_PCI_DEVICE_TABLE; initializer i; @@ - DEFINE_PCI_DEVICE_TABLE(a) + const struct pci_device_id a[] = i; Signed-off-by: Vaishali Thakkar Acked-by: Michael Neuling Acked-by: Ian Munsie Signed-off-by: Michael Ellerman --- drivers/misc/cxl/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 03ddb2d8f974..273374c507f7 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -134,7 +134,7 @@ u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off) return (val >> ((off & 0x3) * 8)) & 0xff; } -static DEFINE_PCI_DEVICE_TABLE(cxl_pci_tbl) = { +static const struct pci_device_id cxl_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0477), }, { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x044b), }, { PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x04cf), }, -- cgit v1.2.3 From a6897f39660cc07fa78b4459d82f12b07abb50b1 Mon Sep 17 00:00:00 2001 From: Vaibhav Jain Date: Tue, 25 Aug 2015 11:04:48 +0530 Subject: cxl: Release irqs if memory allocation fails This minor patch plugs a potential irq leak in case of a memory allocation failure inside function the afu_allocate_irqs. Presently the irqs allocated to the context gets leaked if allocation of either one of context irq_bitmap or irq_names fails. Signed-off-by: Vaibhav Jain Acked-by: Ian Munsie Signed-off-by: Michael Ellerman --- drivers/misc/cxl/irq.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c index 72bf45ae3806..583b42afeda2 100644 --- a/drivers/misc/cxl/irq.c +++ b/drivers/misc/cxl/irq.c @@ -430,6 +430,9 @@ int afu_allocate_irqs(struct cxl_context *ctx, u32 count) int rc, r, i, j = 1; struct cxl_irq_name *irq_name; + /* Initialize the list head to hold irq names */ + INIT_LIST_HEAD(&ctx->irq_names); + if ((rc = cxl_alloc_irq_ranges(&ctx->irqs, ctx->afu->adapter, count))) return rc; @@ -441,13 +444,12 @@ int afu_allocate_irqs(struct cxl_context *ctx, u32 count) ctx->irq_bitmap = kcalloc(BITS_TO_LONGS(count), sizeof(*ctx->irq_bitmap), GFP_KERNEL); if (!ctx->irq_bitmap) - return -ENOMEM; + goto out; /* * Allocate names first. If any fail, bail out before allocating * actual hardware IRQs. */ - INIT_LIST_HEAD(&ctx->irq_names); for (r = 1; r < CXL_IRQ_RANGES; r++) { for (i = 0; i < ctx->irqs.range[r]; i++) { irq_name = kmalloc(sizeof(struct cxl_irq_name), @@ -469,6 +471,7 @@ int afu_allocate_irqs(struct cxl_context *ctx, u32 count) return 0; out: + cxl_release_irq_ranges(&ctx->irqs, ctx->afu->adapter); afu_irq_name_free(ctx); return -ENOMEM; } -- cgit v1.2.3 From 9d8e27673c45927fee9e7d8992ffb325a6b0b0e4 Mon Sep 17 00:00:00 2001 From: Daniel Axtens Date: Fri, 21 Aug 2015 17:25:15 +1000 Subject: cxl: Remove racy attempt to force EEH invocation in reset cxl_reset currently PERSTs the slot, and then repeatedly tries to read MMIO space in order to kick off EEH. There are 2 problems with this: it's unnecessary, and it's racy. It's unnecessary because the PERST will bring down the PHB link. That will be picked up by the CAPP, which will send out an HMI. Skiboot, noticing an HMI from the CAPP, will send an OPAL notification to the kernel, which will trigger EEH recovery. It's also racy: the EEH recovery triggered by the CAPP will eventually cause the MMIO space to have its mapping invalidated and the pointer NULLed out. This races with our attempt to read the MMIO space. This is causing OOPSes in testing. Simply drop all the attempts to force EEH detection, and trust that Skiboot will send the notification and that we'll act on it. The Skiboot code to send the EEH notification has been in Skiboot for as long as CAPP recovery has been supported, so we don't need to worry about breaking obscure setups with ancient firmware. Cc: Ryan Grimm Cc: stable@vger.kernel.org Fixes: 62fa19d4b4fd ("cxl: Add ability to reset the card") Signed-off-by: Daniel Axtens Acked-by: Ian Munsie Signed-off-by: Michael Ellerman --- drivers/misc/cxl/pci.c | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 273374c507f7..6ca7c3d0ef9b 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -876,8 +876,6 @@ int cxl_reset(struct cxl *adapter) { struct pci_dev *dev = to_pci_dev(adapter->dev.parent); int rc; - int i; - u32 val; if (adapter->perst_same_image) { dev_warn(&dev->dev, @@ -895,20 +893,6 @@ int cxl_reset(struct cxl *adapter) return rc; } - /* the PERST done above fences the PHB. So, reset depends on EEH - * to unbind the driver, tell Sapphire to reinit the PHB, and rebind - * the driver. Do an mmio read explictly to ensure EEH notices the - * fenced PHB. Retry for a few seconds before giving up. */ - i = 0; - while (((val = mmio_read32be(adapter->p1_mmio)) != 0xffffffff) && - (i < 5)) { - msleep(500); - i++; - } - - if (val != 0xffffffff) - dev_err(&dev->dev, "cxl: PERST failed to trigger EEH\n"); - return rc; } -- cgit v1.2.3 From af2a50bb0ce1ca7a9c4784813419c922bf2285df Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Thu, 27 Aug 2015 19:50:18 +1000 Subject: cxl: Fix + cleanup error paths in cxl_dev_context_init If the cxl_context_alloc() call fails, we return immediately without releasing the reference on the AFU device, allowing it to leak. This patch switches to using goto style error handling so that the device is released in common code for both error paths, and will also simplify things if we add additional initialisation in this function in the future. Signed-off-by: Ian Munsie Signed-off-by: Michael Ellerman --- drivers/misc/cxl/api.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c index f49e3e5db58d..005adc7d33a2 100644 --- a/drivers/misc/cxl/api.c +++ b/drivers/misc/cxl/api.c @@ -25,19 +25,24 @@ struct cxl_context *cxl_dev_context_init(struct pci_dev *dev) get_device(&afu->dev); ctx = cxl_context_alloc(); - if (IS_ERR(ctx)) - return ctx; + if (IS_ERR(ctx)) { + rc = PTR_ERR(ctx); + goto err_dev; + } /* Make it a slave context. We can promote it later? */ rc = cxl_context_init(ctx, afu, false, NULL); - if (rc) { - kfree(ctx); - put_device(&afu->dev); - return ERR_PTR(-ENOMEM); - } + if (rc) + goto err_ctx; cxl_assign_psn_space(ctx); return ctx; + +err_ctx: + kfree(ctx); +err_dev: + put_device(&afu->dev); + return ERR_PTR(rc); } EXPORT_SYMBOL_GPL(cxl_dev_context_init); -- cgit v1.2.3 From 55e07668fbba9466e6a9ef7650718356cda38406 Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Thu, 27 Aug 2015 19:50:19 +1000 Subject: cxl: Fix force unmapping mmaps of contexts allocated through the kernel api The cxl user api uses the address_space associated with the file when we need to force unmap all cxl mmap regions (e.g. on eeh, driver detach, etc). Currently, contexts allocated through the kernel api do not do this and instead skip the mmap invalidation, potentially allowing them to poke at the hardware after such an event, which may cause all sorts of trouble. This patch allocates an address_space for cxl contexts allocated through the kernel api so that the same invalidate path will for these contexts as well. We don't use the anonymous inode's address_space, as doing so could invalidate any mmaps of completely unrelated drivers using anonymous file descriptors. This patch also introduces a kernelapi flag, so we know when freeing the context if the address_space was allocated by us and needs to be freed. Signed-off-by: Ian Munsie Signed-off-by: Michael Ellerman --- drivers/misc/cxl/api.c | 33 ++++++++++++++++++++++++++++++--- drivers/misc/cxl/context.c | 2 ++ drivers/misc/cxl/cxl.h | 1 + 3 files changed, 33 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c index 005adc7d33a2..8af12c884b04 100644 --- a/drivers/misc/cxl/api.c +++ b/drivers/misc/cxl/api.c @@ -12,11 +12,13 @@ #include #include #include +#include #include "cxl.h" struct cxl_context *cxl_dev_context_init(struct pci_dev *dev) { + struct address_space *mapping; struct cxl_afu *afu; struct cxl_context *ctx; int rc; @@ -30,14 +32,32 @@ struct cxl_context *cxl_dev_context_init(struct pci_dev *dev) goto err_dev; } + ctx->kernelapi = true; + + /* + * Make our own address space since we won't have one from the + * filesystem like the user api has, and even if we do associate a file + * with this context we don't want to use the global anonymous inode's + * address space as that can invalidate unrelated users: + */ + mapping = kmalloc(sizeof(struct address_space), GFP_KERNEL); + if (!mapping) { + rc = -ENOMEM; + goto err_ctx; + } + address_space_init_once(mapping); + /* Make it a slave context. We can promote it later? */ - rc = cxl_context_init(ctx, afu, false, NULL); + rc = cxl_context_init(ctx, afu, false, mapping); if (rc) - goto err_ctx; + goto err_mapping; + cxl_assign_psn_space(ctx); return ctx; +err_mapping: + kfree(mapping); err_ctx: kfree(ctx); err_dev: @@ -260,9 +280,16 @@ struct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops, file = anon_inode_getfile("cxl", fops, ctx, flags); if (IS_ERR(file)) - put_unused_fd(fdtmp); + goto err_fd; + + file->f_mapping = ctx->mapping; + *fd = fdtmp; return file; + +err_fd: + put_unused_fd(fdtmp); + return NULL; } EXPORT_SYMBOL_GPL(cxl_get_fd); diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c index 941fda04aa9a..e762f85ee233 100644 --- a/drivers/misc/cxl/context.c +++ b/drivers/misc/cxl/context.c @@ -272,6 +272,8 @@ static void reclaim_ctx(struct rcu_head *rcu) if (ctx->ff_page) __free_page(ctx->ff_page); ctx->sstp = NULL; + if (ctx->kernelapi) + kfree(ctx->mapping); kfree(ctx); } diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index e7af256f60c5..d6566c60bcb0 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -420,6 +420,7 @@ struct cxl_context { struct mutex mapping_lock; struct page *ff_page; bool mmio_err_ff; + bool kernelapi; spinlock_t sste_lock; /* Protects segment table entries */ struct cxl_sste *sstp; -- cgit v1.2.3 From 390fd5929f52bdfb9dfcc03820041ba556780f4a Mon Sep 17 00:00:00 2001 From: Philippe Bergheaud Date: Fri, 28 Aug 2015 09:37:36 +0200 Subject: cxl: Set up and enable PSL Timebase This patch configures the PSL Timebase function and enables it, after the CAPP has been initialized by OPAL. Acked-by: Ian Munsie Signed-off-by: Philippe Bergheaud Signed-off-by: Michael Ellerman --- drivers/misc/cxl/cxl.h | 5 +++++ drivers/misc/cxl/pci.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index d6566c60bcb0..1c30ef77073d 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -83,8 +83,10 @@ static const cxl_p1_reg_t CXL_PSL_AFUSEL = {0x00B0}; /* 0x00C0:7EFF Implementation dependent area */ static const cxl_p1_reg_t CXL_PSL_FIR1 = {0x0100}; static const cxl_p1_reg_t CXL_PSL_FIR2 = {0x0108}; +static const cxl_p1_reg_t CXL_PSL_Timebase = {0x0110}; static const cxl_p1_reg_t CXL_PSL_VERSION = {0x0118}; static const cxl_p1_reg_t CXL_PSL_RESLCKTO = {0x0128}; +static const cxl_p1_reg_t CXL_PSL_TB_CTLSTAT = {0x0140}; static const cxl_p1_reg_t CXL_PSL_FIR_CNTL = {0x0148}; static const cxl_p1_reg_t CXL_PSL_DSNDCTL = {0x0150}; static const cxl_p1_reg_t CXL_PSL_SNWRALLOC = {0x0158}; @@ -152,6 +154,9 @@ static const cxl_p2n_reg_t CXL_PSL_WED_An = {0x0A0}; #define CXL_PSL_SPAP_Size_Shift 4 #define CXL_PSL_SPAP_V 0x0000000000000001ULL +/****** CXL_PSL_Control ****************************************************/ +#define CXL_PSL_Control_tb 0x0000000000000001ULL + /****** CXL_PSL_DLCNTL *****************************************************/ #define CXL_PSL_DLCNTL_D (0x1ull << (63-28)) #define CXL_PSL_DLCNTL_C (0x1ull << (63-29)) diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 6ca7c3d0ef9b..02c85160bfe9 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -370,6 +370,55 @@ static int init_implementation_adapter_regs(struct cxl *adapter, struct pci_dev return 0; } +#define TBSYNC_CNT(n) (((u64)n & 0x7) << (63-6)) +#define _2048_250MHZ_CYCLES 1 + +static int cxl_setup_psl_timebase(struct cxl *adapter, struct pci_dev *dev) +{ + u64 psl_tb; + int delta; + unsigned int retry = 0; + struct device_node *np; + + if (!(np = pnv_pci_get_phb_node(dev))) + return -ENODEV; + + /* Do not fail when CAPP timebase sync is not supported by OPAL */ + of_node_get(np); + if (! of_get_property(np, "ibm,capp-timebase-sync", NULL)) { + of_node_put(np); + pr_err("PSL: Timebase sync: OPAL support missing\n"); + return 0; + } + of_node_put(np); + + /* + * Setup PSL Timebase Control and Status register + * with the recommended Timebase Sync Count value + */ + cxl_p1_write(adapter, CXL_PSL_TB_CTLSTAT, + TBSYNC_CNT(2 * _2048_250MHZ_CYCLES)); + + /* Enable PSL Timebase */ + cxl_p1_write(adapter, CXL_PSL_Control, 0x0000000000000000); + cxl_p1_write(adapter, CXL_PSL_Control, CXL_PSL_Control_tb); + + /* Wait until CORE TB and PSL TB difference <= 16usecs */ + do { + msleep(1); + if (retry++ > 5) { + pr_err("PSL: Timebase sync: giving up!\n"); + return -EIO; + } + psl_tb = cxl_p1_read(adapter, CXL_PSL_Timebase); + delta = mftb() - psl_tb; + if (delta < 0) + delta = -delta; + } while (cputime_to_usecs(delta) > 16); + + return 0; +} + static int init_implementation_afu_regs(struct cxl_afu *afu) { /* read/write masks for this slice */ @@ -1053,9 +1102,12 @@ err1: return NULL; } +#define CXL_PSL_ErrIVTE_tberror (0x1ull << (63-31)) + static int sanitise_adapter_regs(struct cxl *adapter) { - cxl_p1_write(adapter, CXL_PSL_ErrIVTE, 0x0000000000000000); + /* Clear PSL tberror bit by writing 1 to it */ + cxl_p1_write(adapter, CXL_PSL_ErrIVTE, CXL_PSL_ErrIVTE_tberror); return cxl_tlb_slb_invalidate(adapter); } @@ -1108,6 +1160,9 @@ static int cxl_configure_adapter(struct cxl *adapter, struct pci_dev *dev) if ((rc = pnv_phb_to_cxl_mode(dev, OPAL_PHB_CAPI_MODE_SNOOP_ON))) goto err; + if ((rc = cxl_setup_psl_timebase(adapter, dev))) + goto err; + if ((rc = cxl_register_psl_err_irq(adapter))) goto err; -- cgit v1.2.3