xref: /openbmc/linux/drivers/fpga/dfl-afu-error.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
144d24753SWu Hao // SPDX-License-Identifier: GPL-2.0
244d24753SWu Hao /*
344d24753SWu Hao  * Driver for FPGA Accelerated Function Unit (AFU) Error Reporting
444d24753SWu Hao  *
544d24753SWu Hao  * Copyright 2019 Intel Corporation, Inc.
644d24753SWu Hao  *
744d24753SWu Hao  * Authors:
844d24753SWu Hao  *   Wu Hao <hao.wu@linux.intel.com>
944d24753SWu Hao  *   Xiao Guangrong <guangrong.xiao@linux.intel.com>
1044d24753SWu Hao  *   Joseph Grecco <joe.grecco@intel.com>
1144d24753SWu Hao  *   Enno Luebbers <enno.luebbers@intel.com>
1244d24753SWu Hao  *   Tim Whisonant <tim.whisonant@intel.com>
1344d24753SWu Hao  *   Ananda Ravuri <ananda.ravuri@intel.com>
1444d24753SWu Hao  *   Mitchel Henry <henry.mitchel@intel.com>
1544d24753SWu Hao  */
1644d24753SWu Hao 
17fe6a3d65SXu Yilun #include <linux/fpga-dfl.h>
1844d24753SWu Hao #include <linux/uaccess.h>
1944d24753SWu Hao 
2044d24753SWu Hao #include "dfl-afu.h"
2144d24753SWu Hao 
2244d24753SWu Hao #define PORT_ERROR_MASK		0x8
2344d24753SWu Hao #define PORT_ERROR		0x10
2444d24753SWu Hao #define PORT_FIRST_ERROR	0x18
2544d24753SWu Hao #define PORT_MALFORMED_REQ0	0x20
2644d24753SWu Hao #define PORT_MALFORMED_REQ1	0x28
2744d24753SWu Hao 
2844d24753SWu Hao #define ERROR_MASK		GENMASK_ULL(63, 0)
2944d24753SWu Hao 
3044d24753SWu Hao /* mask or unmask port errors by the error mask register. */
__afu_port_err_mask(struct device * dev,bool mask)3144d24753SWu Hao static void __afu_port_err_mask(struct device *dev, bool mask)
3244d24753SWu Hao {
3344d24753SWu Hao 	void __iomem *base;
3444d24753SWu Hao 
3544d24753SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
3644d24753SWu Hao 
3744d24753SWu Hao 	writeq(mask ? ERROR_MASK : 0, base + PORT_ERROR_MASK);
3844d24753SWu Hao }
3944d24753SWu Hao 
afu_port_err_mask(struct device * dev,bool mask)4044d24753SWu Hao static void afu_port_err_mask(struct device *dev, bool mask)
4144d24753SWu Hao {
4244d24753SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
4344d24753SWu Hao 
4444d24753SWu Hao 	mutex_lock(&pdata->lock);
4544d24753SWu Hao 	__afu_port_err_mask(dev, mask);
4644d24753SWu Hao 	mutex_unlock(&pdata->lock);
4744d24753SWu Hao }
4844d24753SWu Hao 
4944d24753SWu Hao /* clear port errors. */
afu_port_err_clear(struct device * dev,u64 err)5044d24753SWu Hao static int afu_port_err_clear(struct device *dev, u64 err)
5144d24753SWu Hao {
5244d24753SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
5344d24753SWu Hao 	struct platform_device *pdev = to_platform_device(dev);
5444d24753SWu Hao 	void __iomem *base_err, *base_hdr;
55*9a8d3cdaSRuss Weight 	int enable_ret = 0, ret = -EBUSY;
5644d24753SWu Hao 	u64 v;
5744d24753SWu Hao 
5844d24753SWu Hao 	base_err = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
5944d24753SWu Hao 	base_hdr = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
6044d24753SWu Hao 
6144d24753SWu Hao 	mutex_lock(&pdata->lock);
6244d24753SWu Hao 
6344d24753SWu Hao 	/*
6444d24753SWu Hao 	 * clear Port Errors
6544d24753SWu Hao 	 *
6644d24753SWu Hao 	 * - Check for AP6 State
6744d24753SWu Hao 	 * - Halt Port by keeping Port in reset
6844d24753SWu Hao 	 * - Set PORT Error mask to all 1 to mask errors
6944d24753SWu Hao 	 * - Clear all errors
7044d24753SWu Hao 	 * - Set Port mask to all 0 to enable errors
7144d24753SWu Hao 	 * - All errors start capturing new errors
7244d24753SWu Hao 	 * - Enable Port by pulling the port out of reset
7344d24753SWu Hao 	 */
7444d24753SWu Hao 
7544d24753SWu Hao 	/* if device is still in AP6 power state, can not clear any error. */
7644d24753SWu Hao 	v = readq(base_hdr + PORT_HDR_STS);
7744d24753SWu Hao 	if (FIELD_GET(PORT_STS_PWR_STATE, v) == PORT_STS_PWR_STATE_AP6) {
7844d24753SWu Hao 		dev_err(dev, "Could not clear errors, device in AP6 state.\n");
7944d24753SWu Hao 		goto done;
8044d24753SWu Hao 	}
8144d24753SWu Hao 
8244d24753SWu Hao 	/* Halt Port by keeping Port in reset */
8344d24753SWu Hao 	ret = __afu_port_disable(pdev);
8444d24753SWu Hao 	if (ret)
8544d24753SWu Hao 		goto done;
8644d24753SWu Hao 
8744d24753SWu Hao 	/* Mask all errors */
8844d24753SWu Hao 	__afu_port_err_mask(dev, true);
8944d24753SWu Hao 
9044d24753SWu Hao 	/* Clear errors if err input matches with current port errors.*/
9144d24753SWu Hao 	v = readq(base_err + PORT_ERROR);
9244d24753SWu Hao 
9344d24753SWu Hao 	if (v == err) {
9444d24753SWu Hao 		writeq(v, base_err + PORT_ERROR);
9544d24753SWu Hao 
9644d24753SWu Hao 		v = readq(base_err + PORT_FIRST_ERROR);
9744d24753SWu Hao 		writeq(v, base_err + PORT_FIRST_ERROR);
9844d24753SWu Hao 	} else {
99*9a8d3cdaSRuss Weight 		dev_warn(dev, "%s: received 0x%llx, expected 0x%llx\n",
100*9a8d3cdaSRuss Weight 			 __func__, v, err);
10144d24753SWu Hao 		ret = -EINVAL;
10244d24753SWu Hao 	}
10344d24753SWu Hao 
10444d24753SWu Hao 	/* Clear mask */
10544d24753SWu Hao 	__afu_port_err_mask(dev, false);
10644d24753SWu Hao 
107*9a8d3cdaSRuss Weight 	/* Enable the Port by clearing the reset */
108*9a8d3cdaSRuss Weight 	enable_ret = __afu_port_enable(pdev);
10944d24753SWu Hao 
11044d24753SWu Hao done:
11144d24753SWu Hao 	mutex_unlock(&pdata->lock);
112*9a8d3cdaSRuss Weight 	return enable_ret ? enable_ret : ret;
11344d24753SWu Hao }
11444d24753SWu Hao 
errors_show(struct device * dev,struct device_attribute * attr,char * buf)11544d24753SWu Hao static ssize_t errors_show(struct device *dev, struct device_attribute *attr,
11644d24753SWu Hao 			   char *buf)
11744d24753SWu Hao {
11844d24753SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
11944d24753SWu Hao 	void __iomem *base;
12044d24753SWu Hao 	u64 error;
12144d24753SWu Hao 
12244d24753SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
12344d24753SWu Hao 
12444d24753SWu Hao 	mutex_lock(&pdata->lock);
12544d24753SWu Hao 	error = readq(base + PORT_ERROR);
12644d24753SWu Hao 	mutex_unlock(&pdata->lock);
12744d24753SWu Hao 
12844d24753SWu Hao 	return sprintf(buf, "0x%llx\n", (unsigned long long)error);
12944d24753SWu Hao }
13044d24753SWu Hao 
errors_store(struct device * dev,struct device_attribute * attr,const char * buff,size_t count)13144d24753SWu Hao static ssize_t errors_store(struct device *dev, struct device_attribute *attr,
13244d24753SWu Hao 			    const char *buff, size_t count)
13344d24753SWu Hao {
13444d24753SWu Hao 	u64 value;
13544d24753SWu Hao 	int ret;
13644d24753SWu Hao 
13744d24753SWu Hao 	if (kstrtou64(buff, 0, &value))
13844d24753SWu Hao 		return -EINVAL;
13944d24753SWu Hao 
14044d24753SWu Hao 	ret = afu_port_err_clear(dev, value);
14144d24753SWu Hao 
14244d24753SWu Hao 	return ret ? ret : count;
14344d24753SWu Hao }
14444d24753SWu Hao static DEVICE_ATTR_RW(errors);
14544d24753SWu Hao 
first_error_show(struct device * dev,struct device_attribute * attr,char * buf)14644d24753SWu Hao static ssize_t first_error_show(struct device *dev,
14744d24753SWu Hao 				struct device_attribute *attr, char *buf)
14844d24753SWu Hao {
14944d24753SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
15044d24753SWu Hao 	void __iomem *base;
15144d24753SWu Hao 	u64 error;
15244d24753SWu Hao 
15344d24753SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
15444d24753SWu Hao 
15544d24753SWu Hao 	mutex_lock(&pdata->lock);
15644d24753SWu Hao 	error = readq(base + PORT_FIRST_ERROR);
15744d24753SWu Hao 	mutex_unlock(&pdata->lock);
15844d24753SWu Hao 
15944d24753SWu Hao 	return sprintf(buf, "0x%llx\n", (unsigned long long)error);
16044d24753SWu Hao }
16144d24753SWu Hao static DEVICE_ATTR_RO(first_error);
16244d24753SWu Hao 
first_malformed_req_show(struct device * dev,struct device_attribute * attr,char * buf)16344d24753SWu Hao static ssize_t first_malformed_req_show(struct device *dev,
16444d24753SWu Hao 					struct device_attribute *attr,
16544d24753SWu Hao 					char *buf)
16644d24753SWu Hao {
16744d24753SWu Hao 	struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
16844d24753SWu Hao 	void __iomem *base;
16944d24753SWu Hao 	u64 req0, req1;
17044d24753SWu Hao 
17144d24753SWu Hao 	base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR);
17244d24753SWu Hao 
17344d24753SWu Hao 	mutex_lock(&pdata->lock);
17444d24753SWu Hao 	req0 = readq(base + PORT_MALFORMED_REQ0);
17544d24753SWu Hao 	req1 = readq(base + PORT_MALFORMED_REQ1);
17644d24753SWu Hao 	mutex_unlock(&pdata->lock);
17744d24753SWu Hao 
17844d24753SWu Hao 	return sprintf(buf, "0x%016llx%016llx\n",
17944d24753SWu Hao 		       (unsigned long long)req1, (unsigned long long)req0);
18044d24753SWu Hao }
18144d24753SWu Hao static DEVICE_ATTR_RO(first_malformed_req);
18244d24753SWu Hao 
18344d24753SWu Hao static struct attribute *port_err_attrs[] = {
18444d24753SWu Hao 	&dev_attr_errors.attr,
18544d24753SWu Hao 	&dev_attr_first_error.attr,
18644d24753SWu Hao 	&dev_attr_first_malformed_req.attr,
18744d24753SWu Hao 	NULL,
18844d24753SWu Hao };
18944d24753SWu Hao 
port_err_attrs_visible(struct kobject * kobj,struct attribute * attr,int n)19044d24753SWu Hao static umode_t port_err_attrs_visible(struct kobject *kobj,
19144d24753SWu Hao 				      struct attribute *attr, int n)
19244d24753SWu Hao {
19344d24753SWu Hao 	struct device *dev = kobj_to_dev(kobj);
19444d24753SWu Hao 
19544d24753SWu Hao 	/*
19644d24753SWu Hao 	 * sysfs entries are visible only if related private feature is
19744d24753SWu Hao 	 * enumerated.
19844d24753SWu Hao 	 */
19944d24753SWu Hao 	if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_ERROR))
20044d24753SWu Hao 		return 0;
20144d24753SWu Hao 
20244d24753SWu Hao 	return attr->mode;
20344d24753SWu Hao }
20444d24753SWu Hao 
20544d24753SWu Hao const struct attribute_group port_err_group = {
20644d24753SWu Hao 	.name       = "errors",
20744d24753SWu Hao 	.attrs      = port_err_attrs,
20844d24753SWu Hao 	.is_visible = port_err_attrs_visible,
20944d24753SWu Hao };
21044d24753SWu Hao 
port_err_init(struct platform_device * pdev,struct dfl_feature * feature)21144d24753SWu Hao static int port_err_init(struct platform_device *pdev,
21244d24753SWu Hao 			 struct dfl_feature *feature)
21344d24753SWu Hao {
21444d24753SWu Hao 	afu_port_err_mask(&pdev->dev, false);
21544d24753SWu Hao 
21644d24753SWu Hao 	return 0;
21744d24753SWu Hao }
21844d24753SWu Hao 
port_err_uinit(struct platform_device * pdev,struct dfl_feature * feature)21944d24753SWu Hao static void port_err_uinit(struct platform_device *pdev,
22044d24753SWu Hao 			   struct dfl_feature *feature)
22144d24753SWu Hao {
22244d24753SWu Hao 	afu_port_err_mask(&pdev->dev, true);
22344d24753SWu Hao }
22444d24753SWu Hao 
225fe6a3d65SXu Yilun static long
port_err_ioctl(struct platform_device * pdev,struct dfl_feature * feature,unsigned int cmd,unsigned long arg)226fe6a3d65SXu Yilun port_err_ioctl(struct platform_device *pdev, struct dfl_feature *feature,
227fe6a3d65SXu Yilun 	       unsigned int cmd, unsigned long arg)
228fe6a3d65SXu Yilun {
229fe6a3d65SXu Yilun 	switch (cmd) {
230fe6a3d65SXu Yilun 	case DFL_FPGA_PORT_ERR_GET_IRQ_NUM:
231fe6a3d65SXu Yilun 		return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg);
232fe6a3d65SXu Yilun 	case DFL_FPGA_PORT_ERR_SET_IRQ:
233fe6a3d65SXu Yilun 		return dfl_feature_ioctl_set_irq(pdev, feature, arg);
234fe6a3d65SXu Yilun 	default:
235fe6a3d65SXu Yilun 		dev_dbg(&pdev->dev, "%x cmd not handled", cmd);
236fe6a3d65SXu Yilun 		return -ENODEV;
237fe6a3d65SXu Yilun 	}
238fe6a3d65SXu Yilun }
239fe6a3d65SXu Yilun 
24044d24753SWu Hao const struct dfl_feature_id port_err_id_table[] = {
24144d24753SWu Hao 	{.id = PORT_FEATURE_ID_ERROR,},
24244d24753SWu Hao 	{0,}
24344d24753SWu Hao };
24444d24753SWu Hao 
24544d24753SWu Hao const struct dfl_feature_ops port_err_ops = {
24644d24753SWu Hao 	.init = port_err_init,
24744d24753SWu Hao 	.uinit = port_err_uinit,
248fe6a3d65SXu Yilun 	.ioctl = port_err_ioctl,
24944d24753SWu Hao };
250