1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Xilinx ZynqMP OCM ECC Driver
4 *
5 * Copyright (C) 2022 Advanced Micro Devices, Inc.
6 */
7
8#include <linux/edac.h>
9#include <linux/interrupt.h>
10#include <linux/module.h>
11#include <linux/of.h>
12#include <linux/of_platform.h>
13#include <linux/platform_device.h>
14
15#include "edac_module.h"
16
17#define ZYNQMP_OCM_EDAC_MSG_SIZE 256
18
19#define ZYNQMP_OCM_EDAC_STRING "zynqmp_ocm"
20
21/* Error/Interrupt registers */
22#define ERR_CTRL_OFST 0x0
23#define OCM_ISR_OFST 0x04
24#define OCM_IMR_OFST 0x08
25#define OCM_IEN_OFST 0x0C
26#define OCM_IDS_OFST 0x10
27
28/* ECC control register */
29#define ECC_CTRL_OFST 0x14
30
31/* Correctable error info registers */
32#define CE_FFA_OFST 0x1C
33#define CE_FFD0_OFST 0x20
34#define CE_FFD1_OFST 0x24
35#define CE_FFD2_OFST 0x28
36#define CE_FFD3_OFST 0x2C
37#define CE_FFE_OFST 0x30
38
39/* Uncorrectable error info registers */
40#define UE_FFA_OFST 0x34
41#define UE_FFD0_OFST 0x38
42#define UE_FFD1_OFST 0x3C
43#define UE_FFD2_OFST 0x40
44#define UE_FFD3_OFST 0x44
45#define UE_FFE_OFST 0x48
46
47/* ECC control register bit field definitions */
48#define ECC_CTRL_CLR_CE_ERR 0x40
49#define ECC_CTRL_CLR_UE_ERR 0x80
50
51/* Fault injection data and count registers */
52#define OCM_FID0_OFST 0x4C
53#define OCM_FID1_OFST 0x50
54#define OCM_FID2_OFST 0x54
55#define OCM_FID3_OFST 0x58
56#define OCM_FIC_OFST 0x74
57
58#define UE_MAX_BITPOS_LOWER 31
59#define UE_MIN_BITPOS_UPPER 32
60#define UE_MAX_BITPOS_UPPER 63
61
62/* Interrupt masks */
63#define OCM_CEINTR_MASK BIT(6)
64#define OCM_UEINTR_MASK BIT(7)
65#define OCM_ECC_ENABLE_MASK BIT(0)
66
67#define OCM_FICOUNT_MASK GENMASK(23, 0)
68#define OCM_NUM_UE_BITPOS 2
69#define OCM_BASEVAL 0xFFFC0000
70#define EDAC_DEVICE "ZynqMP-OCM"
71
72/**
73 * struct ecc_error_info - ECC error log information
74 * @addr: Fault generated at this address
75 * @fault_lo: Generated fault data (lower 32-bit)
76 * @fault_hi: Generated fault data (upper 32-bit)
77 */
78struct ecc_error_info {
79 u32 addr;
80 u32 fault_lo;
81 u32 fault_hi;
82};
83
84/**
85 * struct ecc_status - ECC status information to report
86 * @ce_cnt: Correctable error count
87 * @ue_cnt: Uncorrectable error count
88 * @ceinfo: Correctable error log information
89 * @ueinfo: Uncorrectable error log information
90 */
91struct ecc_status {
92 u32 ce_cnt;
93 u32 ue_cnt;
94 struct ecc_error_info ceinfo;
95 struct ecc_error_info ueinfo;
96};
97
98/**
99 * struct edac_priv - OCM private instance data
100 * @baseaddr: Base address of the OCM
101 * @message: Buffer for framing the event specific info
102 * @stat: ECC status information
103 * @ce_cnt: Correctable Error count
104 * @ue_cnt: Uncorrectable Error count
105 * @debugfs_dir: Directory entry for debugfs
106 * @ce_bitpos: Bit position for Correctable Error
107 * @ue_bitpos: Array to store UnCorrectable Error bit positions
108 * @fault_injection_cnt: Fault Injection Counter value
109 */
110struct edac_priv {
111 void __iomem *baseaddr;
112 char message[ZYNQMP_OCM_EDAC_MSG_SIZE];
113 struct ecc_status stat;
114 u32 ce_cnt;
115 u32 ue_cnt;
116#ifdef CONFIG_EDAC_DEBUG
117 struct dentry *debugfs_dir;
118 u8 ce_bitpos;
119 u8 ue_bitpos[OCM_NUM_UE_BITPOS];
120 u32 fault_injection_cnt;
121#endif
122};
123
124/**
125 * get_error_info - Get the current ECC error info
126 * @base: Pointer to the base address of the OCM
127 * @p: Pointer to the OCM ECC status structure
128 * @mask: Status register mask value
129 *
130 * Determines there is any ECC error or not
131 *
132 */
133static void get_error_info(void __iomem *base, struct ecc_status *p, int mask)
134{
135 if (mask & OCM_CEINTR_MASK) {
136 p->ce_cnt++;
137 p->ceinfo.fault_lo = readl(addr: base + CE_FFD0_OFST);
138 p->ceinfo.fault_hi = readl(addr: base + CE_FFD1_OFST);
139 p->ceinfo.addr = (OCM_BASEVAL | readl(addr: base + CE_FFA_OFST));
140 writel(ECC_CTRL_CLR_CE_ERR, addr: base + OCM_ISR_OFST);
141 } else if (mask & OCM_UEINTR_MASK) {
142 p->ue_cnt++;
143 p->ueinfo.fault_lo = readl(addr: base + UE_FFD0_OFST);
144 p->ueinfo.fault_hi = readl(addr: base + UE_FFD1_OFST);
145 p->ueinfo.addr = (OCM_BASEVAL | readl(addr: base + UE_FFA_OFST));
146 writel(ECC_CTRL_CLR_UE_ERR, addr: base + OCM_ISR_OFST);
147 }
148}
149
150/**
151 * handle_error - Handle error types CE and UE
152 * @dci: Pointer to the EDAC device instance
153 * @p: Pointer to the OCM ECC status structure
154 *
155 * Handles correctable and uncorrectable errors.
156 */
157static void handle_error(struct edac_device_ctl_info *dci, struct ecc_status *p)
158{
159 struct edac_priv *priv = dci->pvt_info;
160 struct ecc_error_info *pinf;
161
162 if (p->ce_cnt) {
163 pinf = &p->ceinfo;
164 snprintf(buf: priv->message, ZYNQMP_OCM_EDAC_MSG_SIZE,
165 fmt: "\nOCM ECC error type :%s\nAddr: [0x%x]\nFault Data[0x%08x%08x]",
166 "CE", pinf->addr, pinf->fault_hi, pinf->fault_lo);
167 edac_device_handle_ce(edac_dev: dci, inst_nr: 0, block_nr: 0, msg: priv->message);
168 }
169
170 if (p->ue_cnt) {
171 pinf = &p->ueinfo;
172 snprintf(buf: priv->message, ZYNQMP_OCM_EDAC_MSG_SIZE,
173 fmt: "\nOCM ECC error type :%s\nAddr: [0x%x]\nFault Data[0x%08x%08x]",
174 "UE", pinf->addr, pinf->fault_hi, pinf->fault_lo);
175 edac_device_handle_ue(edac_dev: dci, inst_nr: 0, block_nr: 0, msg: priv->message);
176 }
177
178 memset(p, 0, sizeof(*p));
179}
180
181/**
182 * intr_handler - ISR routine
183 * @irq: irq number
184 * @dev_id: device id pointer
185 *
186 * Return: IRQ_NONE, if CE/UE interrupt not set or IRQ_HANDLED otherwise
187 */
188static irqreturn_t intr_handler(int irq, void *dev_id)
189{
190 struct edac_device_ctl_info *dci = dev_id;
191 struct edac_priv *priv = dci->pvt_info;
192 int regval;
193
194 regval = readl(addr: priv->baseaddr + OCM_ISR_OFST);
195 if (!(regval & (OCM_CEINTR_MASK | OCM_UEINTR_MASK))) {
196 WARN_ONCE(1, "Unhandled IRQ%d, ISR: 0x%x", irq, regval);
197 return IRQ_NONE;
198 }
199
200 get_error_info(base: priv->baseaddr, p: &priv->stat, mask: regval);
201
202 priv->ce_cnt += priv->stat.ce_cnt;
203 priv->ue_cnt += priv->stat.ue_cnt;
204 handle_error(dci, p: &priv->stat);
205
206 return IRQ_HANDLED;
207}
208
209/**
210 * get_eccstate - Return the ECC status
211 * @base: Pointer to the OCM base address
212 *
213 * Get the ECC enable/disable status
214 *
215 * Return: ECC status 0/1.
216 */
217static bool get_eccstate(void __iomem *base)
218{
219 return readl(addr: base + ECC_CTRL_OFST) & OCM_ECC_ENABLE_MASK;
220}
221
222#ifdef CONFIG_EDAC_DEBUG
223/**
224 * write_fault_count - write fault injection count
225 * @priv: Pointer to the EDAC private struct
226 *
227 * Update the fault injection count register, once the counter reaches
228 * zero, it injects errors
229 */
230static void write_fault_count(struct edac_priv *priv)
231{
232 u32 ficount = priv->fault_injection_cnt;
233
234 if (ficount & ~OCM_FICOUNT_MASK) {
235 ficount &= OCM_FICOUNT_MASK;
236 edac_printk(KERN_INFO, EDAC_DEVICE,
237 "Fault injection count value truncated to %d\n", ficount);
238 }
239
240 writel(val: ficount, addr: priv->baseaddr + OCM_FIC_OFST);
241}
242
243/*
244 * To get the Correctable Error injected, the following steps are needed:
245 * - Setup the optional Fault Injection Count:
246 * echo <fault_count val> > /sys/kernel/debug/edac/ocm/inject_fault_count
247 * - Write the Correctable Error bit position value:
248 * echo <bit_pos val> > /sys/kernel/debug/edac/ocm/inject_ce_bitpos
249 */
250static ssize_t inject_ce_write(struct file *file, const char __user *data,
251 size_t count, loff_t *ppos)
252{
253 struct edac_device_ctl_info *edac_dev = file->private_data;
254 struct edac_priv *priv = edac_dev->pvt_info;
255 int ret;
256
257 if (!data)
258 return -EFAULT;
259
260 ret = kstrtou8_from_user(s: data, count, base: 0, res: &priv->ce_bitpos);
261 if (ret)
262 return ret;
263
264 if (priv->ce_bitpos > UE_MAX_BITPOS_UPPER)
265 return -EINVAL;
266
267 if (priv->ce_bitpos <= UE_MAX_BITPOS_LOWER) {
268 writel(BIT(priv->ce_bitpos), addr: priv->baseaddr + OCM_FID0_OFST);
269 writel(val: 0, addr: priv->baseaddr + OCM_FID1_OFST);
270 } else {
271 writel(BIT(priv->ce_bitpos - UE_MIN_BITPOS_UPPER),
272 addr: priv->baseaddr + OCM_FID1_OFST);
273 writel(val: 0, addr: priv->baseaddr + OCM_FID0_OFST);
274 }
275
276 write_fault_count(priv);
277
278 return count;
279}
280
281static const struct file_operations inject_ce_fops = {
282 .open = simple_open,
283 .write = inject_ce_write,
284 .llseek = generic_file_llseek,
285};
286
287/*
288 * To get the Uncorrectable Error injected, the following steps are needed:
289 * - Setup the optional Fault Injection Count:
290 * echo <fault_count val> > /sys/kernel/debug/edac/ocm/inject_fault_count
291 * - Write the Uncorrectable Error bit position values:
292 * echo <bit_pos0 val>,<bit_pos1 val> > /sys/kernel/debug/edac/ocm/inject_ue_bitpos
293 */
294static ssize_t inject_ue_write(struct file *file, const char __user *data,
295 size_t count, loff_t *ppos)
296{
297 struct edac_device_ctl_info *edac_dev = file->private_data;
298 struct edac_priv *priv = edac_dev->pvt_info;
299 char buf[6], *pbuf, *token[2];
300 u64 ue_bitpos;
301 int i, ret;
302 u8 len;
303
304 if (!data)
305 return -EFAULT;
306
307 len = min_t(size_t, count, sizeof(buf));
308 if (copy_from_user(to: buf, from: data, n: len))
309 return -EFAULT;
310
311 buf[len] = '\0';
312 pbuf = &buf[0];
313 for (i = 0; i < OCM_NUM_UE_BITPOS; i++)
314 token[i] = strsep(&pbuf, ",");
315
316 ret = kstrtou8(s: token[0], base: 0, res: &priv->ue_bitpos[0]);
317 if (ret)
318 return ret;
319
320 ret = kstrtou8(s: token[1], base: 0, res: &priv->ue_bitpos[1]);
321 if (ret)
322 return ret;
323
324 if (priv->ue_bitpos[0] > UE_MAX_BITPOS_UPPER ||
325 priv->ue_bitpos[1] > UE_MAX_BITPOS_UPPER)
326 return -EINVAL;
327
328 if (priv->ue_bitpos[0] == priv->ue_bitpos[1]) {
329 edac_printk(KERN_ERR, EDAC_DEVICE, "Bit positions should not be equal\n");
330 return -EINVAL;
331 }
332
333 ue_bitpos = BIT(priv->ue_bitpos[0]) | BIT(priv->ue_bitpos[1]);
334
335 writel(val: (u32)ue_bitpos, addr: priv->baseaddr + OCM_FID0_OFST);
336 writel(val: (u32)(ue_bitpos >> 32), addr: priv->baseaddr + OCM_FID1_OFST);
337
338 write_fault_count(priv);
339
340 return count;
341}
342
343static const struct file_operations inject_ue_fops = {
344 .open = simple_open,
345 .write = inject_ue_write,
346 .llseek = generic_file_llseek,
347};
348
349static void setup_debugfs(struct edac_device_ctl_info *edac_dev)
350{
351 struct edac_priv *priv = edac_dev->pvt_info;
352
353 priv->debugfs_dir = edac_debugfs_create_dir(dirname: "ocm");
354 if (!priv->debugfs_dir)
355 return;
356
357 edac_debugfs_create_x32(name: "inject_fault_count", mode: 0644, parent: priv->debugfs_dir,
358 value: &priv->fault_injection_cnt);
359 edac_debugfs_create_file(name: "inject_ue_bitpos", mode: 0644, parent: priv->debugfs_dir,
360 data: edac_dev, fops: &inject_ue_fops);
361 edac_debugfs_create_file(name: "inject_ce_bitpos", mode: 0644, parent: priv->debugfs_dir,
362 data: edac_dev, fops: &inject_ce_fops);
363}
364#endif
365
366static int edac_probe(struct platform_device *pdev)
367{
368 struct edac_device_ctl_info *dci;
369 struct edac_priv *priv;
370 void __iomem *baseaddr;
371 struct resource *res;
372 int irq, ret;
373
374 baseaddr = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res);
375 if (IS_ERR(ptr: baseaddr))
376 return PTR_ERR(ptr: baseaddr);
377
378 if (!get_eccstate(base: baseaddr)) {
379 edac_printk(KERN_INFO, EDAC_DEVICE, "ECC not enabled\n");
380 return -ENXIO;
381 }
382
383 dci = edac_device_alloc_ctl_info(sizeof_private: sizeof(*priv), ZYNQMP_OCM_EDAC_STRING,
384 nr_instances: 1, ZYNQMP_OCM_EDAC_STRING, nr_blocks: 1, offset_value: 0, NULL, nr_attribs: 0,
385 device_index: edac_device_alloc_index());
386 if (!dci)
387 return -ENOMEM;
388
389 priv = dci->pvt_info;
390 platform_set_drvdata(pdev, data: dci);
391 dci->dev = &pdev->dev;
392 priv->baseaddr = baseaddr;
393 dci->mod_name = pdev->dev.driver->name;
394 dci->ctl_name = ZYNQMP_OCM_EDAC_STRING;
395 dci->dev_name = dev_name(dev: &pdev->dev);
396
397 irq = platform_get_irq(pdev, 0);
398 if (irq < 0) {
399 ret = irq;
400 goto free_dev_ctl;
401 }
402
403 ret = devm_request_irq(dev: &pdev->dev, irq, handler: intr_handler, irqflags: 0,
404 devname: dev_name(dev: &pdev->dev), dev_id: dci);
405 if (ret) {
406 edac_printk(KERN_ERR, EDAC_DEVICE, "Failed to request Irq\n");
407 goto free_dev_ctl;
408 }
409
410 /* Enable UE, CE interrupts */
411 writel(val: (OCM_CEINTR_MASK | OCM_UEINTR_MASK), addr: priv->baseaddr + OCM_IEN_OFST);
412
413#ifdef CONFIG_EDAC_DEBUG
414 setup_debugfs(dci);
415#endif
416
417 ret = edac_device_add_device(edac_dev: dci);
418 if (ret)
419 goto free_dev_ctl;
420
421 return 0;
422
423free_dev_ctl:
424 edac_device_free_ctl_info(ctl_info: dci);
425
426 return ret;
427}
428
429static void edac_remove(struct platform_device *pdev)
430{
431 struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);
432 struct edac_priv *priv = dci->pvt_info;
433
434 /* Disable UE, CE interrupts */
435 writel(val: (OCM_CEINTR_MASK | OCM_UEINTR_MASK), addr: priv->baseaddr + OCM_IDS_OFST);
436
437#ifdef CONFIG_EDAC_DEBUG
438 debugfs_remove_recursive(dentry: priv->debugfs_dir);
439#endif
440
441 edac_device_del_device(dev: &pdev->dev);
442 edac_device_free_ctl_info(ctl_info: dci);
443}
444
445static const struct of_device_id zynqmp_ocm_edac_match[] = {
446 { .compatible = "xlnx,zynqmp-ocmc-1.0"},
447 { /* end of table */ }
448};
449
450MODULE_DEVICE_TABLE(of, zynqmp_ocm_edac_match);
451
452static struct platform_driver zynqmp_ocm_edac_driver = {
453 .driver = {
454 .name = "zynqmp-ocm-edac",
455 .of_match_table = zynqmp_ocm_edac_match,
456 },
457 .probe = edac_probe,
458 .remove_new = edac_remove,
459};
460
461module_platform_driver(zynqmp_ocm_edac_driver);
462
463MODULE_AUTHOR("Advanced Micro Devices, Inc");
464MODULE_DESCRIPTION("Xilinx ZynqMP OCM ECC driver");
465MODULE_LICENSE("GPL");
466

source code of linux/drivers/edac/zynqmp_edac.c