diff options
Diffstat (limited to 'drivers/fpga/dfl-afu-main.c')
| -rw-r--r-- | drivers/fpga/dfl-afu-main.c | 381 | 
1 files changed, 338 insertions, 43 deletions
diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c index 02baa6a227c0..e4a34dc7947f 100644 --- a/drivers/fpga/dfl-afu-main.c +++ b/drivers/fpga/dfl-afu-main.c @@ -22,14 +22,17 @@  #include "dfl-afu.h"  /** - * port_enable - enable a port + * __afu_port_enable - enable a port by clear reset   * @pdev: port platform device.   *   * Enable Port by clear the port soft reset bit, which is set by default.   * The AFU is unable to respond to any MMIO access while in reset. - * port_enable function should only be used after port_disable function. + * __afu_port_enable function should only be used after __afu_port_disable + * function. + * + * The caller needs to hold lock for protection.   */ -static void port_enable(struct platform_device *pdev) +void __afu_port_enable(struct platform_device *pdev)  {  	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);  	void __iomem *base; @@ -52,13 +55,14 @@ static void port_enable(struct platform_device *pdev)  #define RST_POLL_TIMEOUT 1000 /* us */  /** - * port_disable - disable a port + * __afu_port_disable - disable a port by hold reset   * @pdev: port platform device.   * - * Disable Port by setting the port soft reset bit, it puts the port into - * reset. + * Disable Port by setting the port soft reset bit, it puts the port into reset. + * + * The caller needs to hold lock for protection.   */ -static int port_disable(struct platform_device *pdev) +int __afu_port_disable(struct platform_device *pdev)  {  	struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);  	void __iomem *base; @@ -104,9 +108,9 @@ static int __port_reset(struct platform_device *pdev)  {  	int ret; -	ret = port_disable(pdev); +	ret = __afu_port_disable(pdev);  	if (!ret) -		port_enable(pdev); +		__afu_port_enable(pdev);  	return ret;  } @@ -141,27 +145,267 @@ id_show(struct device *dev, struct device_attribute *attr, char *buf)  }  static DEVICE_ATTR_RO(id); -static const struct attribute *port_hdr_attrs[] = { +static ssize_t +ltr_show(struct device *dev, struct device_attribute *attr, char *buf) +{ +	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); +	void __iomem *base; +	u64 v; + +	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + +	mutex_lock(&pdata->lock); +	v = readq(base + PORT_HDR_CTRL); +	mutex_unlock(&pdata->lock); + +	return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_CTRL_LATENCY, v)); +} + +static ssize_t +ltr_store(struct device *dev, struct device_attribute *attr, +	  const char *buf, size_t count) +{ +	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); +	void __iomem *base; +	bool ltr; +	u64 v; + +	if (kstrtobool(buf, <r)) +		return -EINVAL; + +	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + +	mutex_lock(&pdata->lock); +	v = readq(base + PORT_HDR_CTRL); +	v &= ~PORT_CTRL_LATENCY; +	v |= FIELD_PREP(PORT_CTRL_LATENCY, ltr ? 1 : 0); +	writeq(v, base + PORT_HDR_CTRL); +	mutex_unlock(&pdata->lock); + +	return count; +} +static DEVICE_ATTR_RW(ltr); + +static ssize_t +ap1_event_show(struct device *dev, struct device_attribute *attr, char *buf) +{ +	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); +	void __iomem *base; +	u64 v; + +	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + +	mutex_lock(&pdata->lock); +	v = readq(base + PORT_HDR_STS); +	mutex_unlock(&pdata->lock); + +	return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP1_EVT, v)); +} + +static ssize_t +ap1_event_store(struct device *dev, struct device_attribute *attr, +		const char *buf, size_t count) +{ +	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); +	void __iomem *base; +	bool clear; + +	if (kstrtobool(buf, &clear) || !clear) +		return -EINVAL; + +	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + +	mutex_lock(&pdata->lock); +	writeq(PORT_STS_AP1_EVT, base + PORT_HDR_STS); +	mutex_unlock(&pdata->lock); + +	return count; +} +static DEVICE_ATTR_RW(ap1_event); + +static ssize_t +ap2_event_show(struct device *dev, struct device_attribute *attr, +	       char *buf) +{ +	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); +	void __iomem *base; +	u64 v; + +	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + +	mutex_lock(&pdata->lock); +	v = readq(base + PORT_HDR_STS); +	mutex_unlock(&pdata->lock); + +	return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP2_EVT, v)); +} + +static ssize_t +ap2_event_store(struct device *dev, struct device_attribute *attr, +		const char *buf, size_t count) +{ +	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); +	void __iomem *base; +	bool clear; + +	if (kstrtobool(buf, &clear) || !clear) +		return -EINVAL; + +	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + +	mutex_lock(&pdata->lock); +	writeq(PORT_STS_AP2_EVT, base + PORT_HDR_STS); +	mutex_unlock(&pdata->lock); + +	return count; +} +static DEVICE_ATTR_RW(ap2_event); + +static ssize_t +power_state_show(struct device *dev, struct device_attribute *attr, char *buf) +{ +	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); +	void __iomem *base; +	u64 v; + +	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + +	mutex_lock(&pdata->lock); +	v = readq(base + PORT_HDR_STS); +	mutex_unlock(&pdata->lock); + +	return sprintf(buf, "0x%x\n", (u8)FIELD_GET(PORT_STS_PWR_STATE, v)); +} +static DEVICE_ATTR_RO(power_state); + +static ssize_t +userclk_freqcmd_store(struct device *dev, struct device_attribute *attr, +		      const char *buf, size_t count) +{ +	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); +	u64 userclk_freq_cmd; +	void __iomem *base; + +	if (kstrtou64(buf, 0, &userclk_freq_cmd)) +		return -EINVAL; + +	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + +	mutex_lock(&pdata->lock); +	writeq(userclk_freq_cmd, base + PORT_HDR_USRCLK_CMD0); +	mutex_unlock(&pdata->lock); + +	return count; +} +static DEVICE_ATTR_WO(userclk_freqcmd); + +static ssize_t +userclk_freqcntrcmd_store(struct device *dev, struct device_attribute *attr, +			  const char *buf, size_t count) +{ +	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); +	u64 userclk_freqcntr_cmd; +	void __iomem *base; + +	if (kstrtou64(buf, 0, &userclk_freqcntr_cmd)) +		return -EINVAL; + +	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + +	mutex_lock(&pdata->lock); +	writeq(userclk_freqcntr_cmd, base + PORT_HDR_USRCLK_CMD1); +	mutex_unlock(&pdata->lock); + +	return count; +} +static DEVICE_ATTR_WO(userclk_freqcntrcmd); + +static ssize_t +userclk_freqsts_show(struct device *dev, struct device_attribute *attr, +		     char *buf) +{ +	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); +	u64 userclk_freqsts; +	void __iomem *base; + +	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + +	mutex_lock(&pdata->lock); +	userclk_freqsts = readq(base + PORT_HDR_USRCLK_STS0); +	mutex_unlock(&pdata->lock); + +	return sprintf(buf, "0x%llx\n", (unsigned long long)userclk_freqsts); +} +static DEVICE_ATTR_RO(userclk_freqsts); + +static ssize_t +userclk_freqcntrsts_show(struct device *dev, struct device_attribute *attr, +			 char *buf) +{ +	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); +	u64 userclk_freqcntrsts; +	void __iomem *base; + +	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + +	mutex_lock(&pdata->lock); +	userclk_freqcntrsts = readq(base + PORT_HDR_USRCLK_STS1); +	mutex_unlock(&pdata->lock); + +	return sprintf(buf, "0x%llx\n", +		       (unsigned long long)userclk_freqcntrsts); +} +static DEVICE_ATTR_RO(userclk_freqcntrsts); + +static struct attribute *port_hdr_attrs[] = {  	&dev_attr_id.attr, +	&dev_attr_ltr.attr, +	&dev_attr_ap1_event.attr, +	&dev_attr_ap2_event.attr, +	&dev_attr_power_state.attr, +	&dev_attr_userclk_freqcmd.attr, +	&dev_attr_userclk_freqcntrcmd.attr, +	&dev_attr_userclk_freqsts.attr, +	&dev_attr_userclk_freqcntrsts.attr,  	NULL,  }; -static int port_hdr_init(struct platform_device *pdev, -			 struct dfl_feature *feature) +static umode_t port_hdr_attrs_visible(struct kobject *kobj, +				      struct attribute *attr, int n)  { -	dev_dbg(&pdev->dev, "PORT HDR Init.\n"); +	struct device *dev = kobj_to_dev(kobj); +	umode_t mode = attr->mode; +	void __iomem *base; -	port_reset(pdev); +	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); + +	if (dfl_feature_revision(base) > 0) { +		/* +		 * userclk sysfs interfaces are only visible in case port +		 * revision is 0, as hardware with revision >0 doesn't +		 * support this. +		 */ +		if (attr == &dev_attr_userclk_freqcmd.attr || +		    attr == &dev_attr_userclk_freqcntrcmd.attr || +		    attr == &dev_attr_userclk_freqsts.attr || +		    attr == &dev_attr_userclk_freqcntrsts.attr) +			mode = 0; +	} -	return sysfs_create_files(&pdev->dev.kobj, port_hdr_attrs); +	return mode;  } -static void port_hdr_uinit(struct platform_device *pdev, -			   struct dfl_feature *feature) +static const struct attribute_group port_hdr_group = { +	.attrs      = port_hdr_attrs, +	.is_visible = port_hdr_attrs_visible, +}; + +static int port_hdr_init(struct platform_device *pdev, +			 struct dfl_feature *feature)  { -	dev_dbg(&pdev->dev, "PORT HDR UInit.\n"); +	port_reset(pdev); -	sysfs_remove_files(&pdev->dev.kobj, port_hdr_attrs); +	return 0;  }  static long @@ -185,9 +429,13 @@ port_hdr_ioctl(struct platform_device *pdev, struct dfl_feature *feature,  	return ret;  } +static const struct dfl_feature_id port_hdr_id_table[] = { +	{.id = PORT_FEATURE_ID_HEADER,}, +	{0,} +}; +  static const struct dfl_feature_ops port_hdr_ops = {  	.init = port_hdr_init, -	.uinit = port_hdr_uinit,  	.ioctl = port_hdr_ioctl,  }; @@ -214,52 +462,91 @@ afu_id_show(struct device *dev, struct device_attribute *attr, char *buf)  }  static DEVICE_ATTR_RO(afu_id); -static const struct attribute *port_afu_attrs[] = { +static struct attribute *port_afu_attrs[] = {  	&dev_attr_afu_id.attr,  	NULL  }; +static umode_t port_afu_attrs_visible(struct kobject *kobj, +				      struct attribute *attr, int n) +{ +	struct device *dev = kobj_to_dev(kobj); + +	/* +	 * sysfs entries are visible only if related private feature is +	 * enumerated. +	 */ +	if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_AFU)) +		return 0; + +	return attr->mode; +} + +static const struct attribute_group port_afu_group = { +	.attrs      = port_afu_attrs, +	.is_visible = port_afu_attrs_visible, +}; +  static int port_afu_init(struct platform_device *pdev,  			 struct dfl_feature *feature)  {  	struct resource *res = &pdev->resource[feature->resource_index]; -	int ret; -	dev_dbg(&pdev->dev, "PORT AFU Init.\n"); +	return afu_mmio_region_add(dev_get_platdata(&pdev->dev), +				   DFL_PORT_REGION_INDEX_AFU, +				   resource_size(res), res->start, +				   DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ | +				   DFL_PORT_REGION_WRITE); +} -	ret = afu_mmio_region_add(dev_get_platdata(&pdev->dev), -				  DFL_PORT_REGION_INDEX_AFU, resource_size(res), -				  res->start, DFL_PORT_REGION_READ | -				  DFL_PORT_REGION_WRITE | DFL_PORT_REGION_MMAP); -	if (ret) -		return ret; +static const struct dfl_feature_id port_afu_id_table[] = { +	{.id = PORT_FEATURE_ID_AFU,}, +	{0,} +}; -	return sysfs_create_files(&pdev->dev.kobj, port_afu_attrs); -} +static const struct dfl_feature_ops port_afu_ops = { +	.init = port_afu_init, +}; -static void port_afu_uinit(struct platform_device *pdev, -			   struct dfl_feature *feature) +static int port_stp_init(struct platform_device *pdev, +			 struct dfl_feature *feature)  { -	dev_dbg(&pdev->dev, "PORT AFU UInit.\n"); +	struct resource *res = &pdev->resource[feature->resource_index]; -	sysfs_remove_files(&pdev->dev.kobj, port_afu_attrs); +	return afu_mmio_region_add(dev_get_platdata(&pdev->dev), +				   DFL_PORT_REGION_INDEX_STP, +				   resource_size(res), res->start, +				   DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ | +				   DFL_PORT_REGION_WRITE);  } -static const struct dfl_feature_ops port_afu_ops = { -	.init = port_afu_init, -	.uinit = port_afu_uinit, +static const struct dfl_feature_id port_stp_id_table[] = { +	{.id = PORT_FEATURE_ID_STP,}, +	{0,} +}; + +static const struct dfl_feature_ops port_stp_ops = { +	.init = port_stp_init,  };  static struct dfl_feature_driver port_feature_drvs[] = {  	{ -		.id = PORT_FEATURE_ID_HEADER, +		.id_table = port_hdr_id_table,  		.ops = &port_hdr_ops,  	},  	{ -		.id = PORT_FEATURE_ID_AFU, +		.id_table = port_afu_id_table,  		.ops = &port_afu_ops,  	},  	{ +		.id_table = port_err_id_table, +		.ops = &port_err_ops, +	}, +	{ +		.id_table = port_stp_id_table, +		.ops = &port_stp_ops, +	}, +	{  		.ops = NULL,  	}  }; @@ -545,9 +832,9 @@ static int port_enable_set(struct platform_device *pdev, bool enable)  	mutex_lock(&pdata->lock);  	if (enable) -		port_enable(pdev); +		__afu_port_enable(pdev);  	else -		ret = port_disable(pdev); +		ret = __afu_port_disable(pdev);  	mutex_unlock(&pdata->lock);  	return ret; @@ -599,9 +886,17 @@ static int afu_remove(struct platform_device *pdev)  	return 0;  } +static const struct attribute_group *afu_dev_groups[] = { +	&port_hdr_group, +	&port_afu_group, +	&port_err_group, +	NULL +}; +  static struct platform_driver afu_driver = {  	.driver	= { -		.name    = DFL_FPGA_FEATURE_DEV_PORT, +		.name	    = DFL_FPGA_FEATURE_DEV_PORT, +		.dev_groups = afu_dev_groups,  	},  	.probe   = afu_probe,  	.remove  = afu_remove,  | 
