xref: /openbmc/linux/drivers/edac/npcm_edac.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1d244c610SMarvin Lin // SPDX-License-Identifier: GPL-2.0-only
2d244c610SMarvin Lin // Copyright (c) 2022 Nuvoton Technology Corporation
3d244c610SMarvin Lin 
4d244c610SMarvin Lin #include <linux/debugfs.h>
5d244c610SMarvin Lin #include <linux/iopoll.h>
6*408d8088SRob Herring #include <linux/of.h>
7*408d8088SRob Herring #include <linux/platform_device.h>
8d244c610SMarvin Lin #include <linux/regmap.h>
9d244c610SMarvin Lin #include "edac_module.h"
10d244c610SMarvin Lin 
11d244c610SMarvin Lin #define EDAC_MOD_NAME			"npcm-edac"
12d244c610SMarvin Lin #define EDAC_MSG_SIZE			256
13d244c610SMarvin Lin 
14d244c610SMarvin Lin /* chip serials */
15d244c610SMarvin Lin #define NPCM7XX_CHIP			BIT(0)
16d244c610SMarvin Lin #define NPCM8XX_CHIP			BIT(1)
17d244c610SMarvin Lin 
18d244c610SMarvin Lin /* syndrome values */
19d244c610SMarvin Lin #define UE_SYNDROME			0x03
20d244c610SMarvin Lin 
21d244c610SMarvin Lin /* error injection */
22d244c610SMarvin Lin #define ERROR_TYPE_CORRECTABLE		0
23d244c610SMarvin Lin #define ERROR_TYPE_UNCORRECTABLE	1
24d244c610SMarvin Lin #define ERROR_LOCATION_DATA		0
25d244c610SMarvin Lin #define ERROR_LOCATION_CHECKCODE	1
26d244c610SMarvin Lin #define ERROR_BIT_DATA_MAX		63
27d244c610SMarvin Lin #define ERROR_BIT_CHECKCODE_MAX		7
28d244c610SMarvin Lin 
29d244c610SMarvin Lin static char data_synd[] = {
30d244c610SMarvin Lin 	0xf4, 0xf1, 0xec, 0xea, 0xe9, 0xe6, 0xe5, 0xe3,
31d244c610SMarvin Lin 	0xdc, 0xda, 0xd9, 0xd6, 0xd5, 0xd3, 0xce, 0xcb,
32d244c610SMarvin Lin 	0xb5, 0xb0, 0xad, 0xab, 0xa8, 0xa7, 0xa4, 0xa2,
33d244c610SMarvin Lin 	0x9d, 0x9b, 0x98, 0x97, 0x94, 0x92, 0x8f, 0x8a,
34d244c610SMarvin Lin 	0x75, 0x70, 0x6d, 0x6b, 0x68, 0x67, 0x64, 0x62,
35d244c610SMarvin Lin 	0x5e, 0x5b, 0x58, 0x57, 0x54, 0x52, 0x4f, 0x4a,
36d244c610SMarvin Lin 	0x34, 0x31, 0x2c, 0x2a, 0x29, 0x26, 0x25, 0x23,
37d244c610SMarvin Lin 	0x1c, 0x1a, 0x19, 0x16, 0x15, 0x13, 0x0e, 0x0b
38d244c610SMarvin Lin };
39d244c610SMarvin Lin 
40d244c610SMarvin Lin static struct regmap *npcm_regmap;
41d244c610SMarvin Lin 
42d244c610SMarvin Lin struct npcm_platform_data {
43d244c610SMarvin Lin 	/* chip serials */
44d244c610SMarvin Lin 	int chip;
45d244c610SMarvin Lin 
46d244c610SMarvin Lin 	/* memory controller registers */
47d244c610SMarvin Lin 	u32 ctl_ecc_en;
48d244c610SMarvin Lin 	u32 ctl_int_status;
49d244c610SMarvin Lin 	u32 ctl_int_ack;
50d244c610SMarvin Lin 	u32 ctl_int_mask_master;
51d244c610SMarvin Lin 	u32 ctl_int_mask_ecc;
52d244c610SMarvin Lin 	u32 ctl_ce_addr_l;
53d244c610SMarvin Lin 	u32 ctl_ce_addr_h;
54d244c610SMarvin Lin 	u32 ctl_ce_data_l;
55d244c610SMarvin Lin 	u32 ctl_ce_data_h;
56d244c610SMarvin Lin 	u32 ctl_ce_synd;
57d244c610SMarvin Lin 	u32 ctl_ue_addr_l;
58d244c610SMarvin Lin 	u32 ctl_ue_addr_h;
59d244c610SMarvin Lin 	u32 ctl_ue_data_l;
60d244c610SMarvin Lin 	u32 ctl_ue_data_h;
61d244c610SMarvin Lin 	u32 ctl_ue_synd;
62d244c610SMarvin Lin 	u32 ctl_source_id;
63d244c610SMarvin Lin 	u32 ctl_controller_busy;
64d244c610SMarvin Lin 	u32 ctl_xor_check_bits;
65d244c610SMarvin Lin 
66d244c610SMarvin Lin 	/* masks and shifts */
67d244c610SMarvin Lin 	u32 ecc_en_mask;
68d244c610SMarvin Lin 	u32 int_status_ce_mask;
69d244c610SMarvin Lin 	u32 int_status_ue_mask;
70d244c610SMarvin Lin 	u32 int_ack_ce_mask;
71d244c610SMarvin Lin 	u32 int_ack_ue_mask;
72d244c610SMarvin Lin 	u32 int_mask_master_non_ecc_mask;
73d244c610SMarvin Lin 	u32 int_mask_master_global_mask;
74d244c610SMarvin Lin 	u32 int_mask_ecc_non_event_mask;
75d244c610SMarvin Lin 	u32 ce_addr_h_mask;
76d244c610SMarvin Lin 	u32 ce_synd_mask;
77d244c610SMarvin Lin 	u32 ce_synd_shift;
78d244c610SMarvin Lin 	u32 ue_addr_h_mask;
79d244c610SMarvin Lin 	u32 ue_synd_mask;
80d244c610SMarvin Lin 	u32 ue_synd_shift;
81d244c610SMarvin Lin 	u32 source_id_ce_mask;
82d244c610SMarvin Lin 	u32 source_id_ce_shift;
83d244c610SMarvin Lin 	u32 source_id_ue_mask;
84d244c610SMarvin Lin 	u32 source_id_ue_shift;
85d244c610SMarvin Lin 	u32 controller_busy_mask;
86d244c610SMarvin Lin 	u32 xor_check_bits_mask;
87d244c610SMarvin Lin 	u32 xor_check_bits_shift;
88d244c610SMarvin Lin 	u32 writeback_en_mask;
89d244c610SMarvin Lin 	u32 fwc_mask;
90d244c610SMarvin Lin };
91d244c610SMarvin Lin 
92d244c610SMarvin Lin struct priv_data {
93d244c610SMarvin Lin 	void __iomem *reg;
94d244c610SMarvin Lin 	char message[EDAC_MSG_SIZE];
95d244c610SMarvin Lin 	const struct npcm_platform_data *pdata;
96d244c610SMarvin Lin 
97d244c610SMarvin Lin 	/* error injection */
98d244c610SMarvin Lin 	struct dentry *debugfs;
99d244c610SMarvin Lin 	u8 error_type;
100d244c610SMarvin Lin 	u8 location;
101d244c610SMarvin Lin 	u8 bit;
102d244c610SMarvin Lin };
103d244c610SMarvin Lin 
handle_ce(struct mem_ctl_info * mci)104d244c610SMarvin Lin static void handle_ce(struct mem_ctl_info *mci)
105d244c610SMarvin Lin {
106d244c610SMarvin Lin 	struct priv_data *priv = mci->pvt_info;
107d244c610SMarvin Lin 	const struct npcm_platform_data *pdata;
108d244c610SMarvin Lin 	u32 val_h = 0, val_l, id, synd;
109d244c610SMarvin Lin 	u64 addr = 0, data = 0;
110d244c610SMarvin Lin 
111d244c610SMarvin Lin 	pdata = priv->pdata;
112d244c610SMarvin Lin 	regmap_read(npcm_regmap, pdata->ctl_ce_addr_l, &val_l);
113d244c610SMarvin Lin 	if (pdata->chip == NPCM8XX_CHIP) {
114d244c610SMarvin Lin 		regmap_read(npcm_regmap, pdata->ctl_ce_addr_h, &val_h);
115d244c610SMarvin Lin 		val_h &= pdata->ce_addr_h_mask;
116d244c610SMarvin Lin 	}
117d244c610SMarvin Lin 	addr = ((addr | val_h) << 32) | val_l;
118d244c610SMarvin Lin 
119d244c610SMarvin Lin 	regmap_read(npcm_regmap, pdata->ctl_ce_data_l, &val_l);
120d244c610SMarvin Lin 	if (pdata->chip == NPCM8XX_CHIP)
121d244c610SMarvin Lin 		regmap_read(npcm_regmap, pdata->ctl_ce_data_h, &val_h);
122d244c610SMarvin Lin 	data = ((data | val_h) << 32) | val_l;
123d244c610SMarvin Lin 
124d244c610SMarvin Lin 	regmap_read(npcm_regmap, pdata->ctl_source_id, &id);
125d244c610SMarvin Lin 	id = (id & pdata->source_id_ce_mask) >> pdata->source_id_ce_shift;
126d244c610SMarvin Lin 
127d244c610SMarvin Lin 	regmap_read(npcm_regmap, pdata->ctl_ce_synd, &synd);
128d244c610SMarvin Lin 	synd = (synd & pdata->ce_synd_mask) >> pdata->ce_synd_shift;
129d244c610SMarvin Lin 
130d244c610SMarvin Lin 	snprintf(priv->message, EDAC_MSG_SIZE,
131d244c610SMarvin Lin 		 "addr = 0x%llx, data = 0x%llx, id = 0x%x", addr, data, id);
132d244c610SMarvin Lin 
133d244c610SMarvin Lin 	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, addr >> PAGE_SHIFT,
134d244c610SMarvin Lin 			     addr & ~PAGE_MASK, synd, 0, 0, -1, priv->message, "");
135d244c610SMarvin Lin }
136d244c610SMarvin Lin 
handle_ue(struct mem_ctl_info * mci)137d244c610SMarvin Lin static void handle_ue(struct mem_ctl_info *mci)
138d244c610SMarvin Lin {
139d244c610SMarvin Lin 	struct priv_data *priv = mci->pvt_info;
140d244c610SMarvin Lin 	const struct npcm_platform_data *pdata;
141d244c610SMarvin Lin 	u32 val_h = 0, val_l, id, synd;
142d244c610SMarvin Lin 	u64 addr = 0, data = 0;
143d244c610SMarvin Lin 
144d244c610SMarvin Lin 	pdata = priv->pdata;
145d244c610SMarvin Lin 	regmap_read(npcm_regmap, pdata->ctl_ue_addr_l, &val_l);
146d244c610SMarvin Lin 	if (pdata->chip == NPCM8XX_CHIP) {
147d244c610SMarvin Lin 		regmap_read(npcm_regmap, pdata->ctl_ue_addr_h, &val_h);
148d244c610SMarvin Lin 		val_h &= pdata->ue_addr_h_mask;
149d244c610SMarvin Lin 	}
150d244c610SMarvin Lin 	addr = ((addr | val_h) << 32) | val_l;
151d244c610SMarvin Lin 
152d244c610SMarvin Lin 	regmap_read(npcm_regmap, pdata->ctl_ue_data_l, &val_l);
153d244c610SMarvin Lin 	if (pdata->chip == NPCM8XX_CHIP)
154d244c610SMarvin Lin 		regmap_read(npcm_regmap, pdata->ctl_ue_data_h, &val_h);
155d244c610SMarvin Lin 	data = ((data | val_h) << 32) | val_l;
156d244c610SMarvin Lin 
157d244c610SMarvin Lin 	regmap_read(npcm_regmap, pdata->ctl_source_id, &id);
158d244c610SMarvin Lin 	id = (id & pdata->source_id_ue_mask) >> pdata->source_id_ue_shift;
159d244c610SMarvin Lin 
160d244c610SMarvin Lin 	regmap_read(npcm_regmap, pdata->ctl_ue_synd, &synd);
161d244c610SMarvin Lin 	synd = (synd & pdata->ue_synd_mask) >> pdata->ue_synd_shift;
162d244c610SMarvin Lin 
163d244c610SMarvin Lin 	snprintf(priv->message, EDAC_MSG_SIZE,
164d244c610SMarvin Lin 		 "addr = 0x%llx, data = 0x%llx, id = 0x%x", addr, data, id);
165d244c610SMarvin Lin 
166d244c610SMarvin Lin 	edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, addr >> PAGE_SHIFT,
167d244c610SMarvin Lin 			     addr & ~PAGE_MASK, synd, 0, 0, -1, priv->message, "");
168d244c610SMarvin Lin }
169d244c610SMarvin Lin 
edac_ecc_isr(int irq,void * dev_id)170d244c610SMarvin Lin static irqreturn_t edac_ecc_isr(int irq, void *dev_id)
171d244c610SMarvin Lin {
172d244c610SMarvin Lin 	const struct npcm_platform_data *pdata;
173d244c610SMarvin Lin 	struct mem_ctl_info *mci = dev_id;
174d244c610SMarvin Lin 	u32 status;
175d244c610SMarvin Lin 
176d244c610SMarvin Lin 	pdata = ((struct priv_data *)mci->pvt_info)->pdata;
177d244c610SMarvin Lin 	regmap_read(npcm_regmap, pdata->ctl_int_status, &status);
178d244c610SMarvin Lin 	if (status & pdata->int_status_ce_mask) {
179d244c610SMarvin Lin 		handle_ce(mci);
180d244c610SMarvin Lin 
181d244c610SMarvin Lin 		/* acknowledge the CE interrupt */
182d244c610SMarvin Lin 		regmap_write(npcm_regmap, pdata->ctl_int_ack,
183d244c610SMarvin Lin 			     pdata->int_ack_ce_mask);
184d244c610SMarvin Lin 		return IRQ_HANDLED;
185d244c610SMarvin Lin 	} else if (status & pdata->int_status_ue_mask) {
186d244c610SMarvin Lin 		handle_ue(mci);
187d244c610SMarvin Lin 
188d244c610SMarvin Lin 		/* acknowledge the UE interrupt */
189d244c610SMarvin Lin 		regmap_write(npcm_regmap, pdata->ctl_int_ack,
190d244c610SMarvin Lin 			     pdata->int_ack_ue_mask);
191d244c610SMarvin Lin 		return IRQ_HANDLED;
192d244c610SMarvin Lin 	}
193d244c610SMarvin Lin 
194d244c610SMarvin Lin 	WARN_ON_ONCE(1);
195d244c610SMarvin Lin 	return IRQ_NONE;
196d244c610SMarvin Lin }
197d244c610SMarvin Lin 
force_ecc_error(struct file * file,const char __user * data,size_t count,loff_t * ppos)198d244c610SMarvin Lin static ssize_t force_ecc_error(struct file *file, const char __user *data,
199d244c610SMarvin Lin 			       size_t count, loff_t *ppos)
200d244c610SMarvin Lin {
201d244c610SMarvin Lin 	struct device *dev = file->private_data;
202d244c610SMarvin Lin 	struct mem_ctl_info *mci = to_mci(dev);
203d244c610SMarvin Lin 	struct priv_data *priv = mci->pvt_info;
204d244c610SMarvin Lin 	const struct npcm_platform_data *pdata;
205d244c610SMarvin Lin 	u32 val, syndrome;
206d244c610SMarvin Lin 	int ret;
207d244c610SMarvin Lin 
208d244c610SMarvin Lin 	pdata = priv->pdata;
209d244c610SMarvin Lin 	edac_printk(KERN_INFO, EDAC_MOD_NAME,
210d244c610SMarvin Lin 		    "force an ECC error, type = %d, location = %d, bit = %d\n",
211d244c610SMarvin Lin 		    priv->error_type, priv->location, priv->bit);
212d244c610SMarvin Lin 
213d244c610SMarvin Lin 	/* ensure no pending writes */
214d244c610SMarvin Lin 	ret = regmap_read_poll_timeout(npcm_regmap, pdata->ctl_controller_busy,
215d244c610SMarvin Lin 				       val, !(val & pdata->controller_busy_mask),
216d244c610SMarvin Lin 				       1000, 10000);
217d244c610SMarvin Lin 	if (ret) {
218d244c610SMarvin Lin 		edac_printk(KERN_INFO, EDAC_MOD_NAME,
219d244c610SMarvin Lin 			    "wait pending writes timeout\n");
220d244c610SMarvin Lin 		return count;
221d244c610SMarvin Lin 	}
222d244c610SMarvin Lin 
223d244c610SMarvin Lin 	regmap_read(npcm_regmap, pdata->ctl_xor_check_bits, &val);
224d244c610SMarvin Lin 	val &= ~pdata->xor_check_bits_mask;
225d244c610SMarvin Lin 
226d244c610SMarvin Lin 	/* write syndrome to XOR_CHECK_BITS */
227d244c610SMarvin Lin 	if (priv->error_type == ERROR_TYPE_CORRECTABLE) {
228d244c610SMarvin Lin 		if (priv->location == ERROR_LOCATION_DATA &&
229d244c610SMarvin Lin 		    priv->bit > ERROR_BIT_DATA_MAX) {
230d244c610SMarvin Lin 			edac_printk(KERN_INFO, EDAC_MOD_NAME,
231d244c610SMarvin Lin 				    "data bit should not exceed %d (%d)\n",
232d244c610SMarvin Lin 				    ERROR_BIT_DATA_MAX, priv->bit);
233d244c610SMarvin Lin 			return count;
234d244c610SMarvin Lin 		}
235d244c610SMarvin Lin 
236d244c610SMarvin Lin 		if (priv->location == ERROR_LOCATION_CHECKCODE &&
237d244c610SMarvin Lin 		    priv->bit > ERROR_BIT_CHECKCODE_MAX) {
238d244c610SMarvin Lin 			edac_printk(KERN_INFO, EDAC_MOD_NAME,
239d244c610SMarvin Lin 				    "checkcode bit should not exceed %d (%d)\n",
240d244c610SMarvin Lin 				    ERROR_BIT_CHECKCODE_MAX, priv->bit);
241d244c610SMarvin Lin 			return count;
242d244c610SMarvin Lin 		}
243d244c610SMarvin Lin 
244d244c610SMarvin Lin 		syndrome = priv->location ? 1 << priv->bit
245d244c610SMarvin Lin 					  : data_synd[priv->bit];
246d244c610SMarvin Lin 
247d244c610SMarvin Lin 		regmap_write(npcm_regmap, pdata->ctl_xor_check_bits,
248d244c610SMarvin Lin 			     val | (syndrome << pdata->xor_check_bits_shift) |
249d244c610SMarvin Lin 			     pdata->writeback_en_mask);
250d244c610SMarvin Lin 	} else if (priv->error_type == ERROR_TYPE_UNCORRECTABLE) {
251d244c610SMarvin Lin 		regmap_write(npcm_regmap, pdata->ctl_xor_check_bits,
252d244c610SMarvin Lin 			     val | (UE_SYNDROME << pdata->xor_check_bits_shift));
253d244c610SMarvin Lin 	}
254d244c610SMarvin Lin 
255d244c610SMarvin Lin 	/* force write check */
256d244c610SMarvin Lin 	regmap_update_bits(npcm_regmap, pdata->ctl_xor_check_bits,
257d244c610SMarvin Lin 			   pdata->fwc_mask, pdata->fwc_mask);
258d244c610SMarvin Lin 
259d244c610SMarvin Lin 	return count;
260d244c610SMarvin Lin }
261d244c610SMarvin Lin 
262d244c610SMarvin Lin static const struct file_operations force_ecc_error_fops = {
263d244c610SMarvin Lin 	.open = simple_open,
264d244c610SMarvin Lin 	.write = force_ecc_error,
265d244c610SMarvin Lin 	.llseek = generic_file_llseek,
266d244c610SMarvin Lin };
267d244c610SMarvin Lin 
268d244c610SMarvin Lin /*
269d244c610SMarvin Lin  * Setup debugfs for error injection.
270d244c610SMarvin Lin  *
271d244c610SMarvin Lin  * Nodes:
272d244c610SMarvin Lin  *   error_type		- 0: CE, 1: UE
273d244c610SMarvin Lin  *   location		- 0: data, 1: checkcode
274d244c610SMarvin Lin  *   bit		- 0 ~ 63 for data and 0 ~ 7 for checkcode
275d244c610SMarvin Lin  *   force_ecc_error	- trigger
276d244c610SMarvin Lin  *
277d244c610SMarvin Lin  * Examples:
278d244c610SMarvin Lin  *   1. Inject a correctable error (CE) at checkcode bit 7.
279d244c610SMarvin Lin  *      ~# echo 0 > /sys/kernel/debug/edac/npcm-edac/error_type
280d244c610SMarvin Lin  *      ~# echo 1 > /sys/kernel/debug/edac/npcm-edac/location
281d244c610SMarvin Lin  *      ~# echo 7 > /sys/kernel/debug/edac/npcm-edac/bit
282d244c610SMarvin Lin  *      ~# echo 1 > /sys/kernel/debug/edac/npcm-edac/force_ecc_error
283d244c610SMarvin Lin  *
284d244c610SMarvin Lin  *   2. Inject an uncorrectable error (UE).
285d244c610SMarvin Lin  *      ~# echo 1 > /sys/kernel/debug/edac/npcm-edac/error_type
286d244c610SMarvin Lin  *      ~# echo 1 > /sys/kernel/debug/edac/npcm-edac/force_ecc_error
287d244c610SMarvin Lin  */
setup_debugfs(struct mem_ctl_info * mci)288d244c610SMarvin Lin static void setup_debugfs(struct mem_ctl_info *mci)
289d244c610SMarvin Lin {
290d244c610SMarvin Lin 	struct priv_data *priv = mci->pvt_info;
291d244c610SMarvin Lin 
292d244c610SMarvin Lin 	priv->debugfs = edac_debugfs_create_dir(mci->mod_name);
293d244c610SMarvin Lin 	if (!priv->debugfs)
294d244c610SMarvin Lin 		return;
295d244c610SMarvin Lin 
296d244c610SMarvin Lin 	edac_debugfs_create_x8("error_type", 0644, priv->debugfs, &priv->error_type);
297d244c610SMarvin Lin 	edac_debugfs_create_x8("location", 0644, priv->debugfs, &priv->location);
298d244c610SMarvin Lin 	edac_debugfs_create_x8("bit", 0644, priv->debugfs, &priv->bit);
299d244c610SMarvin Lin 	edac_debugfs_create_file("force_ecc_error", 0200, priv->debugfs,
300d244c610SMarvin Lin 				 &mci->dev, &force_ecc_error_fops);
301d244c610SMarvin Lin }
302d244c610SMarvin Lin 
setup_irq(struct mem_ctl_info * mci,struct platform_device * pdev)303d244c610SMarvin Lin static int setup_irq(struct mem_ctl_info *mci, struct platform_device *pdev)
304d244c610SMarvin Lin {
305d244c610SMarvin Lin 	const struct npcm_platform_data *pdata;
306d244c610SMarvin Lin 	int ret, irq;
307d244c610SMarvin Lin 
308d244c610SMarvin Lin 	pdata = ((struct priv_data *)mci->pvt_info)->pdata;
309d244c610SMarvin Lin 	irq = platform_get_irq(pdev, 0);
310d244c610SMarvin Lin 	if (irq < 0) {
311d244c610SMarvin Lin 		edac_printk(KERN_ERR, EDAC_MOD_NAME, "IRQ not defined in DTS\n");
312d244c610SMarvin Lin 		return irq;
313d244c610SMarvin Lin 	}
314d244c610SMarvin Lin 
315d244c610SMarvin Lin 	ret = devm_request_irq(&pdev->dev, irq, edac_ecc_isr, 0,
316d244c610SMarvin Lin 			       dev_name(&pdev->dev), mci);
317d244c610SMarvin Lin 	if (ret < 0) {
318d244c610SMarvin Lin 		edac_printk(KERN_ERR, EDAC_MOD_NAME, "failed to request IRQ\n");
319d244c610SMarvin Lin 		return ret;
320d244c610SMarvin Lin 	}
321d244c610SMarvin Lin 
322d244c610SMarvin Lin 	/* enable the functional group of ECC and mask the others */
323d244c610SMarvin Lin 	regmap_write(npcm_regmap, pdata->ctl_int_mask_master,
324d244c610SMarvin Lin 		     pdata->int_mask_master_non_ecc_mask);
325d244c610SMarvin Lin 
326d244c610SMarvin Lin 	if (pdata->chip == NPCM8XX_CHIP)
327d244c610SMarvin Lin 		regmap_write(npcm_regmap, pdata->ctl_int_mask_ecc,
328d244c610SMarvin Lin 			     pdata->int_mask_ecc_non_event_mask);
329d244c610SMarvin Lin 
330d244c610SMarvin Lin 	return 0;
331d244c610SMarvin Lin }
332d244c610SMarvin Lin 
333d244c610SMarvin Lin static const struct regmap_config npcm_regmap_cfg = {
334d244c610SMarvin Lin 	.reg_bits	= 32,
335d244c610SMarvin Lin 	.reg_stride	= 4,
336d244c610SMarvin Lin 	.val_bits	= 32,
337d244c610SMarvin Lin };
338d244c610SMarvin Lin 
edac_probe(struct platform_device * pdev)339d244c610SMarvin Lin static int edac_probe(struct platform_device *pdev)
340d244c610SMarvin Lin {
341d244c610SMarvin Lin 	const struct npcm_platform_data *pdata;
342d244c610SMarvin Lin 	struct device *dev = &pdev->dev;
343d244c610SMarvin Lin 	struct edac_mc_layer layers[1];
344d244c610SMarvin Lin 	struct mem_ctl_info *mci;
345d244c610SMarvin Lin 	struct priv_data *priv;
346d244c610SMarvin Lin 	void __iomem *reg;
347d244c610SMarvin Lin 	u32 val;
348d244c610SMarvin Lin 	int rc;
349d244c610SMarvin Lin 
350d244c610SMarvin Lin 	reg = devm_platform_ioremap_resource(pdev, 0);
351d244c610SMarvin Lin 	if (IS_ERR(reg))
352d244c610SMarvin Lin 		return PTR_ERR(reg);
353d244c610SMarvin Lin 
354d244c610SMarvin Lin 	npcm_regmap = devm_regmap_init_mmio(dev, reg, &npcm_regmap_cfg);
355d244c610SMarvin Lin 	if (IS_ERR(npcm_regmap))
356d244c610SMarvin Lin 		return PTR_ERR(npcm_regmap);
357d244c610SMarvin Lin 
358d244c610SMarvin Lin 	pdata = of_device_get_match_data(dev);
359d244c610SMarvin Lin 	if (!pdata)
360d244c610SMarvin Lin 		return -EINVAL;
361d244c610SMarvin Lin 
362d244c610SMarvin Lin 	/* bail out if ECC is not enabled */
363d244c610SMarvin Lin 	regmap_read(npcm_regmap, pdata->ctl_ecc_en, &val);
364d244c610SMarvin Lin 	if (!(val & pdata->ecc_en_mask)) {
365d244c610SMarvin Lin 		edac_printk(KERN_ERR, EDAC_MOD_NAME, "ECC is not enabled\n");
366d244c610SMarvin Lin 		return -EPERM;
367d244c610SMarvin Lin 	}
368d244c610SMarvin Lin 
369d244c610SMarvin Lin 	edac_op_state = EDAC_OPSTATE_INT;
370d244c610SMarvin Lin 
371d244c610SMarvin Lin 	layers[0].type = EDAC_MC_LAYER_ALL_MEM;
372d244c610SMarvin Lin 	layers[0].size = 1;
373d244c610SMarvin Lin 
374d244c610SMarvin Lin 	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
375d244c610SMarvin Lin 			    sizeof(struct priv_data));
376d244c610SMarvin Lin 	if (!mci)
377d244c610SMarvin Lin 		return -ENOMEM;
378d244c610SMarvin Lin 
379d244c610SMarvin Lin 	mci->pdev = &pdev->dev;
380d244c610SMarvin Lin 	priv = mci->pvt_info;
381d244c610SMarvin Lin 	priv->reg = reg;
382d244c610SMarvin Lin 	priv->pdata = pdata;
383d244c610SMarvin Lin 	platform_set_drvdata(pdev, mci);
384d244c610SMarvin Lin 
385d244c610SMarvin Lin 	mci->mtype_cap = MEM_FLAG_DDR4;
386d244c610SMarvin Lin 	mci->edac_ctl_cap = EDAC_FLAG_SECDED;
387d244c610SMarvin Lin 	mci->scrub_cap = SCRUB_FLAG_HW_SRC;
388d244c610SMarvin Lin 	mci->scrub_mode = SCRUB_HW_SRC;
389d244c610SMarvin Lin 	mci->edac_cap = EDAC_FLAG_SECDED;
390d244c610SMarvin Lin 	mci->ctl_name = "npcm_ddr_controller";
391d244c610SMarvin Lin 	mci->dev_name = dev_name(&pdev->dev);
392d244c610SMarvin Lin 	mci->mod_name = EDAC_MOD_NAME;
393d244c610SMarvin Lin 	mci->ctl_page_to_phys = NULL;
394d244c610SMarvin Lin 
395d244c610SMarvin Lin 	rc = setup_irq(mci, pdev);
396d244c610SMarvin Lin 	if (rc)
397d244c610SMarvin Lin 		goto free_edac_mc;
398d244c610SMarvin Lin 
399d244c610SMarvin Lin 	rc = edac_mc_add_mc(mci);
400d244c610SMarvin Lin 	if (rc)
401d244c610SMarvin Lin 		goto free_edac_mc;
402d244c610SMarvin Lin 
403d244c610SMarvin Lin 	if (IS_ENABLED(CONFIG_EDAC_DEBUG) && pdata->chip == NPCM8XX_CHIP)
404d244c610SMarvin Lin 		setup_debugfs(mci);
405d244c610SMarvin Lin 
406d244c610SMarvin Lin 	return rc;
407d244c610SMarvin Lin 
408d244c610SMarvin Lin free_edac_mc:
409d244c610SMarvin Lin 	edac_mc_free(mci);
410d244c610SMarvin Lin 	return rc;
411d244c610SMarvin Lin }
412d244c610SMarvin Lin 
edac_remove(struct platform_device * pdev)413d244c610SMarvin Lin static int edac_remove(struct platform_device *pdev)
414d244c610SMarvin Lin {
415d244c610SMarvin Lin 	struct mem_ctl_info *mci = platform_get_drvdata(pdev);
416d244c610SMarvin Lin 	struct priv_data *priv = mci->pvt_info;
417d244c610SMarvin Lin 	const struct npcm_platform_data *pdata;
418d244c610SMarvin Lin 
419d244c610SMarvin Lin 	pdata = priv->pdata;
420d244c610SMarvin Lin 	if (IS_ENABLED(CONFIG_EDAC_DEBUG) && pdata->chip == NPCM8XX_CHIP)
421d244c610SMarvin Lin 		edac_debugfs_remove_recursive(priv->debugfs);
422d244c610SMarvin Lin 
423d244c610SMarvin Lin 	edac_mc_del_mc(&pdev->dev);
424d244c610SMarvin Lin 	edac_mc_free(mci);
425d244c610SMarvin Lin 
426d244c610SMarvin Lin 	regmap_write(npcm_regmap, pdata->ctl_int_mask_master,
427d244c610SMarvin Lin 		     pdata->int_mask_master_global_mask);
428d244c610SMarvin Lin 	regmap_update_bits(npcm_regmap, pdata->ctl_ecc_en, pdata->ecc_en_mask, 0);
429d244c610SMarvin Lin 
430d244c610SMarvin Lin 	return 0;
431d244c610SMarvin Lin }
432d244c610SMarvin Lin 
433d244c610SMarvin Lin static const struct npcm_platform_data npcm750_edac = {
434d244c610SMarvin Lin 	.chip				= NPCM7XX_CHIP,
435d244c610SMarvin Lin 
436d244c610SMarvin Lin 	/* memory controller registers */
437d244c610SMarvin Lin 	.ctl_ecc_en			= 0x174,
438d244c610SMarvin Lin 	.ctl_int_status			= 0x1d0,
439d244c610SMarvin Lin 	.ctl_int_ack			= 0x1d4,
440d244c610SMarvin Lin 	.ctl_int_mask_master		= 0x1d8,
441d244c610SMarvin Lin 	.ctl_ce_addr_l			= 0x188,
442d244c610SMarvin Lin 	.ctl_ce_data_l			= 0x190,
443d244c610SMarvin Lin 	.ctl_ce_synd			= 0x18c,
444d244c610SMarvin Lin 	.ctl_ue_addr_l			= 0x17c,
445d244c610SMarvin Lin 	.ctl_ue_data_l			= 0x184,
446d244c610SMarvin Lin 	.ctl_ue_synd			= 0x180,
447d244c610SMarvin Lin 	.ctl_source_id			= 0x194,
448d244c610SMarvin Lin 
449d244c610SMarvin Lin 	/* masks and shifts */
450d244c610SMarvin Lin 	.ecc_en_mask			= BIT(24),
451d244c610SMarvin Lin 	.int_status_ce_mask		= GENMASK(4, 3),
452d244c610SMarvin Lin 	.int_status_ue_mask		= GENMASK(6, 5),
453d244c610SMarvin Lin 	.int_ack_ce_mask		= GENMASK(4, 3),
454d244c610SMarvin Lin 	.int_ack_ue_mask		= GENMASK(6, 5),
455d244c610SMarvin Lin 	.int_mask_master_non_ecc_mask	= GENMASK(30, 7) | GENMASK(2, 0),
456d244c610SMarvin Lin 	.int_mask_master_global_mask	= BIT(31),
457d244c610SMarvin Lin 	.ce_synd_mask			= GENMASK(6, 0),
458d244c610SMarvin Lin 	.ce_synd_shift			= 0,
459d244c610SMarvin Lin 	.ue_synd_mask			= GENMASK(6, 0),
460d244c610SMarvin Lin 	.ue_synd_shift			= 0,
461d244c610SMarvin Lin 	.source_id_ce_mask		= GENMASK(29, 16),
462d244c610SMarvin Lin 	.source_id_ce_shift		= 16,
463d244c610SMarvin Lin 	.source_id_ue_mask		= GENMASK(13, 0),
464d244c610SMarvin Lin 	.source_id_ue_shift		= 0,
465d244c610SMarvin Lin };
466d244c610SMarvin Lin 
467d244c610SMarvin Lin static const struct npcm_platform_data npcm845_edac = {
468d244c610SMarvin Lin 	.chip =				NPCM8XX_CHIP,
469d244c610SMarvin Lin 
470d244c610SMarvin Lin 	/* memory controller registers */
471d244c610SMarvin Lin 	.ctl_ecc_en			= 0x16c,
472d244c610SMarvin Lin 	.ctl_int_status			= 0x228,
473d244c610SMarvin Lin 	.ctl_int_ack			= 0x244,
474d244c610SMarvin Lin 	.ctl_int_mask_master		= 0x220,
475d244c610SMarvin Lin 	.ctl_int_mask_ecc		= 0x260,
476d244c610SMarvin Lin 	.ctl_ce_addr_l			= 0x18c,
477d244c610SMarvin Lin 	.ctl_ce_addr_h			= 0x190,
478d244c610SMarvin Lin 	.ctl_ce_data_l			= 0x194,
479d244c610SMarvin Lin 	.ctl_ce_data_h			= 0x198,
480d244c610SMarvin Lin 	.ctl_ce_synd			= 0x190,
481d244c610SMarvin Lin 	.ctl_ue_addr_l			= 0x17c,
482d244c610SMarvin Lin 	.ctl_ue_addr_h			= 0x180,
483d244c610SMarvin Lin 	.ctl_ue_data_l			= 0x184,
484d244c610SMarvin Lin 	.ctl_ue_data_h			= 0x188,
485d244c610SMarvin Lin 	.ctl_ue_synd			= 0x180,
486d244c610SMarvin Lin 	.ctl_source_id			= 0x19c,
487d244c610SMarvin Lin 	.ctl_controller_busy		= 0x20c,
488d244c610SMarvin Lin 	.ctl_xor_check_bits		= 0x174,
489d244c610SMarvin Lin 
490d244c610SMarvin Lin 	/* masks and shifts */
491d244c610SMarvin Lin 	.ecc_en_mask			= GENMASK(17, 16),
492d244c610SMarvin Lin 	.int_status_ce_mask		= GENMASK(1, 0),
493d244c610SMarvin Lin 	.int_status_ue_mask		= GENMASK(3, 2),
494d244c610SMarvin Lin 	.int_ack_ce_mask		= GENMASK(1, 0),
495d244c610SMarvin Lin 	.int_ack_ue_mask		= GENMASK(3, 2),
496d244c610SMarvin Lin 	.int_mask_master_non_ecc_mask	= GENMASK(30, 3) | GENMASK(1, 0),
497d244c610SMarvin Lin 	.int_mask_master_global_mask	= BIT(31),
498d244c610SMarvin Lin 	.int_mask_ecc_non_event_mask	= GENMASK(8, 4),
499d244c610SMarvin Lin 	.ce_addr_h_mask			= GENMASK(1, 0),
500d244c610SMarvin Lin 	.ce_synd_mask			= GENMASK(15, 8),
501d244c610SMarvin Lin 	.ce_synd_shift			= 8,
502d244c610SMarvin Lin 	.ue_addr_h_mask			= GENMASK(1, 0),
503d244c610SMarvin Lin 	.ue_synd_mask			= GENMASK(15, 8),
504d244c610SMarvin Lin 	.ue_synd_shift			= 8,
505d244c610SMarvin Lin 	.source_id_ce_mask		= GENMASK(29, 16),
506d244c610SMarvin Lin 	.source_id_ce_shift		= 16,
507d244c610SMarvin Lin 	.source_id_ue_mask		= GENMASK(13, 0),
508d244c610SMarvin Lin 	.source_id_ue_shift		= 0,
509d244c610SMarvin Lin 	.controller_busy_mask		= BIT(0),
510d244c610SMarvin Lin 	.xor_check_bits_mask		= GENMASK(23, 16),
511d244c610SMarvin Lin 	.xor_check_bits_shift		= 16,
512d244c610SMarvin Lin 	.writeback_en_mask		= BIT(24),
513d244c610SMarvin Lin 	.fwc_mask			= BIT(8),
514d244c610SMarvin Lin };
515d244c610SMarvin Lin 
516d244c610SMarvin Lin static const struct of_device_id npcm_edac_of_match[] = {
517d244c610SMarvin Lin 	{
518d244c610SMarvin Lin 		.compatible = "nuvoton,npcm750-memory-controller",
519d244c610SMarvin Lin 		.data = &npcm750_edac
520d244c610SMarvin Lin 	},
521d244c610SMarvin Lin 	{
522d244c610SMarvin Lin 		.compatible = "nuvoton,npcm845-memory-controller",
523d244c610SMarvin Lin 		.data = &npcm845_edac
524d244c610SMarvin Lin 	},
525d244c610SMarvin Lin 	{},
526d244c610SMarvin Lin };
527d244c610SMarvin Lin 
528d244c610SMarvin Lin MODULE_DEVICE_TABLE(of, npcm_edac_of_match);
529d244c610SMarvin Lin 
530d244c610SMarvin Lin static struct platform_driver npcm_edac_driver = {
531d244c610SMarvin Lin 	.driver = {
532d244c610SMarvin Lin 		.name = "npcm-edac",
533d244c610SMarvin Lin 		.of_match_table = npcm_edac_of_match,
534d244c610SMarvin Lin 	},
535d244c610SMarvin Lin 	.probe = edac_probe,
536d244c610SMarvin Lin 	.remove = edac_remove,
537d244c610SMarvin Lin };
538d244c610SMarvin Lin 
539d244c610SMarvin Lin module_platform_driver(npcm_edac_driver);
540d244c610SMarvin Lin 
541d244c610SMarvin Lin MODULE_AUTHOR("Medad CChien <medadyoung@gmail.com>");
542d244c610SMarvin Lin MODULE_AUTHOR("Marvin Lin <kflin@nuvoton.com>");
543d244c610SMarvin Lin MODULE_DESCRIPTION("Nuvoton NPCM EDAC Driver");
544d244c610SMarvin Lin MODULE_LICENSE("GPL");
545