Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Driver for FPGA Management Engine Error Management
4 *
5 * Copyright 2019 Intel Corporation, Inc.
6 *
7 * Authors:
8 * Kang Luwei <luwei.kang@intel.com>
9 * Xiao Guangrong <guangrong.xiao@linux.intel.com>
10 * Wu Hao <hao.wu@intel.com>
11 * Joseph Grecco <joe.grecco@intel.com>
12 * Enno Luebbers <enno.luebbers@intel.com>
13 * Tim Whisonant <tim.whisonant@intel.com>
14 * Ananda Ravuri <ananda.ravuri@intel.com>
15 * Mitchel, Henry <henry.mitchel@intel.com>
16 */
17
18#include <linux/fpga-dfl.h>
19#include <linux/uaccess.h>
20
21#include "dfl.h"
22#include "dfl-fme.h"
23
24#define FME_ERROR_MASK 0x8
25#define FME_ERROR 0x10
26#define MBP_ERROR BIT_ULL(6)
27#define PCIE0_ERROR_MASK 0x18
28#define PCIE0_ERROR 0x20
29#define PCIE1_ERROR_MASK 0x28
30#define PCIE1_ERROR 0x30
31#define FME_FIRST_ERROR 0x38
32#define FME_NEXT_ERROR 0x40
33#define RAS_NONFAT_ERROR_MASK 0x48
34#define RAS_NONFAT_ERROR 0x50
35#define RAS_CATFAT_ERROR_MASK 0x58
36#define RAS_CATFAT_ERROR 0x60
37#define RAS_ERROR_INJECT 0x68
38#define INJECT_ERROR_MASK GENMASK_ULL(2, 0)
39
40#define ERROR_MASK GENMASK_ULL(63, 0)
41
42static ssize_t pcie0_errors_show(struct device *dev,
43 struct device_attribute *attr, char *buf)
44{
45 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
46 void __iomem *base;
47 u64 value;
48
49 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
50
51 mutex_lock(&fdata->lock);
52 value = readq(base + PCIE0_ERROR);
53 mutex_unlock(&fdata->lock);
54
55 return sprintf(buf, "0x%llx\n", (unsigned long long)value);
56}
57
58static ssize_t pcie0_errors_store(struct device *dev,
59 struct device_attribute *attr,
60 const char *buf, size_t count)
61{
62 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
63 void __iomem *base;
64 int ret = 0;
65 u64 v, val;
66
67 if (kstrtou64(buf, 0, &val))
68 return -EINVAL;
69
70 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
71
72 mutex_lock(&fdata->lock);
73 writeq(GENMASK_ULL(63, 0), base + PCIE0_ERROR_MASK);
74
75 v = readq(base + PCIE0_ERROR);
76 if (val == v)
77 writeq(v, base + PCIE0_ERROR);
78 else
79 ret = -EINVAL;
80
81 writeq(0ULL, base + PCIE0_ERROR_MASK);
82 mutex_unlock(&fdata->lock);
83 return ret ? ret : count;
84}
85static DEVICE_ATTR_RW(pcie0_errors);
86
87static ssize_t pcie1_errors_show(struct device *dev,
88 struct device_attribute *attr, char *buf)
89{
90 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
91 void __iomem *base;
92 u64 value;
93
94 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
95
96 mutex_lock(&fdata->lock);
97 value = readq(base + PCIE1_ERROR);
98 mutex_unlock(&fdata->lock);
99
100 return sprintf(buf, "0x%llx\n", (unsigned long long)value);
101}
102
103static ssize_t pcie1_errors_store(struct device *dev,
104 struct device_attribute *attr,
105 const char *buf, size_t count)
106{
107 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
108 void __iomem *base;
109 int ret = 0;
110 u64 v, val;
111
112 if (kstrtou64(buf, 0, &val))
113 return -EINVAL;
114
115 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
116
117 mutex_lock(&fdata->lock);
118 writeq(GENMASK_ULL(63, 0), base + PCIE1_ERROR_MASK);
119
120 v = readq(base + PCIE1_ERROR);
121 if (val == v)
122 writeq(v, base + PCIE1_ERROR);
123 else
124 ret = -EINVAL;
125
126 writeq(0ULL, base + PCIE1_ERROR_MASK);
127 mutex_unlock(&fdata->lock);
128 return ret ? ret : count;
129}
130static DEVICE_ATTR_RW(pcie1_errors);
131
132static ssize_t nonfatal_errors_show(struct device *dev,
133 struct device_attribute *attr, char *buf)
134{
135 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
136 void __iomem *base;
137
138 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
139
140 return sprintf(buf, "0x%llx\n",
141 (unsigned long long)readq(base + RAS_NONFAT_ERROR));
142}
143static DEVICE_ATTR_RO(nonfatal_errors);
144
145static ssize_t catfatal_errors_show(struct device *dev,
146 struct device_attribute *attr, char *buf)
147{
148 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
149 void __iomem *base;
150
151 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
152
153 return sprintf(buf, "0x%llx\n",
154 (unsigned long long)readq(base + RAS_CATFAT_ERROR));
155}
156static DEVICE_ATTR_RO(catfatal_errors);
157
158static ssize_t inject_errors_show(struct device *dev,
159 struct device_attribute *attr, char *buf)
160{
161 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
162 void __iomem *base;
163 u64 v;
164
165 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
166
167 mutex_lock(&fdata->lock);
168 v = readq(base + RAS_ERROR_INJECT);
169 mutex_unlock(&fdata->lock);
170
171 return sprintf(buf, "0x%llx\n",
172 (unsigned long long)FIELD_GET(INJECT_ERROR_MASK, v));
173}
174
175static ssize_t inject_errors_store(struct device *dev,
176 struct device_attribute *attr,
177 const char *buf, size_t count)
178{
179 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
180 void __iomem *base;
181 u8 inject_error;
182 u64 v;
183
184 if (kstrtou8(buf, 0, &inject_error))
185 return -EINVAL;
186
187 if (inject_error & ~INJECT_ERROR_MASK)
188 return -EINVAL;
189
190 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
191
192 mutex_lock(&fdata->lock);
193 v = readq(base + RAS_ERROR_INJECT);
194 v &= ~INJECT_ERROR_MASK;
195 v |= FIELD_PREP(INJECT_ERROR_MASK, inject_error);
196 writeq(v, base + RAS_ERROR_INJECT);
197 mutex_unlock(&fdata->lock);
198
199 return count;
200}
201static DEVICE_ATTR_RW(inject_errors);
202
203static ssize_t fme_errors_show(struct device *dev,
204 struct device_attribute *attr, char *buf)
205{
206 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
207 void __iomem *base;
208 u64 value;
209
210 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
211
212 mutex_lock(&fdata->lock);
213 value = readq(base + FME_ERROR);
214 mutex_unlock(&fdata->lock);
215
216 return sprintf(buf, "0x%llx\n", (unsigned long long)value);
217}
218
219static ssize_t fme_errors_store(struct device *dev,
220 struct device_attribute *attr,
221 const char *buf, size_t count)
222{
223 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
224 void __iomem *base;
225 u64 v, val;
226 int ret = 0;
227
228 if (kstrtou64(buf, 0, &val))
229 return -EINVAL;
230
231 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
232
233 mutex_lock(&fdata->lock);
234 writeq(GENMASK_ULL(63, 0), base + FME_ERROR_MASK);
235
236 v = readq(base + FME_ERROR);
237 if (val == v)
238 writeq(v, base + FME_ERROR);
239 else
240 ret = -EINVAL;
241
242 /* Workaround: disable MBP_ERROR if feature revision is 0 */
243 writeq(dfl_feature_revision(base) ? 0ULL : MBP_ERROR,
244 base + FME_ERROR_MASK);
245 mutex_unlock(&fdata->lock);
246 return ret ? ret : count;
247}
248static DEVICE_ATTR_RW(fme_errors);
249
250static ssize_t first_error_show(struct device *dev,
251 struct device_attribute *attr, char *buf)
252{
253 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
254 void __iomem *base;
255 u64 value;
256
257 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
258
259 mutex_lock(&fdata->lock);
260 value = readq(base + FME_FIRST_ERROR);
261 mutex_unlock(&fdata->lock);
262
263 return sprintf(buf, "0x%llx\n", (unsigned long long)value);
264}
265static DEVICE_ATTR_RO(first_error);
266
267static ssize_t next_error_show(struct device *dev,
268 struct device_attribute *attr, char *buf)
269{
270 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
271 void __iomem *base;
272 u64 value;
273
274 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
275
276 mutex_lock(&fdata->lock);
277 value = readq(base + FME_NEXT_ERROR);
278 mutex_unlock(&fdata->lock);
279
280 return sprintf(buf, "0x%llx\n", (unsigned long long)value);
281}
282static DEVICE_ATTR_RO(next_error);
283
284static struct attribute *fme_global_err_attrs[] = {
285 &dev_attr_pcie0_errors.attr,
286 &dev_attr_pcie1_errors.attr,
287 &dev_attr_nonfatal_errors.attr,
288 &dev_attr_catfatal_errors.attr,
289 &dev_attr_inject_errors.attr,
290 &dev_attr_fme_errors.attr,
291 &dev_attr_first_error.attr,
292 &dev_attr_next_error.attr,
293 NULL,
294};
295
296static umode_t fme_global_err_attrs_visible(struct kobject *kobj,
297 struct attribute *attr, int n)
298{
299 struct device *dev = kobj_to_dev(kobj);
300 struct dfl_feature_dev_data *fdata;
301
302 fdata = to_dfl_feature_dev_data(dev);
303 /*
304 * sysfs entries are visible only if related private feature is
305 * enumerated.
306 */
307 if (!dfl_get_feature_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR))
308 return 0;
309
310 return attr->mode;
311}
312
313const struct attribute_group fme_global_err_group = {
314 .name = "errors",
315 .attrs = fme_global_err_attrs,
316 .is_visible = fme_global_err_attrs_visible,
317};
318
319static void fme_err_mask(struct device *dev, bool mask)
320{
321 struct dfl_feature_dev_data *fdata = to_dfl_feature_dev_data(dev);
322 void __iomem *base;
323
324 base = dfl_get_feature_ioaddr_by_id(fdata, FME_FEATURE_ID_GLOBAL_ERR);
325
326 mutex_lock(&fdata->lock);
327
328 /* Workaround: keep MBP_ERROR always masked if revision is 0 */
329 if (dfl_feature_revision(base))
330 writeq(mask ? ERROR_MASK : 0, base + FME_ERROR_MASK);
331 else
332 writeq(mask ? ERROR_MASK : MBP_ERROR, base + FME_ERROR_MASK);
333
334 writeq(mask ? ERROR_MASK : 0, base + PCIE0_ERROR_MASK);
335 writeq(mask ? ERROR_MASK : 0, base + PCIE1_ERROR_MASK);
336 writeq(mask ? ERROR_MASK : 0, base + RAS_NONFAT_ERROR_MASK);
337 writeq(mask ? ERROR_MASK : 0, base + RAS_CATFAT_ERROR_MASK);
338
339 mutex_unlock(&fdata->lock);
340}
341
342static int fme_global_err_init(struct platform_device *pdev,
343 struct dfl_feature *feature)
344{
345 fme_err_mask(&pdev->dev, false);
346
347 return 0;
348}
349
350static void fme_global_err_uinit(struct platform_device *pdev,
351 struct dfl_feature *feature)
352{
353 fme_err_mask(&pdev->dev, true);
354}
355
356static long
357fme_global_error_ioctl(struct platform_device *pdev,
358 struct dfl_feature *feature,
359 unsigned int cmd, unsigned long arg)
360{
361 switch (cmd) {
362 case DFL_FPGA_FME_ERR_GET_IRQ_NUM:
363 return dfl_feature_ioctl_get_num_irqs(pdev, feature, arg);
364 case DFL_FPGA_FME_ERR_SET_IRQ:
365 return dfl_feature_ioctl_set_irq(pdev, feature, arg);
366 default:
367 dev_dbg(&pdev->dev, "%x cmd not handled", cmd);
368 return -ENODEV;
369 }
370}
371
372const struct dfl_feature_id fme_global_err_id_table[] = {
373 {.id = FME_FEATURE_ID_GLOBAL_ERR,},
374 {0,}
375};
376
377const struct dfl_feature_ops fme_global_err_ops = {
378 .init = fme_global_err_init,
379 .uinit = fme_global_err_uinit,
380 .ioctl = fme_global_error_ioctl,
381};