diff --git a/Documentation/ABI/testing/sysfs-class-cxl b/Documentation/ABI/testing/sysfs-class-cxl index d46bba801aace4..acfe9df83139a9 100644 --- a/Documentation/ABI/testing/sysfs-class-cxl +++ b/Documentation/ABI/testing/sysfs-class-cxl @@ -6,6 +6,17 @@ Example: The real path of the attribute /sys/class/cxl/afu0.0s/irqs_max is Slave contexts (eg. /sys/class/cxl/afu0.0s): +What: /sys/class/cxl//afu_err_buf +Date: September 2014 +Contact: linuxppc-dev@lists.ozlabs.org +Description: read only + AFU Error Buffer contents. The contents of this file are + application specific and depends on the AFU being used. + Applications interacting with the AFU can use this attribute + to know about the current error condition and take appropriate + action like logging the event etc. + + What: /sys/class/cxl//irqs_max Date: September 2014 Contact: linuxppc-dev@lists.ozlabs.org @@ -15,6 +26,7 @@ Description: read/write that hardware can support (eg. 2037). Write values will limit userspace applications to that many userspace interrupts. Must be >= irqs_min. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//irqs_min Date: September 2014 @@ -24,6 +36,7 @@ Description: read only userspace must request on a CXL_START_WORK ioctl. Userspace may omit the num_interrupts field in the START_WORK IOCTL to get this minimum automatically. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//mmio_size Date: September 2014 @@ -31,6 +44,7 @@ Contact: linuxppc-dev@lists.ozlabs.org Description: read only Decimal value of the size of the MMIO space that may be mmaped by userspace. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//modes_supported Date: September 2014 @@ -38,6 +52,7 @@ Contact: linuxppc-dev@lists.ozlabs.org Description: read only List of the modes this AFU supports. One per line. Valid entries are: "dedicated_process" and "afu_directed" +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//mode Date: September 2014 @@ -46,6 +61,7 @@ Description: read/write The current mode the AFU is using. Will be one of the modes given in modes_supported. Writing will change the mode provided that no user contexts are attached. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//prefault_mode @@ -59,6 +75,7 @@ Description: read/write descriptor as an effective address and prefault what it points to. all: all segments process calling START_WORK maps. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//reset Date: September 2014 @@ -66,12 +83,14 @@ Contact: linuxppc-dev@lists.ozlabs.org Description: write only Writing 1 here will reset the AFU provided there are not contexts active on the AFU. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//api_version Date: September 2014 Contact: linuxppc-dev@lists.ozlabs.org Description: read only Decimal value of the current version of the kernel/user API. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//api_version_compatible Date: September 2014 @@ -79,6 +98,7 @@ Contact: linuxppc-dev@lists.ozlabs.org Description: read only Decimal value of the the lowest version of the userspace API this this kernel supports. +Users: https://github.com/ibm-capi/libcxl AFU configuration records (eg. /sys/class/cxl/afu0.0/cr0): @@ -92,6 +112,7 @@ Contact: linuxppc-dev@lists.ozlabs.org Description: read only Hexadecimal value of the vendor ID found in this AFU configuration record. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//cr/device Date: February 2015 @@ -99,6 +120,7 @@ Contact: linuxppc-dev@lists.ozlabs.org Description: read only Hexadecimal value of the device ID found in this AFU configuration record. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//cr/class Date: February 2015 @@ -106,6 +128,7 @@ Contact: linuxppc-dev@lists.ozlabs.org Description: read only Hexadecimal value of the class code found in this AFU configuration record. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//cr/config Date: February 2015 @@ -115,6 +138,7 @@ Description: read only record. The format is expected to match the either the standard or extended configuration space defined by the PCIe specification. +Users: https://github.com/ibm-capi/libcxl @@ -126,18 +150,21 @@ Contact: linuxppc-dev@lists.ozlabs.org Description: read only Decimal value of the size of the MMIO space that may be mmaped by userspace. This includes all slave contexts space also. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl/m/pp_mmio_len Date: September 2014 Contact: linuxppc-dev@lists.ozlabs.org Description: read only Decimal value of the Per Process MMIO space length. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl/m/pp_mmio_off Date: September 2014 Contact: linuxppc-dev@lists.ozlabs.org Description: read only Decimal value of the Per Process MMIO space offset. +Users: https://github.com/ibm-capi/libcxl Card info (eg. /sys/class/cxl/card0) @@ -147,12 +174,14 @@ Date: September 2014 Contact: linuxppc-dev@lists.ozlabs.org Description: read only Identifies the CAIA Version the card implements. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//psl_revision Date: September 2014 Contact: linuxppc-dev@lists.ozlabs.org Description: read only Identifies the revision level of the PSL. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//base_image Date: September 2014 @@ -162,6 +191,7 @@ Description: read only that support loadable PSLs. For FPGAs this field identifies the image contained in the on-adapter flash which is loaded during the initial program load. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//image_loaded Date: September 2014 @@ -169,6 +199,7 @@ Contact: linuxppc-dev@lists.ozlabs.org Description: read only Will return "user" or "factory" depending on the image loaded onto the card. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//load_image_on_perst Date: December 2014 @@ -183,6 +214,7 @@ Description: read/write user or factory image to be loaded. Default is to reload on PERST whichever image the card has loaded. +Users: https://github.com/ibm-capi/libcxl What: /sys/class/cxl//reset Date: October 2014 @@ -190,3 +222,4 @@ Contact: linuxppc-dev@lists.ozlabs.org Description: write only Writing 1 will issue a PERST to card which may cause the card to reload the FPGA depending on load_image_on_perst. +Users: https://github.com/ibm-capi/libcxl diff --git a/Documentation/powerpc/cxl.txt b/Documentation/powerpc/cxl.txt index 2c71ecc519d953..2a230d01cd8ce3 100644 --- a/Documentation/powerpc/cxl.txt +++ b/Documentation/powerpc/cxl.txt @@ -133,6 +133,9 @@ User API The following file operations are supported on both slave and master devices. + A userspace library libcxl is avaliable here: + https://github.com/ibm-capi/libcxl + This provides a C interface to this kernel API. open ---- @@ -366,6 +369,7 @@ Sysfs Class enumeration and tuning of the accelerators. Its layout is described in Documentation/ABI/testing/sysfs-class-cxl + Udev rules ========== diff --git a/MAINTAINERS b/MAINTAINERS index 562ae4ef85c8a0..80627ddba3e074 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2946,7 +2946,7 @@ M: Michael Neuling L: linuxppc-dev@lists.ozlabs.org S: Supported F: drivers/misc/cxl/ -F: include/misc/cxl.h +F: include/misc/cxl* F: include/uapi/misc/cxl.h F: Documentation/powerpc/cxl.txt F: Documentation/powerpc/cxl.txt diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h index 9f1371bab5fc21..e9bdda88f1fbb1 100644 --- a/arch/powerpc/include/asm/device.h +++ b/arch/powerpc/include/asm/device.h @@ -46,6 +46,9 @@ struct dev_archdata { #ifdef CONFIG_FAIL_IOMMU int fail_iommu; #endif +#ifdef CONFIG_CXL_BASE + struct cxl_context *cxl_ctx; +#endif }; struct pdev_archdata { diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 1811c44bf34bcb..a049dd50b20c04 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -27,6 +27,8 @@ struct pci_controller_ops { * allow assignment/enabling of the device. */ bool (*enable_device_hook)(struct pci_dev *); + void (*release_device)(struct pci_dev *); + /* Called during PCI resource reassignment */ resource_size_t (*window_alignment)(struct pci_bus *, unsigned long type); void (*reset_secondary_bus)(struct pci_dev *dev); diff --git a/arch/powerpc/include/asm/pnv-pci.h b/arch/powerpc/include/asm/pnv-pci.h index f9b498292a5c1c..6f77f71ee96445 100644 --- a/arch/powerpc/include/asm/pnv-pci.h +++ b/arch/powerpc/include/asm/pnv-pci.h @@ -11,7 +11,7 @@ #define _ASM_PNV_PCI_H #include -#include +#include int pnv_phb_to_cxl_mode(struct pci_dev *dev, uint64_t mode); int pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq, diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 0d054068a21d58..2040cd2f73580c 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -89,6 +89,7 @@ struct pci_controller *pcibios_alloc_controller(struct device_node *dev) #endif return phb; } +EXPORT_SYMBOL_GPL(pcibios_alloc_controller); void pcibios_free_controller(struct pci_controller *phb) { @@ -1447,6 +1448,7 @@ void pcibios_claim_one_bus(struct pci_bus *bus) list_for_each_entry(child_bus, &bus->children, node) pcibios_claim_one_bus(child_bus); } +EXPORT_SYMBOL_GPL(pcibios_claim_one_bus); /* pcibios_finish_adding_to_bus @@ -1680,6 +1682,7 @@ void pcibios_scan_phb(struct pci_controller *hose) pcie_bus_configure_settings(child); } } +EXPORT_SYMBOL_GPL(pcibios_scan_phb); static void fixup_hide_host_resource_fsl(struct pci_dev *dev) { diff --git a/arch/powerpc/kernel/pci-hotplug.c b/arch/powerpc/kernel/pci-hotplug.c index 7ed85a69a9c29b..7f9ed0c1f6b93d 100644 --- a/arch/powerpc/kernel/pci-hotplug.c +++ b/arch/powerpc/kernel/pci-hotplug.c @@ -29,7 +29,12 @@ */ void pcibios_release_device(struct pci_dev *dev) { + struct pci_controller *phb = pci_bus_to_host(dev->bus); + eeh_remove_device(dev); + + if (phb->controller_ops.release_device) + phb->controller_ops.release_device(dev); } /** diff --git a/arch/powerpc/mm/copro_fault.c b/arch/powerpc/mm/copro_fault.c index f031a47d7701e6..6527882ce05ede 100644 --- a/arch/powerpc/mm/copro_fault.c +++ b/arch/powerpc/mm/copro_fault.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include /* * This ought to be kept in sync with the powerpc specific do_page_fault @@ -100,7 +100,7 @@ EXPORT_SYMBOL_GPL(copro_handle_mm_fault); int copro_calculate_slb(struct mm_struct *mm, u64 ea, struct copro_slb *slb) { - u64 vsid; + u64 vsid, vsidkey; int psize, ssize; switch (REGION_ID(ea)) { @@ -109,6 +109,7 @@ int copro_calculate_slb(struct mm_struct *mm, u64 ea, struct copro_slb *slb) psize = get_slice_psize(mm, ea); ssize = user_segment_size(ea); vsid = get_vsid(mm->context.id, ea, ssize); + vsidkey = SLB_VSID_USER; break; case VMALLOC_REGION_ID: pr_devel("%s: 0x%llx -- VMALLOC_REGION_ID\n", __func__, ea); @@ -118,19 +119,21 @@ int copro_calculate_slb(struct mm_struct *mm, u64 ea, struct copro_slb *slb) psize = mmu_io_psize; ssize = mmu_kernel_ssize; vsid = get_kernel_vsid(ea, mmu_kernel_ssize); + vsidkey = SLB_VSID_KERNEL; break; case KERNEL_REGION_ID: pr_devel("%s: 0x%llx -- KERNEL_REGION_ID\n", __func__, ea); psize = mmu_linear_psize; ssize = mmu_kernel_ssize; vsid = get_kernel_vsid(ea, mmu_kernel_ssize); + vsidkey = SLB_VSID_KERNEL; break; default: pr_debug("%s: invalid region access at %016llx\n", __func__, ea); return 1; } - vsid = (vsid << slb_vsid_shift(ssize)) | SLB_VSID_USER; + vsid = (vsid << slb_vsid_shift(ssize)) | vsidkey; vsid |= mmu_psize_defs[psize].sllp | ((ssize == MMU_SEGSIZE_1T) ? SLB_VSID_B_1T : 0); diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c index 9c4880ddecd63f..13befa35d8a8ec 100644 --- a/arch/powerpc/mm/hash_native_64.c +++ b/arch/powerpc/mm/hash_native_64.c @@ -29,7 +29,7 @@ #include #include -#include +#include #ifdef DEBUG_LOW #define DBG_LOW(fmt...) udbg_printf(fmt) diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index f8bc950efcae39..21383f57eaffd2 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c @@ -39,7 +39,7 @@ #include #include -#include +#include #include "powernv.h" #include "pci.h" diff --git a/drivers/misc/cxl/Makefile b/drivers/misc/cxl/Makefile index edb494d3ff271a..14e3f8219a11cf 100644 --- a/drivers/misc/cxl/Makefile +++ b/drivers/misc/cxl/Makefile @@ -1,4 +1,6 @@ -cxl-y += main.o file.o irq.o fault.o native.o context.o sysfs.o debugfs.o pci.o trace.o +cxl-y += main.o file.o irq.o fault.o native.o +cxl-y += context.o sysfs.o debugfs.o pci.o trace.o +cxl-y += vphb.o api.o obj-$(CONFIG_CXL) += cxl.o obj-$(CONFIG_CXL_BASE) += base.o diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c new file mode 100644 index 00000000000000..09901ff37895eb --- /dev/null +++ b/drivers/misc/cxl/api.c @@ -0,0 +1,326 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include "cxl.h" + +struct cxl_context *cxl_dev_context_init(struct pci_dev *dev) +{ + struct cxl_afu *afu; + struct cxl_context *ctx; + int rc; + + afu = cxl_pci_to_afu(dev); + + ctx = cxl_context_alloc(); + if (IS_ERR(ctx)) + return ctx; + + /* Make it a slave context. We can promote it later? */ + rc = cxl_context_init(ctx, afu, false, NULL); + if (rc) { + kfree(ctx); + return ERR_PTR(-ENOMEM); + } + assign_psn_space(ctx); + + return ctx; +} +EXPORT_SYMBOL_GPL(cxl_dev_context_init); + +struct cxl_context *cxl_get_context(struct pci_dev *dev) +{ + return dev->dev.archdata.cxl_ctx; +} +EXPORT_SYMBOL_GPL(cxl_get_context); + +struct device *cxl_get_phys_dev(struct pci_dev *dev) +{ + struct cxl_afu *afu; + + afu = cxl_pci_to_afu(dev); + + return afu->adapter->dev.parent; +} +EXPORT_SYMBOL_GPL(cxl_get_phys_dev); + +int cxl_release_context(struct cxl_context *ctx) +{ + if (ctx->status != CLOSED) + return -EBUSY; + + cxl_context_free(ctx); + + cxl_ctx_put(); + return 0; +} +EXPORT_SYMBOL_GPL(cxl_release_context); + +int cxl_allocate_afu_irqs(struct cxl_context *ctx, int num) +{ + if (num == 0) + num = ctx->afu->pp_irqs; + return afu_allocate_irqs(ctx, num); +} +EXPORT_SYMBOL_GPL(cxl_allocate_afu_irqs); + +void cxl_free_afu_irqs(struct cxl_context *ctx) +{ + cxl_release_irq_ranges(&ctx->irqs, ctx->afu->adapter); +} +EXPORT_SYMBOL_GPL(cxl_free_afu_irqs); + +static irq_hw_number_t cxl_find_afu_irq(struct cxl_context *ctx, int num) +{ + __u16 range; + int r; + + WARN_ON(num == 0); + + for (r = 0; r < CXL_IRQ_RANGES; r++) { + range = ctx->irqs.range[r]; + if (num < range) { + return ctx->irqs.offset[r] + num; + } + num -= range; + } + return 0; +} + +int cxl_map_afu_irq(struct cxl_context *ctx, int num, + irq_handler_t handler, void *cookie, char *name) +{ + irq_hw_number_t hwirq; + + /* + * Find interrupt we are to register. + */ + hwirq = cxl_find_afu_irq(ctx, num); + if (!hwirq) + return -ENOENT; + + return cxl_map_irq(ctx->afu->adapter, hwirq, handler, cookie, name); +} +EXPORT_SYMBOL_GPL(cxl_map_afu_irq); + +void cxl_unmap_afu_irq(struct cxl_context *ctx, int num, void *cookie) +{ + irq_hw_number_t hwirq; + unsigned int virq; + + hwirq = cxl_find_afu_irq(ctx, num); + if (!hwirq) + return; + + virq = irq_find_mapping(NULL, hwirq); + if (virq) + cxl_unmap_irq(virq, cookie); +} +EXPORT_SYMBOL_GPL(cxl_unmap_afu_irq); + +/* + * Start a context + * Code here similar to afu_ioctl_start_work(). + */ +int cxl_start_context(struct cxl_context *ctx, u64 wed, + struct task_struct *task) +{ + int rc = 0; + bool kernel = true; + + pr_devel("%s: pe: %i\n", __func__, ctx->pe); + + mutex_lock(&ctx->status_mutex); + if (ctx->status == STARTED) + goto out; /* already started */ + + if (task) { + ctx->pid = get_task_pid(task, PIDTYPE_PID); + get_pid(ctx->pid); + kernel = false; + } + + cxl_ctx_get(); + + if ((rc = cxl_attach_process(ctx, kernel, wed , 0))) { + put_pid(ctx->pid); + cxl_ctx_put(); + goto out; + } + + ctx->status = STARTED; + get_device(&ctx->afu->dev); +out: + mutex_unlock(&ctx->status_mutex); + return rc; +} +EXPORT_SYMBOL_GPL(cxl_start_context); + +int cxl_process_element(struct cxl_context *ctx) +{ + return ctx->pe; +} +EXPORT_SYMBOL_GPL(cxl_process_element); + +/* Stop a context. Returns 0 on success, otherwise -Errno */ +int cxl_stop_context(struct cxl_context *ctx) +{ + int rc; + + rc = __detach_context(ctx); + if (!rc) + put_device(&ctx->afu->dev); + return rc; +} +EXPORT_SYMBOL_GPL(cxl_stop_context); + +void cxl_set_master(struct cxl_context *ctx) +{ + ctx->master = true; + assign_psn_space(ctx); +} +EXPORT_SYMBOL_GPL(cxl_set_master); + +/* wrappers around afu_* file ops which are EXPORTED */ +int cxl_fd_open(struct inode *inode, struct file *file) +{ + return afu_open(inode, file); +} +EXPORT_SYMBOL_GPL(cxl_fd_open); +int cxl_fd_release(struct inode *inode, struct file *file) +{ + return afu_release(inode, file); +} +EXPORT_SYMBOL_GPL(cxl_fd_release); +long cxl_fd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + return afu_ioctl(file, cmd, arg); +} +EXPORT_SYMBOL_GPL(cxl_fd_ioctl); +int cxl_fd_mmap(struct file *file, struct vm_area_struct *vm) +{ + return afu_mmap(file, vm); +} +EXPORT_SYMBOL_GPL(cxl_fd_mmap); +unsigned int cxl_fd_poll(struct file *file, struct poll_table_struct *poll) +{ + return afu_poll(file, poll); +} +EXPORT_SYMBOL_GPL(cxl_fd_poll); +ssize_t cxl_fd_read(struct file *file, char __user *buf, size_t count, + loff_t *off) +{ + return afu_read(file, buf, count, off); +} +EXPORT_SYMBOL_GPL(cxl_fd_read); + +#define PATCH_FOPS(NAME) if (!fops->NAME) fops->NAME = afu_fops.NAME + +/* Get a struct file and fd for a context and attach the ops */ +struct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops, + int *fd) +{ + struct file *file; + int rc, flags, fdtmp; + + flags = O_RDWR | O_CLOEXEC; + + /* This code is similar to anon_inode_getfd() */ + rc = get_unused_fd_flags(flags); + if (rc < 0) + return ERR_PTR(rc); + fdtmp = rc; + + /* + * Patch the file ops. Needs to be careful that this is rentrant safe. + */ + if (fops) { + PATCH_FOPS(open); + PATCH_FOPS(poll); + PATCH_FOPS(read); + PATCH_FOPS(release); + PATCH_FOPS(unlocked_ioctl); + PATCH_FOPS(compat_ioctl); + PATCH_FOPS(mmap); + } else /* use default ops */ + fops = (struct file_operations *)&afu_fops; + + file = anon_inode_getfile("cxl", fops, ctx, flags); + if (IS_ERR(file)) + put_unused_fd(fdtmp); + *fd = fdtmp; + return file; +} +EXPORT_SYMBOL_GPL(cxl_get_fd); + +int cxl_start_work(struct cxl_context *ctx, + struct cxl_ioctl_start_work *work) +{ + int rc; + + /* code taken from afu_ioctl_start_work */ + if (!(work->flags & CXL_START_WORK_NUM_IRQS)) + work->num_interrupts = ctx->afu->pp_irqs; + else if ((work->num_interrupts < ctx->afu->pp_irqs) || + (work->num_interrupts > ctx->afu->irqs_max)) { + return -EINVAL; + } + + rc = afu_register_irqs(ctx, work->num_interrupts); + if (rc) + return rc; + + rc = cxl_start_context(ctx, work->work_element_descriptor, current); + if (rc < 0) { + afu_release_irqs(ctx, ctx); + return rc; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cxl_start_work); + +void __iomem *cxl_psa_map(struct cxl_context *ctx) +{ + struct cxl_afu *afu = ctx->afu; + int rc; + + rc = afu_check_and_enable(afu); + if (rc) + return NULL; + + pr_devel("%s: psn_phys%llx size:%llx\n", + __func__, afu->psn_phys, afu->adapter->ps_size); + return ioremap(ctx->psn_phys, ctx->psn_size); +} +EXPORT_SYMBOL_GPL(cxl_psa_map); + +void cxl_psa_unmap(void __iomem *addr) +{ + iounmap(addr); +} +EXPORT_SYMBOL_GPL(cxl_psa_unmap); + +int cxl_afu_reset(struct cxl_context *ctx) +{ + struct cxl_afu *afu = ctx->afu; + int rc; + + rc = __cxl_afu_reset(afu); + if (rc) + return rc; + + return afu_check_and_enable(afu); +} +EXPORT_SYMBOL_GPL(cxl_afu_reset); diff --git a/drivers/misc/cxl/base.c b/drivers/misc/cxl/base.c index 0654ad83675eb6..a9f0dd3255a2a0 100644 --- a/drivers/misc/cxl/base.c +++ b/drivers/misc/cxl/base.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include "cxl.h" /* protected by rcu */ diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c index d1b55fe62817dc..7d857b7d686d87 100644 --- a/drivers/misc/cxl/context.c +++ b/drivers/misc/cxl/context.c @@ -174,7 +174,7 @@ int cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma) * return until all outstanding interrupts for this context have completed. The * hardware should no longer access *ctx after this has returned. */ -static void __detach_context(struct cxl_context *ctx) +int __detach_context(struct cxl_context *ctx) { enum cxl_context_status status; @@ -183,12 +183,10 @@ static void __detach_context(struct cxl_context *ctx) ctx->status = CLOSED; mutex_unlock(&ctx->status_mutex); if (status != STARTED) - return; + return -EBUSY; WARN_ON(cxl_detach_process(ctx)); - afu_release_irqs(ctx); - flush_work(&ctx->fault_work); /* Only needed for dedicated process */ - wake_up_all(&ctx->wq); + return 0; } /* @@ -199,7 +197,15 @@ static void __detach_context(struct cxl_context *ctx) */ void cxl_context_detach(struct cxl_context *ctx) { - __detach_context(ctx); + int rc; + + rc = __detach_context(ctx); + if (rc) + return; + + afu_release_irqs(ctx, ctx); + flush_work(&ctx->fault_work); /* Only needed for dedicated process */ + wake_up_all(&ctx->wq); } /* @@ -216,7 +222,7 @@ void cxl_context_detach_all(struct cxl_afu *afu) * Anything done in here needs to be setup before the IDR is * created and torn down after the IDR removed */ - __detach_context(ctx); + cxl_context_detach(ctx); /* * We are force detaching - remove any active PSA mappings so @@ -232,12 +238,9 @@ void cxl_context_detach_all(struct cxl_afu *afu) mutex_unlock(&afu->contexts_lock); } -void cxl_context_free(struct cxl_context *ctx) +static void reclaim_ctx(struct rcu_head *rcu) { - mutex_lock(&ctx->afu->contexts_lock); - idr_remove(&ctx->afu->contexts_idr, ctx->pe); - mutex_unlock(&ctx->afu->contexts_lock); - synchronize_rcu(); + struct cxl_context *ctx = container_of(rcu, struct cxl_context, rcu); free_page((u64)ctx->sstp); ctx->sstp = NULL; @@ -245,3 +248,11 @@ void cxl_context_free(struct cxl_context *ctx) put_pid(ctx->pid); kfree(ctx); } + +void cxl_context_free(struct cxl_context *ctx) +{ + mutex_lock(&ctx->afu->contexts_lock); + idr_remove(&ctx->afu->contexts_idr, ctx->pe); + mutex_unlock(&ctx->afu->contexts_lock); + call_rcu(&ctx->rcu, reclaim_ctx); +} diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h index a1cee4767ec6c5..cedf0df39ee5b8 100644 --- a/drivers/misc/cxl/cxl.h +++ b/drivers/misc/cxl/cxl.h @@ -18,10 +18,11 @@ #include #include #include +#include #include #include #include -#include +#include #include @@ -362,6 +363,10 @@ struct cxl_afu { struct mutex spa_mutex; spinlock_t afu_cntl_lock; + /* AFU error buffer fields and bin attribute for sysfs */ + u64 eb_len, eb_offset; + struct bin_attribute attr_eb; + /* * Only the first part of the SPA is used for the process element * linked list. The only other part that software needs to worry about @@ -375,6 +380,9 @@ struct cxl_afu { int spa_max_procs; unsigned int psl_virq; + /* pointer to the vphb */ + struct pci_controller *phb; + int pp_irqs; int irqs_max; int num_procs; @@ -455,6 +463,8 @@ struct cxl_context { bool pending_irq; bool pending_fault; bool pending_afu_err; + + struct rcu_head rcu; }; struct cxl { @@ -563,6 +573,9 @@ static inline void __iomem *_cxl_p2n_addr(struct cxl_afu *afu, cxl_p2n_reg_t reg u16 cxl_afu_cr_read16(struct cxl_afu *afu, int cr, u64 off); u8 cxl_afu_cr_read8(struct cxl_afu *afu, int cr, u64 off); +ssize_t cxl_afu_read_err_buffer(struct cxl_afu *afu, char *buf, + loff_t off, size_t count); + struct cxl_calls { void (*cxl_slbia)(struct mm_struct *mm); @@ -606,7 +619,7 @@ void cxl_release_psl_err_irq(struct cxl *adapter); int cxl_register_serr_irq(struct cxl_afu *afu); void cxl_release_serr_irq(struct cxl_afu *afu); int afu_register_irqs(struct cxl_context *ctx, u32 count); -void afu_release_irqs(struct cxl_context *ctx); +void afu_release_irqs(struct cxl_context *ctx, void *cookie); irqreturn_t cxl_slice_irq_err(int irq, void *data); int cxl_debugfs_init(void); @@ -629,6 +642,10 @@ int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master, struct address_space *mapping); void cxl_context_free(struct cxl_context *ctx); int cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma); +unsigned int cxl_map_irq(struct cxl *adapter, irq_hw_number_t hwirq, + irq_handler_t handler, void *cookie, const char *name); +void cxl_unmap_irq(unsigned int virq, void *cookie); +int __detach_context(struct cxl_context *ctx); /* This matches the layout of the H_COLLECT_CA_INT_INFO retbuf */ struct cxl_irq_info { @@ -642,6 +659,7 @@ struct cxl_irq_info { u64 padding[3]; /* to match the expected retbuf size for plpar_hcall9 */ }; +void assign_psn_space(struct cxl_context *ctx); int cxl_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr); int cxl_detach_process(struct cxl_context *ctx); @@ -653,11 +671,23 @@ int cxl_check_error(struct cxl_afu *afu); int cxl_afu_slbia(struct cxl_afu *afu); int cxl_tlb_slb_invalidate(struct cxl *adapter); int cxl_afu_disable(struct cxl_afu *afu); -int cxl_afu_reset(struct cxl_afu *afu); +int __cxl_afu_reset(struct cxl_afu *afu); +int afu_check_and_enable(struct cxl_afu *afu); int cxl_psl_purge(struct cxl_afu *afu); void cxl_stop_trace(struct cxl *cxl); +int cxl_pci_vphb_add(struct cxl_afu *afu); +void cxl_pci_vphb_remove(struct cxl_afu *afu); extern struct pci_driver cxl_pci_driver; +int afu_allocate_irqs(struct cxl_context *ctx, u32 count); + +int afu_open(struct inode *inode, struct file *file); +int afu_release(struct inode *inode, struct file *file); +long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +int afu_mmap(struct file *file, struct vm_area_struct *vm); +unsigned int afu_poll(struct file *file, struct poll_table_struct *poll); +ssize_t afu_read(struct file *file, char __user *buf, size_t count, loff_t *off); +extern const struct file_operations afu_fops; #endif diff --git a/drivers/misc/cxl/fault.c b/drivers/misc/cxl/fault.c index 5286b8b704f559..25a5418c55cb89 100644 --- a/drivers/misc/cxl/fault.c +++ b/drivers/misc/cxl/fault.c @@ -172,8 +172,8 @@ void cxl_handle_fault(struct work_struct *fault_work) container_of(fault_work, struct cxl_context, fault_work); u64 dsisr = ctx->dsisr; u64 dar = ctx->dar; - struct task_struct *task; - struct mm_struct *mm; + struct task_struct *task = NULL; + struct mm_struct *mm = NULL; if (cxl_p2n_read(ctx->afu, CXL_PSL_DSISR_An) != dsisr || cxl_p2n_read(ctx->afu, CXL_PSL_DAR_An) != dar || @@ -194,17 +194,19 @@ void cxl_handle_fault(struct work_struct *fault_work) pr_devel("CXL BOTTOM HALF handling fault for afu pe: %i. " "DSISR: %#llx DAR: %#llx\n", ctx->pe, dsisr, dar); - if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) { - pr_devel("cxl_handle_fault unable to get task %i\n", - pid_nr(ctx->pid)); - cxl_ack_ae(ctx); - return; - } - if (!(mm = get_task_mm(task))) { - pr_devel("cxl_handle_fault unable to get mm %i\n", - pid_nr(ctx->pid)); - cxl_ack_ae(ctx); - goto out; + if (!ctx->kernel) { + if (!(task = get_pid_task(ctx->pid, PIDTYPE_PID))) { + pr_devel("cxl_handle_fault unable to get task %i\n", + pid_nr(ctx->pid)); + cxl_ack_ae(ctx); + return; + } + if (!(mm = get_task_mm(task))) { + pr_devel("cxl_handle_fault unable to get mm %i\n", + pid_nr(ctx->pid)); + cxl_ack_ae(ctx); + goto out; + } } if (dsisr & CXL_PSL_DSISR_An_DS) @@ -214,9 +216,11 @@ void cxl_handle_fault(struct work_struct *fault_work) else WARN(1, "cxl_handle_fault has nothing to handle\n"); - mmput(mm); + if (mm) + mmput(mm); out: - put_task_struct(task); + if (task) + put_task_struct(task); } static void cxl_prefault_one(struct cxl_context *ctx, u64 ea) diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index 2364bcadb9a94c..9a26f1eb5fce68 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -96,7 +96,8 @@ static int __afu_open(struct inode *inode, struct file *file, bool master) put_device(&adapter->dev); return rc; } -static int afu_open(struct inode *inode, struct file *file) + +int afu_open(struct inode *inode, struct file *file) { return __afu_open(inode, file, false); } @@ -106,7 +107,7 @@ static int afu_master_open(struct inode *inode, struct file *file) return __afu_open(inode, file, true); } -static int afu_release(struct inode *inode, struct file *file) +int afu_release(struct inode *inode, struct file *file) { struct cxl_context *ctx = file->private_data; @@ -191,7 +192,7 @@ static long afu_ioctl_start_work(struct cxl_context *ctx, if ((rc = cxl_attach_process(ctx, false, work.work_element_descriptor, amr))) { - afu_release_irqs(ctx); + afu_release_irqs(ctx, ctx); goto out; } @@ -212,7 +213,7 @@ static long afu_ioctl_process_element(struct cxl_context *ctx, return 0; } -static long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct cxl_context *ctx = file->private_data; @@ -229,13 +230,13 @@ static long afu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return -EINVAL; } -static long afu_compat_ioctl(struct file *file, unsigned int cmd, +long afu_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return afu_ioctl(file, cmd, arg); } -static int afu_mmap(struct file *file, struct vm_area_struct *vm) +int afu_mmap(struct file *file, struct vm_area_struct *vm) { struct cxl_context *ctx = file->private_data; @@ -246,7 +247,7 @@ static int afu_mmap(struct file *file, struct vm_area_struct *vm) return cxl_context_iomap(ctx, vm); } -static unsigned int afu_poll(struct file *file, struct poll_table_struct *poll) +unsigned int afu_poll(struct file *file, struct poll_table_struct *poll) { struct cxl_context *ctx = file->private_data; int mask = 0; @@ -278,7 +279,7 @@ static inline int ctx_event_pending(struct cxl_context *ctx) ctx->pending_afu_err || (ctx->status == CLOSED)); } -static ssize_t afu_read(struct file *file, char __user *buf, size_t count, +ssize_t afu_read(struct file *file, char __user *buf, size_t count, loff_t *off) { struct cxl_context *ctx = file->private_data; @@ -359,7 +360,11 @@ static ssize_t afu_read(struct file *file, char __user *buf, size_t count, return rc; } -static const struct file_operations afu_fops = { +/* + * Note: if this is updated, we need to update api.c to patch the new ones in + * too + */ +const struct file_operations afu_fops = { .owner = THIS_MODULE, .open = afu_open, .poll = afu_poll, @@ -370,7 +375,7 @@ static const struct file_operations afu_fops = { .mmap = afu_mmap, }; -static const struct file_operations afu_master_fops = { +const struct file_operations afu_master_fops = { .owner = THIS_MODULE, .open = afu_master_open, .poll = afu_poll, diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c index c8929c52669170..680cd263436db5 100644 --- a/drivers/misc/cxl/irq.c +++ b/drivers/misc/cxl/irq.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include "cxl.h" #include "trace.h" @@ -416,9 +416,8 @@ void afu_irq_name_free(struct cxl_context *ctx) } } -int afu_register_irqs(struct cxl_context *ctx, u32 count) +int afu_allocate_irqs(struct cxl_context *ctx, u32 count) { - irq_hw_number_t hwirq; int rc, r, i, j = 1; struct cxl_irq_name *irq_name; @@ -458,6 +457,18 @@ int afu_register_irqs(struct cxl_context *ctx, u32 count) j++; } } + return 0; + +out: + afu_irq_name_free(ctx); + return -ENOMEM; +} + +void afu_register_hwirqs(struct cxl_context *ctx) +{ + irq_hw_number_t hwirq; + struct cxl_irq_name *irq_name; + int r,i; /* We've allocated all memory now, so let's do the irq allocations */ irq_name = list_first_entry(&ctx->irq_names, struct cxl_irq_name, list); @@ -469,15 +480,21 @@ int afu_register_irqs(struct cxl_context *ctx, u32 count) irq_name = list_next_entry(irq_name, list); } } +} - return 0; +int afu_register_irqs(struct cxl_context *ctx, u32 count) +{ + int rc; -out: - afu_irq_name_free(ctx); - return -ENOMEM; -} + rc = afu_allocate_irqs(ctx, count); + if (rc) + return rc; + + afu_register_hwirqs(ctx); + return 0; + } -void afu_release_irqs(struct cxl_context *ctx) +void afu_release_irqs(struct cxl_context *ctx, void *cookie) { irq_hw_number_t hwirq; unsigned int virq; @@ -488,7 +505,7 @@ void afu_release_irqs(struct cxl_context *ctx) for (i = 0; i < ctx->irqs.range[r]; hwirq++, i++) { virq = irq_find_mapping(NULL, hwirq); if (virq) - cxl_unmap_irq(virq, ctx); + cxl_unmap_irq(virq, cookie); } } diff --git a/drivers/misc/cxl/main.c b/drivers/misc/cxl/main.c index 8ccddceead6671..833348e2c9cbc1 100644 --- a/drivers/misc/cxl/main.c +++ b/drivers/misc/cxl/main.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include "cxl.h" #include "trace.h" diff --git a/drivers/misc/cxl/native.c b/drivers/misc/cxl/native.c index 29185fc6127670..2578cebc7effef 100644 --- a/drivers/misc/cxl/native.c +++ b/drivers/misc/cxl/native.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include "cxl.h" #include "trace.h" @@ -73,7 +73,7 @@ int cxl_afu_disable(struct cxl_afu *afu) } /* This will disable as well as reset */ -int cxl_afu_reset(struct cxl_afu *afu) +int __cxl_afu_reset(struct cxl_afu *afu) { pr_devel("AFU reset request\n"); @@ -83,7 +83,7 @@ int cxl_afu_reset(struct cxl_afu *afu) false); } -static int afu_check_and_enable(struct cxl_afu *afu) +int afu_check_and_enable(struct cxl_afu *afu) { if (afu->enabled) return 0; @@ -379,7 +379,7 @@ static int remove_process_element(struct cxl_context *ctx) } -static void assign_psn_space(struct cxl_context *ctx) +void assign_psn_space(struct cxl_context *ctx) { if (!ctx->afu->pp_size || ctx->master) { ctx->psn_phys = ctx->afu->psn_phys; @@ -433,6 +433,7 @@ static int activate_afu_directed(struct cxl_afu *afu) static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr) { u64 sr; + u32 pid; int r, result; assign_psn_space(ctx); @@ -447,15 +448,19 @@ static int attach_afu_directed(struct cxl_context *ctx, u64 wed, u64 amr) sr |= CXL_PSL_SR_An_MP; if (mfspr(SPRN_LPCR) & LPCR_TC) sr |= CXL_PSL_SR_An_TC; - /* HV=0, PR=1, R=1 for userspace - * For kernel contexts: this would need to change - */ - sr |= CXL_PSL_SR_An_PR | CXL_PSL_SR_An_R; - set_endian(sr); - sr &= ~(CXL_PSL_SR_An_HV); - if (!test_tsk_thread_flag(current, TIF_32BIT)) - sr |= CXL_PSL_SR_An_SF; - ctx->elem->common.pid = cpu_to_be32(current->pid); + + if (ctx->kernel) { + sr |= CXL_PSL_SR_An_R | (mfmsr() & MSR_SF) | CXL_PSL_SR_An_HV; + pid = 0; + } else { + sr |= CXL_PSL_SR_An_PR | CXL_PSL_SR_An_R; + set_endian(sr); + sr &= ~(CXL_PSL_SR_An_HV); + if (!test_tsk_thread_flag(current, TIF_32BIT)) + sr |= CXL_PSL_SR_An_SF; + pid = current->pid; + } + ctx->elem->common.pid = cpu_to_be32(pid); ctx->elem->common.tid = 0; ctx->elem->sr = cpu_to_be64(sr); @@ -495,7 +500,7 @@ static int deactivate_afu_directed(struct cxl_afu *afu) cxl_sysfs_afu_m_remove(afu); cxl_chardev_afu_remove(afu); - cxl_afu_reset(afu); + __cxl_afu_reset(afu); cxl_afu_disable(afu); cxl_psl_purge(afu); @@ -530,7 +535,7 @@ static int activate_dedicated_process(struct cxl_afu *afu) static int attach_dedicated(struct cxl_context *ctx, u64 wed, u64 amr) { struct cxl_afu *afu = ctx->afu; - u64 sr; + u64 sr, pid; int rc; sr = 0; @@ -539,10 +544,17 @@ static int attach_dedicated(struct cxl_context *ctx, u64 wed, u64 amr) sr |= CXL_PSL_SR_An_MP; if (mfspr(SPRN_LPCR) & LPCR_TC) sr |= CXL_PSL_SR_An_TC; - sr |= CXL_PSL_SR_An_PR | CXL_PSL_SR_An_R; - if (!test_tsk_thread_flag(current, TIF_32BIT)) - sr |= CXL_PSL_SR_An_SF; - cxl_p2n_write(afu, CXL_PSL_PID_TID_An, (u64)current->pid << 32); + + if (ctx->kernel) { + sr |= CXL_PSL_SR_An_R | (mfmsr() & MSR_SF) | CXL_PSL_SR_An_HV; + pid = 0; + } else { /* User space */ + sr |= CXL_PSL_SR_An_PR | CXL_PSL_SR_An_R; + if (!test_tsk_thread_flag(current, TIF_32BIT)) + sr |= CXL_PSL_SR_An_SF; + pid = (u64)current->pid << 32; + } + cxl_p2n_write(afu, CXL_PSL_PID_TID_An, pid); cxl_p1n_write(afu, CXL_PSL_SR_An, sr); if ((rc = cxl_write_sstp(afu, ctx->sstp0, ctx->sstp1))) @@ -566,7 +578,7 @@ static int attach_dedicated(struct cxl_context *ctx, u64 wed, u64 amr) /* master only context for dedicated */ assign_psn_space(ctx); - if ((rc = cxl_afu_reset(afu))) + if ((rc = __cxl_afu_reset(afu))) return rc; cxl_p2n_write(afu, CXL_PSL_WED_An, wed); @@ -629,7 +641,7 @@ int cxl_attach_process(struct cxl_context *ctx, bool kernel, u64 wed, u64 amr) static inline int detach_process_native_dedicated(struct cxl_context *ctx) { - cxl_afu_reset(ctx->afu); + __cxl_afu_reset(ctx->afu); cxl_afu_disable(ctx->afu); cxl_psl_purge(ctx->afu); return 0; diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c index 1ef01647265f99..206a9fe85bc16b 100644 --- a/drivers/misc/cxl/pci.c +++ b/drivers/misc/cxl/pci.c @@ -90,6 +90,7 @@ /* This works a little different than the p1/p2 register accesses to make it * easier to pull out individual fields */ #define AFUD_READ(afu, off) in_be64(afu->afu_desc_mmio + off) +#define AFUD_READ_LE(afu, off) in_le64(afu->afu_desc_mmio + off) #define EXTRACT_PPC_BIT(val, bit) (!!(val & PPC_BIT(bit))) #define EXTRACT_PPC_BITS(val, bs, be) ((val & PPC_BITMASK(bs, be)) >> PPC_BITLSHIFT(be)) @@ -286,7 +287,8 @@ static void dump_cxl_config_space(struct pci_dev *dev) static void dump_afu_descriptor(struct cxl_afu *afu) { - u64 val; + u64 val, afu_cr_num, afu_cr_off, afu_cr_len; + int i; #define show_reg(name, what) \ dev_info(&afu->dev, "afu desc: %30s: %#llx\n", name, what) @@ -296,6 +298,7 @@ static void dump_afu_descriptor(struct cxl_afu *afu) show_reg("num_of_processes", AFUD_NUM_PROCS(val)); show_reg("num_of_afu_CRs", AFUD_NUM_CRS(val)); show_reg("req_prog_mode", val & 0xffffULL); + afu_cr_num = AFUD_NUM_CRS(val); val = AFUD_READ(afu, 0x8); show_reg("Reserved", val); @@ -307,8 +310,10 @@ static void dump_afu_descriptor(struct cxl_afu *afu) val = AFUD_READ_CR(afu); show_reg("Reserved", (val >> (63-7)) & 0xff); show_reg("AFU_CR_len", AFUD_CR_LEN(val)); + afu_cr_len = AFUD_CR_LEN(val) * 256; val = AFUD_READ_CR_OFF(afu); + afu_cr_off = val; show_reg("AFU_CR_offset", val); val = AFUD_READ_PPPSA(afu); @@ -325,6 +330,11 @@ static void dump_afu_descriptor(struct cxl_afu *afu) val = AFUD_READ_EB_OFF(afu); show_reg("AFU_EB_offset", val); + for (i = 0; i < afu_cr_num; i++) { + val = AFUD_READ_LE(afu, afu_cr_off + i * afu_cr_len); + show_reg("CR Vendor", val & 0xffff); + show_reg("CR Device", (val >> 16) & 0xffff); + } #undef show_reg } @@ -593,6 +603,22 @@ static int cxl_read_afu_descriptor(struct cxl_afu *afu) afu->crs_len = AFUD_CR_LEN(val) * 256; afu->crs_offset = AFUD_READ_CR_OFF(afu); + + /* eb_len is in multiple of 4K */ + afu->eb_len = AFUD_EB_LEN(AFUD_READ_EB(afu)) * 4096; + afu->eb_offset = AFUD_READ_EB_OFF(afu); + + /* eb_off is 4K aligned so lower 12 bits are always zero */ + if (EXTRACT_PPC_BITS(afu->eb_offset, 0, 11) != 0) { + dev_warn(&afu->dev, + "Invalid AFU error buffer offset %Lx\n", + afu->eb_offset); + dev_info(&afu->dev, + "Ignoring AFU error buffer in the descriptor\n"); + /* indicate that no afu buffer exists */ + afu->eb_len = 0; + } + return 0; } @@ -631,7 +657,7 @@ static int sanitise_afu_regs(struct cxl_afu *afu) reg = cxl_p2n_read(afu, CXL_AFU_Cntl_An); if ((reg & CXL_AFU_Cntl_An_ES_MASK) != CXL_AFU_Cntl_An_ES_Disabled) { dev_warn(&afu->dev, "WARNING: AFU was not disabled: %#.16llx\n", reg); - if (cxl_afu_reset(afu)) + if (__cxl_afu_reset(afu)) return -EIO; if (cxl_afu_disable(afu)) return -EIO; @@ -672,6 +698,56 @@ static int sanitise_afu_regs(struct cxl_afu *afu) return 0; } +/* + * afu_eb_read: + * Called from sysfs and reads the afu error info buffer. The h/w only supports + * 4/8 bytes aligned access. So most of the code tries to get around this by + * reading full 8 bytes aligned chunks, copying it to a temp buffer and dropping + * unneeded bytes at the beginning & the end of the requested region. + */ +ssize_t cxl_afu_read_err_buffer(struct cxl_afu *afu, char *buf, + loff_t off, size_t count) +{ + u8 tbuff[8]; + ssize_t bytes_to_copy; + const void __iomem *ebuf = afu->afu_desc_mmio + afu->eb_offset; + const size_t round_ofc = round_down(off + count, 8); + const loff_t aligned_off = ALIGN(off, 8); + + if (!afu->eb_len || (off >= afu->eb_len)) + return 0; + + count = min((size_t)(afu->eb_len - off), count); + + /* handle unaligned access at the beginning of the requested region */ + if (!IS_ALIGNED(off, 8)) { + _memcpy_fromio(tbuff, ebuf + round_down(off, 8), 8); + + bytes_to_copy = min((size_t)(aligned_off - off), count); + memcpy(buf, tbuff + (off & 0x7), bytes_to_copy); + } + + /* + * read all the intermediate aligned bytes. Will be doing aligned + * access to the iomem so can directly copy to user buffer without + * going through tbuff. + */ + bytes_to_copy = round_ofc - aligned_off; + if (bytes_to_copy > 0) + _memcpy_fromio(buf + aligned_off - off, + ebuf + aligned_off, bytes_to_copy); + + /* Read the unaligned bytes at the end */ + if (!IS_ALIGNED(off + count, 8) && (bytes_to_copy >= 0)) { + _memcpy_fromio(tbuff, ebuf + round_ofc, 8); + + bytes_to_copy = (off + count) - round_ofc; + memcpy(buf + count - bytes_to_copy, tbuff, bytes_to_copy); + } + + return count; +} + static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev) { struct cxl_afu *afu; @@ -691,7 +767,7 @@ static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev) goto err2; /* We need to reset the AFU before we can read the AFU descriptor */ - if ((rc = cxl_afu_reset(afu))) + if ((rc = __cxl_afu_reset(afu))) goto err2; if (cxl_verbose) @@ -731,6 +807,9 @@ static int cxl_init_afu(struct cxl *adapter, int slice, struct pci_dev *dev) adapter->afu[afu->slice] = afu; + if ((rc = cxl_pci_vphb_add(afu))) + dev_info(&afu->dev, "Can't register vPHB\n"); + return 0; err_put2: @@ -783,8 +862,10 @@ int cxl_reset(struct cxl *adapter) dev_info(&dev->dev, "CXL reset\n"); - for (i = 0; i < adapter->slices; i++) + for (i = 0; i < adapter->slices; i++) { + cxl_pci_vphb_remove(adapter->afu[i]); cxl_remove_afu(adapter->afu[i]); + } /* pcie_warm_reset requests a fundamental pci reset which includes a * PERST assert/deassert. PERST triggers a loading of the image @@ -857,13 +938,13 @@ static int cxl_read_vsec(struct cxl *adapter, struct pci_dev *dev) u8 image_state; if (!(vsec = find_cxl_vsec(dev))) { - dev_err(&adapter->dev, "ABORTING: CXL VSEC not found!\n"); + dev_err(&dev->dev, "ABORTING: CXL VSEC not found!\n"); return -ENODEV; } CXL_READ_VSEC_LENGTH(dev, vsec, &vseclen); if (vseclen < CXL_VSEC_MIN_SIZE) { - pr_err("ABORTING: CXL VSEC too short\n"); + dev_err(&dev->dev, "ABORTING: CXL VSEC too short\n"); return -EINVAL; } @@ -902,24 +983,24 @@ static int cxl_vsec_looks_ok(struct cxl *adapter, struct pci_dev *dev) return -EBUSY; if (adapter->vsec_status & CXL_UNSUPPORTED_FEATURES) { - dev_err(&adapter->dev, "ABORTING: CXL requires unsupported features\n"); + dev_err(&dev->dev, "ABORTING: CXL requires unsupported features\n"); return -EINVAL; } if (!adapter->slices) { /* Once we support dynamic reprogramming we can use the card if * it supports loadable AFUs */ - dev_err(&adapter->dev, "ABORTING: Device has no AFUs\n"); + dev_err(&dev->dev, "ABORTING: Device has no AFUs\n"); return -EINVAL; } if (!adapter->afu_desc_off || !adapter->afu_desc_size) { - dev_err(&adapter->dev, "ABORTING: VSEC shows no AFU descriptors\n"); + dev_err(&dev->dev, "ABORTING: VSEC shows no AFU descriptors\n"); return -EINVAL; } if (adapter->ps_size > p2_size(dev) - adapter->ps_off) { - dev_err(&adapter->dev, "ABORTING: Problem state size larger than " + dev_err(&dev->dev, "ABORTING: Problem state size larger than " "available in BAR2: 0x%llx > 0x%llx\n", adapter->ps_size, p2_size(dev) - adapter->ps_off); return -EINVAL; @@ -968,6 +1049,15 @@ static struct cxl *cxl_init_adapter(struct pci_dev *dev) if (!(adapter = cxl_alloc_adapter(dev))) return ERR_PTR(-ENOMEM); + if ((rc = cxl_read_vsec(adapter, dev))) + goto err1; + + if ((rc = cxl_vsec_looks_ok(adapter, dev))) + goto err1; + + if ((rc = setup_cxl_bars(dev))) + goto err1; + if ((rc = switch_card_to_cxl(dev))) goto err1; @@ -977,12 +1067,6 @@ static struct cxl *cxl_init_adapter(struct pci_dev *dev) if ((rc = dev_set_name(&adapter->dev, "card%i", adapter->adapter_num))) goto err2; - if ((rc = cxl_read_vsec(adapter, dev))) - goto err2; - - if ((rc = cxl_vsec_looks_ok(adapter, dev))) - goto err2; - if ((rc = cxl_update_image_control(adapter))) goto err2; @@ -1067,9 +1151,6 @@ static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id) if (cxl_verbose) dump_cxl_config_space(dev); - if ((rc = setup_cxl_bars(dev))) - return rc; - if ((rc = pci_enable_device(dev))) { dev_err(&dev->dev, "pci_enable_device failed: %i\n", rc); return rc; @@ -1092,7 +1173,8 @@ static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id) static void cxl_remove(struct pci_dev *dev) { struct cxl *adapter = pci_get_drvdata(dev); - int afu; + struct cxl_afu *afu; + int i; dev_warn(&dev->dev, "pci remove\n"); @@ -1100,8 +1182,11 @@ static void cxl_remove(struct pci_dev *dev) * Lock to prevent someone grabbing a ref through the adapter list as * we are removing it */ - for (afu = 0; afu < adapter->slices; afu++) - cxl_remove_afu(adapter->afu[afu]); + for (i = 0; i < adapter->slices; i++) { + afu = adapter->afu[i]; + cxl_pci_vphb_remove(afu); + cxl_remove_afu(afu); + } cxl_remove_adapter(adapter); } @@ -1110,4 +1195,5 @@ struct pci_driver cxl_pci_driver = { .id_table = cxl_pci_tbl, .probe = cxl_probe, .remove = cxl_remove, + .shutdown = cxl_remove, }; diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c index d0c38c7bc0c4bf..31f38bc71a3d5d 100644 --- a/drivers/misc/cxl/sysfs.c +++ b/drivers/misc/cxl/sysfs.c @@ -185,7 +185,7 @@ static ssize_t reset_store_afu(struct device *device, goto err; } - if ((rc = cxl_afu_reset(afu))) + if ((rc = __cxl_afu_reset(afu))) goto err; rc = count; @@ -356,6 +356,16 @@ static ssize_t api_version_compatible_show(struct device *device, return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION_COMPATIBLE); } +static ssize_t afu_eb_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct cxl_afu *afu = to_cxl_afu(container_of(kobj, + struct device, kobj)); + + return cxl_afu_read_err_buffer(afu, buf, off, count); +} + static struct device_attribute afu_attrs[] = { __ATTR_RO(mmio_size), __ATTR_RO(irqs_min), @@ -534,6 +544,10 @@ void cxl_sysfs_afu_remove(struct cxl_afu *afu) struct afu_config_record *cr, *tmp; int i; + /* remove the err buffer bin attribute */ + if (afu->eb_len) + device_remove_bin_file(&afu->dev, &afu->attr_eb); + for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) device_remove_file(&afu->dev, &afu_attrs[i]); @@ -555,6 +569,22 @@ int cxl_sysfs_afu_add(struct cxl_afu *afu) goto err; } + /* conditionally create the add the binary file for error info buffer */ + if (afu->eb_len) { + afu->attr_eb.attr.name = "afu_err_buff"; + afu->attr_eb.attr.mode = S_IRUGO; + afu->attr_eb.size = afu->eb_len; + afu->attr_eb.read = afu_eb_read; + + rc = device_create_bin_file(&afu->dev, &afu->attr_eb); + if (rc) { + dev_err(&afu->dev, + "Unable to create eb attr for the afu. Err(%d)\n", + rc); + goto err; + } + } + for (i = 0; i < afu->crs_num; i++) { cr = cxl_sysfs_afu_new_cr(afu, i); if (IS_ERR(cr)) { @@ -570,6 +600,9 @@ int cxl_sysfs_afu_add(struct cxl_afu *afu) cxl_sysfs_afu_remove(afu); return rc; err: + /* reset the eb_len as we havent created the bin attr */ + afu->eb_len = 0; + for (i--; i >= 0; i--) device_remove_file(&afu->dev, &afu_attrs[i]); return rc; diff --git a/drivers/misc/cxl/vphb.c b/drivers/misc/cxl/vphb.c new file mode 100644 index 00000000000000..2b84263c4c044c --- /dev/null +++ b/drivers/misc/cxl/vphb.c @@ -0,0 +1,268 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include "cxl.h" + +static int cxl_dma_set_mask(struct pci_dev *pdev, u64 dma_mask) +{ + if (dma_mask < DMA_BIT_MASK(64)) { + pr_info("%s only 64bit DMA supported on CXL", __func__); + return -EIO; + } + + *(pdev->dev.dma_mask) = dma_mask; + return 0; +} + +static int cxl_pci_probe_mode(struct pci_bus *bus) +{ + return PCI_PROBE_NORMAL; +} + +static int cxl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) +{ + return -ENODEV; +} + +static void cxl_teardown_msi_irqs(struct pci_dev *pdev) +{ + /* + * MSI should never be set but need still need to provide this call + * back. + */ +} + +static bool cxl_pci_enable_device_hook(struct pci_dev *dev) +{ + struct pci_controller *phb; + struct cxl_afu *afu; + struct cxl_context *ctx; + + phb = pci_bus_to_host(dev->bus); + afu = (struct cxl_afu *)phb->private_data; + set_dma_ops(&dev->dev, &dma_direct_ops); + set_dma_offset(&dev->dev, PAGE_OFFSET); + + /* + * Allocate a context to do cxl things too. If we eventually do real + * DMA ops, we'll need a default context to attach them to + */ + ctx = cxl_dev_context_init(dev); + if (!ctx) + return false; + dev->dev.archdata.cxl_ctx = ctx; + + return (afu_check_and_enable(afu) == 0); +} + +static void cxl_pci_release_device(struct pci_dev *dev) +{ + struct cxl_context *ctx = cxl_get_context(dev); + + if (ctx) { + if (ctx->status != CLOSED) { + dev_err(&dev->dev, "Default context not closed\n"); + return; + } + cxl_release_context(ctx); + } +} + +static resource_size_t cxl_pci_window_alignment(struct pci_bus *bus, + unsigned long type) +{ + return 1; +} + +static void cxl_pci_reset_secondary_bus(struct pci_dev *dev) +{ + /* Should we do an AFU reset here ? */ +} + +static int cxl_pcie_cfg_record(u8 bus, u8 devfn) +{ + return (bus << 8) + devfn; +} + +static unsigned long cxl_pcie_cfg_addr(struct pci_controller* phb, + u8 bus, u8 devfn, int offset) +{ + int record = cxl_pcie_cfg_record(bus, devfn); + + return (unsigned long)phb->cfg_addr + ((unsigned long)phb->cfg_data * record) + offset; +} + + +static int cxl_pcie_config_info(struct pci_bus *bus, unsigned int devfn, + int offset, int len, + volatile void __iomem **ioaddr, + u32 *mask, int *shift) +{ + struct pci_controller *phb; + struct cxl_afu *afu; + unsigned long addr; + + phb = pci_bus_to_host(bus); + afu = (struct cxl_afu *)phb->private_data; + if (phb == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + if (cxl_pcie_cfg_record(bus->number, devfn) > afu->crs_num) + return PCIBIOS_DEVICE_NOT_FOUND; + if (offset >= (unsigned long)phb->cfg_data) + return PCIBIOS_BAD_REGISTER_NUMBER; + addr = cxl_pcie_cfg_addr(phb, bus->number, devfn, offset); + + *ioaddr = (void *)(addr & ~0x3ULL); + *shift = ((addr & 0x3) * 8); + switch (len) { + case 1: + *mask = 0xff; + break; + case 2: + *mask = 0xffff; + break; + default: + *mask = 0xffffffff; + break; + } + return 0; +} + +static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 *val) +{ + volatile void __iomem *ioaddr; + int shift, rc; + u32 mask; + + rc = cxl_pcie_config_info(bus, devfn, offset, len, &ioaddr, + &mask, &shift); + if (rc) + return rc; + + /* Can only read 32 bits */ + *val = (in_le32(ioaddr) >> shift) & mask; + return PCIBIOS_SUCCESSFUL; +} + +static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 val) +{ + volatile void __iomem *ioaddr; + u32 v, mask; + int shift, rc; + + rc = cxl_pcie_config_info(bus, devfn, offset, len, &ioaddr, + &mask, &shift); + if (rc) + return rc; + + /* Can only write 32 bits so do read-modify-write */ + mask <<= shift; + val <<= shift; + + v = (in_le32(ioaddr) & ~mask) || (val & mask); + + out_le32(ioaddr, v); + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops cxl_pcie_pci_ops = +{ + .read = cxl_pcie_read_config, + .write = cxl_pcie_write_config, +}; + + +static struct pci_controller_ops cxl_pci_controller_ops = +{ + .probe_mode = cxl_pci_probe_mode, + .enable_device_hook = cxl_pci_enable_device_hook, + .release_device = cxl_pci_release_device, + .window_alignment = cxl_pci_window_alignment, + .reset_secondary_bus = cxl_pci_reset_secondary_bus, + .setup_msi_irqs = cxl_setup_msi_irqs, + .teardown_msi_irqs = cxl_teardown_msi_irqs, + .dma_set_mask = cxl_dma_set_mask, +}; + +int cxl_pci_vphb_add(struct cxl_afu *afu) +{ + struct pci_dev *phys_dev; + struct pci_controller *phb, *phys_phb; + + phys_dev = to_pci_dev(afu->adapter->dev.parent); + phys_phb = pci_bus_to_host(phys_dev->bus); + + /* Alloc and setup PHB data structure */ + phb = pcibios_alloc_controller(phys_phb->dn); + + if (!phb) + return -ENODEV; + + /* Setup parent in sysfs */ + phb->parent = &phys_dev->dev; + + /* Setup the PHB using arch provided callback */ + phb->ops = &cxl_pcie_pci_ops; + phb->cfg_addr = afu->afu_desc_mmio + afu->crs_offset; + phb->cfg_data = (void *)(u64)afu->crs_len; + phb->private_data = afu; + phb->controller_ops = cxl_pci_controller_ops; + + /* Scan the bus */ + pcibios_scan_phb(phb); + if (phb->bus == NULL) + return -ENXIO; + + /* Claim resources. This might need some rework as well depending + * whether we are doing probe-only or not, like assigning unassigned + * resources etc... + */ + pcibios_claim_one_bus(phb->bus); + + /* Add probed PCI devices to the device model */ + pci_bus_add_devices(phb->bus); + + afu->phb = phb; + + return 0; +} + + +void cxl_pci_vphb_remove(struct cxl_afu *afu) +{ + struct pci_controller *phb; + + /* If there is no configuration record we won't have one of these */ + if (!afu || !afu->phb) + return; + + phb = afu->phb; + + pci_remove_root_bus(phb->bus); +} + +struct cxl_afu *cxl_pci_to_afu(struct pci_dev *dev) +{ + struct pci_controller *phb; + + phb = pci_bus_to_host(dev->bus); + + return (struct cxl_afu *)phb->private_data; +} +EXPORT_SYMBOL_GPL(cxl_pci_to_afu); + +unsigned int cxl_pci_to_cfg_record(struct pci_dev *dev) +{ + return cxl_pcie_cfg_record(dev->bus->number, dev->devfn); +} +EXPORT_SYMBOL_GPL(cxl_pci_to_cfg_record); diff --git a/include/misc/cxl-base.h b/include/misc/cxl-base.h new file mode 100644 index 00000000000000..5ae962512fb8e7 --- /dev/null +++ b/include/misc/cxl-base.h @@ -0,0 +1,48 @@ +/* + * Copyright 2014 IBM Corp. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _MISC_CXL_BASE_H +#define _MISC_CXL_BASE_H + +#ifdef CONFIG_CXL_BASE + +#define CXL_IRQ_RANGES 4 + +struct cxl_irq_ranges { + irq_hw_number_t offset[CXL_IRQ_RANGES]; + irq_hw_number_t range[CXL_IRQ_RANGES]; +}; + +extern atomic_t cxl_use_count; + +static inline bool cxl_ctx_in_use(void) +{ + return (atomic_read(&cxl_use_count) != 0); +} + +static inline void cxl_ctx_get(void) +{ + atomic_inc(&cxl_use_count); +} + +static inline void cxl_ctx_put(void) +{ + atomic_dec(&cxl_use_count); +} + +void cxl_slbia(struct mm_struct *mm); + +#else /* CONFIG_CXL_BASE */ + +static inline bool cxl_ctx_in_use(void) { return false; } +static inline void cxl_slbia(struct mm_struct *mm) {} + +#endif /* CONFIG_CXL_BASE */ + +#endif diff --git a/include/misc/cxl.h b/include/misc/cxl.h index 975cc7861f184a..5e84390540a3ba 100644 --- a/include/misc/cxl.h +++ b/include/misc/cxl.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 IBM Corp. + * Copyright 2015 IBM Corp. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -10,39 +10,192 @@ #ifndef _MISC_CXL_H #define _MISC_CXL_H -#ifdef CONFIG_CXL_BASE +#include +#include +#include +#include -#define CXL_IRQ_RANGES 4 +/* + * This documents the in kernel API for driver to use CXL. It allows kernel + * drivers to bind to AFUs using an AFU configuration record exposed as a PCI + * configuration record. + * + * This API enables control over AFU and contexts which can't be part of the + * generic PCI API. This API is agnostic to the actual AFU. + */ + +/* Get the AFU associated with a pci_dev */ +struct cxl_afu *cxl_pci_to_afu(struct pci_dev *dev); + +/* Get the AFU conf record number associated with a pci_dev */ +unsigned int cxl_pci_to_cfg_record(struct pci_dev *dev); + +/* Get the physical device (ie. the PCIe card) which the AFU is attached */ +struct device *cxl_get_phys_dev(struct pci_dev *dev); + + +/* + * Context lifetime overview: + * + * An AFU context may be inited and then started and stoppped multiple times + * before it's released. ie. + * - cxl_dev_context_init() + * - cxl_start_context() + * - cxl_stop_context() + * - cxl_start_context() + * - cxl_stop_context() + * ...repeat... + * - cxl_release_context() + * Once released, a context can't be started again. + * + * One context is inited by the cxl driver for every pci_dev. This is to be + * used as a default kernel context. cxl_get_context() will get this + * context. This context will be released by PCI hot unplug, so doesn't need to + * be released explicitly by drivers. + * + * Additional kernel contexts may be inited using cxl_dev_context_init(). + * These must be released using cxl_context_detach(). + * + * Once a context has been inited, IRQs may be configured. Firstly these IRQs + * must be allocated (cxl_allocate_afu_irqs()), then individually mapped to + * specific handlers (cxl_map_afu_irq()). + * + * These IRQs can be unmapped (cxl_unmap_afu_irq()) and finally released + * (cxl_free_afu_irqs()). + * + * The AFU can be reset (cxl_afu_reset()). This will cause the PSL/AFU + * hardware to lose track of all contexts. It's upto the caller of + * cxl_afu_reset() to restart these contexts. + */ + +/* + * On pci_enabled_device(), the cxl driver will init a single cxl context for + * use by the driver. It doesn't start this context (as that will likely + * generate DMA traffic for most AFUs). + * + * This gets the default context associated with this pci_dev. This context + * doesn't need to be released as this will be done by the PCI subsystem on hot + * unplug. + */ +struct cxl_context *cxl_get_context(struct pci_dev *dev); +/* + * Allocate and initalise a context associated with a AFU PCI device. This + * doesn't start the context in the AFU. + */ +struct cxl_context *cxl_dev_context_init(struct pci_dev *dev); +/* + * Release and free a context. Context should be stopped before calling. + */ +int cxl_release_context(struct cxl_context *ctx); -struct cxl_irq_ranges { - irq_hw_number_t offset[CXL_IRQ_RANGES]; - irq_hw_number_t range[CXL_IRQ_RANGES]; -}; +/* + * Allocate AFU interrupts for this context. num=0 will allocate the default + * for this AFU as given in the AFU descriptor. This number doesn't include the + * interrupt 0 (CAIA defines AFU IRQ 0 for page faults). Each interrupt to be + * used must map a handler with cxl_map_afu_irq. + */ +int cxl_allocate_afu_irqs(struct cxl_context *cxl, int num); +/* Free allocated interrupts */ +void cxl_free_afu_irqs(struct cxl_context *cxl); + +/* + * Map a handler for an AFU interrupt associated with a particular context. AFU + * IRQS numbers start from 1 (CAIA defines AFU IRQ 0 for page faults). cookie + * is private data is that will be provided to the interrupt handler. + */ +int cxl_map_afu_irq(struct cxl_context *cxl, int num, + irq_handler_t handler, void *cookie, char *name); +/* unmap mapped IRQ handlers */ +void cxl_unmap_afu_irq(struct cxl_context *cxl, int num, void *cookie); -extern atomic_t cxl_use_count; +/* + * Start work on the AFU. This starts an cxl context and associates it with a + * task. task == NULL will make it a kernel context. + */ +int cxl_start_context(struct cxl_context *ctx, u64 wed, + struct task_struct *task); +/* + * Stop a context and remove it from the PSL + */ +int cxl_stop_context(struct cxl_context *ctx); -static inline bool cxl_ctx_in_use(void) -{ - return (atomic_read(&cxl_use_count) != 0); -} +/* Reset the AFU */ +int cxl_afu_reset(struct cxl_context *ctx); -static inline void cxl_ctx_get(void) -{ - atomic_inc(&cxl_use_count); -} +/* + * Set a context as a master context. + * This sets the default problem space area mapped as the full space, rather + * than just the per context area (for slaves). + */ +void cxl_set_master(struct cxl_context *ctx); -static inline void cxl_ctx_put(void) -{ - atomic_dec(&cxl_use_count); -} +/* + * Map and unmap the AFU Problem Space area. The amount and location mapped + * depends on if this context is a master or slave. + */ +void __iomem *cxl_psa_map(struct cxl_context *ctx); +void cxl_psa_unmap(void __iomem *addr); -void cxl_slbia(struct mm_struct *mm); +/* Get the process element for this context */ +int cxl_process_element(struct cxl_context *ctx); -#else /* CONFIG_CXL_BASE */ -static inline bool cxl_ctx_in_use(void) { return false; } -static inline void cxl_slbia(struct mm_struct *mm) {} +/* + * These calls allow drivers to create their own file descriptors and make them + * identical to the cxl file descriptor user API. An example use case: + * + * struct file_operations cxl_my_fops = {}; + * ...... + * // Init the context + * ctx = cxl_dev_context_init(dev); + * if (IS_ERR(ctx)) + * return PTR_ERR(ctx); + * // Create and attach a new file descriptor to my file ops + * file = cxl_get_fd(ctx, &cxl_my_fops, &fd); + * // Start context + * rc = cxl_start_work(ctx, &work.work); + * if (rc) { + * fput(file); + * put_unused_fd(fd); + * return -ENODEV; + * } + * // No error paths after installing the fd + * fd_install(fd, file); + * return fd; + * + * This inits a context, and gets a file descriptor and associates some file + * ops to that file descriptor. If the file ops are blank, the cxl driver will + * fill them in with the default ones that mimic the standard user API. Once + * completed, the file descriptor can be installed. Once the file descriptor is + * installed, it's visible to the user so no errors must occur past this point. + * + * If cxl_fd_release() file op call is installed, the context will be stopped + * and released when the fd is released. Hence the driver won't need to manage + * this itself. + */ -#endif /* CONFIG_CXL_BASE */ +/* + * Take a context and associate it with my file ops. Returns the associated + * file and file descriptor. Any file ops which are blank are filled in by the + * cxl driver with the default ops to mimic the standard API. + */ +struct file *cxl_get_fd(struct cxl_context *ctx, struct file_operations *fops, + int *fd); +/* + * Start a context associated a struct cxl_ioctl_start_work used by the + * standard cxl user API. + */ +int cxl_start_work(struct cxl_context *ctx, + struct cxl_ioctl_start_work *work); +/* + * Export all the existing fops so drivers can use them + */ +int cxl_fd_open(struct inode *inode, struct file *file); +int cxl_fd_release(struct inode *inode, struct file *file); +long cxl_fd_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +int cxl_fd_mmap(struct file *file, struct vm_area_struct *vm); +unsigned int cxl_fd_poll(struct file *file, struct poll_table_struct *poll); +ssize_t cxl_fd_read(struct file *file, char __user *buf, size_t count, + loff_t *off); -#endif +#endif /* _MISC_CXL_H */