xref: /openbmc/linux/drivers/edac/aspeed_edac.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
19b7e6242SStefan M Schaeckeler // SPDX-License-Identifier: GPL-2.0+
29b7e6242SStefan M Schaeckeler /*
39b7e6242SStefan M Schaeckeler  * Copyright 2018, 2019 Cisco Systems
49b7e6242SStefan M Schaeckeler  */
59b7e6242SStefan M Schaeckeler 
69b7e6242SStefan M Schaeckeler #include <linux/edac.h>
79b7e6242SStefan M Schaeckeler #include <linux/module.h>
89b7e6242SStefan M Schaeckeler #include <linux/init.h>
99b7e6242SStefan M Schaeckeler #include <linux/interrupt.h>
109b7e6242SStefan M Schaeckeler #include <linux/platform_device.h>
119b7e6242SStefan M Schaeckeler #include <linux/stop_machine.h>
129b7e6242SStefan M Schaeckeler #include <linux/io.h>
139b7e6242SStefan M Schaeckeler #include <linux/of_address.h>
149b7e6242SStefan M Schaeckeler #include <linux/regmap.h>
159b7e6242SStefan M Schaeckeler #include "edac_module.h"
169b7e6242SStefan M Schaeckeler 
179b7e6242SStefan M Schaeckeler 
189b7e6242SStefan M Schaeckeler #define DRV_NAME "aspeed-edac"
199b7e6242SStefan M Schaeckeler 
209b7e6242SStefan M Schaeckeler 
219b7e6242SStefan M Schaeckeler #define ASPEED_MCR_PROT        0x00 /* protection key register */
229b7e6242SStefan M Schaeckeler #define ASPEED_MCR_CONF        0x04 /* configuration register */
239b7e6242SStefan M Schaeckeler #define ASPEED_MCR_INTR_CTRL   0x50 /* interrupt control/status register */
249b7e6242SStefan M Schaeckeler #define ASPEED_MCR_ADDR_UNREC  0x58 /* address of first un-recoverable error */
259b7e6242SStefan M Schaeckeler #define ASPEED_MCR_ADDR_REC    0x5c /* address of last recoverable error */
269b7e6242SStefan M Schaeckeler #define ASPEED_MCR_LAST        ASPEED_MCR_ADDR_REC
279b7e6242SStefan M Schaeckeler 
289b7e6242SStefan M Schaeckeler 
299b7e6242SStefan M Schaeckeler #define ASPEED_MCR_PROT_PASSWD	            0xfc600309
309b7e6242SStefan M Schaeckeler #define ASPEED_MCR_CONF_DRAM_TYPE               BIT(4)
319b7e6242SStefan M Schaeckeler #define ASPEED_MCR_CONF_ECC                     BIT(7)
329b7e6242SStefan M Schaeckeler #define ASPEED_MCR_INTR_CTRL_CLEAR             BIT(31)
339b7e6242SStefan M Schaeckeler #define ASPEED_MCR_INTR_CTRL_CNT_REC   GENMASK(23, 16)
349b7e6242SStefan M Schaeckeler #define ASPEED_MCR_INTR_CTRL_CNT_UNREC GENMASK(15, 12)
359b7e6242SStefan M Schaeckeler #define ASPEED_MCR_INTR_CTRL_ENABLE  (BIT(0) | BIT(1))
369b7e6242SStefan M Schaeckeler 
379b7e6242SStefan M Schaeckeler 
389b7e6242SStefan M Schaeckeler static struct regmap *aspeed_regmap;
399b7e6242SStefan M Schaeckeler 
409b7e6242SStefan M Schaeckeler 
regmap_reg_write(void * context,unsigned int reg,unsigned int val)419b7e6242SStefan M Schaeckeler static int regmap_reg_write(void *context, unsigned int reg, unsigned int val)
429b7e6242SStefan M Schaeckeler {
439b7e6242SStefan M Schaeckeler 	void __iomem *regs = (void __iomem *)context;
449b7e6242SStefan M Schaeckeler 
459b7e6242SStefan M Schaeckeler 	/* enable write to MCR register set */
469b7e6242SStefan M Schaeckeler 	writel(ASPEED_MCR_PROT_PASSWD, regs + ASPEED_MCR_PROT);
479b7e6242SStefan M Schaeckeler 
489b7e6242SStefan M Schaeckeler 	writel(val, regs + reg);
499b7e6242SStefan M Schaeckeler 
509b7e6242SStefan M Schaeckeler 	/* disable write to MCR register set */
519b7e6242SStefan M Schaeckeler 	writel(~ASPEED_MCR_PROT_PASSWD, regs + ASPEED_MCR_PROT);
529b7e6242SStefan M Schaeckeler 
539b7e6242SStefan M Schaeckeler 	return 0;
549b7e6242SStefan M Schaeckeler }
559b7e6242SStefan M Schaeckeler 
569b7e6242SStefan M Schaeckeler 
regmap_reg_read(void * context,unsigned int reg,unsigned int * val)579b7e6242SStefan M Schaeckeler static int regmap_reg_read(void *context, unsigned int reg, unsigned int *val)
589b7e6242SStefan M Schaeckeler {
599b7e6242SStefan M Schaeckeler 	void __iomem *regs = (void __iomem *)context;
609b7e6242SStefan M Schaeckeler 
619b7e6242SStefan M Schaeckeler 	*val = readl(regs + reg);
629b7e6242SStefan M Schaeckeler 
639b7e6242SStefan M Schaeckeler 	return 0;
649b7e6242SStefan M Schaeckeler }
659b7e6242SStefan M Schaeckeler 
regmap_is_volatile(struct device * dev,unsigned int reg)669b7e6242SStefan M Schaeckeler static bool regmap_is_volatile(struct device *dev, unsigned int reg)
679b7e6242SStefan M Schaeckeler {
689b7e6242SStefan M Schaeckeler 	switch (reg) {
699b7e6242SStefan M Schaeckeler 	case ASPEED_MCR_PROT:
709b7e6242SStefan M Schaeckeler 	case ASPEED_MCR_INTR_CTRL:
719b7e6242SStefan M Schaeckeler 	case ASPEED_MCR_ADDR_UNREC:
729b7e6242SStefan M Schaeckeler 	case ASPEED_MCR_ADDR_REC:
739b7e6242SStefan M Schaeckeler 		return true;
749b7e6242SStefan M Schaeckeler 	default:
759b7e6242SStefan M Schaeckeler 		return false;
769b7e6242SStefan M Schaeckeler 	}
779b7e6242SStefan M Schaeckeler }
789b7e6242SStefan M Schaeckeler 
799b7e6242SStefan M Schaeckeler 
809b7e6242SStefan M Schaeckeler static const struct regmap_config aspeed_regmap_config = {
819b7e6242SStefan M Schaeckeler 	.reg_bits = 32,
829b7e6242SStefan M Schaeckeler 	.val_bits = 32,
839b7e6242SStefan M Schaeckeler 	.reg_stride = 4,
849b7e6242SStefan M Schaeckeler 	.max_register = ASPEED_MCR_LAST,
859b7e6242SStefan M Schaeckeler 	.reg_write = regmap_reg_write,
869b7e6242SStefan M Schaeckeler 	.reg_read = regmap_reg_read,
879b7e6242SStefan M Schaeckeler 	.volatile_reg = regmap_is_volatile,
889b7e6242SStefan M Schaeckeler 	.fast_io = true,
899b7e6242SStefan M Schaeckeler };
909b7e6242SStefan M Schaeckeler 
919b7e6242SStefan M Schaeckeler 
count_rec(struct mem_ctl_info * mci,u8 rec_cnt,u32 rec_addr)929b7e6242SStefan M Schaeckeler static void count_rec(struct mem_ctl_info *mci, u8 rec_cnt, u32 rec_addr)
939b7e6242SStefan M Schaeckeler {
949b7e6242SStefan M Schaeckeler 	struct csrow_info *csrow = mci->csrows[0];
959b7e6242SStefan M Schaeckeler 	u32 page, offset, syndrome;
969b7e6242SStefan M Schaeckeler 
979b7e6242SStefan M Schaeckeler 	if (!rec_cnt)
989b7e6242SStefan M Schaeckeler 		return;
999b7e6242SStefan M Schaeckeler 
1009b7e6242SStefan M Schaeckeler 	/* report first few errors (if there are) */
1019b7e6242SStefan M Schaeckeler 	/* note: no addresses are recorded */
1029b7e6242SStefan M Schaeckeler 	if (rec_cnt > 1) {
1039b7e6242SStefan M Schaeckeler 		/* page, offset and syndrome are not available */
1049b7e6242SStefan M Schaeckeler 		page = 0;
1059b7e6242SStefan M Schaeckeler 		offset = 0;
1069b7e6242SStefan M Schaeckeler 		syndrome = 0;
1079b7e6242SStefan M Schaeckeler 		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, rec_cnt-1,
1089b7e6242SStefan M Schaeckeler 				     page, offset, syndrome, 0, 0, -1,
1099b7e6242SStefan M Schaeckeler 				     "address(es) not available", "");
1109b7e6242SStefan M Schaeckeler 	}
1119b7e6242SStefan M Schaeckeler 
1129b7e6242SStefan M Schaeckeler 	/* report last error */
1139b7e6242SStefan M Schaeckeler 	/* note: rec_addr is the last recoverable error addr */
1149b7e6242SStefan M Schaeckeler 	page = rec_addr >> PAGE_SHIFT;
1159b7e6242SStefan M Schaeckeler 	offset = rec_addr & ~PAGE_MASK;
1169b7e6242SStefan M Schaeckeler 	/* syndrome is not available */
1179b7e6242SStefan M Schaeckeler 	syndrome = 0;
1189b7e6242SStefan M Schaeckeler 	edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
1199b7e6242SStefan M Schaeckeler 			     csrow->first_page + page, offset, syndrome,
1209b7e6242SStefan M Schaeckeler 			     0, 0, -1, "", "");
1219b7e6242SStefan M Schaeckeler }
1229b7e6242SStefan M Schaeckeler 
1239b7e6242SStefan M Schaeckeler 
count_un_rec(struct mem_ctl_info * mci,u8 un_rec_cnt,u32 un_rec_addr)1249b7e6242SStefan M Schaeckeler static void count_un_rec(struct mem_ctl_info *mci, u8 un_rec_cnt,
1259b7e6242SStefan M Schaeckeler 			 u32 un_rec_addr)
1269b7e6242SStefan M Schaeckeler {
1279b7e6242SStefan M Schaeckeler 	struct csrow_info *csrow = mci->csrows[0];
1289b7e6242SStefan M Schaeckeler 	u32 page, offset, syndrome;
1299b7e6242SStefan M Schaeckeler 
1309b7e6242SStefan M Schaeckeler 	if (!un_rec_cnt)
1319b7e6242SStefan M Schaeckeler 		return;
1329b7e6242SStefan M Schaeckeler 
1339b7e6242SStefan M Schaeckeler 	/* report 1. error */
1349b7e6242SStefan M Schaeckeler 	/* note: un_rec_addr is the first unrecoverable error addr */
1359b7e6242SStefan M Schaeckeler 	page = un_rec_addr >> PAGE_SHIFT;
1369b7e6242SStefan M Schaeckeler 	offset = un_rec_addr & ~PAGE_MASK;
1379b7e6242SStefan M Schaeckeler 	/* syndrome is not available */
1389b7e6242SStefan M Schaeckeler 	syndrome = 0;
1399b7e6242SStefan M Schaeckeler 	edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
1409b7e6242SStefan M Schaeckeler 			     csrow->first_page + page, offset, syndrome,
1419b7e6242SStefan M Schaeckeler 			     0, 0, -1, "", "");
1429b7e6242SStefan M Schaeckeler 
1439b7e6242SStefan M Schaeckeler 	/* report further errors (if there are) */
1449b7e6242SStefan M Schaeckeler 	/* note: no addresses are recorded */
1459b7e6242SStefan M Schaeckeler 	if (un_rec_cnt > 1) {
1469b7e6242SStefan M Schaeckeler 		/* page, offset and syndrome are not available */
1479b7e6242SStefan M Schaeckeler 		page = 0;
1489b7e6242SStefan M Schaeckeler 		offset = 0;
1499b7e6242SStefan M Schaeckeler 		syndrome = 0;
1509b7e6242SStefan M Schaeckeler 		edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, un_rec_cnt-1,
1519b7e6242SStefan M Schaeckeler 				     page, offset, syndrome, 0, 0, -1,
1529b7e6242SStefan M Schaeckeler 				     "address(es) not available", "");
1539b7e6242SStefan M Schaeckeler 	}
1549b7e6242SStefan M Schaeckeler }
1559b7e6242SStefan M Schaeckeler 
1569b7e6242SStefan M Schaeckeler 
mcr_isr(int irq,void * arg)1579b7e6242SStefan M Schaeckeler static irqreturn_t mcr_isr(int irq, void *arg)
1589b7e6242SStefan M Schaeckeler {
1599b7e6242SStefan M Schaeckeler 	struct mem_ctl_info *mci = arg;
1609b7e6242SStefan M Schaeckeler 	u32 rec_addr, un_rec_addr;
1619b7e6242SStefan M Schaeckeler 	u32 reg50, reg5c, reg58;
1629b7e6242SStefan M Schaeckeler 	u8  rec_cnt, un_rec_cnt;
1639b7e6242SStefan M Schaeckeler 
1649b7e6242SStefan M Schaeckeler 	regmap_read(aspeed_regmap, ASPEED_MCR_INTR_CTRL, &reg50);
1659b7e6242SStefan M Schaeckeler 	dev_dbg(mci->pdev, "received edac interrupt w/ mcr register 50: 0x%x\n",
1669b7e6242SStefan M Schaeckeler 		reg50);
1679b7e6242SStefan M Schaeckeler 
1689b7e6242SStefan M Schaeckeler 	/* collect data about recoverable and unrecoverable errors */
1699b7e6242SStefan M Schaeckeler 	rec_cnt = (reg50 & ASPEED_MCR_INTR_CTRL_CNT_REC) >> 16;
1709b7e6242SStefan M Schaeckeler 	un_rec_cnt = (reg50 & ASPEED_MCR_INTR_CTRL_CNT_UNREC) >> 12;
1719b7e6242SStefan M Schaeckeler 
1729b7e6242SStefan M Schaeckeler 	dev_dbg(mci->pdev, "%d recoverable interrupts and %d unrecoverable interrupts\n",
1739b7e6242SStefan M Schaeckeler 		rec_cnt, un_rec_cnt);
1749b7e6242SStefan M Schaeckeler 
1759b7e6242SStefan M Schaeckeler 	regmap_read(aspeed_regmap, ASPEED_MCR_ADDR_UNREC, &reg58);
1769b7e6242SStefan M Schaeckeler 	un_rec_addr = reg58;
1779b7e6242SStefan M Schaeckeler 
1789b7e6242SStefan M Schaeckeler 	regmap_read(aspeed_regmap, ASPEED_MCR_ADDR_REC, &reg5c);
1799b7e6242SStefan M Schaeckeler 	rec_addr = reg5c;
1809b7e6242SStefan M Schaeckeler 
1819b7e6242SStefan M Schaeckeler 	/* clear interrupt flags and error counters: */
1829b7e6242SStefan M Schaeckeler 	regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,
1839b7e6242SStefan M Schaeckeler 			   ASPEED_MCR_INTR_CTRL_CLEAR,
1849b7e6242SStefan M Schaeckeler 			   ASPEED_MCR_INTR_CTRL_CLEAR);
1859b7e6242SStefan M Schaeckeler 
1869b7e6242SStefan M Schaeckeler 	regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,
1879b7e6242SStefan M Schaeckeler 			   ASPEED_MCR_INTR_CTRL_CLEAR, 0);
1889b7e6242SStefan M Schaeckeler 
1899b7e6242SStefan M Schaeckeler 	/* process recoverable and unrecoverable errors */
1909b7e6242SStefan M Schaeckeler 	count_rec(mci, rec_cnt, rec_addr);
1919b7e6242SStefan M Schaeckeler 	count_un_rec(mci, un_rec_cnt, un_rec_addr);
1929b7e6242SStefan M Schaeckeler 
1939b7e6242SStefan M Schaeckeler 	if (!rec_cnt && !un_rec_cnt)
1949b7e6242SStefan M Schaeckeler 		dev_dbg(mci->pdev, "received edac interrupt, but did not find any ECC counters\n");
1959b7e6242SStefan M Schaeckeler 
1969b7e6242SStefan M Schaeckeler 	regmap_read(aspeed_regmap, ASPEED_MCR_INTR_CTRL, &reg50);
1979b7e6242SStefan M Schaeckeler 	dev_dbg(mci->pdev, "edac interrupt handled. mcr reg 50 is now: 0x%x\n",
1989b7e6242SStefan M Schaeckeler 		reg50);
1999b7e6242SStefan M Schaeckeler 
2009b7e6242SStefan M Schaeckeler 	return IRQ_HANDLED;
2019b7e6242SStefan M Schaeckeler }
2029b7e6242SStefan M Schaeckeler 
2039b7e6242SStefan M Schaeckeler 
config_irq(void * ctx,struct platform_device * pdev)2049b7e6242SStefan M Schaeckeler static int config_irq(void *ctx, struct platform_device *pdev)
2059b7e6242SStefan M Schaeckeler {
2069b7e6242SStefan M Schaeckeler 	int irq;
2079b7e6242SStefan M Schaeckeler 	int rc;
2089b7e6242SStefan M Schaeckeler 
2099b7e6242SStefan M Schaeckeler 	/* register interrupt handler */
2109b7e6242SStefan M Schaeckeler 	irq = platform_get_irq(pdev, 0);
2119b7e6242SStefan M Schaeckeler 	dev_dbg(&pdev->dev, "got irq %d\n", irq);
212afce6996SKrzysztof Kozlowski 	if (irq < 0)
213afce6996SKrzysztof Kozlowski 		return irq;
2149b7e6242SStefan M Schaeckeler 
2159b7e6242SStefan M Schaeckeler 	rc = devm_request_irq(&pdev->dev, irq, mcr_isr, IRQF_TRIGGER_HIGH,
2169b7e6242SStefan M Schaeckeler 			      DRV_NAME, ctx);
2179b7e6242SStefan M Schaeckeler 	if (rc) {
2189b7e6242SStefan M Schaeckeler 		dev_err(&pdev->dev, "unable to request irq %d\n", irq);
2199b7e6242SStefan M Schaeckeler 		return rc;
2209b7e6242SStefan M Schaeckeler 	}
2219b7e6242SStefan M Schaeckeler 
2229b7e6242SStefan M Schaeckeler 	/* enable interrupts */
2239b7e6242SStefan M Schaeckeler 	regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,
2249b7e6242SStefan M Schaeckeler 			   ASPEED_MCR_INTR_CTRL_ENABLE,
2259b7e6242SStefan M Schaeckeler 			   ASPEED_MCR_INTR_CTRL_ENABLE);
2269b7e6242SStefan M Schaeckeler 
2279b7e6242SStefan M Schaeckeler 	return 0;
2289b7e6242SStefan M Schaeckeler }
2299b7e6242SStefan M Schaeckeler 
2309b7e6242SStefan M Schaeckeler 
init_csrows(struct mem_ctl_info * mci)2319b7e6242SStefan M Schaeckeler static int init_csrows(struct mem_ctl_info *mci)
2329b7e6242SStefan M Schaeckeler {
2339b7e6242SStefan M Schaeckeler 	struct csrow_info *csrow = mci->csrows[0];
2349b7e6242SStefan M Schaeckeler 	u32 nr_pages, dram_type;
2359b7e6242SStefan M Schaeckeler 	struct dimm_info *dimm;
2369b7e6242SStefan M Schaeckeler 	struct device_node *np;
2379b7e6242SStefan M Schaeckeler 	struct resource r;
2389b7e6242SStefan M Schaeckeler 	u32 reg04;
2399b7e6242SStefan M Schaeckeler 	int rc;
2409b7e6242SStefan M Schaeckeler 
2419b7e6242SStefan M Schaeckeler 	/* retrieve info about physical memory from device tree */
242edfc2d73STroy Lee 	np = of_find_node_by_name(NULL, "memory");
2439b7e6242SStefan M Schaeckeler 	if (!np) {
2449b7e6242SStefan M Schaeckeler 		dev_err(mci->pdev, "dt: missing /memory node\n");
2459b7e6242SStefan M Schaeckeler 		return -ENODEV;
246a651c6c6SXu Wang 	}
2479b7e6242SStefan M Schaeckeler 
2489b7e6242SStefan M Schaeckeler 	rc = of_address_to_resource(np, 0, &r);
2499b7e6242SStefan M Schaeckeler 
2509b7e6242SStefan M Schaeckeler 	of_node_put(np);
2519b7e6242SStefan M Schaeckeler 
2529b7e6242SStefan M Schaeckeler 	if (rc) {
2539b7e6242SStefan M Schaeckeler 		dev_err(mci->pdev, "dt: failed requesting resource for /memory node\n");
2549b7e6242SStefan M Schaeckeler 		return rc;
255a651c6c6SXu Wang 	}
2569b7e6242SStefan M Schaeckeler 
257*2e2f16d5SArnd Bergmann 	dev_dbg(mci->pdev, "dt: /memory node resources: first page %pR, PAGE_SHIFT macro=0x%x\n",
258*2e2f16d5SArnd Bergmann 		&r, PAGE_SHIFT);
2599b7e6242SStefan M Schaeckeler 
2609b7e6242SStefan M Schaeckeler 	csrow->first_page = r.start >> PAGE_SHIFT;
2619b7e6242SStefan M Schaeckeler 	nr_pages = resource_size(&r) >> PAGE_SHIFT;
2629b7e6242SStefan M Schaeckeler 	csrow->last_page = csrow->first_page + nr_pages - 1;
2639b7e6242SStefan M Schaeckeler 
2649b7e6242SStefan M Schaeckeler 	regmap_read(aspeed_regmap, ASPEED_MCR_CONF, &reg04);
2659b7e6242SStefan M Schaeckeler 	dram_type = (reg04 & ASPEED_MCR_CONF_DRAM_TYPE) ? MEM_DDR4 : MEM_DDR3;
2669b7e6242SStefan M Schaeckeler 
2679b7e6242SStefan M Schaeckeler 	dimm = csrow->channels[0]->dimm;
2689b7e6242SStefan M Schaeckeler 	dimm->mtype = dram_type;
2699b7e6242SStefan M Schaeckeler 	dimm->edac_mode = EDAC_SECDED;
2709b7e6242SStefan M Schaeckeler 	dimm->nr_pages = nr_pages / csrow->nr_channels;
2719b7e6242SStefan M Schaeckeler 
2729b7e6242SStefan M Schaeckeler 	dev_dbg(mci->pdev, "initialized dimm with first_page=0x%lx and nr_pages=0x%x\n",
2739b7e6242SStefan M Schaeckeler 		csrow->first_page, nr_pages);
2749b7e6242SStefan M Schaeckeler 
2759b7e6242SStefan M Schaeckeler 	return 0;
2769b7e6242SStefan M Schaeckeler }
2779b7e6242SStefan M Schaeckeler 
2789b7e6242SStefan M Schaeckeler 
aspeed_probe(struct platform_device * pdev)2799b7e6242SStefan M Schaeckeler static int aspeed_probe(struct platform_device *pdev)
2809b7e6242SStefan M Schaeckeler {
2819b7e6242SStefan M Schaeckeler 	struct device *dev = &pdev->dev;
2829b7e6242SStefan M Schaeckeler 	struct edac_mc_layer layers[2];
2839b7e6242SStefan M Schaeckeler 	struct mem_ctl_info *mci;
2849b7e6242SStefan M Schaeckeler 	void __iomem *regs;
2859b7e6242SStefan M Schaeckeler 	u32 reg04;
2869b7e6242SStefan M Schaeckeler 	int rc;
2879b7e6242SStefan M Schaeckeler 
2885bbab3cfSMarkus Elfring 	regs = devm_platform_ioremap_resource(pdev, 0);
2899b7e6242SStefan M Schaeckeler 	if (IS_ERR(regs))
2909b7e6242SStefan M Schaeckeler 		return PTR_ERR(regs);
2919b7e6242SStefan M Schaeckeler 
2929b7e6242SStefan M Schaeckeler 	aspeed_regmap = devm_regmap_init(dev, NULL, (__force void *)regs,
2939b7e6242SStefan M Schaeckeler 					 &aspeed_regmap_config);
2949b7e6242SStefan M Schaeckeler 	if (IS_ERR(aspeed_regmap))
2959b7e6242SStefan M Schaeckeler 		return PTR_ERR(aspeed_regmap);
2969b7e6242SStefan M Schaeckeler 
2979b7e6242SStefan M Schaeckeler 	/* bail out if ECC mode is not configured */
2989b7e6242SStefan M Schaeckeler 	regmap_read(aspeed_regmap, ASPEED_MCR_CONF, &reg04);
2999b7e6242SStefan M Schaeckeler 	if (!(reg04 & ASPEED_MCR_CONF_ECC)) {
3009b7e6242SStefan M Schaeckeler 		dev_err(&pdev->dev, "ECC mode is not configured in u-boot\n");
3019b7e6242SStefan M Schaeckeler 		return -EPERM;
3029b7e6242SStefan M Schaeckeler 	}
3039b7e6242SStefan M Schaeckeler 
3049b7e6242SStefan M Schaeckeler 	edac_op_state = EDAC_OPSTATE_INT;
3059b7e6242SStefan M Schaeckeler 
3069b7e6242SStefan M Schaeckeler 	/* allocate & init EDAC MC data structure */
3079b7e6242SStefan M Schaeckeler 	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
3089b7e6242SStefan M Schaeckeler 	layers[0].size = 1;
3099b7e6242SStefan M Schaeckeler 	layers[0].is_virt_csrow = true;
3109b7e6242SStefan M Schaeckeler 	layers[1].type = EDAC_MC_LAYER_CHANNEL;
3119b7e6242SStefan M Schaeckeler 	layers[1].size = 1;
3129b7e6242SStefan M Schaeckeler 	layers[1].is_virt_csrow = false;
3139b7e6242SStefan M Schaeckeler 
3149b7e6242SStefan M Schaeckeler 	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0);
3159b7e6242SStefan M Schaeckeler 	if (!mci)
3169b7e6242SStefan M Schaeckeler 		return -ENOMEM;
3179b7e6242SStefan M Schaeckeler 
3189b7e6242SStefan M Schaeckeler 	mci->pdev = &pdev->dev;
3199b7e6242SStefan M Schaeckeler 	mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR4;
3209b7e6242SStefan M Schaeckeler 	mci->edac_ctl_cap = EDAC_FLAG_SECDED;
3219b7e6242SStefan M Schaeckeler 	mci->edac_cap = EDAC_FLAG_SECDED;
3229b7e6242SStefan M Schaeckeler 	mci->scrub_cap = SCRUB_FLAG_HW_SRC;
3239b7e6242SStefan M Schaeckeler 	mci->scrub_mode = SCRUB_HW_SRC;
3249b7e6242SStefan M Schaeckeler 	mci->mod_name = DRV_NAME;
3259b7e6242SStefan M Schaeckeler 	mci->ctl_name = "MIC";
3269b7e6242SStefan M Schaeckeler 	mci->dev_name = dev_name(&pdev->dev);
3279b7e6242SStefan M Schaeckeler 
3289b7e6242SStefan M Schaeckeler 	rc = init_csrows(mci);
3299b7e6242SStefan M Schaeckeler 	if (rc) {
3309b7e6242SStefan M Schaeckeler 		dev_err(&pdev->dev, "failed to init csrows\n");
3319b7e6242SStefan M Schaeckeler 		goto probe_exit02;
3329b7e6242SStefan M Schaeckeler 	}
3339b7e6242SStefan M Schaeckeler 
3349b7e6242SStefan M Schaeckeler 	platform_set_drvdata(pdev, mci);
3359b7e6242SStefan M Schaeckeler 
3369b7e6242SStefan M Schaeckeler 	/* register with edac core */
3379b7e6242SStefan M Schaeckeler 	rc = edac_mc_add_mc(mci);
3389b7e6242SStefan M Schaeckeler 	if (rc) {
3399b7e6242SStefan M Schaeckeler 		dev_err(&pdev->dev, "failed to register with EDAC core\n");
3409b7e6242SStefan M Schaeckeler 		goto probe_exit02;
3419b7e6242SStefan M Schaeckeler 	}
3429b7e6242SStefan M Schaeckeler 
3439b7e6242SStefan M Schaeckeler 	/* register interrupt handler and enable interrupts */
3449b7e6242SStefan M Schaeckeler 	rc = config_irq(mci, pdev);
3459b7e6242SStefan M Schaeckeler 	if (rc) {
3469b7e6242SStefan M Schaeckeler 		dev_err(&pdev->dev, "failed setting up irq\n");
3479b7e6242SStefan M Schaeckeler 		goto probe_exit01;
3489b7e6242SStefan M Schaeckeler 	}
3499b7e6242SStefan M Schaeckeler 
3509b7e6242SStefan M Schaeckeler 	return 0;
3519b7e6242SStefan M Schaeckeler 
3529b7e6242SStefan M Schaeckeler probe_exit01:
3539b7e6242SStefan M Schaeckeler 	edac_mc_del_mc(&pdev->dev);
3549b7e6242SStefan M Schaeckeler probe_exit02:
3559b7e6242SStefan M Schaeckeler 	edac_mc_free(mci);
3569b7e6242SStefan M Schaeckeler 	return rc;
3579b7e6242SStefan M Schaeckeler }
3589b7e6242SStefan M Schaeckeler 
3599b7e6242SStefan M Schaeckeler 
aspeed_remove(struct platform_device * pdev)3609b7e6242SStefan M Schaeckeler static int aspeed_remove(struct platform_device *pdev)
3619b7e6242SStefan M Schaeckeler {
3629b7e6242SStefan M Schaeckeler 	struct mem_ctl_info *mci;
3639b7e6242SStefan M Schaeckeler 
3649b7e6242SStefan M Schaeckeler 	/* disable interrupts */
3659b7e6242SStefan M Schaeckeler 	regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,
3669b7e6242SStefan M Schaeckeler 			   ASPEED_MCR_INTR_CTRL_ENABLE, 0);
3679b7e6242SStefan M Schaeckeler 
3689b7e6242SStefan M Schaeckeler 	/* free resources */
3699b7e6242SStefan M Schaeckeler 	mci = edac_mc_del_mc(&pdev->dev);
3709b7e6242SStefan M Schaeckeler 	if (mci)
3719b7e6242SStefan M Schaeckeler 		edac_mc_free(mci);
3729b7e6242SStefan M Schaeckeler 
3739b7e6242SStefan M Schaeckeler 	return 0;
3749b7e6242SStefan M Schaeckeler }
3759b7e6242SStefan M Schaeckeler 
3769b7e6242SStefan M Schaeckeler 
3779b7e6242SStefan M Schaeckeler static const struct of_device_id aspeed_of_match[] = {
378edfc2d73STroy Lee 	{ .compatible = "aspeed,ast2400-sdram-edac" },
3799b7e6242SStefan M Schaeckeler 	{ .compatible = "aspeed,ast2500-sdram-edac" },
380edfc2d73STroy Lee 	{ .compatible = "aspeed,ast2600-sdram-edac" },
3819b7e6242SStefan M Schaeckeler 	{},
3829b7e6242SStefan M Schaeckeler };
3839b7e6242SStefan M Schaeckeler 
384edfc2d73STroy Lee MODULE_DEVICE_TABLE(of, aspeed_of_match);
3859b7e6242SStefan M Schaeckeler 
3869b7e6242SStefan M Schaeckeler static struct platform_driver aspeed_driver = {
3879b7e6242SStefan M Schaeckeler 	.driver		= {
3889b7e6242SStefan M Schaeckeler 		.name	= DRV_NAME,
3899b7e6242SStefan M Schaeckeler 		.of_match_table = aspeed_of_match
3909b7e6242SStefan M Schaeckeler 	},
3919b7e6242SStefan M Schaeckeler 	.probe		= aspeed_probe,
3929b7e6242SStefan M Schaeckeler 	.remove		= aspeed_remove
3939b7e6242SStefan M Schaeckeler };
39407def587SLiu Shixin module_platform_driver(aspeed_driver);
3959b7e6242SStefan M Schaeckeler 
3969b7e6242SStefan M Schaeckeler MODULE_LICENSE("GPL");
3979b7e6242SStefan M Schaeckeler MODULE_AUTHOR("Stefan Schaeckeler <sschaeck@cisco.com>");
398edfc2d73STroy Lee MODULE_DESCRIPTION("Aspeed BMC SoC EDAC driver");
3999b7e6242SStefan M Schaeckeler MODULE_VERSION("1.0");
400