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