Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

EDAC, aspeed: Add an Aspeed AST2500 EDAC driver

Add support for the Aspeed AST2500 SoC.

Signed-off-by: Stefan M Schaeckeler <sschaeck@cisco.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: Andrew Jeffery <andrew@aj.id.au>
Cc: Joel Stanley <joel@jms.id.au>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-aspeed@lists.ozlabs.org
Cc: linux-edac <linux-edac@vger.kernel.org>
Link: https://lkml.kernel.org/r/1547743097-5236-2-git-send-email-schaecsn@gmx.net

authored by

Stefan M Schaeckeler and committed by
Borislav Petkov
9b7e6242 bfeffd15

+444
+6
MAINTAINERS
··· 5396 5396 S: Maintained 5397 5397 F: drivers/edac/amd64_edac* 5398 5398 5399 + EDAC-AST2500 5400 + M: Stefan Schaeckeler <sschaeck@cisco.com> 5401 + S: Supported 5402 + F: drivers/edac/aspeed_edac.c 5403 + F: Documentation/devicetree/bindings/edac/aspeed-sdram-edac.txt 5404 + 5399 5405 EDAC-CALXEDA 5400 5406 M: Robert Richter <rric@kernel.org> 5401 5407 L: linux-edac@vger.kernel.org
+7
arch/arm/boot/dts/aspeed-g5.dtsi
··· 47 47 reg = <0x80000000 0>; 48 48 }; 49 49 50 + edac: sdram@1e6e0000 { 51 + compatible = "aspeed,ast2500-sdram-edac"; 52 + reg = <0x1e6e0000 0x174>; 53 + interrupts = <0>; 54 + status = "disabled"; 55 + }; 56 + 50 57 ahb { 51 58 compatible = "simple-bus"; 52 59 #address-cells = <1>;
+9
drivers/edac/Kconfig
··· 475 475 For debugging issues having to do with stability and overall system 476 476 health, you should probably say 'Y' here. 477 477 478 + config EDAC_ASPEED 479 + tristate "Aspeed AST 2500 SoC" 480 + depends on MACH_ASPEED_G5 481 + help 482 + Support for error detection and correction on the Aspeed AST 2500 SoC. 483 + 484 + First, ECC must be configured in the bootloader. Then, this driver 485 + will expose error counters via the EDAC kernel framework. 486 + 478 487 endif # EDAC
+1
drivers/edac/Makefile
··· 78 78 obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o 79 79 obj-$(CONFIG_EDAC_TI) += ti_edac.o 80 80 obj-$(CONFIG_EDAC_QCOM) += qcom_edac.o 81 + obj-$(CONFIG_EDAC_ASPEED) += aspeed_edac.o
+421
drivers/edac/aspeed_edac.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Copyright 2018, 2019 Cisco Systems 4 + */ 5 + 6 + #include <linux/edac.h> 7 + #include <linux/module.h> 8 + #include <linux/init.h> 9 + #include <linux/interrupt.h> 10 + #include <linux/platform_device.h> 11 + #include <linux/stop_machine.h> 12 + #include <linux/io.h> 13 + #include <linux/of_address.h> 14 + #include <linux/regmap.h> 15 + #include "edac_module.h" 16 + 17 + 18 + #define DRV_NAME "aspeed-edac" 19 + 20 + 21 + #define ASPEED_MCR_PROT 0x00 /* protection key register */ 22 + #define ASPEED_MCR_CONF 0x04 /* configuration register */ 23 + #define ASPEED_MCR_INTR_CTRL 0x50 /* interrupt control/status register */ 24 + #define ASPEED_MCR_ADDR_UNREC 0x58 /* address of first un-recoverable error */ 25 + #define ASPEED_MCR_ADDR_REC 0x5c /* address of last recoverable error */ 26 + #define ASPEED_MCR_LAST ASPEED_MCR_ADDR_REC 27 + 28 + 29 + #define ASPEED_MCR_PROT_PASSWD 0xfc600309 30 + #define ASPEED_MCR_CONF_DRAM_TYPE BIT(4) 31 + #define ASPEED_MCR_CONF_ECC BIT(7) 32 + #define ASPEED_MCR_INTR_CTRL_CLEAR BIT(31) 33 + #define ASPEED_MCR_INTR_CTRL_CNT_REC GENMASK(23, 16) 34 + #define ASPEED_MCR_INTR_CTRL_CNT_UNREC GENMASK(15, 12) 35 + #define ASPEED_MCR_INTR_CTRL_ENABLE (BIT(0) | BIT(1)) 36 + 37 + 38 + static struct regmap *aspeed_regmap; 39 + 40 + 41 + static int regmap_reg_write(void *context, unsigned int reg, unsigned int val) 42 + { 43 + void __iomem *regs = (void __iomem *)context; 44 + 45 + /* enable write to MCR register set */ 46 + writel(ASPEED_MCR_PROT_PASSWD, regs + ASPEED_MCR_PROT); 47 + 48 + writel(val, regs + reg); 49 + 50 + /* disable write to MCR register set */ 51 + writel(~ASPEED_MCR_PROT_PASSWD, regs + ASPEED_MCR_PROT); 52 + 53 + return 0; 54 + } 55 + 56 + 57 + static int regmap_reg_read(void *context, unsigned int reg, unsigned int *val) 58 + { 59 + void __iomem *regs = (void __iomem *)context; 60 + 61 + *val = readl(regs + reg); 62 + 63 + return 0; 64 + } 65 + 66 + static bool regmap_is_volatile(struct device *dev, unsigned int reg) 67 + { 68 + switch (reg) { 69 + case ASPEED_MCR_PROT: 70 + case ASPEED_MCR_INTR_CTRL: 71 + case ASPEED_MCR_ADDR_UNREC: 72 + case ASPEED_MCR_ADDR_REC: 73 + return true; 74 + default: 75 + return false; 76 + } 77 + } 78 + 79 + 80 + static const struct regmap_config aspeed_regmap_config = { 81 + .reg_bits = 32, 82 + .val_bits = 32, 83 + .reg_stride = 4, 84 + .max_register = ASPEED_MCR_LAST, 85 + .reg_write = regmap_reg_write, 86 + .reg_read = regmap_reg_read, 87 + .volatile_reg = regmap_is_volatile, 88 + .fast_io = true, 89 + }; 90 + 91 + 92 + static void count_rec(struct mem_ctl_info *mci, u8 rec_cnt, u32 rec_addr) 93 + { 94 + struct csrow_info *csrow = mci->csrows[0]; 95 + u32 page, offset, syndrome; 96 + 97 + if (!rec_cnt) 98 + return; 99 + 100 + /* report first few errors (if there are) */ 101 + /* note: no addresses are recorded */ 102 + if (rec_cnt > 1) { 103 + /* page, offset and syndrome are not available */ 104 + page = 0; 105 + offset = 0; 106 + syndrome = 0; 107 + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, rec_cnt-1, 108 + page, offset, syndrome, 0, 0, -1, 109 + "address(es) not available", ""); 110 + } 111 + 112 + /* report last error */ 113 + /* note: rec_addr is the last recoverable error addr */ 114 + page = rec_addr >> PAGE_SHIFT; 115 + offset = rec_addr & ~PAGE_MASK; 116 + /* syndrome is not available */ 117 + syndrome = 0; 118 + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 119 + csrow->first_page + page, offset, syndrome, 120 + 0, 0, -1, "", ""); 121 + } 122 + 123 + 124 + static void count_un_rec(struct mem_ctl_info *mci, u8 un_rec_cnt, 125 + u32 un_rec_addr) 126 + { 127 + struct csrow_info *csrow = mci->csrows[0]; 128 + u32 page, offset, syndrome; 129 + 130 + if (!un_rec_cnt) 131 + return; 132 + 133 + /* report 1. error */ 134 + /* note: un_rec_addr is the first unrecoverable error addr */ 135 + page = un_rec_addr >> PAGE_SHIFT; 136 + offset = un_rec_addr & ~PAGE_MASK; 137 + /* syndrome is not available */ 138 + syndrome = 0; 139 + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 140 + csrow->first_page + page, offset, syndrome, 141 + 0, 0, -1, "", ""); 142 + 143 + /* report further errors (if there are) */ 144 + /* note: no addresses are recorded */ 145 + if (un_rec_cnt > 1) { 146 + /* page, offset and syndrome are not available */ 147 + page = 0; 148 + offset = 0; 149 + syndrome = 0; 150 + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, un_rec_cnt-1, 151 + page, offset, syndrome, 0, 0, -1, 152 + "address(es) not available", ""); 153 + } 154 + } 155 + 156 + 157 + static irqreturn_t mcr_isr(int irq, void *arg) 158 + { 159 + struct mem_ctl_info *mci = arg; 160 + u32 rec_addr, un_rec_addr; 161 + u32 reg50, reg5c, reg58; 162 + u8 rec_cnt, un_rec_cnt; 163 + 164 + regmap_read(aspeed_regmap, ASPEED_MCR_INTR_CTRL, &reg50); 165 + dev_dbg(mci->pdev, "received edac interrupt w/ mcr register 50: 0x%x\n", 166 + reg50); 167 + 168 + /* collect data about recoverable and unrecoverable errors */ 169 + rec_cnt = (reg50 & ASPEED_MCR_INTR_CTRL_CNT_REC) >> 16; 170 + un_rec_cnt = (reg50 & ASPEED_MCR_INTR_CTRL_CNT_UNREC) >> 12; 171 + 172 + dev_dbg(mci->pdev, "%d recoverable interrupts and %d unrecoverable interrupts\n", 173 + rec_cnt, un_rec_cnt); 174 + 175 + regmap_read(aspeed_regmap, ASPEED_MCR_ADDR_UNREC, &reg58); 176 + un_rec_addr = reg58; 177 + 178 + regmap_read(aspeed_regmap, ASPEED_MCR_ADDR_REC, &reg5c); 179 + rec_addr = reg5c; 180 + 181 + /* clear interrupt flags and error counters: */ 182 + regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, 183 + ASPEED_MCR_INTR_CTRL_CLEAR, 184 + ASPEED_MCR_INTR_CTRL_CLEAR); 185 + 186 + regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, 187 + ASPEED_MCR_INTR_CTRL_CLEAR, 0); 188 + 189 + /* process recoverable and unrecoverable errors */ 190 + count_rec(mci, rec_cnt, rec_addr); 191 + count_un_rec(mci, un_rec_cnt, un_rec_addr); 192 + 193 + if (!rec_cnt && !un_rec_cnt) 194 + dev_dbg(mci->pdev, "received edac interrupt, but did not find any ECC counters\n"); 195 + 196 + regmap_read(aspeed_regmap, ASPEED_MCR_INTR_CTRL, &reg50); 197 + dev_dbg(mci->pdev, "edac interrupt handled. mcr reg 50 is now: 0x%x\n", 198 + reg50); 199 + 200 + return IRQ_HANDLED; 201 + } 202 + 203 + 204 + static int config_irq(void *ctx, struct platform_device *pdev) 205 + { 206 + int irq; 207 + int rc; 208 + 209 + /* register interrupt handler */ 210 + irq = platform_get_irq(pdev, 0); 211 + dev_dbg(&pdev->dev, "got irq %d\n", irq); 212 + if (!irq) 213 + return -ENODEV; 214 + 215 + rc = devm_request_irq(&pdev->dev, irq, mcr_isr, IRQF_TRIGGER_HIGH, 216 + DRV_NAME, ctx); 217 + if (rc) { 218 + dev_err(&pdev->dev, "unable to request irq %d\n", irq); 219 + return rc; 220 + } 221 + 222 + /* enable interrupts */ 223 + regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, 224 + ASPEED_MCR_INTR_CTRL_ENABLE, 225 + ASPEED_MCR_INTR_CTRL_ENABLE); 226 + 227 + return 0; 228 + } 229 + 230 + 231 + static int init_csrows(struct mem_ctl_info *mci) 232 + { 233 + struct csrow_info *csrow = mci->csrows[0]; 234 + u32 nr_pages, dram_type; 235 + struct dimm_info *dimm; 236 + struct device_node *np; 237 + struct resource r; 238 + u32 reg04; 239 + int rc; 240 + 241 + /* retrieve info about physical memory from device tree */ 242 + np = of_find_node_by_path("/memory"); 243 + if (!np) { 244 + dev_err(mci->pdev, "dt: missing /memory node\n"); 245 + return -ENODEV; 246 + }; 247 + 248 + rc = of_address_to_resource(np, 0, &r); 249 + 250 + of_node_put(np); 251 + 252 + if (rc) { 253 + dev_err(mci->pdev, "dt: failed requesting resource for /memory node\n"); 254 + return rc; 255 + }; 256 + 257 + dev_dbg(mci->pdev, "dt: /memory node resources: first page r.start=0x%x, resource_size=0x%x, PAGE_SHIFT macro=0x%x\n", 258 + r.start, resource_size(&r), PAGE_SHIFT); 259 + 260 + csrow->first_page = r.start >> PAGE_SHIFT; 261 + nr_pages = resource_size(&r) >> PAGE_SHIFT; 262 + csrow->last_page = csrow->first_page + nr_pages - 1; 263 + 264 + regmap_read(aspeed_regmap, ASPEED_MCR_CONF, &reg04); 265 + dram_type = (reg04 & ASPEED_MCR_CONF_DRAM_TYPE) ? MEM_DDR4 : MEM_DDR3; 266 + 267 + dimm = csrow->channels[0]->dimm; 268 + dimm->mtype = dram_type; 269 + dimm->edac_mode = EDAC_SECDED; 270 + dimm->nr_pages = nr_pages / csrow->nr_channels; 271 + 272 + dev_dbg(mci->pdev, "initialized dimm with first_page=0x%lx and nr_pages=0x%x\n", 273 + csrow->first_page, nr_pages); 274 + 275 + return 0; 276 + } 277 + 278 + 279 + static int aspeed_probe(struct platform_device *pdev) 280 + { 281 + struct device *dev = &pdev->dev; 282 + struct edac_mc_layer layers[2]; 283 + struct mem_ctl_info *mci; 284 + struct device_node *np; 285 + struct resource *res; 286 + void __iomem *regs; 287 + u32 reg04; 288 + int rc; 289 + 290 + /* setup regmap */ 291 + np = dev->of_node; 292 + 293 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 294 + if (!res) 295 + return -ENOENT; 296 + 297 + regs = devm_ioremap_resource(dev, res); 298 + if (IS_ERR(regs)) 299 + return PTR_ERR(regs); 300 + 301 + aspeed_regmap = devm_regmap_init(dev, NULL, (__force void *)regs, 302 + &aspeed_regmap_config); 303 + if (IS_ERR(aspeed_regmap)) 304 + return PTR_ERR(aspeed_regmap); 305 + 306 + /* bail out if ECC mode is not configured */ 307 + regmap_read(aspeed_regmap, ASPEED_MCR_CONF, &reg04); 308 + if (!(reg04 & ASPEED_MCR_CONF_ECC)) { 309 + dev_err(&pdev->dev, "ECC mode is not configured in u-boot\n"); 310 + return -EPERM; 311 + } 312 + 313 + edac_op_state = EDAC_OPSTATE_INT; 314 + 315 + /* allocate & init EDAC MC data structure */ 316 + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 317 + layers[0].size = 1; 318 + layers[0].is_virt_csrow = true; 319 + layers[1].type = EDAC_MC_LAYER_CHANNEL; 320 + layers[1].size = 1; 321 + layers[1].is_virt_csrow = false; 322 + 323 + mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, 0); 324 + if (!mci) 325 + return -ENOMEM; 326 + 327 + mci->pdev = &pdev->dev; 328 + mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR4; 329 + mci->edac_ctl_cap = EDAC_FLAG_SECDED; 330 + mci->edac_cap = EDAC_FLAG_SECDED; 331 + mci->scrub_cap = SCRUB_FLAG_HW_SRC; 332 + mci->scrub_mode = SCRUB_HW_SRC; 333 + mci->mod_name = DRV_NAME; 334 + mci->ctl_name = "MIC"; 335 + mci->dev_name = dev_name(&pdev->dev); 336 + 337 + rc = init_csrows(mci); 338 + if (rc) { 339 + dev_err(&pdev->dev, "failed to init csrows\n"); 340 + goto probe_exit02; 341 + } 342 + 343 + platform_set_drvdata(pdev, mci); 344 + 345 + /* register with edac core */ 346 + rc = edac_mc_add_mc(mci); 347 + if (rc) { 348 + dev_err(&pdev->dev, "failed to register with EDAC core\n"); 349 + goto probe_exit02; 350 + } 351 + 352 + /* register interrupt handler and enable interrupts */ 353 + rc = config_irq(mci, pdev); 354 + if (rc) { 355 + dev_err(&pdev->dev, "failed setting up irq\n"); 356 + goto probe_exit01; 357 + } 358 + 359 + return 0; 360 + 361 + probe_exit01: 362 + edac_mc_del_mc(&pdev->dev); 363 + probe_exit02: 364 + edac_mc_free(mci); 365 + return rc; 366 + } 367 + 368 + 369 + static int aspeed_remove(struct platform_device *pdev) 370 + { 371 + struct mem_ctl_info *mci; 372 + 373 + /* disable interrupts */ 374 + regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL, 375 + ASPEED_MCR_INTR_CTRL_ENABLE, 0); 376 + 377 + /* free resources */ 378 + mci = edac_mc_del_mc(&pdev->dev); 379 + if (mci) 380 + edac_mc_free(mci); 381 + 382 + return 0; 383 + } 384 + 385 + 386 + static const struct of_device_id aspeed_of_match[] = { 387 + { .compatible = "aspeed,ast2500-sdram-edac" }, 388 + {}, 389 + }; 390 + 391 + 392 + static struct platform_driver aspeed_driver = { 393 + .driver = { 394 + .name = DRV_NAME, 395 + .of_match_table = aspeed_of_match 396 + }, 397 + .probe = aspeed_probe, 398 + .remove = aspeed_remove 399 + }; 400 + 401 + 402 + static int __init aspeed_init(void) 403 + { 404 + return platform_driver_register(&aspeed_driver); 405 + } 406 + 407 + 408 + static void __exit aspeed_exit(void) 409 + { 410 + platform_driver_unregister(&aspeed_driver); 411 + } 412 + 413 + 414 + module_init(aspeed_init); 415 + module_exit(aspeed_exit); 416 + 417 + 418 + MODULE_LICENSE("GPL"); 419 + MODULE_AUTHOR("Stefan Schaeckeler <sschaeck@cisco.com>"); 420 + MODULE_DESCRIPTION("Aspeed AST2500 EDAC driver"); 421 + MODULE_VERSION("1.0");