623 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2017 Pengutronix, Jan Luebbe <kernel@pengutronix.de>4 */5 6#include <linux/kernel.h>7#include <linux/edac.h>8#include <linux/of.h>9#include <linux/of_device.h>10#include <linux/platform_device.h>11 12#include <asm/hardware/cache-l2x0.h>13#include <asm/hardware/cache-aurora-l2.h>14 15#include "edac_mc.h"16#include "edac_device.h"17#include "edac_module.h"18 19/************************ EDAC MC (DDR RAM) ********************************/20 21#define SDRAM_NUM_CS 422 23#define SDRAM_CONFIG_REG 0x024#define SDRAM_CONFIG_ECC_MASK BIT(18)25#define SDRAM_CONFIG_REGISTERED_MASK BIT(17)26#define SDRAM_CONFIG_BUS_WIDTH_MASK BIT(15)27 28#define SDRAM_ADDR_CTRL_REG 0x1029#define SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(cs) (20+cs)30#define SDRAM_ADDR_CTRL_SIZE_HIGH_MASK(cs) (0x1 << SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(cs))31#define SDRAM_ADDR_CTRL_ADDR_SEL_MASK(cs) BIT(16+cs)32#define SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(cs) (cs*4+2)33#define SDRAM_ADDR_CTRL_SIZE_LOW_MASK(cs) (0x3 << SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(cs))34#define SDRAM_ADDR_CTRL_STRUCT_OFFSET(cs) (cs*4)35#define SDRAM_ADDR_CTRL_STRUCT_MASK(cs) (0x3 << SDRAM_ADDR_CTRL_STRUCT_OFFSET(cs))36 37#define SDRAM_ERR_DATA_H_REG 0x4038#define SDRAM_ERR_DATA_L_REG 0x4439 40#define SDRAM_ERR_RECV_ECC_REG 0x4841#define SDRAM_ERR_RECV_ECC_VALUE_MASK 0xff42 43#define SDRAM_ERR_CALC_ECC_REG 0x4c44#define SDRAM_ERR_CALC_ECC_ROW_OFFSET 845#define SDRAM_ERR_CALC_ECC_ROW_MASK (0xffff << SDRAM_ERR_CALC_ECC_ROW_OFFSET)46#define SDRAM_ERR_CALC_ECC_VALUE_MASK 0xff47 48#define SDRAM_ERR_ADDR_REG 0x5049#define SDRAM_ERR_ADDR_BANK_OFFSET 2350#define SDRAM_ERR_ADDR_BANK_MASK (0x7 << SDRAM_ERR_ADDR_BANK_OFFSET)51#define SDRAM_ERR_ADDR_COL_OFFSET 852#define SDRAM_ERR_ADDR_COL_MASK (0x7fff << SDRAM_ERR_ADDR_COL_OFFSET)53#define SDRAM_ERR_ADDR_CS_OFFSET 154#define SDRAM_ERR_ADDR_CS_MASK (0x3 << SDRAM_ERR_ADDR_CS_OFFSET)55#define SDRAM_ERR_ADDR_TYPE_MASK BIT(0)56 57#define SDRAM_ERR_CTRL_REG 0x5458#define SDRAM_ERR_CTRL_THR_OFFSET 1659#define SDRAM_ERR_CTRL_THR_MASK (0xff << SDRAM_ERR_CTRL_THR_OFFSET)60#define SDRAM_ERR_CTRL_PROP_MASK BIT(9)61 62#define SDRAM_ERR_SBE_COUNT_REG 0x5863#define SDRAM_ERR_DBE_COUNT_REG 0x5c64 65#define SDRAM_ERR_CAUSE_ERR_REG 0xd066#define SDRAM_ERR_CAUSE_MSG_REG 0xd867#define SDRAM_ERR_CAUSE_DBE_MASK BIT(1)68#define SDRAM_ERR_CAUSE_SBE_MASK BIT(0)69 70#define SDRAM_RANK_CTRL_REG 0x1e071#define SDRAM_RANK_CTRL_EXIST_MASK(cs) BIT(cs)72 73struct axp_mc_drvdata {74 void __iomem *base;75 /* width in bytes */76 unsigned int width;77 /* bank interleaving */78 bool cs_addr_sel[SDRAM_NUM_CS];79 80 char msg[128];81};82 83/* derived from "DRAM Address Multiplexing" in the ARMADA XP Functional Spec */84static uint32_t axp_mc_calc_address(struct axp_mc_drvdata *drvdata,85 uint8_t cs, uint8_t bank, uint16_t row,86 uint16_t col)87{88 if (drvdata->width == 8) {89 /* 64 bit */90 if (drvdata->cs_addr_sel[cs])91 /* bank interleaved */92 return (((row & 0xfff8) << 16) |93 ((bank & 0x7) << 16) |94 ((row & 0x7) << 13) |95 ((col & 0x3ff) << 3));96 else97 return (((row & 0xffff << 16) |98 ((bank & 0x7) << 13) |99 ((col & 0x3ff)) << 3));100 } else if (drvdata->width == 4) {101 /* 32 bit */102 if (drvdata->cs_addr_sel[cs])103 /* bank interleaved */104 return (((row & 0xfff0) << 15) |105 ((bank & 0x7) << 16) |106 ((row & 0xf) << 12) |107 ((col & 0x3ff) << 2));108 else109 return (((row & 0xffff << 15) |110 ((bank & 0x7) << 12) |111 ((col & 0x3ff)) << 2));112 } else {113 /* 16 bit */114 if (drvdata->cs_addr_sel[cs])115 /* bank interleaved */116 return (((row & 0xffe0) << 14) |117 ((bank & 0x7) << 16) |118 ((row & 0x1f) << 11) |119 ((col & 0x3ff) << 1));120 else121 return (((row & 0xffff << 14) |122 ((bank & 0x7) << 11) |123 ((col & 0x3ff)) << 1));124 }125}126 127static void axp_mc_check(struct mem_ctl_info *mci)128{129 struct axp_mc_drvdata *drvdata = mci->pvt_info;130 uint32_t data_h, data_l, recv_ecc, calc_ecc, addr;131 uint32_t cnt_sbe, cnt_dbe, cause_err, cause_msg;132 uint32_t row_val, col_val, bank_val, addr_val;133 uint8_t syndrome_val, cs_val;134 char *msg = drvdata->msg;135 136 data_h = readl(drvdata->base + SDRAM_ERR_DATA_H_REG);137 data_l = readl(drvdata->base + SDRAM_ERR_DATA_L_REG);138 recv_ecc = readl(drvdata->base + SDRAM_ERR_RECV_ECC_REG);139 calc_ecc = readl(drvdata->base + SDRAM_ERR_CALC_ECC_REG);140 addr = readl(drvdata->base + SDRAM_ERR_ADDR_REG);141 cnt_sbe = readl(drvdata->base + SDRAM_ERR_SBE_COUNT_REG);142 cnt_dbe = readl(drvdata->base + SDRAM_ERR_DBE_COUNT_REG);143 cause_err = readl(drvdata->base + SDRAM_ERR_CAUSE_ERR_REG);144 cause_msg = readl(drvdata->base + SDRAM_ERR_CAUSE_MSG_REG);145 146 /* clear cause registers */147 writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK),148 drvdata->base + SDRAM_ERR_CAUSE_ERR_REG);149 writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK),150 drvdata->base + SDRAM_ERR_CAUSE_MSG_REG);151 152 /* clear error counter registers */153 if (cnt_sbe)154 writel(0, drvdata->base + SDRAM_ERR_SBE_COUNT_REG);155 if (cnt_dbe)156 writel(0, drvdata->base + SDRAM_ERR_DBE_COUNT_REG);157 158 if (!cnt_sbe && !cnt_dbe)159 return;160 161 if (!(addr & SDRAM_ERR_ADDR_TYPE_MASK)) {162 if (cnt_sbe)163 cnt_sbe--;164 else165 dev_warn(mci->pdev, "inconsistent SBE count detected\n");166 } else {167 if (cnt_dbe)168 cnt_dbe--;169 else170 dev_warn(mci->pdev, "inconsistent DBE count detected\n");171 }172 173 /* report earlier errors */174 if (cnt_sbe)175 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,176 cnt_sbe, /* error count */177 0, 0, 0, /* pfn, offset, syndrome */178 -1, -1, -1, /* top, mid, low layer */179 mci->ctl_name,180 "details unavailable (multiple errors)");181 if (cnt_dbe)182 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,183 cnt_dbe, /* error count */184 0, 0, 0, /* pfn, offset, syndrome */185 -1, -1, -1, /* top, mid, low layer */186 mci->ctl_name,187 "details unavailable (multiple errors)");188 189 /* report details for most recent error */190 cs_val = (addr & SDRAM_ERR_ADDR_CS_MASK) >> SDRAM_ERR_ADDR_CS_OFFSET;191 bank_val = (addr & SDRAM_ERR_ADDR_BANK_MASK) >> SDRAM_ERR_ADDR_BANK_OFFSET;192 row_val = (calc_ecc & SDRAM_ERR_CALC_ECC_ROW_MASK) >> SDRAM_ERR_CALC_ECC_ROW_OFFSET;193 col_val = (addr & SDRAM_ERR_ADDR_COL_MASK) >> SDRAM_ERR_ADDR_COL_OFFSET;194 syndrome_val = (recv_ecc ^ calc_ecc) & 0xff;195 addr_val = axp_mc_calc_address(drvdata, cs_val, bank_val, row_val,196 col_val);197 msg += sprintf(msg, "row=0x%04x ", row_val); /* 11 chars */198 msg += sprintf(msg, "bank=0x%x ", bank_val); /* 9 chars */199 msg += sprintf(msg, "col=0x%04x ", col_val); /* 11 chars */200 msg += sprintf(msg, "cs=%d", cs_val); /* 4 chars */201 202 if (!(addr & SDRAM_ERR_ADDR_TYPE_MASK)) {203 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,204 1, /* error count */205 addr_val >> PAGE_SHIFT,206 addr_val & ~PAGE_MASK,207 syndrome_val,208 cs_val, -1, -1, /* top, mid, low layer */209 mci->ctl_name, drvdata->msg);210 } else {211 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,212 1, /* error count */213 addr_val >> PAGE_SHIFT,214 addr_val & ~PAGE_MASK,215 syndrome_val,216 cs_val, -1, -1, /* top, mid, low layer */217 mci->ctl_name, drvdata->msg);218 }219}220 221static void axp_mc_read_config(struct mem_ctl_info *mci)222{223 struct axp_mc_drvdata *drvdata = mci->pvt_info;224 uint32_t config, addr_ctrl, rank_ctrl;225 unsigned int i, cs_struct, cs_size;226 struct dimm_info *dimm;227 228 config = readl(drvdata->base + SDRAM_CONFIG_REG);229 if (config & SDRAM_CONFIG_BUS_WIDTH_MASK)230 /* 64 bit */231 drvdata->width = 8;232 else233 /* 32 bit */234 drvdata->width = 4;235 236 addr_ctrl = readl(drvdata->base + SDRAM_ADDR_CTRL_REG);237 rank_ctrl = readl(drvdata->base + SDRAM_RANK_CTRL_REG);238 for (i = 0; i < SDRAM_NUM_CS; i++) {239 dimm = mci->dimms[i];240 241 if (!(rank_ctrl & SDRAM_RANK_CTRL_EXIST_MASK(i)))242 continue;243 244 drvdata->cs_addr_sel[i] =245 !!(addr_ctrl & SDRAM_ADDR_CTRL_ADDR_SEL_MASK(i));246 247 cs_struct = (addr_ctrl & SDRAM_ADDR_CTRL_STRUCT_MASK(i)) >> SDRAM_ADDR_CTRL_STRUCT_OFFSET(i);248 cs_size = ((addr_ctrl & SDRAM_ADDR_CTRL_SIZE_HIGH_MASK(i)) >> (SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(i) - 2) |249 ((addr_ctrl & SDRAM_ADDR_CTRL_SIZE_LOW_MASK(i)) >> SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(i)));250 251 switch (cs_size) {252 case 0: /* 2GBit */253 dimm->nr_pages = 524288;254 break;255 case 1: /* 256MBit */256 dimm->nr_pages = 65536;257 break;258 case 2: /* 512MBit */259 dimm->nr_pages = 131072;260 break;261 case 3: /* 1GBit */262 dimm->nr_pages = 262144;263 break;264 case 4: /* 4GBit */265 dimm->nr_pages = 1048576;266 break;267 case 5: /* 8GBit */268 dimm->nr_pages = 2097152;269 break;270 }271 dimm->grain = 8;272 dimm->dtype = cs_struct ? DEV_X16 : DEV_X8;273 dimm->mtype = (config & SDRAM_CONFIG_REGISTERED_MASK) ?274 MEM_RDDR3 : MEM_DDR3;275 dimm->edac_mode = EDAC_SECDED;276 }277}278 279static const struct of_device_id axp_mc_of_match[] = {280 {.compatible = "marvell,armada-xp-sdram-controller",},281 {},282};283MODULE_DEVICE_TABLE(of, axp_mc_of_match);284 285static int axp_mc_probe(struct platform_device *pdev)286{287 struct axp_mc_drvdata *drvdata;288 struct edac_mc_layer layers[1];289 const struct of_device_id *id;290 struct mem_ctl_info *mci;291 void __iomem *base;292 uint32_t config;293 294 base = devm_platform_ioremap_resource(pdev, 0);295 if (IS_ERR(base)) {296 dev_err(&pdev->dev, "Unable to map regs\n");297 return PTR_ERR(base);298 }299 300 config = readl(base + SDRAM_CONFIG_REG);301 if (!(config & SDRAM_CONFIG_ECC_MASK)) {302 dev_warn(&pdev->dev, "SDRAM ECC is not enabled\n");303 return -EINVAL;304 }305 306 layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;307 layers[0].size = SDRAM_NUM_CS;308 layers[0].is_virt_csrow = true;309 310 mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*drvdata));311 if (!mci)312 return -ENOMEM;313 314 drvdata = mci->pvt_info;315 drvdata->base = base;316 mci->pdev = &pdev->dev;317 platform_set_drvdata(pdev, mci);318 319 id = of_match_device(axp_mc_of_match, &pdev->dev);320 mci->edac_check = axp_mc_check;321 mci->mtype_cap = MEM_FLAG_DDR3;322 mci->edac_cap = EDAC_FLAG_SECDED;323 mci->mod_name = pdev->dev.driver->name;324 mci->ctl_name = id ? id->compatible : "unknown";325 mci->dev_name = dev_name(&pdev->dev);326 mci->scrub_mode = SCRUB_NONE;327 328 axp_mc_read_config(mci);329 330 /* These SoCs have a reduced width bus */331 if (of_machine_is_compatible("marvell,armada380") ||332 of_machine_is_compatible("marvell,armadaxp-98dx3236"))333 drvdata->width /= 2;334 335 /* configure SBE threshold */336 /* it seems that SBEs are not captured otherwise */337 writel(1 << SDRAM_ERR_CTRL_THR_OFFSET, drvdata->base + SDRAM_ERR_CTRL_REG);338 339 /* clear cause registers */340 writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), drvdata->base + SDRAM_ERR_CAUSE_ERR_REG);341 writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), drvdata->base + SDRAM_ERR_CAUSE_MSG_REG);342 343 /* clear counter registers */344 writel(0, drvdata->base + SDRAM_ERR_SBE_COUNT_REG);345 writel(0, drvdata->base + SDRAM_ERR_DBE_COUNT_REG);346 347 if (edac_mc_add_mc(mci)) {348 edac_mc_free(mci);349 return -EINVAL;350 }351 edac_op_state = EDAC_OPSTATE_POLL;352 353 return 0;354}355 356static void axp_mc_remove(struct platform_device *pdev)357{358 struct mem_ctl_info *mci = platform_get_drvdata(pdev);359 360 edac_mc_del_mc(&pdev->dev);361 edac_mc_free(mci);362 platform_set_drvdata(pdev, NULL);363}364 365static struct platform_driver axp_mc_driver = {366 .probe = axp_mc_probe,367 .remove_new = axp_mc_remove,368 .driver = {369 .name = "armada_xp_mc_edac",370 .of_match_table = of_match_ptr(axp_mc_of_match),371 },372};373 374/************************ EDAC Device (L2 Cache) ***************************/375 376struct aurora_l2_drvdata {377 void __iomem *base;378 379 char msg[128];380 381 /* error injection via debugfs */382 uint32_t inject_addr;383 uint32_t inject_mask;384 uint8_t inject_ctl;385 386 struct dentry *debugfs;387};388 389#ifdef CONFIG_EDAC_DEBUG390static void aurora_l2_inject(struct aurora_l2_drvdata *drvdata)391{392 drvdata->inject_addr &= AURORA_ERR_INJECT_CTL_ADDR_MASK;393 drvdata->inject_ctl &= AURORA_ERR_INJECT_CTL_EN_MASK;394 writel(0, drvdata->base + AURORA_ERR_INJECT_CTL_REG);395 writel(drvdata->inject_mask, drvdata->base + AURORA_ERR_INJECT_MASK_REG);396 writel(drvdata->inject_addr | drvdata->inject_ctl, drvdata->base + AURORA_ERR_INJECT_CTL_REG);397}398#endif399 400static void aurora_l2_check(struct edac_device_ctl_info *dci)401{402 struct aurora_l2_drvdata *drvdata = dci->pvt_info;403 uint32_t cnt, src, txn, err, attr_cap, addr_cap, way_cap;404 unsigned int cnt_ce, cnt_ue;405 char *msg = drvdata->msg;406 size_t size = sizeof(drvdata->msg);407 size_t len = 0;408 409 cnt = readl(drvdata->base + AURORA_ERR_CNT_REG);410 attr_cap = readl(drvdata->base + AURORA_ERR_ATTR_CAP_REG);411 addr_cap = readl(drvdata->base + AURORA_ERR_ADDR_CAP_REG);412 way_cap = readl(drvdata->base + AURORA_ERR_WAY_CAP_REG);413 414 cnt_ce = (cnt & AURORA_ERR_CNT_CE_MASK) >> AURORA_ERR_CNT_CE_OFFSET;415 cnt_ue = (cnt & AURORA_ERR_CNT_UE_MASK) >> AURORA_ERR_CNT_UE_OFFSET;416 /* clear error counter registers */417 if (cnt_ce || cnt_ue)418 writel(AURORA_ERR_CNT_CLR, drvdata->base + AURORA_ERR_CNT_REG);419 420 if (!(attr_cap & AURORA_ERR_ATTR_CAP_VALID))421 goto clear_remaining;422 423 src = (attr_cap & AURORA_ERR_ATTR_SRC_MSK) >> AURORA_ERR_ATTR_SRC_OFF;424 if (src <= 3)425 len += scnprintf(msg+len, size-len, "src=CPU%d ", src);426 else427 len += scnprintf(msg+len, size-len, "src=IO ");428 429 txn = (attr_cap & AURORA_ERR_ATTR_TXN_MSK) >> AURORA_ERR_ATTR_TXN_OFF;430 switch (txn) {431 case 0:432 len += scnprintf(msg+len, size-len, "txn=Data-Read ");433 break;434 case 1:435 len += scnprintf(msg+len, size-len, "txn=Isn-Read ");436 break;437 case 2:438 len += scnprintf(msg+len, size-len, "txn=Clean-Flush ");439 break;440 case 3:441 len += scnprintf(msg+len, size-len, "txn=Eviction ");442 break;443 case 4:444 len += scnprintf(msg+len, size-len,445 "txn=Read-Modify-Write ");446 break;447 }448 449 err = (attr_cap & AURORA_ERR_ATTR_ERR_MSK) >> AURORA_ERR_ATTR_ERR_OFF;450 switch (err) {451 case 0:452 len += scnprintf(msg+len, size-len, "err=CorrECC ");453 break;454 case 1:455 len += scnprintf(msg+len, size-len, "err=UnCorrECC ");456 break;457 case 2:458 len += scnprintf(msg+len, size-len, "err=TagParity ");459 break;460 }461 462 len += scnprintf(msg+len, size-len, "addr=0x%x ", addr_cap & AURORA_ERR_ADDR_CAP_ADDR_MASK);463 len += scnprintf(msg+len, size-len, "index=0x%x ", (way_cap & AURORA_ERR_WAY_IDX_MSK) >> AURORA_ERR_WAY_IDX_OFF);464 len += scnprintf(msg+len, size-len, "way=0x%x", (way_cap & AURORA_ERR_WAY_CAP_WAY_MASK) >> AURORA_ERR_WAY_CAP_WAY_OFFSET);465 466 /* clear error capture registers */467 writel(AURORA_ERR_ATTR_CAP_VALID, drvdata->base + AURORA_ERR_ATTR_CAP_REG);468 if (err) {469 /* UnCorrECC or TagParity */470 if (cnt_ue)471 cnt_ue--;472 edac_device_handle_ue(dci, 0, 0, drvdata->msg);473 } else {474 if (cnt_ce)475 cnt_ce--;476 edac_device_handle_ce(dci, 0, 0, drvdata->msg);477 }478 479clear_remaining:480 /* report remaining errors */481 while (cnt_ue--)482 edac_device_handle_ue(dci, 0, 0, "details unavailable (multiple errors)");483 while (cnt_ce--)484 edac_device_handle_ue(dci, 0, 0, "details unavailable (multiple errors)");485}486 487static void aurora_l2_poll(struct edac_device_ctl_info *dci)488{489#ifdef CONFIG_EDAC_DEBUG490 struct aurora_l2_drvdata *drvdata = dci->pvt_info;491#endif492 493 aurora_l2_check(dci);494#ifdef CONFIG_EDAC_DEBUG495 aurora_l2_inject(drvdata);496#endif497}498 499static const struct of_device_id aurora_l2_of_match[] = {500 {.compatible = "marvell,aurora-system-cache",},501 {},502};503MODULE_DEVICE_TABLE(of, aurora_l2_of_match);504 505static int aurora_l2_probe(struct platform_device *pdev)506{507 struct aurora_l2_drvdata *drvdata;508 struct edac_device_ctl_info *dci;509 const struct of_device_id *id;510 uint32_t l2x0_aux_ctrl;511 void __iomem *base;512 513 base = devm_platform_ioremap_resource(pdev, 0);514 if (IS_ERR(base)) {515 dev_err(&pdev->dev, "Unable to map regs\n");516 return PTR_ERR(base);517 }518 519 l2x0_aux_ctrl = readl(base + L2X0_AUX_CTRL);520 if (!(l2x0_aux_ctrl & AURORA_ACR_PARITY_EN))521 dev_warn(&pdev->dev, "tag parity is not enabled\n");522 if (!(l2x0_aux_ctrl & AURORA_ACR_ECC_EN))523 dev_warn(&pdev->dev, "data ECC is not enabled\n");524 525 dci = edac_device_alloc_ctl_info(sizeof(*drvdata),526 "cpu", 1, "L", 1, 2, 0);527 if (!dci)528 return -ENOMEM;529 530 drvdata = dci->pvt_info;531 drvdata->base = base;532 dci->dev = &pdev->dev;533 platform_set_drvdata(pdev, dci);534 535 id = of_match_device(aurora_l2_of_match, &pdev->dev);536 dci->edac_check = aurora_l2_poll;537 dci->mod_name = pdev->dev.driver->name;538 dci->ctl_name = id ? id->compatible : "unknown";539 dci->dev_name = dev_name(&pdev->dev);540 541 /* clear registers */542 writel(AURORA_ERR_CNT_CLR, drvdata->base + AURORA_ERR_CNT_REG);543 writel(AURORA_ERR_ATTR_CAP_VALID, drvdata->base + AURORA_ERR_ATTR_CAP_REG);544 545 if (edac_device_add_device(dci)) {546 edac_device_free_ctl_info(dci);547 return -EINVAL;548 }549 550#ifdef CONFIG_EDAC_DEBUG551 drvdata->debugfs = edac_debugfs_create_dir(dev_name(&pdev->dev));552 if (drvdata->debugfs) {553 edac_debugfs_create_x32("inject_addr", 0644,554 drvdata->debugfs,555 &drvdata->inject_addr);556 edac_debugfs_create_x32("inject_mask", 0644,557 drvdata->debugfs,558 &drvdata->inject_mask);559 edac_debugfs_create_x8("inject_ctl", 0644,560 drvdata->debugfs, &drvdata->inject_ctl);561 }562#endif563 564 return 0;565}566 567static void aurora_l2_remove(struct platform_device *pdev)568{569 struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);570#ifdef CONFIG_EDAC_DEBUG571 struct aurora_l2_drvdata *drvdata = dci->pvt_info;572 573 edac_debugfs_remove_recursive(drvdata->debugfs);574#endif575 edac_device_del_device(&pdev->dev);576 edac_device_free_ctl_info(dci);577 platform_set_drvdata(pdev, NULL);578}579 580static struct platform_driver aurora_l2_driver = {581 .probe = aurora_l2_probe,582 .remove_new = aurora_l2_remove,583 .driver = {584 .name = "aurora_l2_edac",585 .of_match_table = of_match_ptr(aurora_l2_of_match),586 },587};588 589/************************ Driver registration ******************************/590 591static struct platform_driver * const drivers[] = {592 &axp_mc_driver,593 &aurora_l2_driver,594};595 596static int __init armada_xp_edac_init(void)597{598 int res;599 600 if (ghes_get_devices())601 return -EBUSY;602 603 /* only polling is supported */604 edac_op_state = EDAC_OPSTATE_POLL;605 606 res = platform_register_drivers(drivers, ARRAY_SIZE(drivers));607 if (res)608 pr_warn("Armada XP EDAC drivers fail to register\n");609 610 return 0;611}612module_init(armada_xp_edac_init);613 614static void __exit armada_xp_edac_exit(void)615{616 platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));617}618module_exit(armada_xp_edac_exit);619 620MODULE_LICENSE("GPL v2");621MODULE_AUTHOR("Pengutronix");622MODULE_DESCRIPTION("EDAC Drivers for Marvell Armada XP SDRAM and L2 Cache Controller");623