1e23a7cdeSTalel Shenhar // SPDX-License-Identifier: GPL-2.0
2e23a7cdeSTalel Shenhar /*
3e23a7cdeSTalel Shenhar * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4e23a7cdeSTalel Shenhar */
5e23a7cdeSTalel Shenhar #include <linux/bitfield.h>
6e23a7cdeSTalel Shenhar #include <linux/bitops.h>
7e23a7cdeSTalel Shenhar #include <linux/edac.h>
8e23a7cdeSTalel Shenhar #include <linux/of_irq.h>
9e23a7cdeSTalel Shenhar #include <linux/platform_device.h>
10e23a7cdeSTalel Shenhar #include <linux/spinlock.h>
11e23a7cdeSTalel Shenhar #include "edac_module.h"
12e23a7cdeSTalel Shenhar
13e23a7cdeSTalel Shenhar /* Registers Offset */
14e23a7cdeSTalel Shenhar #define AL_MC_ECC_CFG 0x70
15e23a7cdeSTalel Shenhar #define AL_MC_ECC_CLEAR 0x7c
16e23a7cdeSTalel Shenhar #define AL_MC_ECC_ERR_COUNT 0x80
17e23a7cdeSTalel Shenhar #define AL_MC_ECC_CE_ADDR0 0x84
18e23a7cdeSTalel Shenhar #define AL_MC_ECC_CE_ADDR1 0x88
19e23a7cdeSTalel Shenhar #define AL_MC_ECC_UE_ADDR0 0xa4
20e23a7cdeSTalel Shenhar #define AL_MC_ECC_UE_ADDR1 0xa8
21e23a7cdeSTalel Shenhar #define AL_MC_ECC_CE_SYND0 0x8c
22e23a7cdeSTalel Shenhar #define AL_MC_ECC_CE_SYND1 0x90
23e23a7cdeSTalel Shenhar #define AL_MC_ECC_CE_SYND2 0x94
24e23a7cdeSTalel Shenhar #define AL_MC_ECC_UE_SYND0 0xac
25e23a7cdeSTalel Shenhar #define AL_MC_ECC_UE_SYND1 0xb0
26e23a7cdeSTalel Shenhar #define AL_MC_ECC_UE_SYND2 0xb4
27e23a7cdeSTalel Shenhar
28e23a7cdeSTalel Shenhar /* Registers Fields */
29e23a7cdeSTalel Shenhar #define AL_MC_ECC_CFG_SCRUB_DISABLED BIT(4)
30e23a7cdeSTalel Shenhar
31e23a7cdeSTalel Shenhar #define AL_MC_ECC_CLEAR_UE_COUNT BIT(3)
32e23a7cdeSTalel Shenhar #define AL_MC_ECC_CLEAR_CE_COUNT BIT(2)
33e23a7cdeSTalel Shenhar #define AL_MC_ECC_CLEAR_UE_ERR BIT(1)
34e23a7cdeSTalel Shenhar #define AL_MC_ECC_CLEAR_CE_ERR BIT(0)
35e23a7cdeSTalel Shenhar
36e23a7cdeSTalel Shenhar #define AL_MC_ECC_ERR_COUNT_UE GENMASK(31, 16)
37e23a7cdeSTalel Shenhar #define AL_MC_ECC_ERR_COUNT_CE GENMASK(15, 0)
38e23a7cdeSTalel Shenhar
39e23a7cdeSTalel Shenhar #define AL_MC_ECC_CE_ADDR0_RANK GENMASK(25, 24)
40e23a7cdeSTalel Shenhar #define AL_MC_ECC_CE_ADDR0_ROW GENMASK(17, 0)
41e23a7cdeSTalel Shenhar
42e23a7cdeSTalel Shenhar #define AL_MC_ECC_CE_ADDR1_BG GENMASK(25, 24)
43e23a7cdeSTalel Shenhar #define AL_MC_ECC_CE_ADDR1_BANK GENMASK(18, 16)
44e23a7cdeSTalel Shenhar #define AL_MC_ECC_CE_ADDR1_COLUMN GENMASK(11, 0)
45e23a7cdeSTalel Shenhar
46e23a7cdeSTalel Shenhar #define AL_MC_ECC_UE_ADDR0_RANK GENMASK(25, 24)
47e23a7cdeSTalel Shenhar #define AL_MC_ECC_UE_ADDR0_ROW GENMASK(17, 0)
48e23a7cdeSTalel Shenhar
49e23a7cdeSTalel Shenhar #define AL_MC_ECC_UE_ADDR1_BG GENMASK(25, 24)
50e23a7cdeSTalel Shenhar #define AL_MC_ECC_UE_ADDR1_BANK GENMASK(18, 16)
51e23a7cdeSTalel Shenhar #define AL_MC_ECC_UE_ADDR1_COLUMN GENMASK(11, 0)
52e23a7cdeSTalel Shenhar
53e23a7cdeSTalel Shenhar #define DRV_NAME "al_mc_edac"
54e23a7cdeSTalel Shenhar #define AL_MC_EDAC_MSG_MAX 256
55e23a7cdeSTalel Shenhar
56e23a7cdeSTalel Shenhar struct al_mc_edac {
57e23a7cdeSTalel Shenhar void __iomem *mmio_base;
58e23a7cdeSTalel Shenhar spinlock_t lock;
59e23a7cdeSTalel Shenhar int irq_ce;
60e23a7cdeSTalel Shenhar int irq_ue;
61e23a7cdeSTalel Shenhar };
62e23a7cdeSTalel Shenhar
prepare_msg(char * message,size_t buffer_size,enum hw_event_mc_err_type type,u8 rank,u32 row,u8 bg,u8 bank,u16 column,u32 syn0,u32 syn1,u32 syn2)63e23a7cdeSTalel Shenhar static void prepare_msg(char *message, size_t buffer_size,
64e23a7cdeSTalel Shenhar enum hw_event_mc_err_type type,
65e23a7cdeSTalel Shenhar u8 rank, u32 row, u8 bg, u8 bank, u16 column,
66e23a7cdeSTalel Shenhar u32 syn0, u32 syn1, u32 syn2)
67e23a7cdeSTalel Shenhar {
68e23a7cdeSTalel Shenhar snprintf(message, buffer_size,
69e23a7cdeSTalel Shenhar "%s rank=0x%x row=0x%x bg=0x%x bank=0x%x col=0x%x syn0: 0x%x syn1: 0x%x syn2: 0x%x",
70e23a7cdeSTalel Shenhar type == HW_EVENT_ERR_UNCORRECTED ? "UE" : "CE",
71e23a7cdeSTalel Shenhar rank, row, bg, bank, column, syn0, syn1, syn2);
72e23a7cdeSTalel Shenhar }
73e23a7cdeSTalel Shenhar
handle_ce(struct mem_ctl_info * mci)74e23a7cdeSTalel Shenhar static int handle_ce(struct mem_ctl_info *mci)
75e23a7cdeSTalel Shenhar {
76e23a7cdeSTalel Shenhar u32 eccerrcnt, ecccaddr0, ecccaddr1, ecccsyn0, ecccsyn1, ecccsyn2, row;
77e23a7cdeSTalel Shenhar struct al_mc_edac *al_mc = mci->pvt_info;
78e23a7cdeSTalel Shenhar char msg[AL_MC_EDAC_MSG_MAX];
79e23a7cdeSTalel Shenhar u16 ce_count, column;
80e23a7cdeSTalel Shenhar unsigned long flags;
81e23a7cdeSTalel Shenhar u8 rank, bg, bank;
82e23a7cdeSTalel Shenhar
83e23a7cdeSTalel Shenhar eccerrcnt = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_ERR_COUNT);
84e23a7cdeSTalel Shenhar ce_count = FIELD_GET(AL_MC_ECC_ERR_COUNT_CE, eccerrcnt);
85e23a7cdeSTalel Shenhar if (!ce_count)
86e23a7cdeSTalel Shenhar return 0;
87e23a7cdeSTalel Shenhar
88e23a7cdeSTalel Shenhar ecccaddr0 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_CE_ADDR0);
89e23a7cdeSTalel Shenhar ecccaddr1 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_CE_ADDR1);
90e23a7cdeSTalel Shenhar ecccsyn0 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_CE_SYND0);
91e23a7cdeSTalel Shenhar ecccsyn1 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_CE_SYND1);
92e23a7cdeSTalel Shenhar ecccsyn2 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_CE_SYND2);
93e23a7cdeSTalel Shenhar
94e23a7cdeSTalel Shenhar writel_relaxed(AL_MC_ECC_CLEAR_CE_COUNT | AL_MC_ECC_CLEAR_CE_ERR,
95e23a7cdeSTalel Shenhar al_mc->mmio_base + AL_MC_ECC_CLEAR);
96e23a7cdeSTalel Shenhar
97e23a7cdeSTalel Shenhar dev_dbg(mci->pdev, "eccuaddr0=0x%08x eccuaddr1=0x%08x\n",
98e23a7cdeSTalel Shenhar ecccaddr0, ecccaddr1);
99e23a7cdeSTalel Shenhar
100e23a7cdeSTalel Shenhar rank = FIELD_GET(AL_MC_ECC_CE_ADDR0_RANK, ecccaddr0);
101e23a7cdeSTalel Shenhar row = FIELD_GET(AL_MC_ECC_CE_ADDR0_ROW, ecccaddr0);
102e23a7cdeSTalel Shenhar
103e23a7cdeSTalel Shenhar bg = FIELD_GET(AL_MC_ECC_CE_ADDR1_BG, ecccaddr1);
104e23a7cdeSTalel Shenhar bank = FIELD_GET(AL_MC_ECC_CE_ADDR1_BANK, ecccaddr1);
105e23a7cdeSTalel Shenhar column = FIELD_GET(AL_MC_ECC_CE_ADDR1_COLUMN, ecccaddr1);
106e23a7cdeSTalel Shenhar
107e23a7cdeSTalel Shenhar prepare_msg(msg, sizeof(msg), HW_EVENT_ERR_CORRECTED,
108e23a7cdeSTalel Shenhar rank, row, bg, bank, column,
109e23a7cdeSTalel Shenhar ecccsyn0, ecccsyn1, ecccsyn2);
110e23a7cdeSTalel Shenhar
111e23a7cdeSTalel Shenhar spin_lock_irqsave(&al_mc->lock, flags);
112e23a7cdeSTalel Shenhar edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
113e23a7cdeSTalel Shenhar ce_count, 0, 0, 0, 0, 0, -1, mci->ctl_name, msg);
114e23a7cdeSTalel Shenhar spin_unlock_irqrestore(&al_mc->lock, flags);
115e23a7cdeSTalel Shenhar
116e23a7cdeSTalel Shenhar return ce_count;
117e23a7cdeSTalel Shenhar }
118e23a7cdeSTalel Shenhar
handle_ue(struct mem_ctl_info * mci)119e23a7cdeSTalel Shenhar static int handle_ue(struct mem_ctl_info *mci)
120e23a7cdeSTalel Shenhar {
121e23a7cdeSTalel Shenhar u32 eccerrcnt, eccuaddr0, eccuaddr1, eccusyn0, eccusyn1, eccusyn2, row;
122e23a7cdeSTalel Shenhar struct al_mc_edac *al_mc = mci->pvt_info;
123e23a7cdeSTalel Shenhar char msg[AL_MC_EDAC_MSG_MAX];
124e23a7cdeSTalel Shenhar u16 ue_count, column;
125e23a7cdeSTalel Shenhar unsigned long flags;
126e23a7cdeSTalel Shenhar u8 rank, bg, bank;
127e23a7cdeSTalel Shenhar
128e23a7cdeSTalel Shenhar eccerrcnt = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_ERR_COUNT);
129e23a7cdeSTalel Shenhar ue_count = FIELD_GET(AL_MC_ECC_ERR_COUNT_UE, eccerrcnt);
130e23a7cdeSTalel Shenhar if (!ue_count)
131e23a7cdeSTalel Shenhar return 0;
132e23a7cdeSTalel Shenhar
133e23a7cdeSTalel Shenhar eccuaddr0 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_UE_ADDR0);
134e23a7cdeSTalel Shenhar eccuaddr1 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_UE_ADDR1);
135e23a7cdeSTalel Shenhar eccusyn0 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_UE_SYND0);
136e23a7cdeSTalel Shenhar eccusyn1 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_UE_SYND1);
137e23a7cdeSTalel Shenhar eccusyn2 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_UE_SYND2);
138e23a7cdeSTalel Shenhar
139e23a7cdeSTalel Shenhar writel_relaxed(AL_MC_ECC_CLEAR_UE_COUNT | AL_MC_ECC_CLEAR_UE_ERR,
140e23a7cdeSTalel Shenhar al_mc->mmio_base + AL_MC_ECC_CLEAR);
141e23a7cdeSTalel Shenhar
142e23a7cdeSTalel Shenhar dev_dbg(mci->pdev, "eccuaddr0=0x%08x eccuaddr1=0x%08x\n",
143e23a7cdeSTalel Shenhar eccuaddr0, eccuaddr1);
144e23a7cdeSTalel Shenhar
145e23a7cdeSTalel Shenhar rank = FIELD_GET(AL_MC_ECC_UE_ADDR0_RANK, eccuaddr0);
146e23a7cdeSTalel Shenhar row = FIELD_GET(AL_MC_ECC_UE_ADDR0_ROW, eccuaddr0);
147e23a7cdeSTalel Shenhar
148e23a7cdeSTalel Shenhar bg = FIELD_GET(AL_MC_ECC_UE_ADDR1_BG, eccuaddr1);
149e23a7cdeSTalel Shenhar bank = FIELD_GET(AL_MC_ECC_UE_ADDR1_BANK, eccuaddr1);
150e23a7cdeSTalel Shenhar column = FIELD_GET(AL_MC_ECC_UE_ADDR1_COLUMN, eccuaddr1);
151e23a7cdeSTalel Shenhar
152e23a7cdeSTalel Shenhar prepare_msg(msg, sizeof(msg), HW_EVENT_ERR_UNCORRECTED,
153e23a7cdeSTalel Shenhar rank, row, bg, bank, column,
154e23a7cdeSTalel Shenhar eccusyn0, eccusyn1, eccusyn2);
155e23a7cdeSTalel Shenhar
156e23a7cdeSTalel Shenhar spin_lock_irqsave(&al_mc->lock, flags);
157e23a7cdeSTalel Shenhar edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
158e23a7cdeSTalel Shenhar ue_count, 0, 0, 0, 0, 0, -1, mci->ctl_name, msg);
159e23a7cdeSTalel Shenhar spin_unlock_irqrestore(&al_mc->lock, flags);
160e23a7cdeSTalel Shenhar
161e23a7cdeSTalel Shenhar return ue_count;
162e23a7cdeSTalel Shenhar }
163e23a7cdeSTalel Shenhar
al_mc_edac_check(struct mem_ctl_info * mci)164e23a7cdeSTalel Shenhar static void al_mc_edac_check(struct mem_ctl_info *mci)
165e23a7cdeSTalel Shenhar {
166e23a7cdeSTalel Shenhar struct al_mc_edac *al_mc = mci->pvt_info;
167e23a7cdeSTalel Shenhar
168e23a7cdeSTalel Shenhar if (al_mc->irq_ue <= 0)
169e23a7cdeSTalel Shenhar handle_ue(mci);
170e23a7cdeSTalel Shenhar
171e23a7cdeSTalel Shenhar if (al_mc->irq_ce <= 0)
172e23a7cdeSTalel Shenhar handle_ce(mci);
173e23a7cdeSTalel Shenhar }
174e23a7cdeSTalel Shenhar
al_mc_edac_irq_handler_ue(int irq,void * info)175e23a7cdeSTalel Shenhar static irqreturn_t al_mc_edac_irq_handler_ue(int irq, void *info)
176e23a7cdeSTalel Shenhar {
177e23a7cdeSTalel Shenhar struct platform_device *pdev = info;
178e23a7cdeSTalel Shenhar struct mem_ctl_info *mci = platform_get_drvdata(pdev);
179e23a7cdeSTalel Shenhar
180e23a7cdeSTalel Shenhar if (handle_ue(mci))
181e23a7cdeSTalel Shenhar return IRQ_HANDLED;
182e23a7cdeSTalel Shenhar return IRQ_NONE;
183e23a7cdeSTalel Shenhar }
184e23a7cdeSTalel Shenhar
al_mc_edac_irq_handler_ce(int irq,void * info)185e23a7cdeSTalel Shenhar static irqreturn_t al_mc_edac_irq_handler_ce(int irq, void *info)
186e23a7cdeSTalel Shenhar {
187e23a7cdeSTalel Shenhar struct platform_device *pdev = info;
188e23a7cdeSTalel Shenhar struct mem_ctl_info *mci = platform_get_drvdata(pdev);
189e23a7cdeSTalel Shenhar
190e23a7cdeSTalel Shenhar if (handle_ce(mci))
191e23a7cdeSTalel Shenhar return IRQ_HANDLED;
192e23a7cdeSTalel Shenhar return IRQ_NONE;
193e23a7cdeSTalel Shenhar }
194e23a7cdeSTalel Shenhar
get_scrub_mode(void __iomem * mmio_base)195e23a7cdeSTalel Shenhar static enum scrub_type get_scrub_mode(void __iomem *mmio_base)
196e23a7cdeSTalel Shenhar {
197e23a7cdeSTalel Shenhar u32 ecccfg0;
198e23a7cdeSTalel Shenhar
199e23a7cdeSTalel Shenhar ecccfg0 = readl(mmio_base + AL_MC_ECC_CFG);
200e23a7cdeSTalel Shenhar
201e23a7cdeSTalel Shenhar if (FIELD_GET(AL_MC_ECC_CFG_SCRUB_DISABLED, ecccfg0))
202e23a7cdeSTalel Shenhar return SCRUB_NONE;
203e23a7cdeSTalel Shenhar else
204e23a7cdeSTalel Shenhar return SCRUB_HW_SRC;
205e23a7cdeSTalel Shenhar }
206e23a7cdeSTalel Shenhar
devm_al_mc_edac_free(void * data)207e23a7cdeSTalel Shenhar static void devm_al_mc_edac_free(void *data)
208e23a7cdeSTalel Shenhar {
209e23a7cdeSTalel Shenhar edac_mc_free(data);
210e23a7cdeSTalel Shenhar }
211e23a7cdeSTalel Shenhar
devm_al_mc_edac_del(void * data)212e23a7cdeSTalel Shenhar static void devm_al_mc_edac_del(void *data)
213e23a7cdeSTalel Shenhar {
214e23a7cdeSTalel Shenhar edac_mc_del_mc(data);
215e23a7cdeSTalel Shenhar }
216e23a7cdeSTalel Shenhar
al_mc_edac_probe(struct platform_device * pdev)217e23a7cdeSTalel Shenhar static int al_mc_edac_probe(struct platform_device *pdev)
218e23a7cdeSTalel Shenhar {
219e23a7cdeSTalel Shenhar struct edac_mc_layer layers[1];
220e23a7cdeSTalel Shenhar struct mem_ctl_info *mci;
221e23a7cdeSTalel Shenhar struct al_mc_edac *al_mc;
222e23a7cdeSTalel Shenhar void __iomem *mmio_base;
223e23a7cdeSTalel Shenhar struct dimm_info *dimm;
224e23a7cdeSTalel Shenhar int ret;
225e23a7cdeSTalel Shenhar
226e23a7cdeSTalel Shenhar mmio_base = devm_platform_ioremap_resource(pdev, 0);
227e23a7cdeSTalel Shenhar if (IS_ERR(mmio_base)) {
228e23a7cdeSTalel Shenhar dev_err(&pdev->dev, "failed to ioremap memory (%ld)\n",
229e23a7cdeSTalel Shenhar PTR_ERR(mmio_base));
230e23a7cdeSTalel Shenhar return PTR_ERR(mmio_base);
231e23a7cdeSTalel Shenhar }
232e23a7cdeSTalel Shenhar
233e23a7cdeSTalel Shenhar layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
234e23a7cdeSTalel Shenhar layers[0].size = 1;
235e23a7cdeSTalel Shenhar layers[0].is_virt_csrow = false;
236e23a7cdeSTalel Shenhar mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
237e23a7cdeSTalel Shenhar sizeof(struct al_mc_edac));
238e23a7cdeSTalel Shenhar if (!mci)
239e23a7cdeSTalel Shenhar return -ENOMEM;
240e23a7cdeSTalel Shenhar
241*470b5256SCai Huoqing ret = devm_add_action_or_reset(&pdev->dev, devm_al_mc_edac_free, mci);
242*470b5256SCai Huoqing if (ret)
243e23a7cdeSTalel Shenhar return ret;
244e23a7cdeSTalel Shenhar
245e23a7cdeSTalel Shenhar platform_set_drvdata(pdev, mci);
246e23a7cdeSTalel Shenhar al_mc = mci->pvt_info;
247e23a7cdeSTalel Shenhar
248e23a7cdeSTalel Shenhar al_mc->mmio_base = mmio_base;
249e23a7cdeSTalel Shenhar
250e23a7cdeSTalel Shenhar al_mc->irq_ue = of_irq_get_byname(pdev->dev.of_node, "ue");
251e23a7cdeSTalel Shenhar if (al_mc->irq_ue <= 0)
252e23a7cdeSTalel Shenhar dev_dbg(&pdev->dev,
253e23a7cdeSTalel Shenhar "no IRQ defined for UE - falling back to polling\n");
254e23a7cdeSTalel Shenhar
255e23a7cdeSTalel Shenhar al_mc->irq_ce = of_irq_get_byname(pdev->dev.of_node, "ce");
256e23a7cdeSTalel Shenhar if (al_mc->irq_ce <= 0)
257e23a7cdeSTalel Shenhar dev_dbg(&pdev->dev,
258e23a7cdeSTalel Shenhar "no IRQ defined for CE - falling back to polling\n");
259e23a7cdeSTalel Shenhar
260e23a7cdeSTalel Shenhar /*
261e23a7cdeSTalel Shenhar * In case both interrupts (ue/ce) are to be found, use interrupt mode.
262e23a7cdeSTalel Shenhar * In case none of the interrupt are foud, use polling mode.
263e23a7cdeSTalel Shenhar * In case only one interrupt is found, use interrupt mode for it but
264e23a7cdeSTalel Shenhar * keep polling mode enable for the other.
265e23a7cdeSTalel Shenhar */
266e23a7cdeSTalel Shenhar if (al_mc->irq_ue <= 0 || al_mc->irq_ce <= 0) {
267e23a7cdeSTalel Shenhar edac_op_state = EDAC_OPSTATE_POLL;
268e23a7cdeSTalel Shenhar mci->edac_check = al_mc_edac_check;
269e23a7cdeSTalel Shenhar } else {
270e23a7cdeSTalel Shenhar edac_op_state = EDAC_OPSTATE_INT;
271e23a7cdeSTalel Shenhar }
272e23a7cdeSTalel Shenhar
273e23a7cdeSTalel Shenhar spin_lock_init(&al_mc->lock);
274e23a7cdeSTalel Shenhar
275e23a7cdeSTalel Shenhar mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR4;
276e23a7cdeSTalel Shenhar mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
277e23a7cdeSTalel Shenhar mci->edac_cap = EDAC_FLAG_SECDED;
278e23a7cdeSTalel Shenhar mci->mod_name = DRV_NAME;
279e23a7cdeSTalel Shenhar mci->ctl_name = "al_mc";
280e23a7cdeSTalel Shenhar mci->pdev = &pdev->dev;
281e23a7cdeSTalel Shenhar mci->scrub_mode = get_scrub_mode(mmio_base);
282e23a7cdeSTalel Shenhar
283e23a7cdeSTalel Shenhar dimm = *mci->dimms;
284e23a7cdeSTalel Shenhar dimm->grain = 1;
285e23a7cdeSTalel Shenhar
286e23a7cdeSTalel Shenhar ret = edac_mc_add_mc(mci);
287e23a7cdeSTalel Shenhar if (ret < 0) {
288e23a7cdeSTalel Shenhar dev_err(&pdev->dev,
289e23a7cdeSTalel Shenhar "fail to add memory controller device (%d)\n",
290e23a7cdeSTalel Shenhar ret);
291e23a7cdeSTalel Shenhar return ret;
292e23a7cdeSTalel Shenhar }
293e23a7cdeSTalel Shenhar
294*470b5256SCai Huoqing ret = devm_add_action_or_reset(&pdev->dev, devm_al_mc_edac_del, &pdev->dev);
295*470b5256SCai Huoqing if (ret)
296e23a7cdeSTalel Shenhar return ret;
297e23a7cdeSTalel Shenhar
298e23a7cdeSTalel Shenhar if (al_mc->irq_ue > 0) {
299e23a7cdeSTalel Shenhar ret = devm_request_irq(&pdev->dev,
300e23a7cdeSTalel Shenhar al_mc->irq_ue,
301e23a7cdeSTalel Shenhar al_mc_edac_irq_handler_ue,
302e23a7cdeSTalel Shenhar IRQF_SHARED,
303e23a7cdeSTalel Shenhar pdev->name,
304e23a7cdeSTalel Shenhar pdev);
305e23a7cdeSTalel Shenhar if (ret != 0) {
306e23a7cdeSTalel Shenhar dev_err(&pdev->dev,
307e23a7cdeSTalel Shenhar "failed to request UE IRQ %d (%d)\n",
308e23a7cdeSTalel Shenhar al_mc->irq_ue, ret);
309e23a7cdeSTalel Shenhar return ret;
310e23a7cdeSTalel Shenhar }
311e23a7cdeSTalel Shenhar }
312e23a7cdeSTalel Shenhar
313e23a7cdeSTalel Shenhar if (al_mc->irq_ce > 0) {
314e23a7cdeSTalel Shenhar ret = devm_request_irq(&pdev->dev,
315e23a7cdeSTalel Shenhar al_mc->irq_ce,
316e23a7cdeSTalel Shenhar al_mc_edac_irq_handler_ce,
317e23a7cdeSTalel Shenhar IRQF_SHARED,
318e23a7cdeSTalel Shenhar pdev->name,
319e23a7cdeSTalel Shenhar pdev);
320e23a7cdeSTalel Shenhar if (ret != 0) {
321e23a7cdeSTalel Shenhar dev_err(&pdev->dev,
322e23a7cdeSTalel Shenhar "failed to request CE IRQ %d (%d)\n",
323e23a7cdeSTalel Shenhar al_mc->irq_ce, ret);
324e23a7cdeSTalel Shenhar return ret;
325e23a7cdeSTalel Shenhar }
326e23a7cdeSTalel Shenhar }
327e23a7cdeSTalel Shenhar
328e23a7cdeSTalel Shenhar return 0;
329e23a7cdeSTalel Shenhar }
330e23a7cdeSTalel Shenhar
331e23a7cdeSTalel Shenhar static const struct of_device_id al_mc_edac_of_match[] = {
332e23a7cdeSTalel Shenhar { .compatible = "amazon,al-mc-edac", },
333e23a7cdeSTalel Shenhar {},
334e23a7cdeSTalel Shenhar };
335e23a7cdeSTalel Shenhar
336e23a7cdeSTalel Shenhar MODULE_DEVICE_TABLE(of, al_mc_edac_of_match);
337e23a7cdeSTalel Shenhar
338e23a7cdeSTalel Shenhar static struct platform_driver al_mc_edac_driver = {
339e23a7cdeSTalel Shenhar .probe = al_mc_edac_probe,
340e23a7cdeSTalel Shenhar .driver = {
341e23a7cdeSTalel Shenhar .name = DRV_NAME,
342e23a7cdeSTalel Shenhar .of_match_table = al_mc_edac_of_match,
343e23a7cdeSTalel Shenhar },
344e23a7cdeSTalel Shenhar };
345e23a7cdeSTalel Shenhar
346e23a7cdeSTalel Shenhar module_platform_driver(al_mc_edac_driver);
347e23a7cdeSTalel Shenhar
348e23a7cdeSTalel Shenhar MODULE_LICENSE("GPL v2");
349e23a7cdeSTalel Shenhar MODULE_AUTHOR("Talel Shenhar");
350e23a7cdeSTalel Shenhar MODULE_DESCRIPTION("Amazon's Annapurna Lab's Memory Controller EDAC Driver");
351