forked from mirrors/linux
		
	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
					
				
					 5 changed files with 129 additions and 5 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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_ */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue