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