12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 214baf4d9SChristophe Lombard /* 314baf4d9SChristophe Lombard * Copyright 2015 IBM Corp. 414baf4d9SChristophe Lombard */ 514baf4d9SChristophe Lombard 614baf4d9SChristophe Lombard #include <linux/spinlock.h> 714baf4d9SChristophe Lombard #include <linux/uaccess.h> 814baf4d9SChristophe Lombard #include <linux/delay.h> 9d8d2af70SChristophe Leroy #include <linux/irqdomain.h> 10d8d2af70SChristophe Leroy #include <linux/platform_device.h> 1114baf4d9SChristophe Lombard 1214baf4d9SChristophe Lombard #include "cxl.h" 1314baf4d9SChristophe Lombard #include "hcalls.h" 1414baf4d9SChristophe Lombard #include "trace.h" 1514baf4d9SChristophe Lombard 160d400f77SChristophe Lombard #define CXL_ERROR_DETECTED_EVENT 1 170d400f77SChristophe Lombard #define CXL_SLOT_RESET_EVENT 2 180d400f77SChristophe Lombard #define CXL_RESUME_EVENT 3 190d400f77SChristophe Lombard 200d400f77SChristophe Lombard static void pci_error_handlers(struct cxl_afu *afu, 210d400f77SChristophe Lombard int bus_error_event, 220d400f77SChristophe Lombard pci_channel_state_t state) 230d400f77SChristophe Lombard { 240d400f77SChristophe Lombard struct pci_dev *afu_dev; 254e59b754SBjorn Helgaas struct pci_driver *afu_drv; 264e59b754SBjorn Helgaas const struct pci_error_handlers *err_handler; 270d400f77SChristophe Lombard 280d400f77SChristophe Lombard if (afu->phb == NULL) 290d400f77SChristophe Lombard return; 300d400f77SChristophe Lombard 310d400f77SChristophe Lombard list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) { 3216bd44e5SUwe Kleine-König afu_drv = to_pci_driver(afu_dev->dev.driver); 334e59b754SBjorn Helgaas if (!afu_drv) 340d400f77SChristophe Lombard continue; 350d400f77SChristophe Lombard 364e59b754SBjorn Helgaas err_handler = afu_drv->err_handler; 370d400f77SChristophe Lombard switch (bus_error_event) { 380d400f77SChristophe Lombard case CXL_ERROR_DETECTED_EVENT: 390d400f77SChristophe Lombard afu_dev->error_state = state; 400d400f77SChristophe Lombard 414e59b754SBjorn Helgaas if (err_handler && 424e59b754SBjorn Helgaas err_handler->error_detected) 434e59b754SBjorn Helgaas err_handler->error_detected(afu_dev, state); 440d400f77SChristophe Lombard break; 450d400f77SChristophe Lombard case CXL_SLOT_RESET_EVENT: 460d400f77SChristophe Lombard afu_dev->error_state = state; 470d400f77SChristophe Lombard 484e59b754SBjorn Helgaas if (err_handler && 494e59b754SBjorn Helgaas err_handler->slot_reset) 504e59b754SBjorn Helgaas err_handler->slot_reset(afu_dev); 510d400f77SChristophe Lombard break; 520d400f77SChristophe Lombard case CXL_RESUME_EVENT: 534e59b754SBjorn Helgaas if (err_handler && 544e59b754SBjorn Helgaas err_handler->resume) 554e59b754SBjorn Helgaas err_handler->resume(afu_dev); 560d400f77SChristophe Lombard break; 570d400f77SChristophe Lombard } 580d400f77SChristophe Lombard } 590d400f77SChristophe Lombard } 6014baf4d9SChristophe Lombard 6114baf4d9SChristophe Lombard static irqreturn_t guest_handle_psl_slice_error(struct cxl_context *ctx, u64 dsisr, 6214baf4d9SChristophe Lombard u64 errstat) 6314baf4d9SChristophe Lombard { 6414baf4d9SChristophe Lombard pr_devel("in %s\n", __func__); 6514baf4d9SChristophe Lombard dev_crit(&ctx->afu->dev, "PSL ERROR STATUS: 0x%.16llx\n", errstat); 6614baf4d9SChristophe Lombard 6714baf4d9SChristophe Lombard return cxl_ops->ack_irq(ctx, 0, errstat); 6814baf4d9SChristophe Lombard } 6914baf4d9SChristophe Lombard 7014baf4d9SChristophe Lombard static ssize_t guest_collect_vpd(struct cxl *adapter, struct cxl_afu *afu, 7114baf4d9SChristophe Lombard void *buf, size_t len) 7214baf4d9SChristophe Lombard { 7314baf4d9SChristophe Lombard unsigned int entries, mod; 7414baf4d9SChristophe Lombard unsigned long **vpd_buf = NULL; 7514baf4d9SChristophe Lombard struct sg_list *le; 7614baf4d9SChristophe Lombard int rc = 0, i, tocopy; 7714baf4d9SChristophe Lombard u64 out = 0; 7814baf4d9SChristophe Lombard 7914baf4d9SChristophe Lombard if (buf == NULL) 8014baf4d9SChristophe Lombard return -EINVAL; 8114baf4d9SChristophe Lombard 8214baf4d9SChristophe Lombard /* number of entries in the list */ 8314baf4d9SChristophe Lombard entries = len / SG_BUFFER_SIZE; 8414baf4d9SChristophe Lombard mod = len % SG_BUFFER_SIZE; 8514baf4d9SChristophe Lombard if (mod) 8614baf4d9SChristophe Lombard entries++; 8714baf4d9SChristophe Lombard 8814baf4d9SChristophe Lombard if (entries > SG_MAX_ENTRIES) { 8914baf4d9SChristophe Lombard entries = SG_MAX_ENTRIES; 9014baf4d9SChristophe Lombard len = SG_MAX_ENTRIES * SG_BUFFER_SIZE; 9114baf4d9SChristophe Lombard mod = 0; 9214baf4d9SChristophe Lombard } 9314baf4d9SChristophe Lombard 946396bb22SKees Cook vpd_buf = kcalloc(entries, sizeof(unsigned long *), GFP_KERNEL); 9514baf4d9SChristophe Lombard if (!vpd_buf) 9614baf4d9SChristophe Lombard return -ENOMEM; 9714baf4d9SChristophe Lombard 9814baf4d9SChristophe Lombard le = (struct sg_list *)get_zeroed_page(GFP_KERNEL); 9914baf4d9SChristophe Lombard if (!le) { 10014baf4d9SChristophe Lombard rc = -ENOMEM; 10114baf4d9SChristophe Lombard goto err1; 10214baf4d9SChristophe Lombard } 10314baf4d9SChristophe Lombard 10414baf4d9SChristophe Lombard for (i = 0; i < entries; i++) { 10514baf4d9SChristophe Lombard vpd_buf[i] = (unsigned long *)get_zeroed_page(GFP_KERNEL); 10614baf4d9SChristophe Lombard if (!vpd_buf[i]) { 10714baf4d9SChristophe Lombard rc = -ENOMEM; 10814baf4d9SChristophe Lombard goto err2; 10914baf4d9SChristophe Lombard } 11014baf4d9SChristophe Lombard le[i].phys_addr = cpu_to_be64(virt_to_phys(vpd_buf[i])); 11114baf4d9SChristophe Lombard le[i].len = cpu_to_be64(SG_BUFFER_SIZE); 11214baf4d9SChristophe Lombard if ((i == (entries - 1)) && mod) 11314baf4d9SChristophe Lombard le[i].len = cpu_to_be64(mod); 11414baf4d9SChristophe Lombard } 11514baf4d9SChristophe Lombard 11614baf4d9SChristophe Lombard if (adapter) 11714baf4d9SChristophe Lombard rc = cxl_h_collect_vpd_adapter(adapter->guest->handle, 11814baf4d9SChristophe Lombard virt_to_phys(le), entries, &out); 11914baf4d9SChristophe Lombard else 12014baf4d9SChristophe Lombard rc = cxl_h_collect_vpd(afu->guest->handle, 0, 12114baf4d9SChristophe Lombard virt_to_phys(le), entries, &out); 12214baf4d9SChristophe Lombard pr_devel("length of available (entries: %i), vpd: %#llx\n", 12314baf4d9SChristophe Lombard entries, out); 12414baf4d9SChristophe Lombard 12514baf4d9SChristophe Lombard if (!rc) { 12614baf4d9SChristophe Lombard /* 12714baf4d9SChristophe Lombard * hcall returns in 'out' the size of available VPDs. 12814baf4d9SChristophe Lombard * It fills the buffer with as much data as possible. 12914baf4d9SChristophe Lombard */ 13014baf4d9SChristophe Lombard if (out < len) 13114baf4d9SChristophe Lombard len = out; 13214baf4d9SChristophe Lombard rc = len; 13314baf4d9SChristophe Lombard if (out) { 13414baf4d9SChristophe Lombard for (i = 0; i < entries; i++) { 13514baf4d9SChristophe Lombard if (len < SG_BUFFER_SIZE) 13614baf4d9SChristophe Lombard tocopy = len; 13714baf4d9SChristophe Lombard else 13814baf4d9SChristophe Lombard tocopy = SG_BUFFER_SIZE; 13914baf4d9SChristophe Lombard memcpy(buf, vpd_buf[i], tocopy); 14014baf4d9SChristophe Lombard buf += tocopy; 14114baf4d9SChristophe Lombard len -= tocopy; 14214baf4d9SChristophe Lombard } 14314baf4d9SChristophe Lombard } 14414baf4d9SChristophe Lombard } 14514baf4d9SChristophe Lombard err2: 14614baf4d9SChristophe Lombard for (i = 0; i < entries; i++) { 14714baf4d9SChristophe Lombard if (vpd_buf[i]) 14814baf4d9SChristophe Lombard free_page((unsigned long) vpd_buf[i]); 14914baf4d9SChristophe Lombard } 15014baf4d9SChristophe Lombard free_page((unsigned long) le); 15114baf4d9SChristophe Lombard err1: 15214baf4d9SChristophe Lombard kfree(vpd_buf); 15314baf4d9SChristophe Lombard return rc; 15414baf4d9SChristophe Lombard } 15514baf4d9SChristophe Lombard 15614baf4d9SChristophe Lombard static int guest_get_irq_info(struct cxl_context *ctx, struct cxl_irq_info *info) 15714baf4d9SChristophe Lombard { 15814baf4d9SChristophe Lombard return cxl_h_collect_int_info(ctx->afu->guest->handle, ctx->process_token, info); 15914baf4d9SChristophe Lombard } 16014baf4d9SChristophe Lombard 16114baf4d9SChristophe Lombard static irqreturn_t guest_psl_irq(int irq, void *data) 16214baf4d9SChristophe Lombard { 16314baf4d9SChristophe Lombard struct cxl_context *ctx = data; 16414baf4d9SChristophe Lombard struct cxl_irq_info irq_info; 16514baf4d9SChristophe Lombard int rc; 16614baf4d9SChristophe Lombard 16714baf4d9SChristophe Lombard pr_devel("%d: received PSL interrupt %i\n", ctx->pe, irq); 16814baf4d9SChristophe Lombard rc = guest_get_irq_info(ctx, &irq_info); 16914baf4d9SChristophe Lombard if (rc) { 17014baf4d9SChristophe Lombard WARN(1, "Unable to get IRQ info: %i\n", rc); 17114baf4d9SChristophe Lombard return IRQ_HANDLED; 17214baf4d9SChristophe Lombard } 17314baf4d9SChristophe Lombard 17464663f37SChristophe Lombard rc = cxl_irq_psl8(irq, ctx, &irq_info); 17514baf4d9SChristophe Lombard return rc; 17614baf4d9SChristophe Lombard } 17714baf4d9SChristophe Lombard 1780d400f77SChristophe Lombard static int afu_read_error_state(struct cxl_afu *afu, int *state_out) 1790d400f77SChristophe Lombard { 1800d400f77SChristophe Lombard u64 state; 1810d400f77SChristophe Lombard int rc = 0; 1820d400f77SChristophe Lombard 183266eab8fSChristophe Lombard if (!afu) 184266eab8fSChristophe Lombard return -EIO; 185266eab8fSChristophe Lombard 1860d400f77SChristophe Lombard rc = cxl_h_read_error_state(afu->guest->handle, &state); 1870d400f77SChristophe Lombard if (!rc) { 1880d400f77SChristophe Lombard WARN_ON(state != H_STATE_NORMAL && 1890d400f77SChristophe Lombard state != H_STATE_DISABLE && 1900d400f77SChristophe Lombard state != H_STATE_TEMP_UNAVAILABLE && 1910d400f77SChristophe Lombard state != H_STATE_PERM_UNAVAILABLE); 1920d400f77SChristophe Lombard *state_out = state & 0xffffffff; 1930d400f77SChristophe Lombard } 1940d400f77SChristophe Lombard return rc; 1950d400f77SChristophe Lombard } 1960d400f77SChristophe Lombard 19714baf4d9SChristophe Lombard static irqreturn_t guest_slice_irq_err(int irq, void *data) 19814baf4d9SChristophe Lombard { 19914baf4d9SChristophe Lombard struct cxl_afu *afu = data; 20014baf4d9SChristophe Lombard int rc; 2016e0c50f9SPhilippe Bergheaud u64 serr, afu_error, dsisr; 20214baf4d9SChristophe Lombard 20314baf4d9SChristophe Lombard rc = cxl_h_get_fn_error_interrupt(afu->guest->handle, &serr); 20414baf4d9SChristophe Lombard if (rc) { 20514baf4d9SChristophe Lombard dev_crit(&afu->dev, "Couldn't read PSL_SERR_An: %d\n", rc); 20614baf4d9SChristophe Lombard return IRQ_HANDLED; 20714baf4d9SChristophe Lombard } 2086e0c50f9SPhilippe Bergheaud afu_error = cxl_p2n_read(afu, CXL_AFU_ERR_An); 2096e0c50f9SPhilippe Bergheaud dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An); 2106e0c50f9SPhilippe Bergheaud cxl_afu_decode_psl_serr(afu, serr); 2116e0c50f9SPhilippe Bergheaud dev_crit(&afu->dev, "AFU_ERR_An: 0x%.16llx\n", afu_error); 2126e0c50f9SPhilippe Bergheaud dev_crit(&afu->dev, "PSL_DSISR_An: 0x%.16llx\n", dsisr); 21314baf4d9SChristophe Lombard 21414baf4d9SChristophe Lombard rc = cxl_h_ack_fn_error_interrupt(afu->guest->handle, serr); 21514baf4d9SChristophe Lombard if (rc) 21614baf4d9SChristophe Lombard dev_crit(&afu->dev, "Couldn't ack slice error interrupt: %d\n", 21714baf4d9SChristophe Lombard rc); 21814baf4d9SChristophe Lombard 21914baf4d9SChristophe Lombard return IRQ_HANDLED; 22014baf4d9SChristophe Lombard } 22114baf4d9SChristophe Lombard 22214baf4d9SChristophe Lombard 22314baf4d9SChristophe Lombard static int irq_alloc_range(struct cxl *adapter, int len, int *irq) 22414baf4d9SChristophe Lombard { 22514baf4d9SChristophe Lombard int i, n; 22614baf4d9SChristophe Lombard struct irq_avail *cur; 22714baf4d9SChristophe Lombard 22814baf4d9SChristophe Lombard for (i = 0; i < adapter->guest->irq_nranges; i++) { 22914baf4d9SChristophe Lombard cur = &adapter->guest->irq_avail[i]; 23014baf4d9SChristophe Lombard n = bitmap_find_next_zero_area(cur->bitmap, cur->range, 23114baf4d9SChristophe Lombard 0, len, 0); 23214baf4d9SChristophe Lombard if (n < cur->range) { 23314baf4d9SChristophe Lombard bitmap_set(cur->bitmap, n, len); 23414baf4d9SChristophe Lombard *irq = cur->offset + n; 23514baf4d9SChristophe Lombard pr_devel("guest: allocate IRQs %#x->%#x\n", 23614baf4d9SChristophe Lombard *irq, *irq + len - 1); 23714baf4d9SChristophe Lombard 23814baf4d9SChristophe Lombard return 0; 23914baf4d9SChristophe Lombard } 24014baf4d9SChristophe Lombard } 24114baf4d9SChristophe Lombard return -ENOSPC; 24214baf4d9SChristophe Lombard } 24314baf4d9SChristophe Lombard 24414baf4d9SChristophe Lombard static int irq_free_range(struct cxl *adapter, int irq, int len) 24514baf4d9SChristophe Lombard { 24614baf4d9SChristophe Lombard int i, n; 24714baf4d9SChristophe Lombard struct irq_avail *cur; 24814baf4d9SChristophe Lombard 24914baf4d9SChristophe Lombard if (len == 0) 25014baf4d9SChristophe Lombard return -ENOENT; 25114baf4d9SChristophe Lombard 25214baf4d9SChristophe Lombard for (i = 0; i < adapter->guest->irq_nranges; i++) { 25314baf4d9SChristophe Lombard cur = &adapter->guest->irq_avail[i]; 25414baf4d9SChristophe Lombard if (irq >= cur->offset && 25514baf4d9SChristophe Lombard (irq + len) <= (cur->offset + cur->range)) { 25614baf4d9SChristophe Lombard n = irq - cur->offset; 25714baf4d9SChristophe Lombard bitmap_clear(cur->bitmap, n, len); 25814baf4d9SChristophe Lombard pr_devel("guest: release IRQs %#x->%#x\n", 25914baf4d9SChristophe Lombard irq, irq + len - 1); 26014baf4d9SChristophe Lombard return 0; 26114baf4d9SChristophe Lombard } 26214baf4d9SChristophe Lombard } 26314baf4d9SChristophe Lombard return -ENOENT; 26414baf4d9SChristophe Lombard } 26514baf4d9SChristophe Lombard 26614baf4d9SChristophe Lombard static int guest_reset(struct cxl *adapter) 26714baf4d9SChristophe Lombard { 2680d400f77SChristophe Lombard struct cxl_afu *afu = NULL; 2690d400f77SChristophe Lombard int i, rc; 27014baf4d9SChristophe Lombard 27114baf4d9SChristophe Lombard pr_devel("Adapter reset request\n"); 272edeb304fSVaibhav Jain spin_lock(&adapter->afu_list_lock); 2730d400f77SChristophe Lombard for (i = 0; i < adapter->slices; i++) { 2740d400f77SChristophe Lombard if ((afu = adapter->afu[i])) { 2750d400f77SChristophe Lombard pci_error_handlers(afu, CXL_ERROR_DETECTED_EVENT, 2760d400f77SChristophe Lombard pci_channel_io_frozen); 2770d400f77SChristophe Lombard cxl_context_detach_all(afu); 2780d400f77SChristophe Lombard } 2790d400f77SChristophe Lombard } 2800d400f77SChristophe Lombard 28114baf4d9SChristophe Lombard rc = cxl_h_reset_adapter(adapter->guest->handle); 2820d400f77SChristophe Lombard for (i = 0; i < adapter->slices; i++) { 2830d400f77SChristophe Lombard if (!rc && (afu = adapter->afu[i])) { 2840d400f77SChristophe Lombard pci_error_handlers(afu, CXL_SLOT_RESET_EVENT, 2850d400f77SChristophe Lombard pci_channel_io_normal); 2860d400f77SChristophe Lombard pci_error_handlers(afu, CXL_RESUME_EVENT, 0); 2870d400f77SChristophe Lombard } 2880d400f77SChristophe Lombard } 289edeb304fSVaibhav Jain spin_unlock(&adapter->afu_list_lock); 29014baf4d9SChristophe Lombard return rc; 29114baf4d9SChristophe Lombard } 29214baf4d9SChristophe Lombard 29314baf4d9SChristophe Lombard static int guest_alloc_one_irq(struct cxl *adapter) 29414baf4d9SChristophe Lombard { 29514baf4d9SChristophe Lombard int irq; 29614baf4d9SChristophe Lombard 29714baf4d9SChristophe Lombard spin_lock(&adapter->guest->irq_alloc_lock); 29814baf4d9SChristophe Lombard if (irq_alloc_range(adapter, 1, &irq)) 29914baf4d9SChristophe Lombard irq = -ENOSPC; 30014baf4d9SChristophe Lombard spin_unlock(&adapter->guest->irq_alloc_lock); 30114baf4d9SChristophe Lombard return irq; 30214baf4d9SChristophe Lombard } 30314baf4d9SChristophe Lombard 30414baf4d9SChristophe Lombard static void guest_release_one_irq(struct cxl *adapter, int irq) 30514baf4d9SChristophe Lombard { 30614baf4d9SChristophe Lombard spin_lock(&adapter->guest->irq_alloc_lock); 30714baf4d9SChristophe Lombard irq_free_range(adapter, irq, 1); 30814baf4d9SChristophe Lombard spin_unlock(&adapter->guest->irq_alloc_lock); 30914baf4d9SChristophe Lombard } 31014baf4d9SChristophe Lombard 31114baf4d9SChristophe Lombard static int guest_alloc_irq_ranges(struct cxl_irq_ranges *irqs, 31214baf4d9SChristophe Lombard struct cxl *adapter, unsigned int num) 31314baf4d9SChristophe Lombard { 31414baf4d9SChristophe Lombard int i, try, irq; 31514baf4d9SChristophe Lombard 31614baf4d9SChristophe Lombard memset(irqs, 0, sizeof(struct cxl_irq_ranges)); 31714baf4d9SChristophe Lombard 31814baf4d9SChristophe Lombard spin_lock(&adapter->guest->irq_alloc_lock); 31914baf4d9SChristophe Lombard for (i = 0; i < CXL_IRQ_RANGES && num; i++) { 32014baf4d9SChristophe Lombard try = num; 32114baf4d9SChristophe Lombard while (try) { 32214baf4d9SChristophe Lombard if (irq_alloc_range(adapter, try, &irq) == 0) 32314baf4d9SChristophe Lombard break; 32414baf4d9SChristophe Lombard try /= 2; 32514baf4d9SChristophe Lombard } 32614baf4d9SChristophe Lombard if (!try) 32714baf4d9SChristophe Lombard goto error; 32814baf4d9SChristophe Lombard irqs->offset[i] = irq; 32914baf4d9SChristophe Lombard irqs->range[i] = try; 33014baf4d9SChristophe Lombard num -= try; 33114baf4d9SChristophe Lombard } 33214baf4d9SChristophe Lombard if (num) 33314baf4d9SChristophe Lombard goto error; 33414baf4d9SChristophe Lombard spin_unlock(&adapter->guest->irq_alloc_lock); 33514baf4d9SChristophe Lombard return 0; 33614baf4d9SChristophe Lombard 33714baf4d9SChristophe Lombard error: 33814baf4d9SChristophe Lombard for (i = 0; i < CXL_IRQ_RANGES; i++) 33914baf4d9SChristophe Lombard irq_free_range(adapter, irqs->offset[i], irqs->range[i]); 34014baf4d9SChristophe Lombard spin_unlock(&adapter->guest->irq_alloc_lock); 34114baf4d9SChristophe Lombard return -ENOSPC; 34214baf4d9SChristophe Lombard } 34314baf4d9SChristophe Lombard 34414baf4d9SChristophe Lombard static void guest_release_irq_ranges(struct cxl_irq_ranges *irqs, 34514baf4d9SChristophe Lombard struct cxl *adapter) 34614baf4d9SChristophe Lombard { 34714baf4d9SChristophe Lombard int i; 34814baf4d9SChristophe Lombard 34914baf4d9SChristophe Lombard spin_lock(&adapter->guest->irq_alloc_lock); 35014baf4d9SChristophe Lombard for (i = 0; i < CXL_IRQ_RANGES; i++) 35114baf4d9SChristophe Lombard irq_free_range(adapter, irqs->offset[i], irqs->range[i]); 35214baf4d9SChristophe Lombard spin_unlock(&adapter->guest->irq_alloc_lock); 35314baf4d9SChristophe Lombard } 35414baf4d9SChristophe Lombard 35514baf4d9SChristophe Lombard static int guest_register_serr_irq(struct cxl_afu *afu) 35614baf4d9SChristophe Lombard { 35714baf4d9SChristophe Lombard afu->err_irq_name = kasprintf(GFP_KERNEL, "cxl-%s-err", 35814baf4d9SChristophe Lombard dev_name(&afu->dev)); 35914baf4d9SChristophe Lombard if (!afu->err_irq_name) 36014baf4d9SChristophe Lombard return -ENOMEM; 36114baf4d9SChristophe Lombard 36214baf4d9SChristophe Lombard if (!(afu->serr_virq = cxl_map_irq(afu->adapter, afu->serr_hwirq, 36314baf4d9SChristophe Lombard guest_slice_irq_err, afu, afu->err_irq_name))) { 36414baf4d9SChristophe Lombard kfree(afu->err_irq_name); 36514baf4d9SChristophe Lombard afu->err_irq_name = NULL; 36614baf4d9SChristophe Lombard return -ENOMEM; 36714baf4d9SChristophe Lombard } 36814baf4d9SChristophe Lombard 36914baf4d9SChristophe Lombard return 0; 37014baf4d9SChristophe Lombard } 37114baf4d9SChristophe Lombard 37214baf4d9SChristophe Lombard static void guest_release_serr_irq(struct cxl_afu *afu) 37314baf4d9SChristophe Lombard { 37414baf4d9SChristophe Lombard cxl_unmap_irq(afu->serr_virq, afu); 37514baf4d9SChristophe Lombard cxl_ops->release_one_irq(afu->adapter, afu->serr_hwirq); 37614baf4d9SChristophe Lombard kfree(afu->err_irq_name); 37714baf4d9SChristophe Lombard } 37814baf4d9SChristophe Lombard 37914baf4d9SChristophe Lombard static int guest_ack_irq(struct cxl_context *ctx, u64 tfc, u64 psl_reset_mask) 38014baf4d9SChristophe Lombard { 38114baf4d9SChristophe Lombard return cxl_h_control_faults(ctx->afu->guest->handle, ctx->process_token, 38214baf4d9SChristophe Lombard tfc >> 32, (psl_reset_mask != 0)); 38314baf4d9SChristophe Lombard } 38414baf4d9SChristophe Lombard 38514baf4d9SChristophe Lombard static void disable_afu_irqs(struct cxl_context *ctx) 38614baf4d9SChristophe Lombard { 38714baf4d9SChristophe Lombard irq_hw_number_t hwirq; 38814baf4d9SChristophe Lombard unsigned int virq; 38914baf4d9SChristophe Lombard int r, i; 39014baf4d9SChristophe Lombard 39114baf4d9SChristophe Lombard pr_devel("Disabling AFU(%d) interrupts\n", ctx->afu->slice); 39214baf4d9SChristophe Lombard for (r = 0; r < CXL_IRQ_RANGES; r++) { 39314baf4d9SChristophe Lombard hwirq = ctx->irqs.offset[r]; 39414baf4d9SChristophe Lombard for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) { 39514baf4d9SChristophe Lombard virq = irq_find_mapping(NULL, hwirq); 39614baf4d9SChristophe Lombard disable_irq(virq); 39714baf4d9SChristophe Lombard } 39814baf4d9SChristophe Lombard } 39914baf4d9SChristophe Lombard } 40014baf4d9SChristophe Lombard 40114baf4d9SChristophe Lombard static void enable_afu_irqs(struct cxl_context *ctx) 40214baf4d9SChristophe Lombard { 40314baf4d9SChristophe Lombard irq_hw_number_t hwirq; 40414baf4d9SChristophe Lombard unsigned int virq; 40514baf4d9SChristophe Lombard int r, i; 40614baf4d9SChristophe Lombard 40714baf4d9SChristophe Lombard pr_devel("Enabling AFU(%d) interrupts\n", ctx->afu->slice); 40814baf4d9SChristophe Lombard for (r = 0; r < CXL_IRQ_RANGES; r++) { 40914baf4d9SChristophe Lombard hwirq = ctx->irqs.offset[r]; 41014baf4d9SChristophe Lombard for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) { 41114baf4d9SChristophe Lombard virq = irq_find_mapping(NULL, hwirq); 41214baf4d9SChristophe Lombard enable_irq(virq); 41314baf4d9SChristophe Lombard } 41414baf4d9SChristophe Lombard } 41514baf4d9SChristophe Lombard } 41614baf4d9SChristophe Lombard 41714baf4d9SChristophe Lombard static int _guest_afu_cr_readXX(int sz, struct cxl_afu *afu, int cr_idx, 41814baf4d9SChristophe Lombard u64 offset, u64 *val) 41914baf4d9SChristophe Lombard { 42014baf4d9SChristophe Lombard unsigned long cr; 42114baf4d9SChristophe Lombard char c; 42214baf4d9SChristophe Lombard int rc = 0; 42314baf4d9SChristophe Lombard 42414baf4d9SChristophe Lombard if (afu->crs_len < sz) 42514baf4d9SChristophe Lombard return -ENOENT; 42614baf4d9SChristophe Lombard 42714baf4d9SChristophe Lombard if (unlikely(offset >= afu->crs_len)) 42814baf4d9SChristophe Lombard return -ERANGE; 42914baf4d9SChristophe Lombard 43014baf4d9SChristophe Lombard cr = get_zeroed_page(GFP_KERNEL); 43114baf4d9SChristophe Lombard if (!cr) 43214baf4d9SChristophe Lombard return -ENOMEM; 43314baf4d9SChristophe Lombard 43414baf4d9SChristophe Lombard rc = cxl_h_get_config(afu->guest->handle, cr_idx, offset, 43514baf4d9SChristophe Lombard virt_to_phys((void *)cr), sz); 43614baf4d9SChristophe Lombard if (rc) 43714baf4d9SChristophe Lombard goto err; 43814baf4d9SChristophe Lombard 43914baf4d9SChristophe Lombard switch (sz) { 44014baf4d9SChristophe Lombard case 1: 44114baf4d9SChristophe Lombard c = *((char *) cr); 44214baf4d9SChristophe Lombard *val = c; 44314baf4d9SChristophe Lombard break; 44414baf4d9SChristophe Lombard case 2: 44514baf4d9SChristophe Lombard *val = in_le16((u16 *)cr); 44614baf4d9SChristophe Lombard break; 44714baf4d9SChristophe Lombard case 4: 44814baf4d9SChristophe Lombard *val = in_le32((unsigned *)cr); 44914baf4d9SChristophe Lombard break; 45014baf4d9SChristophe Lombard case 8: 45114baf4d9SChristophe Lombard *val = in_le64((u64 *)cr); 45214baf4d9SChristophe Lombard break; 45314baf4d9SChristophe Lombard default: 45414baf4d9SChristophe Lombard WARN_ON(1); 45514baf4d9SChristophe Lombard } 45614baf4d9SChristophe Lombard err: 45714baf4d9SChristophe Lombard free_page(cr); 45814baf4d9SChristophe Lombard return rc; 45914baf4d9SChristophe Lombard } 46014baf4d9SChristophe Lombard 46114baf4d9SChristophe Lombard static int guest_afu_cr_read32(struct cxl_afu *afu, int cr_idx, u64 offset, 46214baf4d9SChristophe Lombard u32 *out) 46314baf4d9SChristophe Lombard { 46414baf4d9SChristophe Lombard int rc; 46514baf4d9SChristophe Lombard u64 val; 46614baf4d9SChristophe Lombard 46714baf4d9SChristophe Lombard rc = _guest_afu_cr_readXX(4, afu, cr_idx, offset, &val); 46814baf4d9SChristophe Lombard if (!rc) 46914baf4d9SChristophe Lombard *out = (u32) val; 47014baf4d9SChristophe Lombard return rc; 47114baf4d9SChristophe Lombard } 47214baf4d9SChristophe Lombard 47314baf4d9SChristophe Lombard static int guest_afu_cr_read16(struct cxl_afu *afu, int cr_idx, u64 offset, 47414baf4d9SChristophe Lombard u16 *out) 47514baf4d9SChristophe Lombard { 47614baf4d9SChristophe Lombard int rc; 47714baf4d9SChristophe Lombard u64 val; 47814baf4d9SChristophe Lombard 47914baf4d9SChristophe Lombard rc = _guest_afu_cr_readXX(2, afu, cr_idx, offset, &val); 48014baf4d9SChristophe Lombard if (!rc) 48114baf4d9SChristophe Lombard *out = (u16) val; 48214baf4d9SChristophe Lombard return rc; 48314baf4d9SChristophe Lombard } 48414baf4d9SChristophe Lombard 48514baf4d9SChristophe Lombard static int guest_afu_cr_read8(struct cxl_afu *afu, int cr_idx, u64 offset, 48614baf4d9SChristophe Lombard u8 *out) 48714baf4d9SChristophe Lombard { 48814baf4d9SChristophe Lombard int rc; 48914baf4d9SChristophe Lombard u64 val; 49014baf4d9SChristophe Lombard 49114baf4d9SChristophe Lombard rc = _guest_afu_cr_readXX(1, afu, cr_idx, offset, &val); 49214baf4d9SChristophe Lombard if (!rc) 49314baf4d9SChristophe Lombard *out = (u8) val; 49414baf4d9SChristophe Lombard return rc; 49514baf4d9SChristophe Lombard } 49614baf4d9SChristophe Lombard 49714baf4d9SChristophe Lombard static int guest_afu_cr_read64(struct cxl_afu *afu, int cr_idx, u64 offset, 49814baf4d9SChristophe Lombard u64 *out) 49914baf4d9SChristophe Lombard { 50014baf4d9SChristophe Lombard return _guest_afu_cr_readXX(8, afu, cr_idx, offset, out); 50114baf4d9SChristophe Lombard } 50214baf4d9SChristophe Lombard 503d601ea91SFrederic Barrat static int guest_afu_cr_write32(struct cxl_afu *afu, int cr, u64 off, u32 in) 504d601ea91SFrederic Barrat { 505d601ea91SFrederic Barrat /* config record is not writable from guest */ 506d601ea91SFrederic Barrat return -EPERM; 507d601ea91SFrederic Barrat } 508d601ea91SFrederic Barrat 509d601ea91SFrederic Barrat static int guest_afu_cr_write16(struct cxl_afu *afu, int cr, u64 off, u16 in) 510d601ea91SFrederic Barrat { 511d601ea91SFrederic Barrat /* config record is not writable from guest */ 512d601ea91SFrederic Barrat return -EPERM; 513d601ea91SFrederic Barrat } 514d601ea91SFrederic Barrat 515d601ea91SFrederic Barrat static int guest_afu_cr_write8(struct cxl_afu *afu, int cr, u64 off, u8 in) 516d601ea91SFrederic Barrat { 517d601ea91SFrederic Barrat /* config record is not writable from guest */ 518d601ea91SFrederic Barrat return -EPERM; 519d601ea91SFrederic Barrat } 520d601ea91SFrederic Barrat 52114baf4d9SChristophe Lombard static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr) 52214baf4d9SChristophe Lombard { 52314baf4d9SChristophe Lombard struct cxl_process_element_hcall *elem; 52414baf4d9SChristophe Lombard struct cxl *adapter = ctx->afu->adapter; 52514baf4d9SChristophe Lombard const struct cred *cred; 52614baf4d9SChristophe Lombard u32 pid, idx; 52714baf4d9SChristophe Lombard int rc, r, i; 52814baf4d9SChristophe Lombard u64 mmio_addr, mmio_size; 52914baf4d9SChristophe Lombard __be64 flags = 0; 53014baf4d9SChristophe Lombard 53114baf4d9SChristophe Lombard /* Must be 8 byte aligned and cannot cross a 4096 byte boundary */ 53214baf4d9SChristophe Lombard if (!(elem = (struct cxl_process_element_hcall *) 53314baf4d9SChristophe Lombard get_zeroed_page(GFP_KERNEL))) 53414baf4d9SChristophe Lombard return -ENOMEM; 53514baf4d9SChristophe Lombard 53614baf4d9SChristophe Lombard elem->version = cpu_to_be64(CXL_PROCESS_ELEMENT_VERSION); 53714baf4d9SChristophe Lombard if (ctx->kernel) { 53814baf4d9SChristophe Lombard pid = 0; 53914baf4d9SChristophe Lombard flags |= CXL_PE_TRANSLATION_ENABLED; 54014baf4d9SChristophe Lombard flags |= CXL_PE_PRIVILEGED_PROCESS; 54114baf4d9SChristophe Lombard if (mfmsr() & MSR_SF) 54214baf4d9SChristophe Lombard flags |= CXL_PE_64_BIT; 54314baf4d9SChristophe Lombard } else { 54414baf4d9SChristophe Lombard pid = current->pid; 54514baf4d9SChristophe Lombard flags |= CXL_PE_PROBLEM_STATE; 54614baf4d9SChristophe Lombard flags |= CXL_PE_TRANSLATION_ENABLED; 54714baf4d9SChristophe Lombard if (!test_tsk_thread_flag(current, TIF_32BIT)) 54814baf4d9SChristophe Lombard flags |= CXL_PE_64_BIT; 54914baf4d9SChristophe Lombard cred = get_current_cred(); 55014baf4d9SChristophe Lombard if (uid_eq(cred->euid, GLOBAL_ROOT_UID)) 55114baf4d9SChristophe Lombard flags |= CXL_PE_PRIVILEGED_PROCESS; 55214baf4d9SChristophe Lombard put_cred(cred); 55314baf4d9SChristophe Lombard } 55414baf4d9SChristophe Lombard elem->flags = cpu_to_be64(flags); 55514baf4d9SChristophe Lombard elem->common.tid = cpu_to_be32(0); /* Unused */ 55614baf4d9SChristophe Lombard elem->common.pid = cpu_to_be32(pid); 55714baf4d9SChristophe Lombard elem->common.csrp = cpu_to_be64(0); /* disable */ 558f24be42aSChristophe Lombard elem->common.u.psl8.aurp0 = cpu_to_be64(0); /* disable */ 559f24be42aSChristophe Lombard elem->common.u.psl8.aurp1 = cpu_to_be64(0); /* disable */ 56014baf4d9SChristophe Lombard 56114baf4d9SChristophe Lombard cxl_prefault(ctx, wed); 56214baf4d9SChristophe Lombard 563f24be42aSChristophe Lombard elem->common.u.psl8.sstp0 = cpu_to_be64(ctx->sstp0); 564f24be42aSChristophe Lombard elem->common.u.psl8.sstp1 = cpu_to_be64(ctx->sstp1); 5653c206fa7SIan Munsie 5663c206fa7SIan Munsie /* 5673c206fa7SIan Munsie * Ensure we have at least one interrupt allocated to take faults for 5683c206fa7SIan Munsie * kernel contexts that may not have allocated any AFU IRQs at all: 5693c206fa7SIan Munsie */ 5703c206fa7SIan Munsie if (ctx->irqs.range[0] == 0) { 5713c206fa7SIan Munsie rc = afu_register_irqs(ctx, 0); 5723c206fa7SIan Munsie if (rc) 5733c206fa7SIan Munsie goto out_free; 5743c206fa7SIan Munsie } 5753c206fa7SIan Munsie 57614baf4d9SChristophe Lombard for (r = 0; r < CXL_IRQ_RANGES; r++) { 57714baf4d9SChristophe Lombard for (i = 0; i < ctx->irqs.range[r]; i++) { 57814baf4d9SChristophe Lombard if (r == 0 && i == 0) { 57914baf4d9SChristophe Lombard elem->pslVirtualIsn = cpu_to_be32(ctx->irqs.offset[0]); 58014baf4d9SChristophe Lombard } else { 58114baf4d9SChristophe Lombard idx = ctx->irqs.offset[r] + i - adapter->guest->irq_base_offset; 58214baf4d9SChristophe Lombard elem->applicationVirtualIsnBitmap[idx / 8] |= 0x80 >> (idx % 8); 58314baf4d9SChristophe Lombard } 58414baf4d9SChristophe Lombard } 58514baf4d9SChristophe Lombard } 58614baf4d9SChristophe Lombard elem->common.amr = cpu_to_be64(amr); 58714baf4d9SChristophe Lombard elem->common.wed = cpu_to_be64(wed); 58814baf4d9SChristophe Lombard 58914baf4d9SChristophe Lombard disable_afu_irqs(ctx); 59014baf4d9SChristophe Lombard 59114baf4d9SChristophe Lombard rc = cxl_h_attach_process(ctx->afu->guest->handle, elem, 59214baf4d9SChristophe Lombard &ctx->process_token, &mmio_addr, &mmio_size); 59314baf4d9SChristophe Lombard if (rc == H_SUCCESS) { 59414baf4d9SChristophe Lombard if (ctx->master || !ctx->afu->pp_psa) { 59514baf4d9SChristophe Lombard ctx->psn_phys = ctx->afu->psn_phys; 59614baf4d9SChristophe Lombard ctx->psn_size = ctx->afu->adapter->ps_size; 59714baf4d9SChristophe Lombard } else { 59814baf4d9SChristophe Lombard ctx->psn_phys = mmio_addr; 59914baf4d9SChristophe Lombard ctx->psn_size = mmio_size; 60014baf4d9SChristophe Lombard } 60114baf4d9SChristophe Lombard if (ctx->afu->pp_psa && mmio_size && 60214baf4d9SChristophe Lombard ctx->afu->pp_size == 0) { 60314baf4d9SChristophe Lombard /* 60414baf4d9SChristophe Lombard * There's no property in the device tree to read the 60514baf4d9SChristophe Lombard * pp_size. We only find out at the 1st attach. 60614baf4d9SChristophe Lombard * Compared to bare-metal, it is too late and we 60714baf4d9SChristophe Lombard * should really lock here. However, on powerVM, 60814baf4d9SChristophe Lombard * pp_size is really only used to display in /sys. 60914baf4d9SChristophe Lombard * Being discussed with pHyp for their next release. 61014baf4d9SChristophe Lombard */ 61114baf4d9SChristophe Lombard ctx->afu->pp_size = mmio_size; 61214baf4d9SChristophe Lombard } 61314baf4d9SChristophe Lombard /* from PAPR: process element is bytes 4-7 of process token */ 61414baf4d9SChristophe Lombard ctx->external_pe = ctx->process_token & 0xFFFFFFFF; 61514baf4d9SChristophe Lombard pr_devel("CXL pe=%i is known as %i for pHyp, mmio_size=%#llx", 61614baf4d9SChristophe Lombard ctx->pe, ctx->external_pe, ctx->psn_size); 61714baf4d9SChristophe Lombard ctx->pe_inserted = true; 61814baf4d9SChristophe Lombard enable_afu_irqs(ctx); 61914baf4d9SChristophe Lombard } 62014baf4d9SChristophe Lombard 6213c206fa7SIan Munsie out_free: 62214baf4d9SChristophe Lombard free_page((u64)elem); 62314baf4d9SChristophe Lombard return rc; 62414baf4d9SChristophe Lombard } 62514baf4d9SChristophe Lombard 62614baf4d9SChristophe Lombard static int guest_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr) 62714baf4d9SChristophe Lombard { 62814baf4d9SChristophe Lombard pr_devel("in %s\n", __func__); 62914baf4d9SChristophe Lombard 63014baf4d9SChristophe Lombard ctx->kernel = kernel; 63114baf4d9SChristophe Lombard if (ctx->afu->current_mode == CXL_MODE_DIRECTED) 63214baf4d9SChristophe Lombard return attach_afu_directed(ctx, wed, amr); 63314baf4d9SChristophe Lombard 63414baf4d9SChristophe Lombard /* dedicated mode not supported on FW840 */ 63514baf4d9SChristophe Lombard 63614baf4d9SChristophe Lombard return -EINVAL; 63714baf4d9SChristophe Lombard } 63814baf4d9SChristophe Lombard 63914baf4d9SChristophe Lombard static int detach_afu_directed(struct cxl_context *ctx) 64014baf4d9SChristophe Lombard { 64114baf4d9SChristophe Lombard if (!ctx->pe_inserted) 64214baf4d9SChristophe Lombard return 0; 64314baf4d9SChristophe Lombard if (cxl_h_detach_process(ctx->afu->guest->handle, ctx->process_token)) 64414baf4d9SChristophe Lombard return -1; 64514baf4d9SChristophe Lombard return 0; 64614baf4d9SChristophe Lombard } 64714baf4d9SChristophe Lombard 64814baf4d9SChristophe Lombard static int guest_detach_process(struct cxl_context *ctx) 64914baf4d9SChristophe Lombard { 65014baf4d9SChristophe Lombard pr_devel("in %s\n", __func__); 65114baf4d9SChristophe Lombard trace_cxl_detach(ctx); 65214baf4d9SChristophe Lombard 6530d400f77SChristophe Lombard if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu)) 65414baf4d9SChristophe Lombard return -EIO; 65514baf4d9SChristophe Lombard 65614baf4d9SChristophe Lombard if (ctx->afu->current_mode == CXL_MODE_DIRECTED) 65714baf4d9SChristophe Lombard return detach_afu_directed(ctx); 65814baf4d9SChristophe Lombard 65914baf4d9SChristophe Lombard return -EINVAL; 66014baf4d9SChristophe Lombard } 66114baf4d9SChristophe Lombard 66214baf4d9SChristophe Lombard static void guest_release_afu(struct device *dev) 66314baf4d9SChristophe Lombard { 66414baf4d9SChristophe Lombard struct cxl_afu *afu = to_cxl_afu(dev); 66514baf4d9SChristophe Lombard 66614baf4d9SChristophe Lombard pr_devel("%s\n", __func__); 66714baf4d9SChristophe Lombard 66814baf4d9SChristophe Lombard idr_destroy(&afu->contexts_idr); 66914baf4d9SChristophe Lombard 67014baf4d9SChristophe Lombard kfree(afu->guest); 67114baf4d9SChristophe Lombard kfree(afu); 67214baf4d9SChristophe Lombard } 67314baf4d9SChristophe Lombard 67414baf4d9SChristophe Lombard ssize_t cxl_guest_read_afu_vpd(struct cxl_afu *afu, void *buf, size_t len) 67514baf4d9SChristophe Lombard { 67614baf4d9SChristophe Lombard return guest_collect_vpd(NULL, afu, buf, len); 67714baf4d9SChristophe Lombard } 67814baf4d9SChristophe Lombard 67914baf4d9SChristophe Lombard #define ERR_BUFF_MAX_COPY_SIZE PAGE_SIZE 68014baf4d9SChristophe Lombard static ssize_t guest_afu_read_err_buffer(struct cxl_afu *afu, char *buf, 68114baf4d9SChristophe Lombard loff_t off, size_t count) 68214baf4d9SChristophe Lombard { 68314baf4d9SChristophe Lombard void *tbuf = NULL; 68414baf4d9SChristophe Lombard int rc = 0; 68514baf4d9SChristophe Lombard 68614baf4d9SChristophe Lombard tbuf = (void *) get_zeroed_page(GFP_KERNEL); 68714baf4d9SChristophe Lombard if (!tbuf) 68814baf4d9SChristophe Lombard return -ENOMEM; 68914baf4d9SChristophe Lombard 69014baf4d9SChristophe Lombard rc = cxl_h_get_afu_err(afu->guest->handle, 69114baf4d9SChristophe Lombard off & 0x7, 69214baf4d9SChristophe Lombard virt_to_phys(tbuf), 69314baf4d9SChristophe Lombard count); 69414baf4d9SChristophe Lombard if (rc) 69514baf4d9SChristophe Lombard goto err; 69614baf4d9SChristophe Lombard 69714baf4d9SChristophe Lombard if (count > ERR_BUFF_MAX_COPY_SIZE) 69814baf4d9SChristophe Lombard count = ERR_BUFF_MAX_COPY_SIZE - (off & 0x7); 69914baf4d9SChristophe Lombard memcpy(buf, tbuf, count); 70014baf4d9SChristophe Lombard err: 70114baf4d9SChristophe Lombard free_page((u64)tbuf); 70214baf4d9SChristophe Lombard 70314baf4d9SChristophe Lombard return rc; 70414baf4d9SChristophe Lombard } 70514baf4d9SChristophe Lombard 70614baf4d9SChristophe Lombard static int guest_afu_check_and_enable(struct cxl_afu *afu) 70714baf4d9SChristophe Lombard { 70814baf4d9SChristophe Lombard return 0; 70914baf4d9SChristophe Lombard } 71014baf4d9SChristophe Lombard 7114752876cSChristophe Lombard static bool guest_support_attributes(const char *attr_name, 7124752876cSChristophe Lombard enum cxl_attrs type) 7134752876cSChristophe Lombard { 7144752876cSChristophe Lombard switch (type) { 7154752876cSChristophe Lombard case CXL_ADAPTER_ATTRS: 7164752876cSChristophe Lombard if ((strcmp(attr_name, "base_image") == 0) || 7174752876cSChristophe Lombard (strcmp(attr_name, "load_image_on_perst") == 0) || 7184752876cSChristophe Lombard (strcmp(attr_name, "perst_reloads_same_image") == 0) || 7194752876cSChristophe Lombard (strcmp(attr_name, "image_loaded") == 0)) 7204752876cSChristophe Lombard return false; 7214752876cSChristophe Lombard break; 7224752876cSChristophe Lombard case CXL_AFU_MASTER_ATTRS: 7234752876cSChristophe Lombard if ((strcmp(attr_name, "pp_mmio_off") == 0)) 7244752876cSChristophe Lombard return false; 7254752876cSChristophe Lombard break; 7264752876cSChristophe Lombard case CXL_AFU_ATTRS: 7274752876cSChristophe Lombard break; 7284752876cSChristophe Lombard default: 7294752876cSChristophe Lombard break; 7304752876cSChristophe Lombard } 7314752876cSChristophe Lombard 7324752876cSChristophe Lombard return true; 7334752876cSChristophe Lombard } 7344752876cSChristophe Lombard 73514baf4d9SChristophe Lombard static int activate_afu_directed(struct cxl_afu *afu) 73614baf4d9SChristophe Lombard { 73714baf4d9SChristophe Lombard int rc; 73814baf4d9SChristophe Lombard 73914baf4d9SChristophe Lombard dev_info(&afu->dev, "Activating AFU(%d) directed mode\n", afu->slice); 74014baf4d9SChristophe Lombard 74114baf4d9SChristophe Lombard afu->current_mode = CXL_MODE_DIRECTED; 74214baf4d9SChristophe Lombard 74314baf4d9SChristophe Lombard afu->num_procs = afu->max_procs_virtualised; 74414baf4d9SChristophe Lombard 74514baf4d9SChristophe Lombard if ((rc = cxl_chardev_m_afu_add(afu))) 74614baf4d9SChristophe Lombard return rc; 74714baf4d9SChristophe Lombard 74814baf4d9SChristophe Lombard if ((rc = cxl_sysfs_afu_m_add(afu))) 74914baf4d9SChristophe Lombard goto err; 75014baf4d9SChristophe Lombard 75114baf4d9SChristophe Lombard if ((rc = cxl_chardev_s_afu_add(afu))) 75214baf4d9SChristophe Lombard goto err1; 75314baf4d9SChristophe Lombard 75414baf4d9SChristophe Lombard return 0; 75514baf4d9SChristophe Lombard err1: 75614baf4d9SChristophe Lombard cxl_sysfs_afu_m_remove(afu); 75714baf4d9SChristophe Lombard err: 75814baf4d9SChristophe Lombard cxl_chardev_afu_remove(afu); 75914baf4d9SChristophe Lombard return rc; 76014baf4d9SChristophe Lombard } 76114baf4d9SChristophe Lombard 76214baf4d9SChristophe Lombard static int guest_afu_activate_mode(struct cxl_afu *afu, int mode) 76314baf4d9SChristophe Lombard { 76414baf4d9SChristophe Lombard if (!mode) 76514baf4d9SChristophe Lombard return 0; 76614baf4d9SChristophe Lombard if (!(mode & afu->modes_supported)) 76714baf4d9SChristophe Lombard return -EINVAL; 76814baf4d9SChristophe Lombard 76914baf4d9SChristophe Lombard if (mode == CXL_MODE_DIRECTED) 77014baf4d9SChristophe Lombard return activate_afu_directed(afu); 77114baf4d9SChristophe Lombard 77214baf4d9SChristophe Lombard if (mode == CXL_MODE_DEDICATED) 77314baf4d9SChristophe Lombard dev_err(&afu->dev, "Dedicated mode not supported\n"); 77414baf4d9SChristophe Lombard 77514baf4d9SChristophe Lombard return -EINVAL; 77614baf4d9SChristophe Lombard } 77714baf4d9SChristophe Lombard 77814baf4d9SChristophe Lombard static int deactivate_afu_directed(struct cxl_afu *afu) 77914baf4d9SChristophe Lombard { 78014baf4d9SChristophe Lombard dev_info(&afu->dev, "Deactivating AFU(%d) directed mode\n", afu->slice); 78114baf4d9SChristophe Lombard 78214baf4d9SChristophe Lombard afu->current_mode = 0; 78314baf4d9SChristophe Lombard afu->num_procs = 0; 78414baf4d9SChristophe Lombard 78514baf4d9SChristophe Lombard cxl_sysfs_afu_m_remove(afu); 78614baf4d9SChristophe Lombard cxl_chardev_afu_remove(afu); 78714baf4d9SChristophe Lombard 78814baf4d9SChristophe Lombard cxl_ops->afu_reset(afu); 78914baf4d9SChristophe Lombard 79014baf4d9SChristophe Lombard return 0; 79114baf4d9SChristophe Lombard } 79214baf4d9SChristophe Lombard 79314baf4d9SChristophe Lombard static int guest_afu_deactivate_mode(struct cxl_afu *afu, int mode) 79414baf4d9SChristophe Lombard { 79514baf4d9SChristophe Lombard if (!mode) 79614baf4d9SChristophe Lombard return 0; 79714baf4d9SChristophe Lombard if (!(mode & afu->modes_supported)) 79814baf4d9SChristophe Lombard return -EINVAL; 79914baf4d9SChristophe Lombard 80014baf4d9SChristophe Lombard if (mode == CXL_MODE_DIRECTED) 80114baf4d9SChristophe Lombard return deactivate_afu_directed(afu); 80214baf4d9SChristophe Lombard return 0; 80314baf4d9SChristophe Lombard } 80414baf4d9SChristophe Lombard 80514baf4d9SChristophe Lombard static int guest_afu_reset(struct cxl_afu *afu) 80614baf4d9SChristophe Lombard { 80714baf4d9SChristophe Lombard pr_devel("AFU(%d) reset request\n", afu->slice); 80814baf4d9SChristophe Lombard return cxl_h_reset_afu(afu->guest->handle); 80914baf4d9SChristophe Lombard } 81014baf4d9SChristophe Lombard 81114baf4d9SChristophe Lombard static int guest_map_slice_regs(struct cxl_afu *afu) 81214baf4d9SChristophe Lombard { 81314baf4d9SChristophe Lombard if (!(afu->p2n_mmio = ioremap(afu->guest->p2n_phys, afu->guest->p2n_size))) { 81414baf4d9SChristophe Lombard dev_err(&afu->dev, "Error mapping AFU(%d) MMIO regions\n", 81514baf4d9SChristophe Lombard afu->slice); 81614baf4d9SChristophe Lombard return -ENOMEM; 81714baf4d9SChristophe Lombard } 81814baf4d9SChristophe Lombard return 0; 81914baf4d9SChristophe Lombard } 82014baf4d9SChristophe Lombard 82114baf4d9SChristophe Lombard static void guest_unmap_slice_regs(struct cxl_afu *afu) 82214baf4d9SChristophe Lombard { 82314baf4d9SChristophe Lombard if (afu->p2n_mmio) 82414baf4d9SChristophe Lombard iounmap(afu->p2n_mmio); 82514baf4d9SChristophe Lombard } 82614baf4d9SChristophe Lombard 8270d400f77SChristophe Lombard static int afu_update_state(struct cxl_afu *afu) 82814baf4d9SChristophe Lombard { 8290d400f77SChristophe Lombard int rc, cur_state; 8300d400f77SChristophe Lombard 8310d400f77SChristophe Lombard rc = afu_read_error_state(afu, &cur_state); 8320d400f77SChristophe Lombard if (rc) 8330d400f77SChristophe Lombard return rc; 8340d400f77SChristophe Lombard 8350d400f77SChristophe Lombard if (afu->guest->previous_state == cur_state) 8360d400f77SChristophe Lombard return 0; 8370d400f77SChristophe Lombard 8380d400f77SChristophe Lombard pr_devel("AFU(%d) update state to %#x\n", afu->slice, cur_state); 8390d400f77SChristophe Lombard 8400d400f77SChristophe Lombard switch (cur_state) { 8410d400f77SChristophe Lombard case H_STATE_NORMAL: 8420d400f77SChristophe Lombard afu->guest->previous_state = cur_state; 8430d400f77SChristophe Lombard break; 8440d400f77SChristophe Lombard 8450d400f77SChristophe Lombard case H_STATE_DISABLE: 8460d400f77SChristophe Lombard pci_error_handlers(afu, CXL_ERROR_DETECTED_EVENT, 8470d400f77SChristophe Lombard pci_channel_io_frozen); 8480d400f77SChristophe Lombard 8490d400f77SChristophe Lombard cxl_context_detach_all(afu); 8500d400f77SChristophe Lombard if ((rc = cxl_ops->afu_reset(afu))) 8510d400f77SChristophe Lombard pr_devel("reset hcall failed %d\n", rc); 8520d400f77SChristophe Lombard 8530d400f77SChristophe Lombard rc = afu_read_error_state(afu, &cur_state); 8540d400f77SChristophe Lombard if (!rc && cur_state == H_STATE_NORMAL) { 8550d400f77SChristophe Lombard pci_error_handlers(afu, CXL_SLOT_RESET_EVENT, 8560d400f77SChristophe Lombard pci_channel_io_normal); 8570d400f77SChristophe Lombard pci_error_handlers(afu, CXL_RESUME_EVENT, 0); 8580d400f77SChristophe Lombard } 8590d400f77SChristophe Lombard afu->guest->previous_state = 0; 8600d400f77SChristophe Lombard break; 8610d400f77SChristophe Lombard 8620d400f77SChristophe Lombard case H_STATE_TEMP_UNAVAILABLE: 8630d400f77SChristophe Lombard afu->guest->previous_state = cur_state; 8640d400f77SChristophe Lombard break; 8650d400f77SChristophe Lombard 8660d400f77SChristophe Lombard case H_STATE_PERM_UNAVAILABLE: 8670d400f77SChristophe Lombard dev_err(&afu->dev, "AFU is in permanent error state\n"); 8680d400f77SChristophe Lombard pci_error_handlers(afu, CXL_ERROR_DETECTED_EVENT, 8690d400f77SChristophe Lombard pci_channel_io_perm_failure); 8700d400f77SChristophe Lombard afu->guest->previous_state = cur_state; 8710d400f77SChristophe Lombard break; 8720d400f77SChristophe Lombard 8730d400f77SChristophe Lombard default: 8740d400f77SChristophe Lombard pr_err("Unexpected AFU(%d) error state: %#x\n", 8750d400f77SChristophe Lombard afu->slice, cur_state); 8760d400f77SChristophe Lombard return -EINVAL; 8770d400f77SChristophe Lombard } 8780d400f77SChristophe Lombard 8790d400f77SChristophe Lombard return rc; 8800d400f77SChristophe Lombard } 8810d400f77SChristophe Lombard 882266eab8fSChristophe Lombard static void afu_handle_errstate(struct work_struct *work) 8830d400f77SChristophe Lombard { 884266eab8fSChristophe Lombard struct cxl_afu_guest *afu_guest = 885266eab8fSChristophe Lombard container_of(to_delayed_work(work), struct cxl_afu_guest, work_err); 8860d400f77SChristophe Lombard 887266eab8fSChristophe Lombard if (!afu_update_state(afu_guest->parent) && 888266eab8fSChristophe Lombard afu_guest->previous_state == H_STATE_PERM_UNAVAILABLE) 889266eab8fSChristophe Lombard return; 890266eab8fSChristophe Lombard 8913382a622SAndrew Donnellan if (afu_guest->handle_err) 892266eab8fSChristophe Lombard schedule_delayed_work(&afu_guest->work_err, 893266eab8fSChristophe Lombard msecs_to_jiffies(3000)); 8940d400f77SChristophe Lombard } 8950d400f77SChristophe Lombard 8960d400f77SChristophe Lombard static bool guest_link_ok(struct cxl *cxl, struct cxl_afu *afu) 8970d400f77SChristophe Lombard { 8980d400f77SChristophe Lombard int state; 8990d400f77SChristophe Lombard 900266eab8fSChristophe Lombard if (afu && (!afu_read_error_state(afu, &state))) { 901266eab8fSChristophe Lombard if (state == H_STATE_NORMAL) 9020d400f77SChristophe Lombard return true; 9030d400f77SChristophe Lombard } 9040d400f77SChristophe Lombard 905266eab8fSChristophe Lombard return false; 90614baf4d9SChristophe Lombard } 90714baf4d9SChristophe Lombard 90814baf4d9SChristophe Lombard static int afu_properties_look_ok(struct cxl_afu *afu) 90914baf4d9SChristophe Lombard { 91014baf4d9SChristophe Lombard if (afu->pp_irqs < 0) { 91114baf4d9SChristophe Lombard dev_err(&afu->dev, "Unexpected per-process minimum interrupt value\n"); 91214baf4d9SChristophe Lombard return -EINVAL; 91314baf4d9SChristophe Lombard } 91414baf4d9SChristophe Lombard 91514baf4d9SChristophe Lombard if (afu->max_procs_virtualised < 1) { 91614baf4d9SChristophe Lombard dev_err(&afu->dev, "Unexpected max number of processes virtualised value\n"); 91714baf4d9SChristophe Lombard return -EINVAL; 91814baf4d9SChristophe Lombard } 91914baf4d9SChristophe Lombard 92014baf4d9SChristophe Lombard return 0; 92114baf4d9SChristophe Lombard } 92214baf4d9SChristophe Lombard 92314baf4d9SChristophe Lombard int cxl_guest_init_afu(struct cxl *adapter, int slice, struct device_node *afu_np) 92414baf4d9SChristophe Lombard { 92514baf4d9SChristophe Lombard struct cxl_afu *afu; 92614baf4d9SChristophe Lombard bool free = true; 92714baf4d9SChristophe Lombard int rc; 92814baf4d9SChristophe Lombard 92914baf4d9SChristophe Lombard pr_devel("in %s - AFU(%d)\n", __func__, slice); 93014baf4d9SChristophe Lombard if (!(afu = cxl_alloc_afu(adapter, slice))) 93114baf4d9SChristophe Lombard return -ENOMEM; 93214baf4d9SChristophe Lombard 93314baf4d9SChristophe Lombard if (!(afu->guest = kzalloc(sizeof(struct cxl_afu_guest), GFP_KERNEL))) { 93414baf4d9SChristophe Lombard kfree(afu); 93514baf4d9SChristophe Lombard return -ENOMEM; 93614baf4d9SChristophe Lombard } 93714baf4d9SChristophe Lombard 93814baf4d9SChristophe Lombard if ((rc = dev_set_name(&afu->dev, "afu%i.%i", 93914baf4d9SChristophe Lombard adapter->adapter_num, 94014baf4d9SChristophe Lombard slice))) 94114baf4d9SChristophe Lombard goto err1; 94214baf4d9SChristophe Lombard 94314baf4d9SChristophe Lombard adapter->slices++; 94414baf4d9SChristophe Lombard 94514baf4d9SChristophe Lombard if ((rc = cxl_of_read_afu_handle(afu, afu_np))) 94614baf4d9SChristophe Lombard goto err1; 94714baf4d9SChristophe Lombard 94814baf4d9SChristophe Lombard if ((rc = cxl_ops->afu_reset(afu))) 94914baf4d9SChristophe Lombard goto err1; 95014baf4d9SChristophe Lombard 95114baf4d9SChristophe Lombard if ((rc = cxl_of_read_afu_properties(afu, afu_np))) 95214baf4d9SChristophe Lombard goto err1; 95314baf4d9SChristophe Lombard 95414baf4d9SChristophe Lombard if ((rc = afu_properties_look_ok(afu))) 95514baf4d9SChristophe Lombard goto err1; 95614baf4d9SChristophe Lombard 95714baf4d9SChristophe Lombard if ((rc = guest_map_slice_regs(afu))) 95814baf4d9SChristophe Lombard goto err1; 95914baf4d9SChristophe Lombard 96014baf4d9SChristophe Lombard if ((rc = guest_register_serr_irq(afu))) 96114baf4d9SChristophe Lombard goto err2; 96214baf4d9SChristophe Lombard 96314baf4d9SChristophe Lombard /* 96414baf4d9SChristophe Lombard * After we call this function we must not free the afu directly, even 96514baf4d9SChristophe Lombard * if it returns an error! 96614baf4d9SChristophe Lombard */ 96714baf4d9SChristophe Lombard if ((rc = cxl_register_afu(afu))) 96814baf4d9SChristophe Lombard goto err_put1; 96914baf4d9SChristophe Lombard 97014baf4d9SChristophe Lombard if ((rc = cxl_sysfs_afu_add(afu))) 97114baf4d9SChristophe Lombard goto err_put1; 97214baf4d9SChristophe Lombard 97314baf4d9SChristophe Lombard /* 97414baf4d9SChristophe Lombard * pHyp doesn't expose the programming models supported by the 97514baf4d9SChristophe Lombard * AFU. pHyp currently only supports directed mode. If it adds 97614baf4d9SChristophe Lombard * dedicated mode later, this version of cxl has no way to 97714baf4d9SChristophe Lombard * detect it. So we'll initialize the driver, but the first 97814baf4d9SChristophe Lombard * attach will fail. 97914baf4d9SChristophe Lombard * Being discussed with pHyp to do better (likely new property) 98014baf4d9SChristophe Lombard */ 98114baf4d9SChristophe Lombard if (afu->max_procs_virtualised == 1) 98214baf4d9SChristophe Lombard afu->modes_supported = CXL_MODE_DEDICATED; 98314baf4d9SChristophe Lombard else 98414baf4d9SChristophe Lombard afu->modes_supported = CXL_MODE_DIRECTED; 98514baf4d9SChristophe Lombard 98614baf4d9SChristophe Lombard if ((rc = cxl_afu_select_best_mode(afu))) 98714baf4d9SChristophe Lombard goto err_put2; 98814baf4d9SChristophe Lombard 98914baf4d9SChristophe Lombard adapter->afu[afu->slice] = afu; 99014baf4d9SChristophe Lombard 99114baf4d9SChristophe Lombard afu->enabled = true; 99214baf4d9SChristophe Lombard 993266eab8fSChristophe Lombard /* 994266eab8fSChristophe Lombard * wake up the cpu periodically to check the state 995266eab8fSChristophe Lombard * of the AFU using "afu" stored in the guest structure. 996266eab8fSChristophe Lombard */ 997266eab8fSChristophe Lombard afu->guest->parent = afu; 998266eab8fSChristophe Lombard afu->guest->handle_err = true; 999266eab8fSChristophe Lombard INIT_DELAYED_WORK(&afu->guest->work_err, afu_handle_errstate); 1000266eab8fSChristophe Lombard schedule_delayed_work(&afu->guest->work_err, msecs_to_jiffies(1000)); 1001266eab8fSChristophe Lombard 1002d601ea91SFrederic Barrat if ((rc = cxl_pci_vphb_add(afu))) 1003d601ea91SFrederic Barrat dev_info(&afu->dev, "Can't register vPHB\n"); 1004d601ea91SFrederic Barrat 100514baf4d9SChristophe Lombard return 0; 100614baf4d9SChristophe Lombard 100714baf4d9SChristophe Lombard err_put2: 100814baf4d9SChristophe Lombard cxl_sysfs_afu_remove(afu); 100914baf4d9SChristophe Lombard err_put1: 101014baf4d9SChristophe Lombard device_unregister(&afu->dev); 101114baf4d9SChristophe Lombard free = false; 101214baf4d9SChristophe Lombard guest_release_serr_irq(afu); 101314baf4d9SChristophe Lombard err2: 101414baf4d9SChristophe Lombard guest_unmap_slice_regs(afu); 101514baf4d9SChristophe Lombard err1: 101614baf4d9SChristophe Lombard if (free) { 101714baf4d9SChristophe Lombard kfree(afu->guest); 101814baf4d9SChristophe Lombard kfree(afu); 101914baf4d9SChristophe Lombard } 102014baf4d9SChristophe Lombard return rc; 102114baf4d9SChristophe Lombard } 102214baf4d9SChristophe Lombard 102314baf4d9SChristophe Lombard void cxl_guest_remove_afu(struct cxl_afu *afu) 102414baf4d9SChristophe Lombard { 102514baf4d9SChristophe Lombard if (!afu) 102614baf4d9SChristophe Lombard return; 102714baf4d9SChristophe Lombard 1028266eab8fSChristophe Lombard /* flush and stop pending job */ 1029266eab8fSChristophe Lombard afu->guest->handle_err = false; 1030266eab8fSChristophe Lombard flush_delayed_work(&afu->guest->work_err); 1031266eab8fSChristophe Lombard 1032d601ea91SFrederic Barrat cxl_pci_vphb_remove(afu); 103314baf4d9SChristophe Lombard cxl_sysfs_afu_remove(afu); 103414baf4d9SChristophe Lombard 103514baf4d9SChristophe Lombard spin_lock(&afu->adapter->afu_list_lock); 103614baf4d9SChristophe Lombard afu->adapter->afu[afu->slice] = NULL; 103714baf4d9SChristophe Lombard spin_unlock(&afu->adapter->afu_list_lock); 103814baf4d9SChristophe Lombard 103914baf4d9SChristophe Lombard cxl_context_detach_all(afu); 104014baf4d9SChristophe Lombard cxl_ops->afu_deactivate_mode(afu, afu->current_mode); 104114baf4d9SChristophe Lombard guest_release_serr_irq(afu); 104214baf4d9SChristophe Lombard guest_unmap_slice_regs(afu); 104314baf4d9SChristophe Lombard 104414baf4d9SChristophe Lombard device_unregister(&afu->dev); 104514baf4d9SChristophe Lombard } 104614baf4d9SChristophe Lombard 104714baf4d9SChristophe Lombard static void free_adapter(struct cxl *adapter) 104814baf4d9SChristophe Lombard { 104914baf4d9SChristophe Lombard struct irq_avail *cur; 105014baf4d9SChristophe Lombard int i; 105114baf4d9SChristophe Lombard 10528fbaa51dSAndrew Donnellan if (adapter->guest) { 105314baf4d9SChristophe Lombard if (adapter->guest->irq_avail) { 105414baf4d9SChristophe Lombard for (i = 0; i < adapter->guest->irq_nranges; i++) { 105514baf4d9SChristophe Lombard cur = &adapter->guest->irq_avail[i]; 1056*4b00b176SChristophe JAILLET bitmap_free(cur->bitmap); 105714baf4d9SChristophe Lombard } 105814baf4d9SChristophe Lombard kfree(adapter->guest->irq_avail); 105914baf4d9SChristophe Lombard } 106014baf4d9SChristophe Lombard kfree(adapter->guest->status); 106114baf4d9SChristophe Lombard kfree(adapter->guest); 10628fbaa51dSAndrew Donnellan } 10638fbaa51dSAndrew Donnellan cxl_remove_adapter_nr(adapter); 106414baf4d9SChristophe Lombard kfree(adapter); 106514baf4d9SChristophe Lombard } 106614baf4d9SChristophe Lombard 106714baf4d9SChristophe Lombard static int properties_look_ok(struct cxl *adapter) 106814baf4d9SChristophe Lombard { 106914baf4d9SChristophe Lombard /* The absence of this property means that the operational 107014baf4d9SChristophe Lombard * status is unknown or okay 107114baf4d9SChristophe Lombard */ 107214baf4d9SChristophe Lombard if (strlen(adapter->guest->status) && 107314baf4d9SChristophe Lombard strcmp(adapter->guest->status, "okay")) { 107414baf4d9SChristophe Lombard pr_err("ABORTING:Bad operational status of the device\n"); 107514baf4d9SChristophe Lombard return -EINVAL; 107614baf4d9SChristophe Lombard } 107714baf4d9SChristophe Lombard 107814baf4d9SChristophe Lombard return 0; 107914baf4d9SChristophe Lombard } 108014baf4d9SChristophe Lombard 108114baf4d9SChristophe Lombard ssize_t cxl_guest_read_adapter_vpd(struct cxl *adapter, void *buf, size_t len) 108214baf4d9SChristophe Lombard { 108314baf4d9SChristophe Lombard return guest_collect_vpd(adapter, NULL, buf, len); 108414baf4d9SChristophe Lombard } 108514baf4d9SChristophe Lombard 108614baf4d9SChristophe Lombard void cxl_guest_remove_adapter(struct cxl *adapter) 108714baf4d9SChristophe Lombard { 108814baf4d9SChristophe Lombard pr_devel("in %s\n", __func__); 108914baf4d9SChristophe Lombard 109014baf4d9SChristophe Lombard cxl_sysfs_adapter_remove(adapter); 109114baf4d9SChristophe Lombard 1092594ff7d0SChristophe Lombard cxl_guest_remove_chardev(adapter); 109314baf4d9SChristophe Lombard device_unregister(&adapter->dev); 109414baf4d9SChristophe Lombard } 109514baf4d9SChristophe Lombard 109614baf4d9SChristophe Lombard static void release_adapter(struct device *dev) 109714baf4d9SChristophe Lombard { 109814baf4d9SChristophe Lombard free_adapter(to_cxl_adapter(dev)); 109914baf4d9SChristophe Lombard } 110014baf4d9SChristophe Lombard 110114baf4d9SChristophe Lombard struct cxl *cxl_guest_init_adapter(struct device_node *np, struct platform_device *pdev) 110214baf4d9SChristophe Lombard { 110314baf4d9SChristophe Lombard struct cxl *adapter; 110414baf4d9SChristophe Lombard bool free = true; 110514baf4d9SChristophe Lombard int rc; 110614baf4d9SChristophe Lombard 110714baf4d9SChristophe Lombard if (!(adapter = cxl_alloc_adapter())) 110814baf4d9SChristophe Lombard return ERR_PTR(-ENOMEM); 110914baf4d9SChristophe Lombard 111014baf4d9SChristophe Lombard if (!(adapter->guest = kzalloc(sizeof(struct cxl_guest), GFP_KERNEL))) { 111114baf4d9SChristophe Lombard free_adapter(adapter); 111214baf4d9SChristophe Lombard return ERR_PTR(-ENOMEM); 111314baf4d9SChristophe Lombard } 111414baf4d9SChristophe Lombard 111514baf4d9SChristophe Lombard adapter->slices = 0; 111614baf4d9SChristophe Lombard adapter->guest->pdev = pdev; 111714baf4d9SChristophe Lombard adapter->dev.parent = &pdev->dev; 111814baf4d9SChristophe Lombard adapter->dev.release = release_adapter; 111914baf4d9SChristophe Lombard dev_set_drvdata(&pdev->dev, adapter); 112014baf4d9SChristophe Lombard 1121e009a7e8SFrederic Barrat /* 1122e009a7e8SFrederic Barrat * Hypervisor controls PSL timebase initialization (p1 register). 1123e009a7e8SFrederic Barrat * On FW840, PSL is initialized. 1124e009a7e8SFrederic Barrat */ 1125e009a7e8SFrederic Barrat adapter->psl_timebase_synced = true; 1126e009a7e8SFrederic Barrat 112714baf4d9SChristophe Lombard if ((rc = cxl_of_read_adapter_handle(adapter, np))) 112814baf4d9SChristophe Lombard goto err1; 112914baf4d9SChristophe Lombard 113014baf4d9SChristophe Lombard if ((rc = cxl_of_read_adapter_properties(adapter, np))) 113114baf4d9SChristophe Lombard goto err1; 113214baf4d9SChristophe Lombard 113314baf4d9SChristophe Lombard if ((rc = properties_look_ok(adapter))) 113414baf4d9SChristophe Lombard goto err1; 113514baf4d9SChristophe Lombard 1136594ff7d0SChristophe Lombard if ((rc = cxl_guest_add_chardev(adapter))) 1137594ff7d0SChristophe Lombard goto err1; 1138594ff7d0SChristophe Lombard 113914baf4d9SChristophe Lombard /* 114014baf4d9SChristophe Lombard * After we call this function we must not free the adapter directly, 114114baf4d9SChristophe Lombard * even if it returns an error! 114214baf4d9SChristophe Lombard */ 114314baf4d9SChristophe Lombard if ((rc = cxl_register_adapter(adapter))) 114414baf4d9SChristophe Lombard goto err_put1; 114514baf4d9SChristophe Lombard 114614baf4d9SChristophe Lombard if ((rc = cxl_sysfs_adapter_add(adapter))) 114714baf4d9SChristophe Lombard goto err_put1; 114814baf4d9SChristophe Lombard 114970b565bbSVaibhav Jain /* release the context lock as the adapter is configured */ 115070b565bbSVaibhav Jain cxl_adapter_context_unlock(adapter); 115170b565bbSVaibhav Jain 115214baf4d9SChristophe Lombard return adapter; 115314baf4d9SChristophe Lombard 115414baf4d9SChristophe Lombard err_put1: 115514baf4d9SChristophe Lombard device_unregister(&adapter->dev); 115614baf4d9SChristophe Lombard free = false; 1157594ff7d0SChristophe Lombard cxl_guest_remove_chardev(adapter); 115814baf4d9SChristophe Lombard err1: 115914baf4d9SChristophe Lombard if (free) 116014baf4d9SChristophe Lombard free_adapter(adapter); 116114baf4d9SChristophe Lombard return ERR_PTR(rc); 116214baf4d9SChristophe Lombard } 116314baf4d9SChristophe Lombard 1164594ff7d0SChristophe Lombard void cxl_guest_reload_module(struct cxl *adapter) 1165594ff7d0SChristophe Lombard { 1166594ff7d0SChristophe Lombard struct platform_device *pdev; 1167594ff7d0SChristophe Lombard 1168594ff7d0SChristophe Lombard pdev = adapter->guest->pdev; 1169594ff7d0SChristophe Lombard cxl_guest_remove_adapter(adapter); 1170594ff7d0SChristophe Lombard 1171594ff7d0SChristophe Lombard cxl_of_probe(pdev); 1172594ff7d0SChristophe Lombard } 1173594ff7d0SChristophe Lombard 117414baf4d9SChristophe Lombard const struct cxl_backend_ops cxl_guest_ops = { 117514baf4d9SChristophe Lombard .module = THIS_MODULE, 117614baf4d9SChristophe Lombard .adapter_reset = guest_reset, 117714baf4d9SChristophe Lombard .alloc_one_irq = guest_alloc_one_irq, 117814baf4d9SChristophe Lombard .release_one_irq = guest_release_one_irq, 117914baf4d9SChristophe Lombard .alloc_irq_ranges = guest_alloc_irq_ranges, 118014baf4d9SChristophe Lombard .release_irq_ranges = guest_release_irq_ranges, 118114baf4d9SChristophe Lombard .setup_irq = NULL, 118214baf4d9SChristophe Lombard .handle_psl_slice_error = guest_handle_psl_slice_error, 118314baf4d9SChristophe Lombard .psl_interrupt = guest_psl_irq, 118414baf4d9SChristophe Lombard .ack_irq = guest_ack_irq, 118514baf4d9SChristophe Lombard .attach_process = guest_attach_process, 118614baf4d9SChristophe Lombard .detach_process = guest_detach_process, 1187292841b0SIan Munsie .update_ivtes = NULL, 11884752876cSChristophe Lombard .support_attributes = guest_support_attributes, 118914baf4d9SChristophe Lombard .link_ok = guest_link_ok, 119014baf4d9SChristophe Lombard .release_afu = guest_release_afu, 119114baf4d9SChristophe Lombard .afu_read_err_buffer = guest_afu_read_err_buffer, 119214baf4d9SChristophe Lombard .afu_check_and_enable = guest_afu_check_and_enable, 119314baf4d9SChristophe Lombard .afu_activate_mode = guest_afu_activate_mode, 119414baf4d9SChristophe Lombard .afu_deactivate_mode = guest_afu_deactivate_mode, 119514baf4d9SChristophe Lombard .afu_reset = guest_afu_reset, 119614baf4d9SChristophe Lombard .afu_cr_read8 = guest_afu_cr_read8, 119714baf4d9SChristophe Lombard .afu_cr_read16 = guest_afu_cr_read16, 119814baf4d9SChristophe Lombard .afu_cr_read32 = guest_afu_cr_read32, 119914baf4d9SChristophe Lombard .afu_cr_read64 = guest_afu_cr_read64, 1200d601ea91SFrederic Barrat .afu_cr_write8 = guest_afu_cr_write8, 1201d601ea91SFrederic Barrat .afu_cr_write16 = guest_afu_cr_write16, 1202d601ea91SFrederic Barrat .afu_cr_write32 = guest_afu_cr_write32, 1203d601ea91SFrederic Barrat .read_adapter_vpd = cxl_guest_read_adapter_vpd, 120414baf4d9SChristophe Lombard }; 1205