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