xref: /openbmc/linux/drivers/edac/al_mc_edac.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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