114baf4d9SChristophe Lombard /* 214baf4d9SChristophe Lombard * Copyright 2015 IBM Corp. 314baf4d9SChristophe Lombard * 414baf4d9SChristophe Lombard * This program is free software; you can redistribute it and/or 514baf4d9SChristophe Lombard * modify it under the terms of the GNU General Public License 614baf4d9SChristophe Lombard * as published by the Free Software Foundation; either version 714baf4d9SChristophe Lombard * 2 of the License, or (at your option) any later version. 814baf4d9SChristophe Lombard */ 914baf4d9SChristophe Lombard 1014baf4d9SChristophe Lombard #include <linux/spinlock.h> 1114baf4d9SChristophe Lombard #include <linux/uaccess.h> 1214baf4d9SChristophe Lombard #include <linux/delay.h> 1314baf4d9SChristophe Lombard 1414baf4d9SChristophe Lombard #include "cxl.h" 1514baf4d9SChristophe Lombard #include "hcalls.h" 1614baf4d9SChristophe Lombard #include "trace.h" 1714baf4d9SChristophe Lombard 1814baf4d9SChristophe Lombard 1914baf4d9SChristophe Lombard static irqreturn_t guest_handle_psl_slice_error(struct cxl_context *ctx, u64 dsisr, 2014baf4d9SChristophe Lombard u64 errstat) 2114baf4d9SChristophe Lombard { 2214baf4d9SChristophe Lombard pr_devel("in %s\n", __func__); 2314baf4d9SChristophe Lombard dev_crit(&ctx->afu->dev, "PSL ERROR STATUS: 0x%.16llx\n", errstat); 2414baf4d9SChristophe Lombard 2514baf4d9SChristophe Lombard return cxl_ops->ack_irq(ctx, 0, errstat); 2614baf4d9SChristophe Lombard } 2714baf4d9SChristophe Lombard 2814baf4d9SChristophe Lombard static ssize_t guest_collect_vpd(struct cxl *adapter, struct cxl_afu *afu, 2914baf4d9SChristophe Lombard void *buf, size_t len) 3014baf4d9SChristophe Lombard { 3114baf4d9SChristophe Lombard unsigned int entries, mod; 3214baf4d9SChristophe Lombard unsigned long **vpd_buf = NULL; 3314baf4d9SChristophe Lombard struct sg_list *le; 3414baf4d9SChristophe Lombard int rc = 0, i, tocopy; 3514baf4d9SChristophe Lombard u64 out = 0; 3614baf4d9SChristophe Lombard 3714baf4d9SChristophe Lombard if (buf == NULL) 3814baf4d9SChristophe Lombard return -EINVAL; 3914baf4d9SChristophe Lombard 4014baf4d9SChristophe Lombard /* number of entries in the list */ 4114baf4d9SChristophe Lombard entries = len / SG_BUFFER_SIZE; 4214baf4d9SChristophe Lombard mod = len % SG_BUFFER_SIZE; 4314baf4d9SChristophe Lombard if (mod) 4414baf4d9SChristophe Lombard entries++; 4514baf4d9SChristophe Lombard 4614baf4d9SChristophe Lombard if (entries > SG_MAX_ENTRIES) { 4714baf4d9SChristophe Lombard entries = SG_MAX_ENTRIES; 4814baf4d9SChristophe Lombard len = SG_MAX_ENTRIES * SG_BUFFER_SIZE; 4914baf4d9SChristophe Lombard mod = 0; 5014baf4d9SChristophe Lombard } 5114baf4d9SChristophe Lombard 5214baf4d9SChristophe Lombard vpd_buf = kzalloc(entries * sizeof(unsigned long *), GFP_KERNEL); 5314baf4d9SChristophe Lombard if (!vpd_buf) 5414baf4d9SChristophe Lombard return -ENOMEM; 5514baf4d9SChristophe Lombard 5614baf4d9SChristophe Lombard le = (struct sg_list *)get_zeroed_page(GFP_KERNEL); 5714baf4d9SChristophe Lombard if (!le) { 5814baf4d9SChristophe Lombard rc = -ENOMEM; 5914baf4d9SChristophe Lombard goto err1; 6014baf4d9SChristophe Lombard } 6114baf4d9SChristophe Lombard 6214baf4d9SChristophe Lombard for (i = 0; i < entries; i++) { 6314baf4d9SChristophe Lombard vpd_buf[i] = (unsigned long *)get_zeroed_page(GFP_KERNEL); 6414baf4d9SChristophe Lombard if (!vpd_buf[i]) { 6514baf4d9SChristophe Lombard rc = -ENOMEM; 6614baf4d9SChristophe Lombard goto err2; 6714baf4d9SChristophe Lombard } 6814baf4d9SChristophe Lombard le[i].phys_addr = cpu_to_be64(virt_to_phys(vpd_buf[i])); 6914baf4d9SChristophe Lombard le[i].len = cpu_to_be64(SG_BUFFER_SIZE); 7014baf4d9SChristophe Lombard if ((i == (entries - 1)) && mod) 7114baf4d9SChristophe Lombard le[i].len = cpu_to_be64(mod); 7214baf4d9SChristophe Lombard } 7314baf4d9SChristophe Lombard 7414baf4d9SChristophe Lombard if (adapter) 7514baf4d9SChristophe Lombard rc = cxl_h_collect_vpd_adapter(adapter->guest->handle, 7614baf4d9SChristophe Lombard virt_to_phys(le), entries, &out); 7714baf4d9SChristophe Lombard else 7814baf4d9SChristophe Lombard rc = cxl_h_collect_vpd(afu->guest->handle, 0, 7914baf4d9SChristophe Lombard virt_to_phys(le), entries, &out); 8014baf4d9SChristophe Lombard pr_devel("length of available (entries: %i), vpd: %#llx\n", 8114baf4d9SChristophe Lombard entries, out); 8214baf4d9SChristophe Lombard 8314baf4d9SChristophe Lombard if (!rc) { 8414baf4d9SChristophe Lombard /* 8514baf4d9SChristophe Lombard * hcall returns in 'out' the size of available VPDs. 8614baf4d9SChristophe Lombard * It fills the buffer with as much data as possible. 8714baf4d9SChristophe Lombard */ 8814baf4d9SChristophe Lombard if (out < len) 8914baf4d9SChristophe Lombard len = out; 9014baf4d9SChristophe Lombard rc = len; 9114baf4d9SChristophe Lombard if (out) { 9214baf4d9SChristophe Lombard for (i = 0; i < entries; i++) { 9314baf4d9SChristophe Lombard if (len < SG_BUFFER_SIZE) 9414baf4d9SChristophe Lombard tocopy = len; 9514baf4d9SChristophe Lombard else 9614baf4d9SChristophe Lombard tocopy = SG_BUFFER_SIZE; 9714baf4d9SChristophe Lombard memcpy(buf, vpd_buf[i], tocopy); 9814baf4d9SChristophe Lombard buf += tocopy; 9914baf4d9SChristophe Lombard len -= tocopy; 10014baf4d9SChristophe Lombard } 10114baf4d9SChristophe Lombard } 10214baf4d9SChristophe Lombard } 10314baf4d9SChristophe Lombard err2: 10414baf4d9SChristophe Lombard for (i = 0; i < entries; i++) { 10514baf4d9SChristophe Lombard if (vpd_buf[i]) 10614baf4d9SChristophe Lombard free_page((unsigned long) vpd_buf[i]); 10714baf4d9SChristophe Lombard } 10814baf4d9SChristophe Lombard free_page((unsigned long) le); 10914baf4d9SChristophe Lombard err1: 11014baf4d9SChristophe Lombard kfree(vpd_buf); 11114baf4d9SChristophe Lombard return rc; 11214baf4d9SChristophe Lombard } 11314baf4d9SChristophe Lombard 11414baf4d9SChristophe Lombard static int guest_get_irq_info(struct cxl_context *ctx, struct cxl_irq_info *info) 11514baf4d9SChristophe Lombard { 11614baf4d9SChristophe Lombard return cxl_h_collect_int_info(ctx->afu->guest->handle, ctx->process_token, info); 11714baf4d9SChristophe Lombard } 11814baf4d9SChristophe Lombard 11914baf4d9SChristophe Lombard static irqreturn_t guest_psl_irq(int irq, void *data) 12014baf4d9SChristophe Lombard { 12114baf4d9SChristophe Lombard struct cxl_context *ctx = data; 12214baf4d9SChristophe Lombard struct cxl_irq_info irq_info; 12314baf4d9SChristophe Lombard int rc; 12414baf4d9SChristophe Lombard 12514baf4d9SChristophe Lombard pr_devel("%d: received PSL interrupt %i\n", ctx->pe, irq); 12614baf4d9SChristophe Lombard rc = guest_get_irq_info(ctx, &irq_info); 12714baf4d9SChristophe Lombard if (rc) { 12814baf4d9SChristophe Lombard WARN(1, "Unable to get IRQ info: %i\n", rc); 12914baf4d9SChristophe Lombard return IRQ_HANDLED; 13014baf4d9SChristophe Lombard } 13114baf4d9SChristophe Lombard 13214baf4d9SChristophe Lombard rc = cxl_irq(irq, ctx, &irq_info); 13314baf4d9SChristophe Lombard return rc; 13414baf4d9SChristophe Lombard } 13514baf4d9SChristophe Lombard 13614baf4d9SChristophe Lombard static irqreturn_t guest_slice_irq_err(int irq, void *data) 13714baf4d9SChristophe Lombard { 13814baf4d9SChristophe Lombard struct cxl_afu *afu = data; 13914baf4d9SChristophe Lombard int rc; 14014baf4d9SChristophe Lombard u64 serr; 14114baf4d9SChristophe Lombard 14214baf4d9SChristophe Lombard WARN(irq, "CXL SLICE ERROR interrupt %i\n", irq); 14314baf4d9SChristophe Lombard rc = cxl_h_get_fn_error_interrupt(afu->guest->handle, &serr); 14414baf4d9SChristophe Lombard if (rc) { 14514baf4d9SChristophe Lombard dev_crit(&afu->dev, "Couldn't read PSL_SERR_An: %d\n", rc); 14614baf4d9SChristophe Lombard return IRQ_HANDLED; 14714baf4d9SChristophe Lombard } 14814baf4d9SChristophe Lombard dev_crit(&afu->dev, "PSL_SERR_An: 0x%.16llx\n", serr); 14914baf4d9SChristophe Lombard 15014baf4d9SChristophe Lombard rc = cxl_h_ack_fn_error_interrupt(afu->guest->handle, serr); 15114baf4d9SChristophe Lombard if (rc) 15214baf4d9SChristophe Lombard dev_crit(&afu->dev, "Couldn't ack slice error interrupt: %d\n", 15314baf4d9SChristophe Lombard rc); 15414baf4d9SChristophe Lombard 15514baf4d9SChristophe Lombard return IRQ_HANDLED; 15614baf4d9SChristophe Lombard } 15714baf4d9SChristophe Lombard 15814baf4d9SChristophe Lombard 15914baf4d9SChristophe Lombard static int irq_alloc_range(struct cxl *adapter, int len, int *irq) 16014baf4d9SChristophe Lombard { 16114baf4d9SChristophe Lombard int i, n; 16214baf4d9SChristophe Lombard struct irq_avail *cur; 16314baf4d9SChristophe Lombard 16414baf4d9SChristophe Lombard for (i = 0; i < adapter->guest->irq_nranges; i++) { 16514baf4d9SChristophe Lombard cur = &adapter->guest->irq_avail[i]; 16614baf4d9SChristophe Lombard n = bitmap_find_next_zero_area(cur->bitmap, cur->range, 16714baf4d9SChristophe Lombard 0, len, 0); 16814baf4d9SChristophe Lombard if (n < cur->range) { 16914baf4d9SChristophe Lombard bitmap_set(cur->bitmap, n, len); 17014baf4d9SChristophe Lombard *irq = cur->offset + n; 17114baf4d9SChristophe Lombard pr_devel("guest: allocate IRQs %#x->%#x\n", 17214baf4d9SChristophe Lombard *irq, *irq + len - 1); 17314baf4d9SChristophe Lombard 17414baf4d9SChristophe Lombard return 0; 17514baf4d9SChristophe Lombard } 17614baf4d9SChristophe Lombard } 17714baf4d9SChristophe Lombard return -ENOSPC; 17814baf4d9SChristophe Lombard } 17914baf4d9SChristophe Lombard 18014baf4d9SChristophe Lombard static int irq_free_range(struct cxl *adapter, int irq, int len) 18114baf4d9SChristophe Lombard { 18214baf4d9SChristophe Lombard int i, n; 18314baf4d9SChristophe Lombard struct irq_avail *cur; 18414baf4d9SChristophe Lombard 18514baf4d9SChristophe Lombard if (len == 0) 18614baf4d9SChristophe Lombard return -ENOENT; 18714baf4d9SChristophe Lombard 18814baf4d9SChristophe Lombard for (i = 0; i < adapter->guest->irq_nranges; i++) { 18914baf4d9SChristophe Lombard cur = &adapter->guest->irq_avail[i]; 19014baf4d9SChristophe Lombard if (irq >= cur->offset && 19114baf4d9SChristophe Lombard (irq + len) <= (cur->offset + cur->range)) { 19214baf4d9SChristophe Lombard n = irq - cur->offset; 19314baf4d9SChristophe Lombard bitmap_clear(cur->bitmap, n, len); 19414baf4d9SChristophe Lombard pr_devel("guest: release IRQs %#x->%#x\n", 19514baf4d9SChristophe Lombard irq, irq + len - 1); 19614baf4d9SChristophe Lombard return 0; 19714baf4d9SChristophe Lombard } 19814baf4d9SChristophe Lombard } 19914baf4d9SChristophe Lombard return -ENOENT; 20014baf4d9SChristophe Lombard } 20114baf4d9SChristophe Lombard 20214baf4d9SChristophe Lombard static int guest_reset(struct cxl *adapter) 20314baf4d9SChristophe Lombard { 20414baf4d9SChristophe Lombard int rc; 20514baf4d9SChristophe Lombard 20614baf4d9SChristophe Lombard pr_devel("Adapter reset request\n"); 20714baf4d9SChristophe Lombard rc = cxl_h_reset_adapter(adapter->guest->handle); 20814baf4d9SChristophe Lombard return rc; 20914baf4d9SChristophe Lombard } 21014baf4d9SChristophe Lombard 21114baf4d9SChristophe Lombard static int guest_alloc_one_irq(struct cxl *adapter) 21214baf4d9SChristophe Lombard { 21314baf4d9SChristophe Lombard int irq; 21414baf4d9SChristophe Lombard 21514baf4d9SChristophe Lombard spin_lock(&adapter->guest->irq_alloc_lock); 21614baf4d9SChristophe Lombard if (irq_alloc_range(adapter, 1, &irq)) 21714baf4d9SChristophe Lombard irq = -ENOSPC; 21814baf4d9SChristophe Lombard spin_unlock(&adapter->guest->irq_alloc_lock); 21914baf4d9SChristophe Lombard return irq; 22014baf4d9SChristophe Lombard } 22114baf4d9SChristophe Lombard 22214baf4d9SChristophe Lombard static void guest_release_one_irq(struct cxl *adapter, int irq) 22314baf4d9SChristophe Lombard { 22414baf4d9SChristophe Lombard spin_lock(&adapter->guest->irq_alloc_lock); 22514baf4d9SChristophe Lombard irq_free_range(adapter, irq, 1); 22614baf4d9SChristophe Lombard spin_unlock(&adapter->guest->irq_alloc_lock); 22714baf4d9SChristophe Lombard } 22814baf4d9SChristophe Lombard 22914baf4d9SChristophe Lombard static int guest_alloc_irq_ranges(struct cxl_irq_ranges *irqs, 23014baf4d9SChristophe Lombard struct cxl *adapter, unsigned int num) 23114baf4d9SChristophe Lombard { 23214baf4d9SChristophe Lombard int i, try, irq; 23314baf4d9SChristophe Lombard 23414baf4d9SChristophe Lombard memset(irqs, 0, sizeof(struct cxl_irq_ranges)); 23514baf4d9SChristophe Lombard 23614baf4d9SChristophe Lombard spin_lock(&adapter->guest->irq_alloc_lock); 23714baf4d9SChristophe Lombard for (i = 0; i < CXL_IRQ_RANGES && num; i++) { 23814baf4d9SChristophe Lombard try = num; 23914baf4d9SChristophe Lombard while (try) { 24014baf4d9SChristophe Lombard if (irq_alloc_range(adapter, try, &irq) == 0) 24114baf4d9SChristophe Lombard break; 24214baf4d9SChristophe Lombard try /= 2; 24314baf4d9SChristophe Lombard } 24414baf4d9SChristophe Lombard if (!try) 24514baf4d9SChristophe Lombard goto error; 24614baf4d9SChristophe Lombard irqs->offset[i] = irq; 24714baf4d9SChristophe Lombard irqs->range[i] = try; 24814baf4d9SChristophe Lombard num -= try; 24914baf4d9SChristophe Lombard } 25014baf4d9SChristophe Lombard if (num) 25114baf4d9SChristophe Lombard goto error; 25214baf4d9SChristophe Lombard spin_unlock(&adapter->guest->irq_alloc_lock); 25314baf4d9SChristophe Lombard return 0; 25414baf4d9SChristophe Lombard 25514baf4d9SChristophe Lombard error: 25614baf4d9SChristophe Lombard for (i = 0; i < CXL_IRQ_RANGES; i++) 25714baf4d9SChristophe Lombard irq_free_range(adapter, irqs->offset[i], irqs->range[i]); 25814baf4d9SChristophe Lombard spin_unlock(&adapter->guest->irq_alloc_lock); 25914baf4d9SChristophe Lombard return -ENOSPC; 26014baf4d9SChristophe Lombard } 26114baf4d9SChristophe Lombard 26214baf4d9SChristophe Lombard static void guest_release_irq_ranges(struct cxl_irq_ranges *irqs, 26314baf4d9SChristophe Lombard struct cxl *adapter) 26414baf4d9SChristophe Lombard { 26514baf4d9SChristophe Lombard int i; 26614baf4d9SChristophe Lombard 26714baf4d9SChristophe Lombard spin_lock(&adapter->guest->irq_alloc_lock); 26814baf4d9SChristophe Lombard for (i = 0; i < CXL_IRQ_RANGES; i++) 26914baf4d9SChristophe Lombard irq_free_range(adapter, irqs->offset[i], irqs->range[i]); 27014baf4d9SChristophe Lombard spin_unlock(&adapter->guest->irq_alloc_lock); 27114baf4d9SChristophe Lombard } 27214baf4d9SChristophe Lombard 27314baf4d9SChristophe Lombard static int guest_register_serr_irq(struct cxl_afu *afu) 27414baf4d9SChristophe Lombard { 27514baf4d9SChristophe Lombard afu->err_irq_name = kasprintf(GFP_KERNEL, "cxl-%s-err", 27614baf4d9SChristophe Lombard dev_name(&afu->dev)); 27714baf4d9SChristophe Lombard if (!afu->err_irq_name) 27814baf4d9SChristophe Lombard return -ENOMEM; 27914baf4d9SChristophe Lombard 28014baf4d9SChristophe Lombard if (!(afu->serr_virq = cxl_map_irq(afu->adapter, afu->serr_hwirq, 28114baf4d9SChristophe Lombard guest_slice_irq_err, afu, afu->err_irq_name))) { 28214baf4d9SChristophe Lombard kfree(afu->err_irq_name); 28314baf4d9SChristophe Lombard afu->err_irq_name = NULL; 28414baf4d9SChristophe Lombard return -ENOMEM; 28514baf4d9SChristophe Lombard } 28614baf4d9SChristophe Lombard 28714baf4d9SChristophe Lombard return 0; 28814baf4d9SChristophe Lombard } 28914baf4d9SChristophe Lombard 29014baf4d9SChristophe Lombard static void guest_release_serr_irq(struct cxl_afu *afu) 29114baf4d9SChristophe Lombard { 29214baf4d9SChristophe Lombard cxl_unmap_irq(afu->serr_virq, afu); 29314baf4d9SChristophe Lombard cxl_ops->release_one_irq(afu->adapter, afu->serr_hwirq); 29414baf4d9SChristophe Lombard kfree(afu->err_irq_name); 29514baf4d9SChristophe Lombard } 29614baf4d9SChristophe Lombard 29714baf4d9SChristophe Lombard static int guest_ack_irq(struct cxl_context *ctx, u64 tfc, u64 psl_reset_mask) 29814baf4d9SChristophe Lombard { 29914baf4d9SChristophe Lombard return cxl_h_control_faults(ctx->afu->guest->handle, ctx->process_token, 30014baf4d9SChristophe Lombard tfc >> 32, (psl_reset_mask != 0)); 30114baf4d9SChristophe Lombard } 30214baf4d9SChristophe Lombard 30314baf4d9SChristophe Lombard static void disable_afu_irqs(struct cxl_context *ctx) 30414baf4d9SChristophe Lombard { 30514baf4d9SChristophe Lombard irq_hw_number_t hwirq; 30614baf4d9SChristophe Lombard unsigned int virq; 30714baf4d9SChristophe Lombard int r, i; 30814baf4d9SChristophe Lombard 30914baf4d9SChristophe Lombard pr_devel("Disabling AFU(%d) interrupts\n", ctx->afu->slice); 31014baf4d9SChristophe Lombard for (r = 0; r < CXL_IRQ_RANGES; r++) { 31114baf4d9SChristophe Lombard hwirq = ctx->irqs.offset[r]; 31214baf4d9SChristophe Lombard for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) { 31314baf4d9SChristophe Lombard virq = irq_find_mapping(NULL, hwirq); 31414baf4d9SChristophe Lombard disable_irq(virq); 31514baf4d9SChristophe Lombard } 31614baf4d9SChristophe Lombard } 31714baf4d9SChristophe Lombard } 31814baf4d9SChristophe Lombard 31914baf4d9SChristophe Lombard static void enable_afu_irqs(struct cxl_context *ctx) 32014baf4d9SChristophe Lombard { 32114baf4d9SChristophe Lombard irq_hw_number_t hwirq; 32214baf4d9SChristophe Lombard unsigned int virq; 32314baf4d9SChristophe Lombard int r, i; 32414baf4d9SChristophe Lombard 32514baf4d9SChristophe Lombard pr_devel("Enabling AFU(%d) interrupts\n", ctx->afu->slice); 32614baf4d9SChristophe Lombard for (r = 0; r < CXL_IRQ_RANGES; r++) { 32714baf4d9SChristophe Lombard hwirq = ctx->irqs.offset[r]; 32814baf4d9SChristophe Lombard for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) { 32914baf4d9SChristophe Lombard virq = irq_find_mapping(NULL, hwirq); 33014baf4d9SChristophe Lombard enable_irq(virq); 33114baf4d9SChristophe Lombard } 33214baf4d9SChristophe Lombard } 33314baf4d9SChristophe Lombard } 33414baf4d9SChristophe Lombard 33514baf4d9SChristophe Lombard static int _guest_afu_cr_readXX(int sz, struct cxl_afu *afu, int cr_idx, 33614baf4d9SChristophe Lombard u64 offset, u64 *val) 33714baf4d9SChristophe Lombard { 33814baf4d9SChristophe Lombard unsigned long cr; 33914baf4d9SChristophe Lombard char c; 34014baf4d9SChristophe Lombard int rc = 0; 34114baf4d9SChristophe Lombard 34214baf4d9SChristophe Lombard if (afu->crs_len < sz) 34314baf4d9SChristophe Lombard return -ENOENT; 34414baf4d9SChristophe Lombard 34514baf4d9SChristophe Lombard if (unlikely(offset >= afu->crs_len)) 34614baf4d9SChristophe Lombard return -ERANGE; 34714baf4d9SChristophe Lombard 34814baf4d9SChristophe Lombard cr = get_zeroed_page(GFP_KERNEL); 34914baf4d9SChristophe Lombard if (!cr) 35014baf4d9SChristophe Lombard return -ENOMEM; 35114baf4d9SChristophe Lombard 35214baf4d9SChristophe Lombard rc = cxl_h_get_config(afu->guest->handle, cr_idx, offset, 35314baf4d9SChristophe Lombard virt_to_phys((void *)cr), sz); 35414baf4d9SChristophe Lombard if (rc) 35514baf4d9SChristophe Lombard goto err; 35614baf4d9SChristophe Lombard 35714baf4d9SChristophe Lombard switch (sz) { 35814baf4d9SChristophe Lombard case 1: 35914baf4d9SChristophe Lombard c = *((char *) cr); 36014baf4d9SChristophe Lombard *val = c; 36114baf4d9SChristophe Lombard break; 36214baf4d9SChristophe Lombard case 2: 36314baf4d9SChristophe Lombard *val = in_le16((u16 *)cr); 36414baf4d9SChristophe Lombard break; 36514baf4d9SChristophe Lombard case 4: 36614baf4d9SChristophe Lombard *val = in_le32((unsigned *)cr); 36714baf4d9SChristophe Lombard break; 36814baf4d9SChristophe Lombard case 8: 36914baf4d9SChristophe Lombard *val = in_le64((u64 *)cr); 37014baf4d9SChristophe Lombard break; 37114baf4d9SChristophe Lombard default: 37214baf4d9SChristophe Lombard WARN_ON(1); 37314baf4d9SChristophe Lombard } 37414baf4d9SChristophe Lombard err: 37514baf4d9SChristophe Lombard free_page(cr); 37614baf4d9SChristophe Lombard return rc; 37714baf4d9SChristophe Lombard } 37814baf4d9SChristophe Lombard 37914baf4d9SChristophe Lombard static int guest_afu_cr_read32(struct cxl_afu *afu, int cr_idx, u64 offset, 38014baf4d9SChristophe Lombard u32 *out) 38114baf4d9SChristophe Lombard { 38214baf4d9SChristophe Lombard int rc; 38314baf4d9SChristophe Lombard u64 val; 38414baf4d9SChristophe Lombard 38514baf4d9SChristophe Lombard rc = _guest_afu_cr_readXX(4, afu, cr_idx, offset, &val); 38614baf4d9SChristophe Lombard if (!rc) 38714baf4d9SChristophe Lombard *out = (u32) val; 38814baf4d9SChristophe Lombard return rc; 38914baf4d9SChristophe Lombard } 39014baf4d9SChristophe Lombard 39114baf4d9SChristophe Lombard static int guest_afu_cr_read16(struct cxl_afu *afu, int cr_idx, u64 offset, 39214baf4d9SChristophe Lombard u16 *out) 39314baf4d9SChristophe Lombard { 39414baf4d9SChristophe Lombard int rc; 39514baf4d9SChristophe Lombard u64 val; 39614baf4d9SChristophe Lombard 39714baf4d9SChristophe Lombard rc = _guest_afu_cr_readXX(2, afu, cr_idx, offset, &val); 39814baf4d9SChristophe Lombard if (!rc) 39914baf4d9SChristophe Lombard *out = (u16) val; 40014baf4d9SChristophe Lombard return rc; 40114baf4d9SChristophe Lombard } 40214baf4d9SChristophe Lombard 40314baf4d9SChristophe Lombard static int guest_afu_cr_read8(struct cxl_afu *afu, int cr_idx, u64 offset, 40414baf4d9SChristophe Lombard u8 *out) 40514baf4d9SChristophe Lombard { 40614baf4d9SChristophe Lombard int rc; 40714baf4d9SChristophe Lombard u64 val; 40814baf4d9SChristophe Lombard 40914baf4d9SChristophe Lombard rc = _guest_afu_cr_readXX(1, afu, cr_idx, offset, &val); 41014baf4d9SChristophe Lombard if (!rc) 41114baf4d9SChristophe Lombard *out = (u8) val; 41214baf4d9SChristophe Lombard return rc; 41314baf4d9SChristophe Lombard } 41414baf4d9SChristophe Lombard 41514baf4d9SChristophe Lombard static int guest_afu_cr_read64(struct cxl_afu *afu, int cr_idx, u64 offset, 41614baf4d9SChristophe Lombard u64 *out) 41714baf4d9SChristophe Lombard { 41814baf4d9SChristophe Lombard return _guest_afu_cr_readXX(8, afu, cr_idx, offset, out); 41914baf4d9SChristophe Lombard } 42014baf4d9SChristophe Lombard 42114baf4d9SChristophe Lombard static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr) 42214baf4d9SChristophe Lombard { 42314baf4d9SChristophe Lombard struct cxl_process_element_hcall *elem; 42414baf4d9SChristophe Lombard struct cxl *adapter = ctx->afu->adapter; 42514baf4d9SChristophe Lombard const struct cred *cred; 42614baf4d9SChristophe Lombard u32 pid, idx; 42714baf4d9SChristophe Lombard int rc, r, i; 42814baf4d9SChristophe Lombard u64 mmio_addr, mmio_size; 42914baf4d9SChristophe Lombard __be64 flags = 0; 43014baf4d9SChristophe Lombard 43114baf4d9SChristophe Lombard /* Must be 8 byte aligned and cannot cross a 4096 byte boundary */ 43214baf4d9SChristophe Lombard if (!(elem = (struct cxl_process_element_hcall *) 43314baf4d9SChristophe Lombard get_zeroed_page(GFP_KERNEL))) 43414baf4d9SChristophe Lombard return -ENOMEM; 43514baf4d9SChristophe Lombard 43614baf4d9SChristophe Lombard elem->version = cpu_to_be64(CXL_PROCESS_ELEMENT_VERSION); 43714baf4d9SChristophe Lombard if (ctx->kernel) { 43814baf4d9SChristophe Lombard pid = 0; 43914baf4d9SChristophe Lombard flags |= CXL_PE_TRANSLATION_ENABLED; 44014baf4d9SChristophe Lombard flags |= CXL_PE_PRIVILEGED_PROCESS; 44114baf4d9SChristophe Lombard if (mfmsr() & MSR_SF) 44214baf4d9SChristophe Lombard flags |= CXL_PE_64_BIT; 44314baf4d9SChristophe Lombard } else { 44414baf4d9SChristophe Lombard pid = current->pid; 44514baf4d9SChristophe Lombard flags |= CXL_PE_PROBLEM_STATE; 44614baf4d9SChristophe Lombard flags |= CXL_PE_TRANSLATION_ENABLED; 44714baf4d9SChristophe Lombard if (!test_tsk_thread_flag(current, TIF_32BIT)) 44814baf4d9SChristophe Lombard flags |= CXL_PE_64_BIT; 44914baf4d9SChristophe Lombard cred = get_current_cred(); 45014baf4d9SChristophe Lombard if (uid_eq(cred->euid, GLOBAL_ROOT_UID)) 45114baf4d9SChristophe Lombard flags |= CXL_PE_PRIVILEGED_PROCESS; 45214baf4d9SChristophe Lombard put_cred(cred); 45314baf4d9SChristophe Lombard } 45414baf4d9SChristophe Lombard elem->flags = cpu_to_be64(flags); 45514baf4d9SChristophe Lombard elem->common.tid = cpu_to_be32(0); /* Unused */ 45614baf4d9SChristophe Lombard elem->common.pid = cpu_to_be32(pid); 45714baf4d9SChristophe Lombard elem->common.csrp = cpu_to_be64(0); /* disable */ 45814baf4d9SChristophe Lombard elem->common.aurp0 = cpu_to_be64(0); /* disable */ 45914baf4d9SChristophe Lombard elem->common.aurp1 = cpu_to_be64(0); /* disable */ 46014baf4d9SChristophe Lombard 46114baf4d9SChristophe Lombard cxl_prefault(ctx, wed); 46214baf4d9SChristophe Lombard 46314baf4d9SChristophe Lombard elem->common.sstp0 = cpu_to_be64(ctx->sstp0); 46414baf4d9SChristophe Lombard elem->common.sstp1 = cpu_to_be64(ctx->sstp1); 46514baf4d9SChristophe Lombard for (r = 0; r < CXL_IRQ_RANGES; r++) { 46614baf4d9SChristophe Lombard for (i = 0; i < ctx->irqs.range[r]; i++) { 46714baf4d9SChristophe Lombard if (r == 0 && i == 0) { 46814baf4d9SChristophe Lombard elem->pslVirtualIsn = cpu_to_be32(ctx->irqs.offset[0]); 46914baf4d9SChristophe Lombard } else { 47014baf4d9SChristophe Lombard idx = ctx->irqs.offset[r] + i - adapter->guest->irq_base_offset; 47114baf4d9SChristophe Lombard elem->applicationVirtualIsnBitmap[idx / 8] |= 0x80 >> (idx % 8); 47214baf4d9SChristophe Lombard } 47314baf4d9SChristophe Lombard } 47414baf4d9SChristophe Lombard } 47514baf4d9SChristophe Lombard elem->common.amr = cpu_to_be64(amr); 47614baf4d9SChristophe Lombard elem->common.wed = cpu_to_be64(wed); 47714baf4d9SChristophe Lombard 47814baf4d9SChristophe Lombard disable_afu_irqs(ctx); 47914baf4d9SChristophe Lombard 48014baf4d9SChristophe Lombard rc = cxl_h_attach_process(ctx->afu->guest->handle, elem, 48114baf4d9SChristophe Lombard &ctx->process_token, &mmio_addr, &mmio_size); 48214baf4d9SChristophe Lombard if (rc == H_SUCCESS) { 48314baf4d9SChristophe Lombard if (ctx->master || !ctx->afu->pp_psa) { 48414baf4d9SChristophe Lombard ctx->psn_phys = ctx->afu->psn_phys; 48514baf4d9SChristophe Lombard ctx->psn_size = ctx->afu->adapter->ps_size; 48614baf4d9SChristophe Lombard } else { 48714baf4d9SChristophe Lombard ctx->psn_phys = mmio_addr; 48814baf4d9SChristophe Lombard ctx->psn_size = mmio_size; 48914baf4d9SChristophe Lombard } 49014baf4d9SChristophe Lombard if (ctx->afu->pp_psa && mmio_size && 49114baf4d9SChristophe Lombard ctx->afu->pp_size == 0) { 49214baf4d9SChristophe Lombard /* 49314baf4d9SChristophe Lombard * There's no property in the device tree to read the 49414baf4d9SChristophe Lombard * pp_size. We only find out at the 1st attach. 49514baf4d9SChristophe Lombard * Compared to bare-metal, it is too late and we 49614baf4d9SChristophe Lombard * should really lock here. However, on powerVM, 49714baf4d9SChristophe Lombard * pp_size is really only used to display in /sys. 49814baf4d9SChristophe Lombard * Being discussed with pHyp for their next release. 49914baf4d9SChristophe Lombard */ 50014baf4d9SChristophe Lombard ctx->afu->pp_size = mmio_size; 50114baf4d9SChristophe Lombard } 50214baf4d9SChristophe Lombard /* from PAPR: process element is bytes 4-7 of process token */ 50314baf4d9SChristophe Lombard ctx->external_pe = ctx->process_token & 0xFFFFFFFF; 50414baf4d9SChristophe Lombard pr_devel("CXL pe=%i is known as %i for pHyp, mmio_size=%#llx", 50514baf4d9SChristophe Lombard ctx->pe, ctx->external_pe, ctx->psn_size); 50614baf4d9SChristophe Lombard ctx->pe_inserted = true; 50714baf4d9SChristophe Lombard enable_afu_irqs(ctx); 50814baf4d9SChristophe Lombard } 50914baf4d9SChristophe Lombard 51014baf4d9SChristophe Lombard free_page((u64)elem); 51114baf4d9SChristophe Lombard return rc; 51214baf4d9SChristophe Lombard } 51314baf4d9SChristophe Lombard 51414baf4d9SChristophe Lombard static int guest_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr) 51514baf4d9SChristophe Lombard { 51614baf4d9SChristophe Lombard pr_devel("in %s\n", __func__); 51714baf4d9SChristophe Lombard 51814baf4d9SChristophe Lombard ctx->kernel = kernel; 51914baf4d9SChristophe Lombard if (ctx->afu->current_mode == CXL_MODE_DIRECTED) 52014baf4d9SChristophe Lombard return attach_afu_directed(ctx, wed, amr); 52114baf4d9SChristophe Lombard 52214baf4d9SChristophe Lombard /* dedicated mode not supported on FW840 */ 52314baf4d9SChristophe Lombard 52414baf4d9SChristophe Lombard return -EINVAL; 52514baf4d9SChristophe Lombard } 52614baf4d9SChristophe Lombard 52714baf4d9SChristophe Lombard static int detach_afu_directed(struct cxl_context *ctx) 52814baf4d9SChristophe Lombard { 52914baf4d9SChristophe Lombard if (!ctx->pe_inserted) 53014baf4d9SChristophe Lombard return 0; 53114baf4d9SChristophe Lombard if (cxl_h_detach_process(ctx->afu->guest->handle, ctx->process_token)) 53214baf4d9SChristophe Lombard return -1; 53314baf4d9SChristophe Lombard return 0; 53414baf4d9SChristophe Lombard } 53514baf4d9SChristophe Lombard 53614baf4d9SChristophe Lombard static int guest_detach_process(struct cxl_context *ctx) 53714baf4d9SChristophe Lombard { 53814baf4d9SChristophe Lombard pr_devel("in %s\n", __func__); 53914baf4d9SChristophe Lombard trace_cxl_detach(ctx); 54014baf4d9SChristophe Lombard 54114baf4d9SChristophe Lombard if (!cxl_ops->link_ok(ctx->afu->adapter)) 54214baf4d9SChristophe Lombard return -EIO; 54314baf4d9SChristophe Lombard 54414baf4d9SChristophe Lombard if (ctx->afu->current_mode == CXL_MODE_DIRECTED) 54514baf4d9SChristophe Lombard return detach_afu_directed(ctx); 54614baf4d9SChristophe Lombard 54714baf4d9SChristophe Lombard return -EINVAL; 54814baf4d9SChristophe Lombard } 54914baf4d9SChristophe Lombard 55014baf4d9SChristophe Lombard static void guest_release_afu(struct device *dev) 55114baf4d9SChristophe Lombard { 55214baf4d9SChristophe Lombard struct cxl_afu *afu = to_cxl_afu(dev); 55314baf4d9SChristophe Lombard 55414baf4d9SChristophe Lombard pr_devel("%s\n", __func__); 55514baf4d9SChristophe Lombard 55614baf4d9SChristophe Lombard idr_destroy(&afu->contexts_idr); 55714baf4d9SChristophe Lombard 55814baf4d9SChristophe Lombard kfree(afu->guest); 55914baf4d9SChristophe Lombard kfree(afu); 56014baf4d9SChristophe Lombard } 56114baf4d9SChristophe Lombard 56214baf4d9SChristophe Lombard ssize_t cxl_guest_read_afu_vpd(struct cxl_afu *afu, void *buf, size_t len) 56314baf4d9SChristophe Lombard { 56414baf4d9SChristophe Lombard return guest_collect_vpd(NULL, afu, buf, len); 56514baf4d9SChristophe Lombard } 56614baf4d9SChristophe Lombard 56714baf4d9SChristophe Lombard #define ERR_BUFF_MAX_COPY_SIZE PAGE_SIZE 56814baf4d9SChristophe Lombard static ssize_t guest_afu_read_err_buffer(struct cxl_afu *afu, char *buf, 56914baf4d9SChristophe Lombard loff_t off, size_t count) 57014baf4d9SChristophe Lombard { 57114baf4d9SChristophe Lombard void *tbuf = NULL; 57214baf4d9SChristophe Lombard int rc = 0; 57314baf4d9SChristophe Lombard 57414baf4d9SChristophe Lombard tbuf = (void *) get_zeroed_page(GFP_KERNEL); 57514baf4d9SChristophe Lombard if (!tbuf) 57614baf4d9SChristophe Lombard return -ENOMEM; 57714baf4d9SChristophe Lombard 57814baf4d9SChristophe Lombard rc = cxl_h_get_afu_err(afu->guest->handle, 57914baf4d9SChristophe Lombard off & 0x7, 58014baf4d9SChristophe Lombard virt_to_phys(tbuf), 58114baf4d9SChristophe Lombard count); 58214baf4d9SChristophe Lombard if (rc) 58314baf4d9SChristophe Lombard goto err; 58414baf4d9SChristophe Lombard 58514baf4d9SChristophe Lombard if (count > ERR_BUFF_MAX_COPY_SIZE) 58614baf4d9SChristophe Lombard count = ERR_BUFF_MAX_COPY_SIZE - (off & 0x7); 58714baf4d9SChristophe Lombard memcpy(buf, tbuf, count); 58814baf4d9SChristophe Lombard err: 58914baf4d9SChristophe Lombard free_page((u64)tbuf); 59014baf4d9SChristophe Lombard 59114baf4d9SChristophe Lombard return rc; 59214baf4d9SChristophe Lombard } 59314baf4d9SChristophe Lombard 59414baf4d9SChristophe Lombard static int guest_afu_check_and_enable(struct cxl_afu *afu) 59514baf4d9SChristophe Lombard { 59614baf4d9SChristophe Lombard return 0; 59714baf4d9SChristophe Lombard } 59814baf4d9SChristophe Lombard 5994752876cSChristophe Lombard static bool guest_support_attributes(const char *attr_name, 6004752876cSChristophe Lombard enum cxl_attrs type) 6014752876cSChristophe Lombard { 6024752876cSChristophe Lombard switch (type) { 6034752876cSChristophe Lombard case CXL_ADAPTER_ATTRS: 6044752876cSChristophe Lombard if ((strcmp(attr_name, "base_image") == 0) || 6054752876cSChristophe Lombard (strcmp(attr_name, "load_image_on_perst") == 0) || 6064752876cSChristophe Lombard (strcmp(attr_name, "perst_reloads_same_image") == 0) || 6074752876cSChristophe Lombard (strcmp(attr_name, "image_loaded") == 0)) 6084752876cSChristophe Lombard return false; 6094752876cSChristophe Lombard break; 6104752876cSChristophe Lombard case CXL_AFU_MASTER_ATTRS: 6114752876cSChristophe Lombard if ((strcmp(attr_name, "pp_mmio_off") == 0)) 6124752876cSChristophe Lombard return false; 6134752876cSChristophe Lombard break; 6144752876cSChristophe Lombard case CXL_AFU_ATTRS: 6154752876cSChristophe Lombard break; 6164752876cSChristophe Lombard default: 6174752876cSChristophe Lombard break; 6184752876cSChristophe Lombard } 6194752876cSChristophe Lombard 6204752876cSChristophe Lombard return true; 6214752876cSChristophe Lombard } 6224752876cSChristophe Lombard 62314baf4d9SChristophe Lombard static int activate_afu_directed(struct cxl_afu *afu) 62414baf4d9SChristophe Lombard { 62514baf4d9SChristophe Lombard int rc; 62614baf4d9SChristophe Lombard 62714baf4d9SChristophe Lombard dev_info(&afu->dev, "Activating AFU(%d) directed mode\n", afu->slice); 62814baf4d9SChristophe Lombard 62914baf4d9SChristophe Lombard afu->current_mode = CXL_MODE_DIRECTED; 63014baf4d9SChristophe Lombard 63114baf4d9SChristophe Lombard afu->num_procs = afu->max_procs_virtualised; 63214baf4d9SChristophe Lombard 63314baf4d9SChristophe Lombard if ((rc = cxl_chardev_m_afu_add(afu))) 63414baf4d9SChristophe Lombard return rc; 63514baf4d9SChristophe Lombard 63614baf4d9SChristophe Lombard if ((rc = cxl_sysfs_afu_m_add(afu))) 63714baf4d9SChristophe Lombard goto err; 63814baf4d9SChristophe Lombard 63914baf4d9SChristophe Lombard if ((rc = cxl_chardev_s_afu_add(afu))) 64014baf4d9SChristophe Lombard goto err1; 64114baf4d9SChristophe Lombard 64214baf4d9SChristophe Lombard return 0; 64314baf4d9SChristophe Lombard err1: 64414baf4d9SChristophe Lombard cxl_sysfs_afu_m_remove(afu); 64514baf4d9SChristophe Lombard err: 64614baf4d9SChristophe Lombard cxl_chardev_afu_remove(afu); 64714baf4d9SChristophe Lombard return rc; 64814baf4d9SChristophe Lombard } 64914baf4d9SChristophe Lombard 65014baf4d9SChristophe Lombard static int guest_afu_activate_mode(struct cxl_afu *afu, int mode) 65114baf4d9SChristophe Lombard { 65214baf4d9SChristophe Lombard if (!mode) 65314baf4d9SChristophe Lombard return 0; 65414baf4d9SChristophe Lombard if (!(mode & afu->modes_supported)) 65514baf4d9SChristophe Lombard return -EINVAL; 65614baf4d9SChristophe Lombard 65714baf4d9SChristophe Lombard if (mode == CXL_MODE_DIRECTED) 65814baf4d9SChristophe Lombard return activate_afu_directed(afu); 65914baf4d9SChristophe Lombard 66014baf4d9SChristophe Lombard if (mode == CXL_MODE_DEDICATED) 66114baf4d9SChristophe Lombard dev_err(&afu->dev, "Dedicated mode not supported\n"); 66214baf4d9SChristophe Lombard 66314baf4d9SChristophe Lombard return -EINVAL; 66414baf4d9SChristophe Lombard } 66514baf4d9SChristophe Lombard 66614baf4d9SChristophe Lombard static int deactivate_afu_directed(struct cxl_afu *afu) 66714baf4d9SChristophe Lombard { 66814baf4d9SChristophe Lombard dev_info(&afu->dev, "Deactivating AFU(%d) directed mode\n", afu->slice); 66914baf4d9SChristophe Lombard 67014baf4d9SChristophe Lombard afu->current_mode = 0; 67114baf4d9SChristophe Lombard afu->num_procs = 0; 67214baf4d9SChristophe Lombard 67314baf4d9SChristophe Lombard cxl_sysfs_afu_m_remove(afu); 67414baf4d9SChristophe Lombard cxl_chardev_afu_remove(afu); 67514baf4d9SChristophe Lombard 67614baf4d9SChristophe Lombard cxl_ops->afu_reset(afu); 67714baf4d9SChristophe Lombard 67814baf4d9SChristophe Lombard return 0; 67914baf4d9SChristophe Lombard } 68014baf4d9SChristophe Lombard 68114baf4d9SChristophe Lombard static int guest_afu_deactivate_mode(struct cxl_afu *afu, int mode) 68214baf4d9SChristophe Lombard { 68314baf4d9SChristophe Lombard if (!mode) 68414baf4d9SChristophe Lombard return 0; 68514baf4d9SChristophe Lombard if (!(mode & afu->modes_supported)) 68614baf4d9SChristophe Lombard return -EINVAL; 68714baf4d9SChristophe Lombard 68814baf4d9SChristophe Lombard if (mode == CXL_MODE_DIRECTED) 68914baf4d9SChristophe Lombard return deactivate_afu_directed(afu); 69014baf4d9SChristophe Lombard return 0; 69114baf4d9SChristophe Lombard } 69214baf4d9SChristophe Lombard 69314baf4d9SChristophe Lombard static int guest_afu_reset(struct cxl_afu *afu) 69414baf4d9SChristophe Lombard { 69514baf4d9SChristophe Lombard pr_devel("AFU(%d) reset request\n", afu->slice); 69614baf4d9SChristophe Lombard return cxl_h_reset_afu(afu->guest->handle); 69714baf4d9SChristophe Lombard } 69814baf4d9SChristophe Lombard 69914baf4d9SChristophe Lombard static int guest_map_slice_regs(struct cxl_afu *afu) 70014baf4d9SChristophe Lombard { 70114baf4d9SChristophe Lombard if (!(afu->p2n_mmio = ioremap(afu->guest->p2n_phys, afu->guest->p2n_size))) { 70214baf4d9SChristophe Lombard dev_err(&afu->dev, "Error mapping AFU(%d) MMIO regions\n", 70314baf4d9SChristophe Lombard afu->slice); 70414baf4d9SChristophe Lombard return -ENOMEM; 70514baf4d9SChristophe Lombard } 70614baf4d9SChristophe Lombard return 0; 70714baf4d9SChristophe Lombard } 70814baf4d9SChristophe Lombard 70914baf4d9SChristophe Lombard static void guest_unmap_slice_regs(struct cxl_afu *afu) 71014baf4d9SChristophe Lombard { 71114baf4d9SChristophe Lombard if (afu->p2n_mmio) 71214baf4d9SChristophe Lombard iounmap(afu->p2n_mmio); 71314baf4d9SChristophe Lombard } 71414baf4d9SChristophe Lombard 71514baf4d9SChristophe Lombard static bool guest_link_ok(struct cxl *cxl) 71614baf4d9SChristophe Lombard { 71714baf4d9SChristophe Lombard return true; 71814baf4d9SChristophe Lombard } 71914baf4d9SChristophe Lombard 72014baf4d9SChristophe Lombard static int afu_properties_look_ok(struct cxl_afu *afu) 72114baf4d9SChristophe Lombard { 72214baf4d9SChristophe Lombard if (afu->pp_irqs < 0) { 72314baf4d9SChristophe Lombard dev_err(&afu->dev, "Unexpected per-process minimum interrupt value\n"); 72414baf4d9SChristophe Lombard return -EINVAL; 72514baf4d9SChristophe Lombard } 72614baf4d9SChristophe Lombard 72714baf4d9SChristophe Lombard if (afu->max_procs_virtualised < 1) { 72814baf4d9SChristophe Lombard dev_err(&afu->dev, "Unexpected max number of processes virtualised value\n"); 72914baf4d9SChristophe Lombard return -EINVAL; 73014baf4d9SChristophe Lombard } 73114baf4d9SChristophe Lombard 73214baf4d9SChristophe Lombard if (afu->crs_len < 0) { 73314baf4d9SChristophe Lombard dev_err(&afu->dev, "Unexpected configuration record size value\n"); 73414baf4d9SChristophe Lombard return -EINVAL; 73514baf4d9SChristophe Lombard } 73614baf4d9SChristophe Lombard 73714baf4d9SChristophe Lombard return 0; 73814baf4d9SChristophe Lombard } 73914baf4d9SChristophe Lombard 74014baf4d9SChristophe Lombard int cxl_guest_init_afu(struct cxl *adapter, int slice, struct device_node *afu_np) 74114baf4d9SChristophe Lombard { 74214baf4d9SChristophe Lombard struct cxl_afu *afu; 74314baf4d9SChristophe Lombard bool free = true; 74414baf4d9SChristophe Lombard int rc; 74514baf4d9SChristophe Lombard 74614baf4d9SChristophe Lombard pr_devel("in %s - AFU(%d)\n", __func__, slice); 74714baf4d9SChristophe Lombard if (!(afu = cxl_alloc_afu(adapter, slice))) 74814baf4d9SChristophe Lombard return -ENOMEM; 74914baf4d9SChristophe Lombard 75014baf4d9SChristophe Lombard if (!(afu->guest = kzalloc(sizeof(struct cxl_afu_guest), GFP_KERNEL))) { 75114baf4d9SChristophe Lombard kfree(afu); 75214baf4d9SChristophe Lombard return -ENOMEM; 75314baf4d9SChristophe Lombard } 75414baf4d9SChristophe Lombard 75514baf4d9SChristophe Lombard if ((rc = dev_set_name(&afu->dev, "afu%i.%i", 75614baf4d9SChristophe Lombard adapter->adapter_num, 75714baf4d9SChristophe Lombard slice))) 75814baf4d9SChristophe Lombard goto err1; 75914baf4d9SChristophe Lombard 76014baf4d9SChristophe Lombard adapter->slices++; 76114baf4d9SChristophe Lombard 76214baf4d9SChristophe Lombard if ((rc = cxl_of_read_afu_handle(afu, afu_np))) 76314baf4d9SChristophe Lombard goto err1; 76414baf4d9SChristophe Lombard 76514baf4d9SChristophe Lombard if ((rc = cxl_ops->afu_reset(afu))) 76614baf4d9SChristophe Lombard goto err1; 76714baf4d9SChristophe Lombard 76814baf4d9SChristophe Lombard if ((rc = cxl_of_read_afu_properties(afu, afu_np))) 76914baf4d9SChristophe Lombard goto err1; 77014baf4d9SChristophe Lombard 77114baf4d9SChristophe Lombard if ((rc = afu_properties_look_ok(afu))) 77214baf4d9SChristophe Lombard goto err1; 77314baf4d9SChristophe Lombard 77414baf4d9SChristophe Lombard if ((rc = guest_map_slice_regs(afu))) 77514baf4d9SChristophe Lombard goto err1; 77614baf4d9SChristophe Lombard 77714baf4d9SChristophe Lombard if ((rc = guest_register_serr_irq(afu))) 77814baf4d9SChristophe Lombard goto err2; 77914baf4d9SChristophe Lombard 78014baf4d9SChristophe Lombard /* 78114baf4d9SChristophe Lombard * After we call this function we must not free the afu directly, even 78214baf4d9SChristophe Lombard * if it returns an error! 78314baf4d9SChristophe Lombard */ 78414baf4d9SChristophe Lombard if ((rc = cxl_register_afu(afu))) 78514baf4d9SChristophe Lombard goto err_put1; 78614baf4d9SChristophe Lombard 78714baf4d9SChristophe Lombard if ((rc = cxl_sysfs_afu_add(afu))) 78814baf4d9SChristophe Lombard goto err_put1; 78914baf4d9SChristophe Lombard 79014baf4d9SChristophe Lombard /* 79114baf4d9SChristophe Lombard * pHyp doesn't expose the programming models supported by the 79214baf4d9SChristophe Lombard * AFU. pHyp currently only supports directed mode. If it adds 79314baf4d9SChristophe Lombard * dedicated mode later, this version of cxl has no way to 79414baf4d9SChristophe Lombard * detect it. So we'll initialize the driver, but the first 79514baf4d9SChristophe Lombard * attach will fail. 79614baf4d9SChristophe Lombard * Being discussed with pHyp to do better (likely new property) 79714baf4d9SChristophe Lombard */ 79814baf4d9SChristophe Lombard if (afu->max_procs_virtualised == 1) 79914baf4d9SChristophe Lombard afu->modes_supported = CXL_MODE_DEDICATED; 80014baf4d9SChristophe Lombard else 80114baf4d9SChristophe Lombard afu->modes_supported = CXL_MODE_DIRECTED; 80214baf4d9SChristophe Lombard 80314baf4d9SChristophe Lombard if ((rc = cxl_afu_select_best_mode(afu))) 80414baf4d9SChristophe Lombard goto err_put2; 80514baf4d9SChristophe Lombard 80614baf4d9SChristophe Lombard adapter->afu[afu->slice] = afu; 80714baf4d9SChristophe Lombard 80814baf4d9SChristophe Lombard afu->enabled = true; 80914baf4d9SChristophe Lombard 81014baf4d9SChristophe Lombard return 0; 81114baf4d9SChristophe Lombard 81214baf4d9SChristophe Lombard err_put2: 81314baf4d9SChristophe Lombard cxl_sysfs_afu_remove(afu); 81414baf4d9SChristophe Lombard err_put1: 81514baf4d9SChristophe Lombard device_unregister(&afu->dev); 81614baf4d9SChristophe Lombard free = false; 81714baf4d9SChristophe Lombard guest_release_serr_irq(afu); 81814baf4d9SChristophe Lombard err2: 81914baf4d9SChristophe Lombard guest_unmap_slice_regs(afu); 82014baf4d9SChristophe Lombard err1: 82114baf4d9SChristophe Lombard if (free) { 82214baf4d9SChristophe Lombard kfree(afu->guest); 82314baf4d9SChristophe Lombard kfree(afu); 82414baf4d9SChristophe Lombard } 82514baf4d9SChristophe Lombard return rc; 82614baf4d9SChristophe Lombard } 82714baf4d9SChristophe Lombard 82814baf4d9SChristophe Lombard void cxl_guest_remove_afu(struct cxl_afu *afu) 82914baf4d9SChristophe Lombard { 83014baf4d9SChristophe Lombard pr_devel("in %s - AFU(%d)\n", __func__, afu->slice); 83114baf4d9SChristophe Lombard 83214baf4d9SChristophe Lombard if (!afu) 83314baf4d9SChristophe Lombard return; 83414baf4d9SChristophe Lombard 83514baf4d9SChristophe Lombard cxl_sysfs_afu_remove(afu); 83614baf4d9SChristophe Lombard 83714baf4d9SChristophe Lombard spin_lock(&afu->adapter->afu_list_lock); 83814baf4d9SChristophe Lombard afu->adapter->afu[afu->slice] = NULL; 83914baf4d9SChristophe Lombard spin_unlock(&afu->adapter->afu_list_lock); 84014baf4d9SChristophe Lombard 84114baf4d9SChristophe Lombard cxl_context_detach_all(afu); 84214baf4d9SChristophe Lombard cxl_ops->afu_deactivate_mode(afu, afu->current_mode); 84314baf4d9SChristophe Lombard guest_release_serr_irq(afu); 84414baf4d9SChristophe Lombard guest_unmap_slice_regs(afu); 84514baf4d9SChristophe Lombard 84614baf4d9SChristophe Lombard device_unregister(&afu->dev); 84714baf4d9SChristophe Lombard } 84814baf4d9SChristophe Lombard 84914baf4d9SChristophe Lombard static void free_adapter(struct cxl *adapter) 85014baf4d9SChristophe Lombard { 85114baf4d9SChristophe Lombard struct irq_avail *cur; 85214baf4d9SChristophe Lombard int i; 85314baf4d9SChristophe Lombard 85414baf4d9SChristophe Lombard if (adapter->guest->irq_avail) { 85514baf4d9SChristophe Lombard for (i = 0; i < adapter->guest->irq_nranges; i++) { 85614baf4d9SChristophe Lombard cur = &adapter->guest->irq_avail[i]; 85714baf4d9SChristophe Lombard kfree(cur->bitmap); 85814baf4d9SChristophe Lombard } 85914baf4d9SChristophe Lombard kfree(adapter->guest->irq_avail); 86014baf4d9SChristophe Lombard } 86114baf4d9SChristophe Lombard kfree(adapter->guest->status); 86214baf4d9SChristophe Lombard cxl_remove_adapter_nr(adapter); 86314baf4d9SChristophe Lombard kfree(adapter->guest); 86414baf4d9SChristophe Lombard kfree(adapter); 86514baf4d9SChristophe Lombard } 86614baf4d9SChristophe Lombard 86714baf4d9SChristophe Lombard static int properties_look_ok(struct cxl *adapter) 86814baf4d9SChristophe Lombard { 86914baf4d9SChristophe Lombard /* The absence of this property means that the operational 87014baf4d9SChristophe Lombard * status is unknown or okay 87114baf4d9SChristophe Lombard */ 87214baf4d9SChristophe Lombard if (strlen(adapter->guest->status) && 87314baf4d9SChristophe Lombard strcmp(adapter->guest->status, "okay")) { 87414baf4d9SChristophe Lombard pr_err("ABORTING:Bad operational status of the device\n"); 87514baf4d9SChristophe Lombard return -EINVAL; 87614baf4d9SChristophe Lombard } 87714baf4d9SChristophe Lombard 87814baf4d9SChristophe Lombard return 0; 87914baf4d9SChristophe Lombard } 88014baf4d9SChristophe Lombard 88114baf4d9SChristophe Lombard ssize_t cxl_guest_read_adapter_vpd(struct cxl *adapter, void *buf, size_t len) 88214baf4d9SChristophe Lombard { 88314baf4d9SChristophe Lombard return guest_collect_vpd(adapter, NULL, buf, len); 88414baf4d9SChristophe Lombard } 88514baf4d9SChristophe Lombard 88614baf4d9SChristophe Lombard void cxl_guest_remove_adapter(struct cxl *adapter) 88714baf4d9SChristophe Lombard { 88814baf4d9SChristophe Lombard pr_devel("in %s\n", __func__); 88914baf4d9SChristophe Lombard 89014baf4d9SChristophe Lombard cxl_sysfs_adapter_remove(adapter); 89114baf4d9SChristophe Lombard 892*594ff7d0SChristophe Lombard cxl_guest_remove_chardev(adapter); 89314baf4d9SChristophe Lombard device_unregister(&adapter->dev); 89414baf4d9SChristophe Lombard } 89514baf4d9SChristophe Lombard 89614baf4d9SChristophe Lombard static void release_adapter(struct device *dev) 89714baf4d9SChristophe Lombard { 89814baf4d9SChristophe Lombard free_adapter(to_cxl_adapter(dev)); 89914baf4d9SChristophe Lombard } 90014baf4d9SChristophe Lombard 90114baf4d9SChristophe Lombard struct cxl *cxl_guest_init_adapter(struct device_node *np, struct platform_device *pdev) 90214baf4d9SChristophe Lombard { 90314baf4d9SChristophe Lombard struct cxl *adapter; 90414baf4d9SChristophe Lombard bool free = true; 90514baf4d9SChristophe Lombard int rc; 90614baf4d9SChristophe Lombard 90714baf4d9SChristophe Lombard if (!(adapter = cxl_alloc_adapter())) 90814baf4d9SChristophe Lombard return ERR_PTR(-ENOMEM); 90914baf4d9SChristophe Lombard 91014baf4d9SChristophe Lombard if (!(adapter->guest = kzalloc(sizeof(struct cxl_guest), GFP_KERNEL))) { 91114baf4d9SChristophe Lombard free_adapter(adapter); 91214baf4d9SChristophe Lombard return ERR_PTR(-ENOMEM); 91314baf4d9SChristophe Lombard } 91414baf4d9SChristophe Lombard 91514baf4d9SChristophe Lombard adapter->slices = 0; 91614baf4d9SChristophe Lombard adapter->guest->pdev = pdev; 91714baf4d9SChristophe Lombard adapter->dev.parent = &pdev->dev; 91814baf4d9SChristophe Lombard adapter->dev.release = release_adapter; 91914baf4d9SChristophe Lombard dev_set_drvdata(&pdev->dev, adapter); 92014baf4d9SChristophe Lombard 92114baf4d9SChristophe Lombard if ((rc = cxl_of_read_adapter_handle(adapter, np))) 92214baf4d9SChristophe Lombard goto err1; 92314baf4d9SChristophe Lombard 92414baf4d9SChristophe Lombard if ((rc = cxl_of_read_adapter_properties(adapter, np))) 92514baf4d9SChristophe Lombard goto err1; 92614baf4d9SChristophe Lombard 92714baf4d9SChristophe Lombard if ((rc = properties_look_ok(adapter))) 92814baf4d9SChristophe Lombard goto err1; 92914baf4d9SChristophe Lombard 930*594ff7d0SChristophe Lombard if ((rc = cxl_guest_add_chardev(adapter))) 931*594ff7d0SChristophe Lombard goto err1; 932*594ff7d0SChristophe Lombard 93314baf4d9SChristophe Lombard /* 93414baf4d9SChristophe Lombard * After we call this function we must not free the adapter directly, 93514baf4d9SChristophe Lombard * even if it returns an error! 93614baf4d9SChristophe Lombard */ 93714baf4d9SChristophe Lombard if ((rc = cxl_register_adapter(adapter))) 93814baf4d9SChristophe Lombard goto err_put1; 93914baf4d9SChristophe Lombard 94014baf4d9SChristophe Lombard if ((rc = cxl_sysfs_adapter_add(adapter))) 94114baf4d9SChristophe Lombard goto err_put1; 94214baf4d9SChristophe Lombard 94314baf4d9SChristophe Lombard return adapter; 94414baf4d9SChristophe Lombard 94514baf4d9SChristophe Lombard err_put1: 94614baf4d9SChristophe Lombard device_unregister(&adapter->dev); 94714baf4d9SChristophe Lombard free = false; 948*594ff7d0SChristophe Lombard cxl_guest_remove_chardev(adapter); 94914baf4d9SChristophe Lombard err1: 95014baf4d9SChristophe Lombard if (free) 95114baf4d9SChristophe Lombard free_adapter(adapter); 95214baf4d9SChristophe Lombard return ERR_PTR(rc); 95314baf4d9SChristophe Lombard } 95414baf4d9SChristophe Lombard 955*594ff7d0SChristophe Lombard void cxl_guest_reload_module(struct cxl *adapter) 956*594ff7d0SChristophe Lombard { 957*594ff7d0SChristophe Lombard struct platform_device *pdev; 958*594ff7d0SChristophe Lombard 959*594ff7d0SChristophe Lombard pdev = adapter->guest->pdev; 960*594ff7d0SChristophe Lombard cxl_guest_remove_adapter(adapter); 961*594ff7d0SChristophe Lombard 962*594ff7d0SChristophe Lombard cxl_of_probe(pdev); 963*594ff7d0SChristophe Lombard } 964*594ff7d0SChristophe Lombard 96514baf4d9SChristophe Lombard const struct cxl_backend_ops cxl_guest_ops = { 96614baf4d9SChristophe Lombard .module = THIS_MODULE, 96714baf4d9SChristophe Lombard .adapter_reset = guest_reset, 96814baf4d9SChristophe Lombard .alloc_one_irq = guest_alloc_one_irq, 96914baf4d9SChristophe Lombard .release_one_irq = guest_release_one_irq, 97014baf4d9SChristophe Lombard .alloc_irq_ranges = guest_alloc_irq_ranges, 97114baf4d9SChristophe Lombard .release_irq_ranges = guest_release_irq_ranges, 97214baf4d9SChristophe Lombard .setup_irq = NULL, 97314baf4d9SChristophe Lombard .handle_psl_slice_error = guest_handle_psl_slice_error, 97414baf4d9SChristophe Lombard .psl_interrupt = guest_psl_irq, 97514baf4d9SChristophe Lombard .ack_irq = guest_ack_irq, 97614baf4d9SChristophe Lombard .attach_process = guest_attach_process, 97714baf4d9SChristophe Lombard .detach_process = guest_detach_process, 9784752876cSChristophe Lombard .support_attributes = guest_support_attributes, 97914baf4d9SChristophe Lombard .link_ok = guest_link_ok, 98014baf4d9SChristophe Lombard .release_afu = guest_release_afu, 98114baf4d9SChristophe Lombard .afu_read_err_buffer = guest_afu_read_err_buffer, 98214baf4d9SChristophe Lombard .afu_check_and_enable = guest_afu_check_and_enable, 98314baf4d9SChristophe Lombard .afu_activate_mode = guest_afu_activate_mode, 98414baf4d9SChristophe Lombard .afu_deactivate_mode = guest_afu_deactivate_mode, 98514baf4d9SChristophe Lombard .afu_reset = guest_afu_reset, 98614baf4d9SChristophe Lombard .afu_cr_read8 = guest_afu_cr_read8, 98714baf4d9SChristophe Lombard .afu_cr_read16 = guest_afu_cr_read16, 98814baf4d9SChristophe Lombard .afu_cr_read32 = guest_afu_cr_read32, 98914baf4d9SChristophe Lombard .afu_cr_read64 = guest_afu_cr_read64, 99014baf4d9SChristophe Lombard }; 991