162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2014 IBM Corp. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/interrupt.h> 762306a36Sopenharmony_ci#include <linux/irqdomain.h> 862306a36Sopenharmony_ci#include <linux/workqueue.h> 962306a36Sopenharmony_ci#include <linux/sched.h> 1062306a36Sopenharmony_ci#include <linux/wait.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/pid.h> 1362306a36Sopenharmony_ci#include <asm/cputable.h> 1462306a36Sopenharmony_ci#include <misc/cxl-base.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "cxl.h" 1762306a36Sopenharmony_ci#include "trace.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic int afu_irq_range_start(void) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_HVMODE)) 2262306a36Sopenharmony_ci return 1; 2362306a36Sopenharmony_ci return 0; 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic irqreturn_t schedule_cxl_fault(struct cxl_context *ctx, u64 dsisr, u64 dar) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci ctx->dsisr = dsisr; 2962306a36Sopenharmony_ci ctx->dar = dar; 3062306a36Sopenharmony_ci schedule_work(&ctx->fault_work); 3162306a36Sopenharmony_ci return IRQ_HANDLED; 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciirqreturn_t cxl_irq_psl9(int irq, struct cxl_context *ctx, struct cxl_irq_info *irq_info) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci u64 dsisr, dar; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci dsisr = irq_info->dsisr; 3962306a36Sopenharmony_ci dar = irq_info->dar; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci trace_cxl_psl9_irq(ctx, irq, dsisr, dar); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci pr_devel("CXL interrupt %i for afu pe: %i DSISR: %#llx DAR: %#llx\n", irq, ctx->pe, dsisr, dar); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (dsisr & CXL_PSL9_DSISR_An_TF) { 4662306a36Sopenharmony_ci pr_devel("CXL interrupt: Scheduling translation fault handling for later (pe: %i)\n", ctx->pe); 4762306a36Sopenharmony_ci return schedule_cxl_fault(ctx, dsisr, dar); 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (dsisr & CXL_PSL9_DSISR_An_PE) 5162306a36Sopenharmony_ci return cxl_ops->handle_psl_slice_error(ctx, dsisr, 5262306a36Sopenharmony_ci irq_info->errstat); 5362306a36Sopenharmony_ci if (dsisr & CXL_PSL9_DSISR_An_AE) { 5462306a36Sopenharmony_ci pr_devel("CXL interrupt: AFU Error 0x%016llx\n", irq_info->afu_err); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (ctx->pending_afu_err) { 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * This shouldn't happen - the PSL treats these errors 5962306a36Sopenharmony_ci * as fatal and will have reset the AFU, so there's not 6062306a36Sopenharmony_ci * much point buffering multiple AFU errors. 6162306a36Sopenharmony_ci * OTOH if we DO ever see a storm of these come in it's 6262306a36Sopenharmony_ci * probably best that we log them somewhere: 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci dev_err_ratelimited(&ctx->afu->dev, "CXL AFU Error undelivered to pe %i: 0x%016llx\n", 6562306a36Sopenharmony_ci ctx->pe, irq_info->afu_err); 6662306a36Sopenharmony_ci } else { 6762306a36Sopenharmony_ci spin_lock(&ctx->lock); 6862306a36Sopenharmony_ci ctx->afu_err = irq_info->afu_err; 6962306a36Sopenharmony_ci ctx->pending_afu_err = 1; 7062306a36Sopenharmony_ci spin_unlock(&ctx->lock); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci wake_up_all(&ctx->wq); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci cxl_ops->ack_irq(ctx, CXL_PSL_TFC_An_A, 0); 7662306a36Sopenharmony_ci return IRQ_HANDLED; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci if (dsisr & CXL_PSL9_DSISR_An_OC) 7962306a36Sopenharmony_ci pr_devel("CXL interrupt: OS Context Warning\n"); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci WARN(1, "Unhandled CXL PSL IRQ\n"); 8262306a36Sopenharmony_ci return IRQ_HANDLED; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciirqreturn_t cxl_irq_psl8(int irq, struct cxl_context *ctx, struct cxl_irq_info *irq_info) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci u64 dsisr, dar; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci dsisr = irq_info->dsisr; 9062306a36Sopenharmony_ci dar = irq_info->dar; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci trace_cxl_psl_irq(ctx, irq, dsisr, dar); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci pr_devel("CXL interrupt %i for afu pe: %i DSISR: %#llx DAR: %#llx\n", irq, ctx->pe, dsisr, dar); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_DS) { 9762306a36Sopenharmony_ci /* 9862306a36Sopenharmony_ci * We don't inherently need to sleep to handle this, but we do 9962306a36Sopenharmony_ci * need to get a ref to the task's mm, which we can't do from 10062306a36Sopenharmony_ci * irq context without the potential for a deadlock since it 10162306a36Sopenharmony_ci * takes the task_lock. An alternate option would be to keep a 10262306a36Sopenharmony_ci * reference to the task's mm the entire time it has cxl open, 10362306a36Sopenharmony_ci * but to do that we need to solve the issue where we hold a 10462306a36Sopenharmony_ci * ref to the mm, but the mm can hold a ref to the fd after an 10562306a36Sopenharmony_ci * mmap preventing anything from being cleaned up. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci pr_devel("Scheduling segment miss handling for later pe: %i\n", ctx->pe); 10862306a36Sopenharmony_ci return schedule_cxl_fault(ctx, dsisr, dar); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_M) 11262306a36Sopenharmony_ci pr_devel("CXL interrupt: PTE not found\n"); 11362306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_P) 11462306a36Sopenharmony_ci pr_devel("CXL interrupt: Storage protection violation\n"); 11562306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_A) 11662306a36Sopenharmony_ci pr_devel("CXL interrupt: AFU lock access to write through or cache inhibited storage\n"); 11762306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_S) 11862306a36Sopenharmony_ci pr_devel("CXL interrupt: Access was afu_wr or afu_zero\n"); 11962306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_K) 12062306a36Sopenharmony_ci pr_devel("CXL interrupt: Access not permitted by virtual page class key protection\n"); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_DM) { 12362306a36Sopenharmony_ci /* 12462306a36Sopenharmony_ci * In some cases we might be able to handle the fault 12562306a36Sopenharmony_ci * immediately if hash_page would succeed, but we still need 12662306a36Sopenharmony_ci * the task's mm, which as above we can't get without a lock 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci pr_devel("Scheduling page fault handling for later pe: %i\n", ctx->pe); 12962306a36Sopenharmony_ci return schedule_cxl_fault(ctx, dsisr, dar); 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_ST) 13262306a36Sopenharmony_ci WARN(1, "CXL interrupt: Segment Table PTE not found\n"); 13362306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_UR) 13462306a36Sopenharmony_ci pr_devel("CXL interrupt: AURP PTE not found\n"); 13562306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_PE) 13662306a36Sopenharmony_ci return cxl_ops->handle_psl_slice_error(ctx, dsisr, 13762306a36Sopenharmony_ci irq_info->errstat); 13862306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_AE) { 13962306a36Sopenharmony_ci pr_devel("CXL interrupt: AFU Error 0x%016llx\n", irq_info->afu_err); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (ctx->pending_afu_err) { 14262306a36Sopenharmony_ci /* 14362306a36Sopenharmony_ci * This shouldn't happen - the PSL treats these errors 14462306a36Sopenharmony_ci * as fatal and will have reset the AFU, so there's not 14562306a36Sopenharmony_ci * much point buffering multiple AFU errors. 14662306a36Sopenharmony_ci * OTOH if we DO ever see a storm of these come in it's 14762306a36Sopenharmony_ci * probably best that we log them somewhere: 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci dev_err_ratelimited(&ctx->afu->dev, "CXL AFU Error " 15062306a36Sopenharmony_ci "undelivered to pe %i: 0x%016llx\n", 15162306a36Sopenharmony_ci ctx->pe, irq_info->afu_err); 15262306a36Sopenharmony_ci } else { 15362306a36Sopenharmony_ci spin_lock(&ctx->lock); 15462306a36Sopenharmony_ci ctx->afu_err = irq_info->afu_err; 15562306a36Sopenharmony_ci ctx->pending_afu_err = true; 15662306a36Sopenharmony_ci spin_unlock(&ctx->lock); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci wake_up_all(&ctx->wq); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci cxl_ops->ack_irq(ctx, CXL_PSL_TFC_An_A, 0); 16262306a36Sopenharmony_ci return IRQ_HANDLED; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci if (dsisr & CXL_PSL_DSISR_An_OC) 16562306a36Sopenharmony_ci pr_devel("CXL interrupt: OS Context Warning\n"); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci WARN(1, "Unhandled CXL PSL IRQ\n"); 16862306a36Sopenharmony_ci return IRQ_HANDLED; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic irqreturn_t cxl_irq_afu(int irq, void *data) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct cxl_context *ctx = data; 17462306a36Sopenharmony_ci irq_hw_number_t hwirq = irqd_to_hwirq(irq_get_irq_data(irq)); 17562306a36Sopenharmony_ci int irq_off, afu_irq = 0; 17662306a36Sopenharmony_ci __u16 range; 17762306a36Sopenharmony_ci int r; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* 18062306a36Sopenharmony_ci * Look for the interrupt number. 18162306a36Sopenharmony_ci * On bare-metal, we know range 0 only contains the PSL 18262306a36Sopenharmony_ci * interrupt so we could start counting at range 1 and initialize 18362306a36Sopenharmony_ci * afu_irq at 1. 18462306a36Sopenharmony_ci * In a guest, range 0 also contains AFU interrupts, so it must 18562306a36Sopenharmony_ci * be counted for. Therefore we initialize afu_irq at 0 to take into 18662306a36Sopenharmony_ci * account the PSL interrupt. 18762306a36Sopenharmony_ci * 18862306a36Sopenharmony_ci * For code-readability, it just seems easier to go over all 18962306a36Sopenharmony_ci * the ranges on bare-metal and guest. The end result is the same. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci for (r = 0; r < CXL_IRQ_RANGES; r++) { 19262306a36Sopenharmony_ci irq_off = hwirq - ctx->irqs.offset[r]; 19362306a36Sopenharmony_ci range = ctx->irqs.range[r]; 19462306a36Sopenharmony_ci if (irq_off >= 0 && irq_off < range) { 19562306a36Sopenharmony_ci afu_irq += irq_off; 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci afu_irq += range; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci if (unlikely(r >= CXL_IRQ_RANGES)) { 20162306a36Sopenharmony_ci WARN(1, "Received AFU IRQ out of range for pe %i (virq %i hwirq %lx)\n", 20262306a36Sopenharmony_ci ctx->pe, irq, hwirq); 20362306a36Sopenharmony_ci return IRQ_HANDLED; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci trace_cxl_afu_irq(ctx, afu_irq, irq, hwirq); 20762306a36Sopenharmony_ci pr_devel("Received AFU interrupt %i for pe: %i (virq %i hwirq %lx)\n", 20862306a36Sopenharmony_ci afu_irq, ctx->pe, irq, hwirq); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (unlikely(!ctx->irq_bitmap)) { 21162306a36Sopenharmony_ci WARN(1, "Received AFU IRQ for context with no IRQ bitmap\n"); 21262306a36Sopenharmony_ci return IRQ_HANDLED; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci spin_lock(&ctx->lock); 21562306a36Sopenharmony_ci set_bit(afu_irq - 1, ctx->irq_bitmap); 21662306a36Sopenharmony_ci ctx->pending_irq = true; 21762306a36Sopenharmony_ci spin_unlock(&ctx->lock); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci wake_up_all(&ctx->wq); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return IRQ_HANDLED; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ciunsigned int cxl_map_irq(struct cxl *adapter, irq_hw_number_t hwirq, 22562306a36Sopenharmony_ci irq_handler_t handler, void *cookie, const char *name) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci unsigned int virq; 22862306a36Sopenharmony_ci int result; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* IRQ Domain? */ 23162306a36Sopenharmony_ci virq = irq_create_mapping(NULL, hwirq); 23262306a36Sopenharmony_ci if (!virq) { 23362306a36Sopenharmony_ci dev_warn(&adapter->dev, "cxl_map_irq: irq_create_mapping failed\n"); 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (cxl_ops->setup_irq) 23862306a36Sopenharmony_ci cxl_ops->setup_irq(adapter, hwirq, virq); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci pr_devel("hwirq %#lx mapped to virq %u\n", hwirq, virq); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci result = request_irq(virq, handler, 0, name, cookie); 24362306a36Sopenharmony_ci if (result) { 24462306a36Sopenharmony_ci dev_warn(&adapter->dev, "cxl_map_irq: request_irq failed: %i\n", result); 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return virq; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_civoid cxl_unmap_irq(unsigned int virq, void *cookie) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci free_irq(virq, cookie); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ciint cxl_register_one_irq(struct cxl *adapter, 25762306a36Sopenharmony_ci irq_handler_t handler, 25862306a36Sopenharmony_ci void *cookie, 25962306a36Sopenharmony_ci irq_hw_number_t *dest_hwirq, 26062306a36Sopenharmony_ci unsigned int *dest_virq, 26162306a36Sopenharmony_ci const char *name) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci int hwirq, virq; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if ((hwirq = cxl_ops->alloc_one_irq(adapter)) < 0) 26662306a36Sopenharmony_ci return hwirq; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (!(virq = cxl_map_irq(adapter, hwirq, handler, cookie, name))) 26962306a36Sopenharmony_ci goto err; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci *dest_hwirq = hwirq; 27262306a36Sopenharmony_ci *dest_virq = virq; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cierr: 27762306a36Sopenharmony_ci cxl_ops->release_one_irq(adapter, hwirq); 27862306a36Sopenharmony_ci return -ENOMEM; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_civoid afu_irq_name_free(struct cxl_context *ctx) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct cxl_irq_name *irq_name, *tmp; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci list_for_each_entry_safe(irq_name, tmp, &ctx->irq_names, list) { 28662306a36Sopenharmony_ci kfree(irq_name->name); 28762306a36Sopenharmony_ci list_del(&irq_name->list); 28862306a36Sopenharmony_ci kfree(irq_name); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciint afu_allocate_irqs(struct cxl_context *ctx, u32 count) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci int rc, r, i, j = 1; 29562306a36Sopenharmony_ci struct cxl_irq_name *irq_name; 29662306a36Sopenharmony_ci int alloc_count; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* 29962306a36Sopenharmony_ci * In native mode, range 0 is reserved for the multiplexed 30062306a36Sopenharmony_ci * PSL interrupt. It has been allocated when the AFU was initialized. 30162306a36Sopenharmony_ci * 30262306a36Sopenharmony_ci * In a guest, the PSL interrupt is not mutliplexed, but per-context, 30362306a36Sopenharmony_ci * and is the first interrupt from range 0. It still needs to be 30462306a36Sopenharmony_ci * allocated, so bump the count by one. 30562306a36Sopenharmony_ci */ 30662306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_HVMODE)) 30762306a36Sopenharmony_ci alloc_count = count; 30862306a36Sopenharmony_ci else 30962306a36Sopenharmony_ci alloc_count = count + 1; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if ((rc = cxl_ops->alloc_irq_ranges(&ctx->irqs, ctx->afu->adapter, 31262306a36Sopenharmony_ci alloc_count))) 31362306a36Sopenharmony_ci return rc; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (cpu_has_feature(CPU_FTR_HVMODE)) { 31662306a36Sopenharmony_ci /* Multiplexed PSL Interrupt */ 31762306a36Sopenharmony_ci ctx->irqs.offset[0] = ctx->afu->native->psl_hwirq; 31862306a36Sopenharmony_ci ctx->irqs.range[0] = 1; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ctx->irq_count = count; 32262306a36Sopenharmony_ci ctx->irq_bitmap = bitmap_zalloc(count, GFP_KERNEL); 32362306a36Sopenharmony_ci if (!ctx->irq_bitmap) 32462306a36Sopenharmony_ci goto out; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* 32762306a36Sopenharmony_ci * Allocate names first. If any fail, bail out before allocating 32862306a36Sopenharmony_ci * actual hardware IRQs. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci for (r = afu_irq_range_start(); r < CXL_IRQ_RANGES; r++) { 33162306a36Sopenharmony_ci for (i = 0; i < ctx->irqs.range[r]; i++) { 33262306a36Sopenharmony_ci irq_name = kmalloc(sizeof(struct cxl_irq_name), 33362306a36Sopenharmony_ci GFP_KERNEL); 33462306a36Sopenharmony_ci if (!irq_name) 33562306a36Sopenharmony_ci goto out; 33662306a36Sopenharmony_ci irq_name->name = kasprintf(GFP_KERNEL, "cxl-%s-pe%i-%i", 33762306a36Sopenharmony_ci dev_name(&ctx->afu->dev), 33862306a36Sopenharmony_ci ctx->pe, j); 33962306a36Sopenharmony_ci if (!irq_name->name) { 34062306a36Sopenharmony_ci kfree(irq_name); 34162306a36Sopenharmony_ci goto out; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci /* Add to tail so next look get the correct order */ 34462306a36Sopenharmony_ci list_add_tail(&irq_name->list, &ctx->irq_names); 34562306a36Sopenharmony_ci j++; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ciout: 35162306a36Sopenharmony_ci cxl_ops->release_irq_ranges(&ctx->irqs, ctx->afu->adapter); 35262306a36Sopenharmony_ci bitmap_free(ctx->irq_bitmap); 35362306a36Sopenharmony_ci afu_irq_name_free(ctx); 35462306a36Sopenharmony_ci return -ENOMEM; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic void afu_register_hwirqs(struct cxl_context *ctx) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci irq_hw_number_t hwirq; 36062306a36Sopenharmony_ci struct cxl_irq_name *irq_name; 36162306a36Sopenharmony_ci int r, i; 36262306a36Sopenharmony_ci irqreturn_t (*handler)(int irq, void *data); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* We've allocated all memory now, so let's do the irq allocations */ 36562306a36Sopenharmony_ci irq_name = list_first_entry(&ctx->irq_names, struct cxl_irq_name, list); 36662306a36Sopenharmony_ci for (r = afu_irq_range_start(); r < CXL_IRQ_RANGES; r++) { 36762306a36Sopenharmony_ci hwirq = ctx->irqs.offset[r]; 36862306a36Sopenharmony_ci for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) { 36962306a36Sopenharmony_ci if (r == 0 && i == 0) 37062306a36Sopenharmony_ci /* 37162306a36Sopenharmony_ci * The very first interrupt of range 0 is 37262306a36Sopenharmony_ci * always the PSL interrupt, but we only 37362306a36Sopenharmony_ci * need to connect a handler for guests, 37462306a36Sopenharmony_ci * because there's one PSL interrupt per 37562306a36Sopenharmony_ci * context. 37662306a36Sopenharmony_ci * On bare-metal, the PSL interrupt is 37762306a36Sopenharmony_ci * multiplexed and was setup when the AFU 37862306a36Sopenharmony_ci * was configured. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci handler = cxl_ops->psl_interrupt; 38162306a36Sopenharmony_ci else 38262306a36Sopenharmony_ci handler = cxl_irq_afu; 38362306a36Sopenharmony_ci cxl_map_irq(ctx->afu->adapter, hwirq, handler, ctx, 38462306a36Sopenharmony_ci irq_name->name); 38562306a36Sopenharmony_ci irq_name = list_next_entry(irq_name, list); 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ciint afu_register_irqs(struct cxl_context *ctx, u32 count) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci int rc; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci rc = afu_allocate_irqs(ctx, count); 39562306a36Sopenharmony_ci if (rc) 39662306a36Sopenharmony_ci return rc; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci afu_register_hwirqs(ctx); 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_civoid afu_release_irqs(struct cxl_context *ctx, void *cookie) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci irq_hw_number_t hwirq; 40562306a36Sopenharmony_ci unsigned int virq; 40662306a36Sopenharmony_ci int r, i; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci for (r = afu_irq_range_start(); r < CXL_IRQ_RANGES; r++) { 40962306a36Sopenharmony_ci hwirq = ctx->irqs.offset[r]; 41062306a36Sopenharmony_ci for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) { 41162306a36Sopenharmony_ci virq = irq_find_mapping(NULL, hwirq); 41262306a36Sopenharmony_ci if (virq) 41362306a36Sopenharmony_ci cxl_unmap_irq(virq, cookie); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci afu_irq_name_free(ctx); 41862306a36Sopenharmony_ci cxl_ops->release_irq_ranges(&ctx->irqs, ctx->afu->adapter); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci ctx->irq_count = 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_civoid cxl_afu_decode_psl_serr(struct cxl_afu *afu, u64 serr) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci dev_crit(&afu->dev, 42662306a36Sopenharmony_ci "PSL Slice error received. Check AFU for root cause.\n"); 42762306a36Sopenharmony_ci dev_crit(&afu->dev, "PSL_SERR_An: 0x%016llx\n", serr); 42862306a36Sopenharmony_ci if (serr & CXL_PSL_SERR_An_afuto) 42962306a36Sopenharmony_ci dev_crit(&afu->dev, "AFU MMIO Timeout\n"); 43062306a36Sopenharmony_ci if (serr & CXL_PSL_SERR_An_afudis) 43162306a36Sopenharmony_ci dev_crit(&afu->dev, 43262306a36Sopenharmony_ci "MMIO targeted Accelerator that was not enabled\n"); 43362306a36Sopenharmony_ci if (serr & CXL_PSL_SERR_An_afuov) 43462306a36Sopenharmony_ci dev_crit(&afu->dev, "AFU CTAG Overflow\n"); 43562306a36Sopenharmony_ci if (serr & CXL_PSL_SERR_An_badsrc) 43662306a36Sopenharmony_ci dev_crit(&afu->dev, "Bad Interrupt Source\n"); 43762306a36Sopenharmony_ci if (serr & CXL_PSL_SERR_An_badctx) 43862306a36Sopenharmony_ci dev_crit(&afu->dev, "Bad Context Handle\n"); 43962306a36Sopenharmony_ci if (serr & CXL_PSL_SERR_An_llcmdis) 44062306a36Sopenharmony_ci dev_crit(&afu->dev, "LLCMD to Disabled AFU\n"); 44162306a36Sopenharmony_ci if (serr & CXL_PSL_SERR_An_llcmdto) 44262306a36Sopenharmony_ci dev_crit(&afu->dev, "LLCMD Timeout to AFU\n"); 44362306a36Sopenharmony_ci if (serr & CXL_PSL_SERR_An_afupar) 44462306a36Sopenharmony_ci dev_crit(&afu->dev, "AFU MMIO Parity Error\n"); 44562306a36Sopenharmony_ci if (serr & CXL_PSL_SERR_An_afudup) 44662306a36Sopenharmony_ci dev_crit(&afu->dev, "AFU MMIO Duplicate CTAG Error\n"); 44762306a36Sopenharmony_ci if (serr & CXL_PSL_SERR_An_AE) 44862306a36Sopenharmony_ci dev_crit(&afu->dev, 44962306a36Sopenharmony_ci "AFU asserted JDONE with JERROR in AFU Directed Mode\n"); 45062306a36Sopenharmony_ci} 451