EDAC, altera: Add Arria10 SD-MMC EDAC support

Add Altera Arria10 SD-MMC FIFO memory EDAC support. The SD-MMC is a
dual port RAM implementation which is different than any of the other
peripherals and therefore requires additional code.

Signed-off-by: Thor Thayer <tthayer@opensource.altera.com>
Cc: dinguyen@opensource.altera.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-edac <linux-edac@vger.kernel.org>
Link: http://lkml.kernel.org/r/1470753653-23465-3-git-send-email-tthayer@opensource.altera.com
Signed-off-by: Borislav Petkov <bp@suse.de>
This commit is contained in:
Thor Thayer 2016-08-09 09:40:52 -05:00 committed by Borislav Petkov
parent 5e40cd4d25
commit 911049845d
3 changed files with 199 additions and 1 deletions

View file

@ -426,6 +426,13 @@ config EDAC_ALTERA_QSPI
Support for error detection and correction on the Support for error detection and correction on the
Altera QSPI FIFO Memory for Altera SoCs. Altera QSPI FIFO Memory for Altera SoCs.
config EDAC_ALTERA_SDMMC
bool "Altera SDMMC FIFO ECC"
depends on EDAC_ALTERA=y && MMC_DW
help
Support for error detection and correction on the
Altera SDMMC FIFO Memory for Altera SoCs.
config EDAC_SYNOPSYS config EDAC_SYNOPSYS
tristate "Synopsys DDR Memory Controller" tristate "Synopsys DDR Memory Controller"
depends on EDAC_MM_EDAC && ARCH_ZYNQ depends on EDAC_MM_EDAC && ARCH_ZYNQ

View file

@ -1393,6 +1393,188 @@ early_initcall(socfpga_init_qspi_ecc);
#endif /* CONFIG_EDAC_ALTERA_QSPI */ #endif /* CONFIG_EDAC_ALTERA_QSPI */
/********************* SDMMC Device Functions **********************/
#ifdef CONFIG_EDAC_ALTERA_SDMMC
static const struct edac_device_prv_data a10_sdmmceccb_data;
static int altr_portb_setup(struct altr_edac_device_dev *device)
{
struct edac_device_ctl_info *dci;
struct altr_edac_device_dev *altdev;
char *ecc_name = "sdmmcb-ecc";
int edac_idx, rc;
struct device_node *np;
const struct edac_device_prv_data *prv = &a10_sdmmceccb_data;
rc = altr_check_ecc_deps(device);
if (rc)
return rc;
np = of_find_compatible_node(NULL, NULL, "altr,socfpga-sdmmc-ecc");
if (!np) {
edac_printk(KERN_WARNING, EDAC_DEVICE, "SDMMC node not found\n");
return -ENODEV;
}
/* Create the PortB EDAC device */
edac_idx = edac_device_alloc_index();
dci = edac_device_alloc_ctl_info(sizeof(*altdev), ecc_name, 1,
ecc_name, 1, 0, NULL, 0, edac_idx);
if (!dci) {
edac_printk(KERN_ERR, EDAC_DEVICE,
"%s: Unable to allocate PortB EDAC device\n",
ecc_name);
return -ENOMEM;
}
/* Initialize the PortB EDAC device structure from PortA structure */
altdev = dci->pvt_info;
*altdev = *device;
if (!devres_open_group(&altdev->ddev, altr_portb_setup, GFP_KERNEL))
return -ENOMEM;
/* Update PortB specific values */
altdev->edac_dev_name = ecc_name;
altdev->edac_idx = edac_idx;
altdev->edac_dev = dci;
altdev->data = prv;
dci->dev = &altdev->ddev;
dci->ctl_name = "Altera ECC Manager";
dci->mod_name = ecc_name;
dci->dev_name = ecc_name;
/* Update the IRQs for PortB */
altdev->sb_irq = irq_of_parse_and_map(np, 2);
if (!altdev->sb_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB SBIRQ alloc\n");
rc = -ENODEV;
goto err_release_group_1;
}
rc = devm_request_irq(&altdev->ddev, altdev->sb_irq,
prv->ecc_irq_handler,
IRQF_SHARED, ecc_name, altdev);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE, "PortB SBERR IRQ error\n");
goto err_release_group_1;
}
altdev->db_irq = irq_of_parse_and_map(np, 3);
if (!altdev->db_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
rc = -ENODEV;
goto err_release_group_1;
}
rc = devm_request_irq(&altdev->ddev, altdev->db_irq,
prv->ecc_irq_handler,
IRQF_SHARED, ecc_name, altdev);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n");
goto err_release_group_1;
}
rc = edac_device_add_device(dci);
if (rc) {
edac_printk(KERN_ERR, EDAC_DEVICE,
"edac_device_add_device portB failed\n");
rc = -ENOMEM;
goto err_release_group_1;
}
altr_create_edacdev_dbgfs(dci, prv);
list_add(&altdev->next, &altdev->edac->a10_ecc_devices);
devres_remove_group(&altdev->ddev, altr_portb_setup);
return 0;
err_release_group_1:
edac_device_free_ctl_info(dci);
devres_release_group(&altdev->ddev, altr_portb_setup);
edac_printk(KERN_ERR, EDAC_DEVICE,
"%s:Error setting up EDAC device: %d\n", ecc_name, rc);
return rc;
}
static irqreturn_t altr_edac_a10_ecc_irq_portb(int irq, void *dev_id)
{
struct altr_edac_device_dev *ad = dev_id;
void __iomem *base = ad->base;
const struct edac_device_prv_data *priv = ad->data;
if (irq == ad->sb_irq) {
writel(priv->ce_clear_mask,
base + ALTR_A10_ECC_INTSTAT_OFST);
edac_device_handle_ce(ad->edac_dev, 0, 0, ad->edac_dev_name);
return IRQ_HANDLED;
} else if (irq == ad->db_irq) {
writel(priv->ue_clear_mask,
base + ALTR_A10_ECC_INTSTAT_OFST);
edac_device_handle_ue(ad->edac_dev, 0, 0, ad->edac_dev_name);
return IRQ_HANDLED;
}
WARN_ONCE(1, "Unhandled IRQ%d on Port B.", irq);
return IRQ_NONE;
}
static const struct edac_device_prv_data a10_sdmmcecca_data = {
.setup = altr_portb_setup,
.ce_clear_mask = ALTR_A10_ECC_SERRPENA,
.ue_clear_mask = ALTR_A10_ECC_DERRPENA,
.dbgfs_name = "altr_trigger",
.ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL,
.ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST,
.ce_set_mask = ALTR_A10_ECC_SERRPENA,
.ue_set_mask = ALTR_A10_ECC_DERRPENA,
.set_err_ofst = ALTR_A10_ECC_INTTEST_OFST,
.ecc_irq_handler = altr_edac_a10_ecc_irq,
.inject_fops = &altr_edac_a10_device_inject_fops,
};
static const struct edac_device_prv_data a10_sdmmceccb_data = {
.setup = altr_portb_setup,
.ce_clear_mask = ALTR_A10_ECC_SERRPENB,
.ue_clear_mask = ALTR_A10_ECC_DERRPENB,
.dbgfs_name = "altr_trigger",
.ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL,
.ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST,
.ce_set_mask = ALTR_A10_ECC_TSERRB,
.ue_set_mask = ALTR_A10_ECC_TDERRB,
.set_err_ofst = ALTR_A10_ECC_INTTEST_OFST,
.ecc_irq_handler = altr_edac_a10_ecc_irq_portb,
.inject_fops = &altr_edac_a10_device_inject_fops,
};
static int __init socfpga_init_sdmmc_ecc(void)
{
int rc = -ENODEV;
struct device_node *child = of_find_compatible_node(NULL, NULL,
"altr,socfpga-sdmmc-ecc");
if (!child) {
edac_printk(KERN_WARNING, EDAC_DEVICE, "SDMMC node not found\n");
return -ENODEV;
}
if (!of_device_is_available(child))
goto exit;
if (validate_parent_available(child))
goto exit;
rc = altr_init_a10_ecc_block(child, ALTR_A10_SDMMC_IRQ_MASK,
a10_sdmmcecca_data.ecc_enable_mask, 1);
exit:
of_node_put(child);
return rc;
}
early_initcall(socfpga_init_sdmmc_ecc);
#endif /* CONFIG_EDAC_ALTERA_SDMMC */
/********************* Arria10 EDAC Device Functions *************************/ /********************* Arria10 EDAC Device Functions *************************/
static const struct of_device_id altr_edac_a10_device_of_match[] = { static const struct of_device_id altr_edac_a10_device_of_match[] = {
#ifdef CONFIG_EDAC_ALTERA_L2C #ifdef CONFIG_EDAC_ALTERA_L2C
@ -1417,6 +1599,9 @@ static const struct of_device_id altr_edac_a10_device_of_match[] = {
#endif #endif
#ifdef CONFIG_EDAC_ALTERA_QSPI #ifdef CONFIG_EDAC_ALTERA_QSPI
{ .compatible = "altr,socfpga-qspi-ecc", .data = &a10_qspiecc_data }, { .compatible = "altr,socfpga-qspi-ecc", .data = &a10_qspiecc_data },
#endif
#ifdef CONFIG_EDAC_ALTERA_SDMMC
{ .compatible = "altr,socfpga-sdmmc-ecc", .data = &a10_sdmmcecca_data },
#endif #endif
{}, {},
}; };
@ -1711,7 +1896,8 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
of_device_is_compatible(child, "altr,socfpga-nand-ecc") || of_device_is_compatible(child, "altr,socfpga-nand-ecc") ||
of_device_is_compatible(child, "altr,socfpga-dma-ecc") || of_device_is_compatible(child, "altr,socfpga-dma-ecc") ||
of_device_is_compatible(child, "altr,socfpga-usb-ecc") || of_device_is_compatible(child, "altr,socfpga-usb-ecc") ||
of_device_is_compatible(child, "altr,socfpga-qspi-ecc")) of_device_is_compatible(child, "altr,socfpga-qspi-ecc") ||
of_device_is_compatible(child, "altr,socfpga-sdmmc-ecc"))
altr_edac_a10_device_add(edac, child); altr_edac_a10_device_add(edac, child);

View file

@ -250,6 +250,8 @@ struct altr_sdram_mc_data {
#define ALTR_A10_ECC_INTTEST_OFST 0x24 #define ALTR_A10_ECC_INTTEST_OFST 0x24
#define ALTR_A10_ECC_TSERRA BIT(0) #define ALTR_A10_ECC_TSERRA BIT(0)
#define ALTR_A10_ECC_TDERRA BIT(8) #define ALTR_A10_ECC_TDERRA BIT(8)
#define ALTR_A10_ECC_TSERRB BIT(16)
#define ALTR_A10_ECC_TDERRB BIT(24)
/* ECC Manager Defines */ /* ECC Manager Defines */
#define A10_SYSMGR_ECC_INTMASK_SET_OFST 0x94 #define A10_SYSMGR_ECC_INTMASK_SET_OFST 0x94
@ -288,6 +290,9 @@ struct altr_sdram_mc_data {
/* Arria 10 Ethernet ECC Management Group Defines */ /* Arria 10 Ethernet ECC Management Group Defines */
#define ALTR_A10_COMMON_ECC_EN_CTL BIT(0) #define ALTR_A10_COMMON_ECC_EN_CTL BIT(0)
/* Arria 10 SDMMC ECC Management Group Defines */
#define ALTR_A10_SDMMC_IRQ_MASK (BIT(16) | BIT(15))
/* A10 ECC Controller memory initialization timeout */ /* A10 ECC Controller memory initialization timeout */
#define ALTR_A10_ECC_INIT_WATCHDOG_10US 10000 #define ALTR_A10_ECC_INIT_WATCHDOG_10US 10000