mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-29 09:12:07 +00:00
ocxl: control via sysfs whether the FPGA is reloaded on a link reset
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
This commit is contained in:
parent
acccc984c1
commit
87db7579eb
@ -33,3 +33,14 @@ Date: January 2018
|
|||||||
Contact: linuxppc-dev@lists.ozlabs.org
|
Contact: linuxppc-dev@lists.ozlabs.org
|
||||||
Description: read/write
|
Description: read/write
|
||||||
Give access the global mmio area for the AFU
|
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
|
||||||
|
@ -71,6 +71,20 @@ static int find_dvsec_afu_ctrl(struct pci_dev *dev, u8 afu_idx)
|
|||||||
return 0;
|
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)
|
static void read_pasid(struct pci_dev *dev, struct ocxl_fn_config *fn)
|
||||||
{
|
{
|
||||||
u16 val;
|
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)
|
static int read_dvsec_vendor(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
int pos;
|
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
|
* It's only used on function 0 to specify the version of some
|
||||||
* version of some logic blocks. Some older images may not
|
* logic blocks and to give access to special registers to
|
||||||
* even have it so we ignore any errors
|
* enable host-based flashing.
|
||||||
*/
|
*/
|
||||||
if (PCI_FUNC(dev->devfn) != 0)
|
if (PCI_FUNC(dev->devfn) != 0)
|
||||||
return 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_CFG_VERS, &cfg);
|
||||||
pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_TLX_VERS, &tlx);
|
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_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, "Vendor specific DVSEC:\n");
|
||||||
dev_dbg(&dev->dev, " CFG version = 0x%x\n", cfg);
|
dev_dbg(&dev->dev, " CFG version = 0x%x\n", cfg);
|
||||||
dev_dbg(&dev->dev, " TLX version = 0x%x\n", tlx);
|
dev_dbg(&dev->dev, " TLX version = 0x%x\n", tlx);
|
||||||
dev_dbg(&dev->dev, " DLX version = 0x%x\n", dlx);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
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.
|
* Check if an AFU index is valid for the given function.
|
||||||
*
|
*
|
||||||
|
@ -51,11 +51,46 @@ static ssize_t contexts_show(struct device *device,
|
|||||||
afu->pasid_count, afu->pasid_max);
|
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[] = {
|
static struct device_attribute afu_attrs[] = {
|
||||||
__ATTR_RO(global_mmio_size),
|
__ATTR_RO(global_mmio_size),
|
||||||
__ATTR_RO(pp_mmio_size),
|
__ATTR_RO(pp_mmio_size),
|
||||||
__ATTR_RO(afu_version),
|
__ATTR_RO(afu_version),
|
||||||
__ATTR_RO(contexts),
|
__ATTR_RO(contexts),
|
||||||
|
__ATTR_RW(reload_on_reset),
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t global_mmio_read(struct file *filp, struct kobject *kobj,
|
static ssize_t global_mmio_read(struct file *filp, struct kobject *kobj,
|
||||||
|
@ -41,5 +41,6 @@
|
|||||||
#define OCXL_DVSEC_VENDOR_CFG_VERS 0x0C
|
#define OCXL_DVSEC_VENDOR_CFG_VERS 0x0C
|
||||||
#define OCXL_DVSEC_VENDOR_TLX_VERS 0x10
|
#define OCXL_DVSEC_VENDOR_TLX_VERS 0x10
|
||||||
#define OCXL_DVSEC_VENDOR_DLX_VERS 0x20
|
#define OCXL_DVSEC_VENDOR_DLX_VERS 0x20
|
||||||
|
#define OCXL_DVSEC_VENDOR_RESET_RELOAD 0x38
|
||||||
|
|
||||||
#endif /* _OCXL_CONFIG_H_ */
|
#endif /* _OCXL_CONFIG_H_ */
|
||||||
|
Loading…
Reference in New Issue
Block a user