18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
68c2ecf20Sopenharmony_ci#include <linux/bitops.h>
78c2ecf20Sopenharmony_ci#include <linux/edac.h>
88c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
118c2ecf20Sopenharmony_ci#include "edac_module.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/* Registers Offset */
148c2ecf20Sopenharmony_ci#define AL_MC_ECC_CFG		0x70
158c2ecf20Sopenharmony_ci#define AL_MC_ECC_CLEAR		0x7c
168c2ecf20Sopenharmony_ci#define AL_MC_ECC_ERR_COUNT	0x80
178c2ecf20Sopenharmony_ci#define AL_MC_ECC_CE_ADDR0	0x84
188c2ecf20Sopenharmony_ci#define AL_MC_ECC_CE_ADDR1	0x88
198c2ecf20Sopenharmony_ci#define AL_MC_ECC_UE_ADDR0	0xa4
208c2ecf20Sopenharmony_ci#define AL_MC_ECC_UE_ADDR1	0xa8
218c2ecf20Sopenharmony_ci#define AL_MC_ECC_CE_SYND0	0x8c
228c2ecf20Sopenharmony_ci#define AL_MC_ECC_CE_SYND1	0x90
238c2ecf20Sopenharmony_ci#define AL_MC_ECC_CE_SYND2	0x94
248c2ecf20Sopenharmony_ci#define AL_MC_ECC_UE_SYND0	0xac
258c2ecf20Sopenharmony_ci#define AL_MC_ECC_UE_SYND1	0xb0
268c2ecf20Sopenharmony_ci#define AL_MC_ECC_UE_SYND2	0xb4
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* Registers Fields */
298c2ecf20Sopenharmony_ci#define AL_MC_ECC_CFG_SCRUB_DISABLED	BIT(4)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define AL_MC_ECC_CLEAR_UE_COUNT	BIT(3)
328c2ecf20Sopenharmony_ci#define AL_MC_ECC_CLEAR_CE_COUNT	BIT(2)
338c2ecf20Sopenharmony_ci#define AL_MC_ECC_CLEAR_UE_ERR		BIT(1)
348c2ecf20Sopenharmony_ci#define AL_MC_ECC_CLEAR_CE_ERR		BIT(0)
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define AL_MC_ECC_ERR_COUNT_UE		GENMASK(31, 16)
378c2ecf20Sopenharmony_ci#define AL_MC_ECC_ERR_COUNT_CE		GENMASK(15, 0)
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#define AL_MC_ECC_CE_ADDR0_RANK		GENMASK(25, 24)
408c2ecf20Sopenharmony_ci#define AL_MC_ECC_CE_ADDR0_ROW		GENMASK(17, 0)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define AL_MC_ECC_CE_ADDR1_BG		GENMASK(25, 24)
438c2ecf20Sopenharmony_ci#define AL_MC_ECC_CE_ADDR1_BANK		GENMASK(18, 16)
448c2ecf20Sopenharmony_ci#define AL_MC_ECC_CE_ADDR1_COLUMN	GENMASK(11, 0)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define AL_MC_ECC_UE_ADDR0_RANK		GENMASK(25, 24)
478c2ecf20Sopenharmony_ci#define AL_MC_ECC_UE_ADDR0_ROW		GENMASK(17, 0)
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define AL_MC_ECC_UE_ADDR1_BG		GENMASK(25, 24)
508c2ecf20Sopenharmony_ci#define AL_MC_ECC_UE_ADDR1_BANK		GENMASK(18, 16)
518c2ecf20Sopenharmony_ci#define AL_MC_ECC_UE_ADDR1_COLUMN	GENMASK(11, 0)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define DRV_NAME "al_mc_edac"
548c2ecf20Sopenharmony_ci#define AL_MC_EDAC_MSG_MAX 256
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistruct al_mc_edac {
578c2ecf20Sopenharmony_ci	void __iomem *mmio_base;
588c2ecf20Sopenharmony_ci	spinlock_t lock;
598c2ecf20Sopenharmony_ci	int irq_ce;
608c2ecf20Sopenharmony_ci	int irq_ue;
618c2ecf20Sopenharmony_ci};
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic void prepare_msg(char *message, size_t buffer_size,
648c2ecf20Sopenharmony_ci			enum hw_event_mc_err_type type,
658c2ecf20Sopenharmony_ci			u8 rank, u32 row, u8 bg, u8 bank, u16 column,
668c2ecf20Sopenharmony_ci			u32 syn0, u32 syn1, u32 syn2)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	snprintf(message, buffer_size,
698c2ecf20Sopenharmony_ci		 "%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",
708c2ecf20Sopenharmony_ci		 type == HW_EVENT_ERR_UNCORRECTED ? "UE" : "CE",
718c2ecf20Sopenharmony_ci		 rank, row, bg, bank, column, syn0, syn1, syn2);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int handle_ce(struct mem_ctl_info *mci)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	u32 eccerrcnt, ecccaddr0, ecccaddr1, ecccsyn0, ecccsyn1, ecccsyn2, row;
778c2ecf20Sopenharmony_ci	struct al_mc_edac *al_mc = mci->pvt_info;
788c2ecf20Sopenharmony_ci	char msg[AL_MC_EDAC_MSG_MAX];
798c2ecf20Sopenharmony_ci	u16 ce_count, column;
808c2ecf20Sopenharmony_ci	unsigned long flags;
818c2ecf20Sopenharmony_ci	u8 rank, bg, bank;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	eccerrcnt = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_ERR_COUNT);
848c2ecf20Sopenharmony_ci	ce_count = FIELD_GET(AL_MC_ECC_ERR_COUNT_CE, eccerrcnt);
858c2ecf20Sopenharmony_ci	if (!ce_count)
868c2ecf20Sopenharmony_ci		return 0;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	ecccaddr0 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_CE_ADDR0);
898c2ecf20Sopenharmony_ci	ecccaddr1 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_CE_ADDR1);
908c2ecf20Sopenharmony_ci	ecccsyn0 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_CE_SYND0);
918c2ecf20Sopenharmony_ci	ecccsyn1 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_CE_SYND1);
928c2ecf20Sopenharmony_ci	ecccsyn2 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_CE_SYND2);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	writel_relaxed(AL_MC_ECC_CLEAR_CE_COUNT | AL_MC_ECC_CLEAR_CE_ERR,
958c2ecf20Sopenharmony_ci		       al_mc->mmio_base + AL_MC_ECC_CLEAR);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	dev_dbg(mci->pdev, "eccuaddr0=0x%08x eccuaddr1=0x%08x\n",
988c2ecf20Sopenharmony_ci		ecccaddr0, ecccaddr1);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	rank = FIELD_GET(AL_MC_ECC_CE_ADDR0_RANK, ecccaddr0);
1018c2ecf20Sopenharmony_ci	row = FIELD_GET(AL_MC_ECC_CE_ADDR0_ROW, ecccaddr0);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	bg = FIELD_GET(AL_MC_ECC_CE_ADDR1_BG, ecccaddr1);
1048c2ecf20Sopenharmony_ci	bank = FIELD_GET(AL_MC_ECC_CE_ADDR1_BANK, ecccaddr1);
1058c2ecf20Sopenharmony_ci	column = FIELD_GET(AL_MC_ECC_CE_ADDR1_COLUMN, ecccaddr1);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	prepare_msg(msg, sizeof(msg), HW_EVENT_ERR_CORRECTED,
1088c2ecf20Sopenharmony_ci		    rank, row, bg, bank, column,
1098c2ecf20Sopenharmony_ci		    ecccsyn0, ecccsyn1, ecccsyn2);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	spin_lock_irqsave(&al_mc->lock, flags);
1128c2ecf20Sopenharmony_ci	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
1138c2ecf20Sopenharmony_ci			     ce_count, 0, 0, 0, 0, 0, -1, mci->ctl_name, msg);
1148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&al_mc->lock, flags);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return ce_count;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic int handle_ue(struct mem_ctl_info *mci)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	u32 eccerrcnt, eccuaddr0, eccuaddr1, eccusyn0, eccusyn1, eccusyn2, row;
1228c2ecf20Sopenharmony_ci	struct al_mc_edac *al_mc = mci->pvt_info;
1238c2ecf20Sopenharmony_ci	char msg[AL_MC_EDAC_MSG_MAX];
1248c2ecf20Sopenharmony_ci	u16 ue_count, column;
1258c2ecf20Sopenharmony_ci	unsigned long flags;
1268c2ecf20Sopenharmony_ci	u8 rank, bg, bank;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	eccerrcnt = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_ERR_COUNT);
1298c2ecf20Sopenharmony_ci	ue_count = FIELD_GET(AL_MC_ECC_ERR_COUNT_UE, eccerrcnt);
1308c2ecf20Sopenharmony_ci	if (!ue_count)
1318c2ecf20Sopenharmony_ci		return 0;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	eccuaddr0 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_UE_ADDR0);
1348c2ecf20Sopenharmony_ci	eccuaddr1 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_UE_ADDR1);
1358c2ecf20Sopenharmony_ci	eccusyn0 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_UE_SYND0);
1368c2ecf20Sopenharmony_ci	eccusyn1 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_UE_SYND1);
1378c2ecf20Sopenharmony_ci	eccusyn2 = readl_relaxed(al_mc->mmio_base + AL_MC_ECC_UE_SYND2);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	writel_relaxed(AL_MC_ECC_CLEAR_UE_COUNT | AL_MC_ECC_CLEAR_UE_ERR,
1408c2ecf20Sopenharmony_ci		       al_mc->mmio_base + AL_MC_ECC_CLEAR);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	dev_dbg(mci->pdev, "eccuaddr0=0x%08x eccuaddr1=0x%08x\n",
1438c2ecf20Sopenharmony_ci		eccuaddr0, eccuaddr1);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	rank = FIELD_GET(AL_MC_ECC_UE_ADDR0_RANK, eccuaddr0);
1468c2ecf20Sopenharmony_ci	row = FIELD_GET(AL_MC_ECC_UE_ADDR0_ROW, eccuaddr0);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	bg = FIELD_GET(AL_MC_ECC_UE_ADDR1_BG, eccuaddr1);
1498c2ecf20Sopenharmony_ci	bank = FIELD_GET(AL_MC_ECC_UE_ADDR1_BANK, eccuaddr1);
1508c2ecf20Sopenharmony_ci	column = FIELD_GET(AL_MC_ECC_UE_ADDR1_COLUMN, eccuaddr1);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	prepare_msg(msg, sizeof(msg), HW_EVENT_ERR_UNCORRECTED,
1538c2ecf20Sopenharmony_ci		    rank, row, bg, bank, column,
1548c2ecf20Sopenharmony_ci		    eccusyn0, eccusyn1, eccusyn2);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	spin_lock_irqsave(&al_mc->lock, flags);
1578c2ecf20Sopenharmony_ci	edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
1588c2ecf20Sopenharmony_ci			     ue_count, 0, 0, 0, 0, 0, -1, mci->ctl_name, msg);
1598c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&al_mc->lock, flags);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return ue_count;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic void al_mc_edac_check(struct mem_ctl_info *mci)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct al_mc_edac *al_mc = mci->pvt_info;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (al_mc->irq_ue <= 0)
1698c2ecf20Sopenharmony_ci		handle_ue(mci);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (al_mc->irq_ce <= 0)
1728c2ecf20Sopenharmony_ci		handle_ce(mci);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic irqreturn_t al_mc_edac_irq_handler_ue(int irq, void *info)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct platform_device *pdev = info;
1788c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = platform_get_drvdata(pdev);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (handle_ue(mci))
1818c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1828c2ecf20Sopenharmony_ci	return IRQ_NONE;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic irqreturn_t al_mc_edac_irq_handler_ce(int irq, void *info)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct platform_device *pdev = info;
1888c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci = platform_get_drvdata(pdev);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (handle_ce(mci))
1918c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1928c2ecf20Sopenharmony_ci	return IRQ_NONE;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic enum scrub_type get_scrub_mode(void __iomem *mmio_base)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	u32 ecccfg0;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	ecccfg0 = readl(mmio_base + AL_MC_ECC_CFG);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (FIELD_GET(AL_MC_ECC_CFG_SCRUB_DISABLED, ecccfg0))
2028c2ecf20Sopenharmony_ci		return SCRUB_NONE;
2038c2ecf20Sopenharmony_ci	else
2048c2ecf20Sopenharmony_ci		return SCRUB_HW_SRC;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic void devm_al_mc_edac_free(void *data)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	edac_mc_free(data);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic void devm_al_mc_edac_del(void *data)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	edac_mc_del_mc(data);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int al_mc_edac_probe(struct platform_device *pdev)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct edac_mc_layer layers[1];
2208c2ecf20Sopenharmony_ci	struct mem_ctl_info *mci;
2218c2ecf20Sopenharmony_ci	struct al_mc_edac *al_mc;
2228c2ecf20Sopenharmony_ci	void __iomem *mmio_base;
2238c2ecf20Sopenharmony_ci	struct dimm_info *dimm;
2248c2ecf20Sopenharmony_ci	int ret;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	mmio_base = devm_platform_ioremap_resource(pdev, 0);
2278c2ecf20Sopenharmony_ci	if (IS_ERR(mmio_base)) {
2288c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap memory (%ld)\n",
2298c2ecf20Sopenharmony_ci			PTR_ERR(mmio_base));
2308c2ecf20Sopenharmony_ci		return PTR_ERR(mmio_base);
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
2348c2ecf20Sopenharmony_ci	layers[0].size = 1;
2358c2ecf20Sopenharmony_ci	layers[0].is_virt_csrow = false;
2368c2ecf20Sopenharmony_ci	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
2378c2ecf20Sopenharmony_ci			    sizeof(struct al_mc_edac));
2388c2ecf20Sopenharmony_ci	if (!mci)
2398c2ecf20Sopenharmony_ci		return -ENOMEM;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	ret = devm_add_action(&pdev->dev, devm_al_mc_edac_free, mci);
2428c2ecf20Sopenharmony_ci	if (ret) {
2438c2ecf20Sopenharmony_ci		edac_mc_free(mci);
2448c2ecf20Sopenharmony_ci		return ret;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, mci);
2488c2ecf20Sopenharmony_ci	al_mc = mci->pvt_info;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	al_mc->mmio_base = mmio_base;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	al_mc->irq_ue = of_irq_get_byname(pdev->dev.of_node, "ue");
2538c2ecf20Sopenharmony_ci	if (al_mc->irq_ue <= 0)
2548c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev,
2558c2ecf20Sopenharmony_ci			"no IRQ defined for UE - falling back to polling\n");
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	al_mc->irq_ce = of_irq_get_byname(pdev->dev.of_node, "ce");
2588c2ecf20Sopenharmony_ci	if (al_mc->irq_ce <= 0)
2598c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev,
2608c2ecf20Sopenharmony_ci			"no IRQ defined for CE - falling back to polling\n");
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/*
2638c2ecf20Sopenharmony_ci	 * In case both interrupts (ue/ce) are to be found, use interrupt mode.
2648c2ecf20Sopenharmony_ci	 * In case none of the interrupt are foud, use polling mode.
2658c2ecf20Sopenharmony_ci	 * In case only one interrupt is found, use interrupt mode for it but
2668c2ecf20Sopenharmony_ci	 * keep polling mode enable for the other.
2678c2ecf20Sopenharmony_ci	 */
2688c2ecf20Sopenharmony_ci	if (al_mc->irq_ue <= 0 || al_mc->irq_ce <= 0) {
2698c2ecf20Sopenharmony_ci		edac_op_state = EDAC_OPSTATE_POLL;
2708c2ecf20Sopenharmony_ci		mci->edac_check = al_mc_edac_check;
2718c2ecf20Sopenharmony_ci	} else {
2728c2ecf20Sopenharmony_ci		edac_op_state = EDAC_OPSTATE_INT;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	spin_lock_init(&al_mc->lock);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR4;
2788c2ecf20Sopenharmony_ci	mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
2798c2ecf20Sopenharmony_ci	mci->edac_cap = EDAC_FLAG_SECDED;
2808c2ecf20Sopenharmony_ci	mci->mod_name = DRV_NAME;
2818c2ecf20Sopenharmony_ci	mci->ctl_name = "al_mc";
2828c2ecf20Sopenharmony_ci	mci->pdev = &pdev->dev;
2838c2ecf20Sopenharmony_ci	mci->scrub_mode = get_scrub_mode(mmio_base);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	dimm = *mci->dimms;
2868c2ecf20Sopenharmony_ci	dimm->grain = 1;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	ret = edac_mc_add_mc(mci);
2898c2ecf20Sopenharmony_ci	if (ret < 0) {
2908c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
2918c2ecf20Sopenharmony_ci			"fail to add memory controller device (%d)\n",
2928c2ecf20Sopenharmony_ci			ret);
2938c2ecf20Sopenharmony_ci		return ret;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	ret = devm_add_action(&pdev->dev, devm_al_mc_edac_del, &pdev->dev);
2978c2ecf20Sopenharmony_ci	if (ret) {
2988c2ecf20Sopenharmony_ci		edac_mc_del_mc(&pdev->dev);
2998c2ecf20Sopenharmony_ci		return ret;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if (al_mc->irq_ue > 0) {
3038c2ecf20Sopenharmony_ci		ret = devm_request_irq(&pdev->dev,
3048c2ecf20Sopenharmony_ci				       al_mc->irq_ue,
3058c2ecf20Sopenharmony_ci				       al_mc_edac_irq_handler_ue,
3068c2ecf20Sopenharmony_ci				       IRQF_SHARED,
3078c2ecf20Sopenharmony_ci				       pdev->name,
3088c2ecf20Sopenharmony_ci				       pdev);
3098c2ecf20Sopenharmony_ci		if (ret != 0) {
3108c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
3118c2ecf20Sopenharmony_ci				"failed to request UE IRQ %d (%d)\n",
3128c2ecf20Sopenharmony_ci				al_mc->irq_ue, ret);
3138c2ecf20Sopenharmony_ci			return ret;
3148c2ecf20Sopenharmony_ci		}
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (al_mc->irq_ce > 0) {
3188c2ecf20Sopenharmony_ci		ret = devm_request_irq(&pdev->dev,
3198c2ecf20Sopenharmony_ci				       al_mc->irq_ce,
3208c2ecf20Sopenharmony_ci				       al_mc_edac_irq_handler_ce,
3218c2ecf20Sopenharmony_ci				       IRQF_SHARED,
3228c2ecf20Sopenharmony_ci				       pdev->name,
3238c2ecf20Sopenharmony_ci				       pdev);
3248c2ecf20Sopenharmony_ci		if (ret != 0) {
3258c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
3268c2ecf20Sopenharmony_ci				"failed to request CE IRQ %d (%d)\n",
3278c2ecf20Sopenharmony_ci				al_mc->irq_ce, ret);
3288c2ecf20Sopenharmony_ci			return ret;
3298c2ecf20Sopenharmony_ci		}
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return 0;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic const struct of_device_id al_mc_edac_of_match[] = {
3368c2ecf20Sopenharmony_ci	{ .compatible = "amazon,al-mc-edac", },
3378c2ecf20Sopenharmony_ci	{},
3388c2ecf20Sopenharmony_ci};
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, al_mc_edac_of_match);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic struct platform_driver al_mc_edac_driver = {
3438c2ecf20Sopenharmony_ci	.probe = al_mc_edac_probe,
3448c2ecf20Sopenharmony_ci	.driver = {
3458c2ecf20Sopenharmony_ci		.name = DRV_NAME,
3468c2ecf20Sopenharmony_ci		.of_match_table = al_mc_edac_of_match,
3478c2ecf20Sopenharmony_ci	},
3488c2ecf20Sopenharmony_ci};
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cimodule_platform_driver(al_mc_edac_driver);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
3538c2ecf20Sopenharmony_ciMODULE_AUTHOR("Talel Shenhar");
3548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Amazon's Annapurna Lab's Memory Controller EDAC Driver");
355