1 // SPDX-License-Identifier: GPL-2.0
3 * Xilinx ZynqMP OCM ECC Driver
5 * Copyright (C) 2022 Advanced Micro Devices, Inc.
8 #include <linux/edac.h>
9 #include <linux/interrupt.h>
10 #include <linux/module.h>
12 #include <linux/of_platform.h>
13 #include <linux/platform_device.h>
15 #include "edac_module.h"
17 #define ZYNQMP_OCM_EDAC_MSG_SIZE 256
19 #define ZYNQMP_OCM_EDAC_STRING "zynqmp_ocm"
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
28 /* ECC control register */
29 #define ECC_CTRL_OFST 0x14
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
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
47 /* ECC control register bit field definitions */
48 #define ECC_CTRL_CLR_CE_ERR 0x40
49 #define ECC_CTRL_CLR_UE_ERR 0x80
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
58 #define UE_MAX_BITPOS_LOWER 31
59 #define UE_MIN_BITPOS_UPPER 32
60 #define UE_MAX_BITPOS_UPPER 63
63 #define OCM_CEINTR_MASK BIT(6)
64 #define OCM_UEINTR_MASK BIT(7)
65 #define OCM_ECC_ENABLE_MASK BIT(0)
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"
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)
78 struct ecc_error_info
{
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
94 struct ecc_error_info ceinfo
;
95 struct ecc_error_info ueinfo
;
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
111 void __iomem
*baseaddr
;
112 char message
[ZYNQMP_OCM_EDAC_MSG_SIZE
];
113 struct ecc_status stat
;
116 #ifdef CONFIG_EDAC_DEBUG
117 struct dentry
*debugfs_dir
;
119 u8 ue_bitpos
[OCM_NUM_UE_BITPOS
];
120 u32 fault_injection_cnt
;
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
130 * Determines there is any ECC error or not
133 static void get_error_info(void __iomem
*base
, struct ecc_status
*p
, int mask
)
135 if (mask
& OCM_CEINTR_MASK
) {
137 p
->ceinfo
.fault_lo
= readl(base
+ CE_FFD0_OFST
);
138 p
->ceinfo
.fault_hi
= readl(base
+ CE_FFD1_OFST
);
139 p
->ceinfo
.addr
= (OCM_BASEVAL
| readl(base
+ CE_FFA_OFST
));
140 writel(ECC_CTRL_CLR_CE_ERR
, base
+ OCM_ISR_OFST
);
141 } else if (mask
& OCM_UEINTR_MASK
) {
143 p
->ueinfo
.fault_lo
= readl(base
+ UE_FFD0_OFST
);
144 p
->ueinfo
.fault_hi
= readl(base
+ UE_FFD1_OFST
);
145 p
->ueinfo
.addr
= (OCM_BASEVAL
| readl(base
+ UE_FFA_OFST
));
146 writel(ECC_CTRL_CLR_UE_ERR
, base
+ OCM_ISR_OFST
);
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
155 * Handles correctable and uncorrectable errors.
157 static void handle_error(struct edac_device_ctl_info
*dci
, struct ecc_status
*p
)
159 struct edac_priv
*priv
= dci
->pvt_info
;
160 struct ecc_error_info
*pinf
;
164 snprintf(priv
->message
, ZYNQMP_OCM_EDAC_MSG_SIZE
,
165 "\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(dci
, 0, 0, priv
->message
);
172 snprintf(priv
->message
, ZYNQMP_OCM_EDAC_MSG_SIZE
,
173 "\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(dci
, 0, 0, priv
->message
);
178 memset(p
, 0, sizeof(*p
));
182 * intr_handler - ISR routine
184 * @dev_id: device id pointer
186 * Return: IRQ_NONE, if CE/UE interrupt not set or IRQ_HANDLED otherwise
188 static irqreturn_t
intr_handler(int irq
, void *dev_id
)
190 struct edac_device_ctl_info
*dci
= dev_id
;
191 struct edac_priv
*priv
= dci
->pvt_info
;
194 regval
= readl(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
);
200 get_error_info(priv
->baseaddr
, &priv
->stat
, regval
);
202 priv
->ce_cnt
+= priv
->stat
.ce_cnt
;
203 priv
->ue_cnt
+= priv
->stat
.ue_cnt
;
204 handle_error(dci
, &priv
->stat
);
210 * get_eccstate - Return the ECC status
211 * @base: Pointer to the OCM base address
213 * Get the ECC enable/disable status
215 * Return: ECC status 0/1.
217 static bool get_eccstate(void __iomem
*base
)
219 return readl(base
+ ECC_CTRL_OFST
) & OCM_ECC_ENABLE_MASK
;
222 #ifdef CONFIG_EDAC_DEBUG
224 * write_fault_count - write fault injection count
225 * @priv: Pointer to the EDAC private struct
227 * Update the fault injection count register, once the counter reaches
228 * zero, it injects errors
230 static void write_fault_count(struct edac_priv
*priv
)
232 u32 ficount
= priv
->fault_injection_cnt
;
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
);
240 writel(ficount
, priv
->baseaddr
+ OCM_FIC_OFST
);
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
250 static ssize_t
inject_ce_write(struct file
*file
, const char __user
*data
,
251 size_t count
, loff_t
*ppos
)
253 struct edac_device_ctl_info
*edac_dev
= file
->private_data
;
254 struct edac_priv
*priv
= edac_dev
->pvt_info
;
260 ret
= kstrtou8_from_user(data
, count
, 0, &priv
->ce_bitpos
);
264 if (priv
->ce_bitpos
> UE_MAX_BITPOS_UPPER
)
267 if (priv
->ce_bitpos
<= UE_MAX_BITPOS_LOWER
) {
268 writel(BIT(priv
->ce_bitpos
), priv
->baseaddr
+ OCM_FID0_OFST
);
269 writel(0, priv
->baseaddr
+ OCM_FID1_OFST
);
271 writel(BIT(priv
->ce_bitpos
- UE_MIN_BITPOS_UPPER
),
272 priv
->baseaddr
+ OCM_FID1_OFST
);
273 writel(0, priv
->baseaddr
+ OCM_FID0_OFST
);
276 write_fault_count(priv
);
281 static const struct file_operations inject_ce_fops
= {
283 .write
= inject_ce_write
,
284 .llseek
= generic_file_llseek
,
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
294 static ssize_t
inject_ue_write(struct file
*file
, const char __user
*data
,
295 size_t count
, loff_t
*ppos
)
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];
307 len
= min_t(size_t, count
, sizeof(buf
));
308 if (copy_from_user(buf
, data
, len
))
313 for (i
= 0; i
< OCM_NUM_UE_BITPOS
; i
++)
314 token
[i
] = strsep(&pbuf
, ",");
316 ret
= kstrtou8(token
[0], 0, &priv
->ue_bitpos
[0]);
320 ret
= kstrtou8(token
[1], 0, &priv
->ue_bitpos
[1]);
324 if (priv
->ue_bitpos
[0] > UE_MAX_BITPOS_UPPER
||
325 priv
->ue_bitpos
[1] > UE_MAX_BITPOS_UPPER
)
328 if (priv
->ue_bitpos
[0] == priv
->ue_bitpos
[1]) {
329 edac_printk(KERN_ERR
, EDAC_DEVICE
, "Bit positions should not be equal\n");
333 ue_bitpos
= BIT(priv
->ue_bitpos
[0]) | BIT(priv
->ue_bitpos
[1]);
335 writel((u32
)ue_bitpos
, priv
->baseaddr
+ OCM_FID0_OFST
);
336 writel((u32
)(ue_bitpos
>> 32), priv
->baseaddr
+ OCM_FID1_OFST
);
338 write_fault_count(priv
);
343 static const struct file_operations inject_ue_fops
= {
345 .write
= inject_ue_write
,
346 .llseek
= generic_file_llseek
,
349 static void setup_debugfs(struct edac_device_ctl_info
*edac_dev
)
351 struct edac_priv
*priv
= edac_dev
->pvt_info
;
353 priv
->debugfs_dir
= edac_debugfs_create_dir("ocm");
354 if (!priv
->debugfs_dir
)
357 edac_debugfs_create_x32("inject_fault_count", 0644, priv
->debugfs_dir
,
358 &priv
->fault_injection_cnt
);
359 edac_debugfs_create_file("inject_ue_bitpos", 0644, priv
->debugfs_dir
,
360 edac_dev
, &inject_ue_fops
);
361 edac_debugfs_create_file("inject_ce_bitpos", 0644, priv
->debugfs_dir
,
362 edac_dev
, &inject_ce_fops
);
366 static int edac_probe(struct platform_device
*pdev
)
368 struct edac_device_ctl_info
*dci
;
369 struct edac_priv
*priv
;
370 void __iomem
*baseaddr
;
371 struct resource
*res
;
374 baseaddr
= devm_platform_get_and_ioremap_resource(pdev
, 0, &res
);
375 if (IS_ERR(baseaddr
))
376 return PTR_ERR(baseaddr
);
378 if (!get_eccstate(baseaddr
)) {
379 edac_printk(KERN_INFO
, EDAC_DEVICE
, "ECC not enabled\n");
383 dci
= edac_device_alloc_ctl_info(sizeof(*priv
), ZYNQMP_OCM_EDAC_STRING
,
384 1, ZYNQMP_OCM_EDAC_STRING
, 1, 0,
385 edac_device_alloc_index());
389 priv
= dci
->pvt_info
;
390 platform_set_drvdata(pdev
, 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(&pdev
->dev
);
397 irq
= platform_get_irq(pdev
, 0);
403 ret
= devm_request_irq(&pdev
->dev
, irq
, intr_handler
, 0,
404 dev_name(&pdev
->dev
), dci
);
406 edac_printk(KERN_ERR
, EDAC_DEVICE
, "Failed to request Irq\n");
410 /* Enable UE, CE interrupts */
411 writel((OCM_CEINTR_MASK
| OCM_UEINTR_MASK
), priv
->baseaddr
+ OCM_IEN_OFST
);
413 #ifdef CONFIG_EDAC_DEBUG
417 ret
= edac_device_add_device(dci
);
424 edac_device_free_ctl_info(dci
);
429 static void edac_remove(struct platform_device
*pdev
)
431 struct edac_device_ctl_info
*dci
= platform_get_drvdata(pdev
);
432 struct edac_priv
*priv
= dci
->pvt_info
;
434 /* Disable UE, CE interrupts */
435 writel((OCM_CEINTR_MASK
| OCM_UEINTR_MASK
), priv
->baseaddr
+ OCM_IDS_OFST
);
437 #ifdef CONFIG_EDAC_DEBUG
438 debugfs_remove_recursive(priv
->debugfs_dir
);
441 edac_device_del_device(&pdev
->dev
);
442 edac_device_free_ctl_info(dci
);
445 static const struct of_device_id zynqmp_ocm_edac_match
[] = {
446 { .compatible
= "xlnx,zynqmp-ocmc-1.0"},
447 { /* end of table */ }
450 MODULE_DEVICE_TABLE(of
, zynqmp_ocm_edac_match
);
452 static struct platform_driver zynqmp_ocm_edac_driver
= {
454 .name
= "zynqmp-ocm-edac",
455 .of_match_table
= zynqmp_ocm_edac_match
,
458 .remove_new
= edac_remove
,
461 module_platform_driver(zynqmp_ocm_edac_driver
);
463 MODULE_AUTHOR("Advanced Micro Devices, Inc");
464 MODULE_DESCRIPTION("Xilinx ZynqMP OCM ECC driver");
465 MODULE_LICENSE("GPL");