Commit | Line | Data |
---|---|---|
44d24753 WH |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Driver for FPGA Accelerated Function Unit (AFU) Error Reporting | |
4 | * | |
5 | * Copyright 2019 Intel Corporation, Inc. | |
6 | * | |
7 | * Authors: | |
8 | * Wu Hao <hao.wu@linux.intel.com> | |
9 | * Xiao Guangrong <guangrong.xiao@linux.intel.com> | |
10 | * Joseph Grecco <joe.grecco@intel.com> | |
11 | * Enno Luebbers <enno.luebbers@intel.com> | |
12 | * Tim Whisonant <tim.whisonant@intel.com> | |
13 | * Ananda Ravuri <ananda.ravuri@intel.com> | |
14 | * Mitchel Henry <henry.mitchel@intel.com> | |
15 | */ | |
16 | ||
fe6a3d65 | 17 | #include <linux/fpga-dfl.h> |
44d24753 WH |
18 | #include <linux/uaccess.h> |
19 | ||
20 | #include "dfl-afu.h" | |
21 | ||
22 | #define PORT_ERROR_MASK 0x8 | |
23 | #define PORT_ERROR 0x10 | |
24 | #define PORT_FIRST_ERROR 0x18 | |
25 | #define PORT_MALFORMED_REQ0 0x20 | |
26 | #define PORT_MALFORMED_REQ1 0x28 | |
27 | ||
28 | #define ERROR_MASK GENMASK_ULL(63, 0) | |
29 | ||
30 | /* mask or unmask port errors by the error mask register. */ | |
31 | static void __afu_port_err_mask(struct device *dev, bool mask) | |
32 | { | |
33 | void __iomem *base; | |
34 | ||
35 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR); | |
36 | ||
37 | writeq(mask ? ERROR_MASK : 0, base + PORT_ERROR_MASK); | |
38 | } | |
39 | ||
40 | static void afu_port_err_mask(struct device *dev, bool mask) | |
41 | { | |
42 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
43 | ||
44 | mutex_lock(&pdata->lock); | |
45 | __afu_port_err_mask(dev, mask); | |
46 | mutex_unlock(&pdata->lock); | |
47 | } | |
48 | ||
49 | /* clear port errors. */ | |
50 | static int afu_port_err_clear(struct device *dev, u64 err) | |
51 | { | |
52 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
53 | struct platform_device *pdev = to_platform_device(dev); | |
54 | void __iomem *base_err, *base_hdr; | |
9a8d3cda | 55 | int enable_ret = 0, ret = -EBUSY; |
44d24753 WH |
56 | u64 v; |
57 | ||
58 | base_err = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR); | |
59 | base_hdr = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER); | |
60 | ||
61 | mutex_lock(&pdata->lock); | |
62 | ||
63 | /* | |
64 | * clear Port Errors | |
65 | * | |
66 | * - Check for AP6 State | |
67 | * - Halt Port by keeping Port in reset | |
68 | * - Set PORT Error mask to all 1 to mask errors | |
69 | * - Clear all errors | |
70 | * - Set Port mask to all 0 to enable errors | |
71 | * - All errors start capturing new errors | |
72 | * - Enable Port by pulling the port out of reset | |
73 | */ | |
74 | ||
75 | /* if device is still in AP6 power state, can not clear any error. */ | |
76 | v = readq(base_hdr + PORT_HDR_STS); | |
77 | if (FIELD_GET(PORT_STS_PWR_STATE, v) == PORT_STS_PWR_STATE_AP6) { | |
78 | dev_err(dev, "Could not clear errors, device in AP6 state.\n"); | |
79 | goto done; | |
80 | } | |
81 | ||
82 | /* Halt Port by keeping Port in reset */ | |
83 | ret = __afu_port_disable(pdev); | |
84 | if (ret) | |
85 | goto done; | |
86 | ||
87 | /* Mask all errors */ | |
88 | __afu_port_err_mask(dev, true); | |
89 | ||
90 | /* Clear errors if err input matches with current port errors.*/ | |
91 | v = readq(base_err + PORT_ERROR); | |
92 | ||
93 | if (v == err) { | |
94 | writeq(v, base_err + PORT_ERROR); | |
95 | ||
96 | v = readq(base_err + PORT_FIRST_ERROR); | |
97 | writeq(v, base_err + PORT_FIRST_ERROR); | |
98 | } else { | |
9a8d3cda RW |
99 | dev_warn(dev, "%s: received 0x%llx, expected 0x%llx\n", |
100 | __func__, v, err); | |
44d24753 WH |
101 | ret = -EINVAL; |
102 | } | |
103 | ||
104 | /* Clear mask */ | |
105 | __afu_port_err_mask(dev, false); | |
106 | ||
9a8d3cda RW |
107 | /* Enable the Port by clearing the reset */ |
108 | enable_ret = __afu_port_enable(pdev); | |
44d24753 WH |
109 | |
110 | done: | |
111 | mutex_unlock(&pdata->lock); | |
9a8d3cda | 112 | return enable_ret ? enable_ret : ret; |
44d24753 WH |
113 | } |
114 | ||
115 | static ssize_t errors_show(struct device *dev, struct device_attribute *attr, | |
116 | char *buf) | |
117 | { | |
118 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
119 | void __iomem *base; | |
120 | u64 error; | |
121 | ||
122 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR); | |
123 | ||
124 | mutex_lock(&pdata->lock); | |
125 | error = readq(base + PORT_ERROR); | |
126 | mutex_unlock(&pdata->lock); | |
127 | ||
128 | return sprintf(buf, "0x%llx\n", (unsigned long long)error); | |
129 | } | |
130 | ||
131 | static ssize_t errors_store(struct device *dev, struct device_attribute *attr, | |
132 | const char *buff, size_t count) | |
133 | { | |
134 | u64 value; | |
135 | int ret; | |
136 | ||
137 | if (kstrtou64(buff, 0, &value)) | |
138 | return -EINVAL; | |
139 | ||
140 | ret = afu_port_err_clear(dev, value); | |
141 | ||
142 | return ret ? ret : count; | |
143 | } | |
144 | static DEVICE_ATTR_RW(errors); | |
145 | ||
146 | static ssize_t first_error_show(struct device *dev, | |
147 | struct device_attribute *attr, char *buf) | |
148 | { | |
149 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
150 | void __iomem *base; | |
151 | u64 error; | |
152 | ||
153 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR); | |
154 | ||
155 | mutex_lock(&pdata->lock); | |
156 | error = readq(base + PORT_FIRST_ERROR); | |
157 | mutex_unlock(&pdata->lock); | |
158 | ||
159 | return sprintf(buf, "0x%llx\n", (unsigned long long)error); | |
160 | } | |
161 | static DEVICE_ATTR_RO(first_error); | |
162 | ||
163 | static ssize_t first_malformed_req_show(struct device *dev, | |
164 | struct device_attribute *attr, | |
165 | char *buf) | |
166 | { | |
167 | struct dfl_feature_platform_data *pdata = dev_get_platdata(dev); | |
168 | void __iomem *base; | |
169 | u64 req0, req1; | |
170 | ||
171 | base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_ERROR); | |
172 | ||
173 | mutex_lock(&pdata->lock); | |
174 | req0 = readq(base + PORT_MALFORMED_REQ0); | |
175 | req1 = readq(base + PORT_MALFORMED_REQ1); | |
176 | mutex_unlock(&pdata->lock); | |
177 | ||
178 | return sprintf(buf, "0x%016llx%016llx\n", | |
179 | (unsigned long long)req1, (unsigned long long)req0); | |
180 | } | |
181 | static DEVICE_ATTR_RO(first_malformed_req); | |
182 | ||
183 | static struct attribute *port_err_attrs[] = { | |
184 | &dev_attr_errors.attr, | |
185 | &dev_attr_first_error.attr, | |
186 | &dev_attr_first_malformed_req.attr, | |
187 | NULL, | |
188 | }; | |
189 | ||
190 | static umode_t port_err_attrs_visible(struct kobject *kobj, | |
191 | struct attribute *attr, int n) | |
192 | { | |
193 | struct device *dev = kobj_to_dev(kobj); | |
194 | ||
195 | /* | |
196 | * sysfs entries are visible only if related private feature is | |
197 | * enumerated. | |
198 | */ | |
199 | if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_ERROR)) | |
200 | return 0; | |
201 | ||
202 | return attr->mode; | |
203 | } | |
204 | ||
205 | const struct attribute_group port_err_group = { | |
206 | .name = "errors", | |
207 | .attrs = port_err_attrs, | |
208 | .is_visible = port_err_attrs_visible, | |
209 | }; | |
210 | ||
211 | static int port_err_init(struct platform_device *pdev, | |
212 | struct dfl_feature *feature) | |
213 | { | |
214 | afu_port_err_mask(&pdev->dev, false); | |
215 | ||
216 | return 0; | |
217 | } | |
218 | ||
219 | static void port_err_uinit(struct platform_device *pdev, | |
220 | struct dfl_feature *feature) | |
221 | { | |
222 | afu_port_err_mask(&pdev->dev, true); | |
223 | } | |
224 | ||
fe6a3d65 XY |
225 | static long |
226 | port_err_ioctl(struct platform_device *pdev, struct dfl_feature *feature, | |
227 | unsigned int cmd, unsigned long arg) | |
228 | { | |
229 | switch (cmd) { | |
230 | case DFL_FPGA_PORT_ERR_GET_IRQ_NUM: | |
231 | return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg); | |
232 | case DFL_FPGA_PORT_ERR_SET_IRQ: | |
233 | return dfl_feature_ioctl_set_irq(pdev, feature, arg); | |
234 | default: | |
235 | dev_dbg(&pdev->dev, "%x cmd not handled", cmd); | |
236 | return -ENODEV; | |
237 | } | |
238 | } | |
239 | ||
44d24753 WH |
240 | const struct dfl_feature_id port_err_id_table[] = { |
241 | {.id = PORT_FEATURE_ID_ERROR,}, | |
242 | {0,} | |
243 | }; | |
244 | ||
245 | const struct dfl_feature_ops port_err_ops = { | |
246 | .init = port_err_init, | |
247 | .uinit = port_err_uinit, | |
fe6a3d65 | 248 | .ioctl = port_err_ioctl, |
44d24753 | 249 | }; |