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