Skip to content

Commit

Permalink
ocxl: control via sysfs whether the FPGA is reloaded on a link reset
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Frederic Barrat <[email protected]>
Reviewed-by: Andrew Donnellan <[email protected]>
Signed-off-by: Michael Ellerman <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
  • Loading branch information
philippe56 authored and mpe committed Jul 15, 2020
1 parent acccc98 commit 87db757
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 5 deletions.
11 changes: 11 additions & 0 deletions Documentation/ABI/testing/sysfs-class-ocxl
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,14 @@ Date: January 2018
Contact: [email protected]
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: [email protected]
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
81 changes: 76 additions & 5 deletions drivers/misc/ocxl/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down
6 changes: 6 additions & 0 deletions drivers/misc/ocxl/ocxl_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,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.
*
Expand Down
35 changes: 35 additions & 0 deletions drivers/misc/ocxl/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions include/misc/ocxl-config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */

0 comments on commit 87db757

Please sign in to comment.