xref: /openbmc/linux/drivers/edac/zynqmp_edac.c (revision 3bd2706c)
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