ocxl: control via sysfs whether the FPGA is reloaded on a link reset
authorPhilippe Bergheaud <felix@linux.ibm.com>
Fri, 19 Jun 2020 14:04:39 +0000 (16:04 +0200)
committerMichael Ellerman <mpe@ellerman.id.au>
Wed, 15 Jul 2020 01:07:19 +0000 (11:07 +1000)
Some opencapi FPGA images allow to control if the FPGA should be reloaded
on the next adapter reset. If it is supported, the image specifies it
through a Vendor Specific DVSEC in the config space of function 0.

Signed-off-by: Philippe Bergheaud <felix@linux.ibm.com>
Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com>
Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200619140439.153962-1-fbarrat@linux.ibm.com
Documentation/ABI/testing/sysfs-class-ocxl
drivers/misc/ocxl/config.c
drivers/misc/ocxl/ocxl_internal.h
drivers/misc/ocxl/sysfs.c
include/misc/ocxl-config.h

index b5b1fa1..ae1276e 100644 (file)
@@ -33,3 +33,14 @@ Date:                January 2018
 Contact:       linuxppc-dev@lists.ozlabs.org
 Description:   read/write
                Give access the global mmio area for the AFU
+
+What:          /sys/class/ocxl/<afu name>/reload_on_reset
+Date:          February 2020
+Contact:       linuxppc-dev@lists.ozlabs.org
+Description:   read/write
+               Control whether the FPGA is reloaded on a link reset. Enabled
+               through a vendor-specific logic block on the FPGA.
+                       0       Do not reload FPGA image from flash
+                       1       Reload FPGA image from flash
+                       unavailable
+                               The device does not support this capability
index c8e19bf..42f7a12 100644 (file)
@@ -71,6 +71,20 @@ static int find_dvsec_afu_ctrl(struct pci_dev *dev, u8 afu_idx)
        return 0;
 }
 
+/**
+ * get_function_0() - Find a related PCI device (function 0)
+ * @device: PCI device to match
+ *
+ * Returns a pointer to the related device, or null if not found
+ */
+static struct pci_dev *get_function_0(struct pci_dev *dev)
+{
+       unsigned int devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0);
+
+       return pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
+                                          dev->bus->number, devfn);
+}
+
 static void read_pasid(struct pci_dev *dev, struct ocxl_fn_config *fn)
 {
        u16 val;
@@ -159,14 +173,15 @@ static int read_dvsec_afu_info(struct pci_dev *dev, struct ocxl_fn_config *fn)
 static int read_dvsec_vendor(struct pci_dev *dev)
 {
        int pos;
-       u32 cfg, tlx, dlx;
+       u32 cfg, tlx, dlx, reset_reload;
 
        /*
-        * vendor specific DVSEC is optional
+        * vendor specific DVSEC, for IBM images only. Some older
+        * images may not have it
         *
-        * It's currently only used on function 0 to specify the
-        * version of some logic blocks. Some older images may not
-        * even have it so we ignore any errors
+        * It's only used on function 0 to specify the version of some
+        * logic blocks and to give access to special registers to
+        * enable host-based flashing.
         */
        if (PCI_FUNC(dev->devfn) != 0)
                return 0;
@@ -178,11 +193,67 @@ static int read_dvsec_vendor(struct pci_dev *dev)
        pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_CFG_VERS, &cfg);
        pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_TLX_VERS, &tlx);
        pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_DLX_VERS, &dlx);
+       pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD,
+                             &reset_reload);
 
        dev_dbg(&dev->dev, "Vendor specific DVSEC:\n");
        dev_dbg(&dev->dev, "  CFG version = 0x%x\n", cfg);
        dev_dbg(&dev->dev, "  TLX version = 0x%x\n", tlx);
        dev_dbg(&dev->dev, "  DLX version = 0x%x\n", dlx);
+       dev_dbg(&dev->dev, "  ResetReload = 0x%x\n", reset_reload);
+       return 0;
+}
+
+static int get_dvsec_vendor0(struct pci_dev *dev, struct pci_dev **dev0,
+                            int *out_pos)
+{
+       int pos;
+
+       if (PCI_FUNC(dev->devfn) != 0) {
+               dev = get_function_0(dev);
+               if (!dev)
+                       return -1;
+       }
+       pos = find_dvsec(dev, OCXL_DVSEC_VENDOR_ID);
+       if (!pos)
+               return -1;
+       *dev0 = dev;
+       *out_pos = pos;
+       return 0;
+}
+
+int ocxl_config_get_reset_reload(struct pci_dev *dev, int *val)
+{
+       struct pci_dev *dev0;
+       u32 reset_reload;
+       int pos;
+
+       if (get_dvsec_vendor0(dev, &dev0, &pos))
+               return -1;
+
+       pci_read_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD,
+                             &reset_reload);
+       *val = !!(reset_reload & BIT(0));
+       return 0;
+}
+
+int ocxl_config_set_reset_reload(struct pci_dev *dev, int val)
+{
+       struct pci_dev *dev0;
+       u32 reset_reload;
+       int pos;
+
+       if (get_dvsec_vendor0(dev, &dev0, &pos))
+               return -1;
+
+       pci_read_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD,
+                             &reset_reload);
+       if (val)
+               reset_reload |= BIT(0);
+       else
+               reset_reload &= ~BIT(0);
+       pci_write_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD,
+                              reset_reload);
        return 0;
 }
 
index 345bf84..af9a84a 100644 (file)
@@ -113,6 +113,12 @@ void ocxl_actag_afu_free(struct ocxl_fn *fn, u32 start, u32 size);
 int ocxl_config_get_pasid_info(struct pci_dev *dev, int *count);
 
 /*
+ * Control whether the FPGA is reloaded on a link reset
+ */
+int ocxl_config_get_reset_reload(struct pci_dev *dev, int *val);
+int ocxl_config_set_reset_reload(struct pci_dev *dev, int val);
+
+/*
  * Check if an AFU index is valid for the given function.
  *
  * AFU indexes can be sparse, so a driver should check all indexes up
index 58f1ba2..25c78df 100644 (file)
@@ -51,11 +51,46 @@ static ssize_t contexts_show(struct device *device,
                        afu->pasid_count, afu->pasid_max);
 }
 
+static ssize_t reload_on_reset_show(struct device *device,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       struct ocxl_afu *afu = to_afu(device);
+       struct ocxl_fn *fn = afu->fn;
+       struct pci_dev *pci_dev = to_pci_dev(fn->dev.parent);
+       int val;
+
+       if (ocxl_config_get_reset_reload(pci_dev, &val))
+               return scnprintf(buf, PAGE_SIZE, "unavailable\n");
+
+       return scnprintf(buf, PAGE_SIZE, "%d\n", val);
+}
+
+static ssize_t reload_on_reset_store(struct device *device,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct ocxl_afu *afu = to_afu(device);
+       struct ocxl_fn *fn = afu->fn;
+       struct pci_dev *pci_dev = to_pci_dev(fn->dev.parent);
+       int rc, val;
+
+       rc = kstrtoint(buf, 0, &val);
+       if (rc || (val != 0 && val != 1))
+               return -EINVAL;
+
+       if (ocxl_config_set_reset_reload(pci_dev, val))
+               return -ENODEV;
+
+       return count;
+}
+
 static struct device_attribute afu_attrs[] = {
        __ATTR_RO(global_mmio_size),
        __ATTR_RO(pp_mmio_size),
        __ATTR_RO(afu_version),
        __ATTR_RO(contexts),
+       __ATTR_RW(reload_on_reset),
 };
 
 static ssize_t global_mmio_read(struct file *filp, struct kobject *kobj,
index 3526fa9..ccfd3b4 100644 (file)
@@ -41,5 +41,6 @@
 #define   OCXL_DVSEC_VENDOR_CFG_VERS            0x0C
 #define   OCXL_DVSEC_VENDOR_TLX_VERS            0x10
 #define   OCXL_DVSEC_VENDOR_DLX_VERS            0x20
+#define   OCXL_DVSEC_VENDOR_RESET_RELOAD        0x38
 
 #endif /* _OCXL_CONFIG_H_ */