diff options
author | Thor Thayer <tthayer@opensource.altera.com> | 2016-08-09 10:40:52 -0400 |
---|---|---|
committer | Borislav Petkov <bp@suse.de> | 2016-08-10 08:43:14 -0400 |
commit | 911049845d7096c27304930ff478cbf3f2623ba3 (patch) | |
tree | 5cef3310ecfab5ef2bd1a401a620912c5de34f5b /drivers/edac/altera_edac.c | |
parent | 5e40cd4d258cc0728585a94fd81f73488f7cdce7 (diff) |
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>
Diffstat (limited to 'drivers/edac/altera_edac.c')
-rw-r--r-- | drivers/edac/altera_edac.c | 188 |
1 files changed, 187 insertions, 1 deletions
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c index 28247f82e1d5..efaf727be25a 100644 --- a/drivers/edac/altera_edac.c +++ b/drivers/edac/altera_edac.c | |||
@@ -1393,6 +1393,188 @@ early_initcall(socfpga_init_qspi_ecc); | |||
1393 | 1393 | ||
1394 | #endif /* CONFIG_EDAC_ALTERA_QSPI */ | 1394 | #endif /* CONFIG_EDAC_ALTERA_QSPI */ |
1395 | 1395 | ||
1396 | /********************* SDMMC Device Functions **********************/ | ||
1397 | |||
1398 | #ifdef CONFIG_EDAC_ALTERA_SDMMC | ||
1399 | |||
1400 | static const struct edac_device_prv_data a10_sdmmceccb_data; | ||
1401 | static int altr_portb_setup(struct altr_edac_device_dev *device) | ||
1402 | { | ||
1403 | struct edac_device_ctl_info *dci; | ||
1404 | struct altr_edac_device_dev *altdev; | ||
1405 | char *ecc_name = "sdmmcb-ecc"; | ||
1406 | int edac_idx, rc; | ||
1407 | struct device_node *np; | ||
1408 | const struct edac_device_prv_data *prv = &a10_sdmmceccb_data; | ||
1409 | |||
1410 | rc = altr_check_ecc_deps(device); | ||
1411 | if (rc) | ||
1412 | return rc; | ||
1413 | |||
1414 | np = of_find_compatible_node(NULL, NULL, "altr,socfpga-sdmmc-ecc"); | ||
1415 | if (!np) { | ||
1416 | edac_printk(KERN_WARNING, EDAC_DEVICE, "SDMMC node not found\n"); | ||
1417 | return -ENODEV; | ||
1418 | } | ||
1419 | |||
1420 | /* Create the PortB EDAC device */ | ||
1421 | edac_idx = edac_device_alloc_index(); | ||
1422 | dci = edac_device_alloc_ctl_info(sizeof(*altdev), ecc_name, 1, | ||
1423 | ecc_name, 1, 0, NULL, 0, edac_idx); | ||
1424 | if (!dci) { | ||
1425 | edac_printk(KERN_ERR, EDAC_DEVICE, | ||
1426 | "%s: Unable to allocate PortB EDAC device\n", | ||
1427 | ecc_name); | ||
1428 | return -ENOMEM; | ||
1429 | } | ||
1430 | |||
1431 | /* Initialize the PortB EDAC device structure from PortA structure */ | ||
1432 | altdev = dci->pvt_info; | ||
1433 | *altdev = *device; | ||
1434 | |||
1435 | if (!devres_open_group(&altdev->ddev, altr_portb_setup, GFP_KERNEL)) | ||
1436 | return -ENOMEM; | ||
1437 | |||
1438 | /* Update PortB specific values */ | ||
1439 | altdev->edac_dev_name = ecc_name; | ||
1440 | altdev->edac_idx = edac_idx; | ||
1441 | altdev->edac_dev = dci; | ||
1442 | altdev->data = prv; | ||
1443 | dci->dev = &altdev->ddev; | ||
1444 | dci->ctl_name = "Altera ECC Manager"; | ||
1445 | dci->mod_name = ecc_name; | ||
1446 | dci->dev_name = ecc_name; | ||
1447 | |||
1448 | /* Update the IRQs for PortB */ | ||
1449 | altdev->sb_irq = irq_of_parse_and_map(np, 2); | ||
1450 | if (!altdev->sb_irq) { | ||
1451 | edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB SBIRQ alloc\n"); | ||
1452 | rc = -ENODEV; | ||
1453 | goto err_release_group_1; | ||
1454 | } | ||
1455 | rc = devm_request_irq(&altdev->ddev, altdev->sb_irq, | ||
1456 | prv->ecc_irq_handler, | ||
1457 | IRQF_SHARED, ecc_name, altdev); | ||
1458 | if (rc) { | ||
1459 | edac_printk(KERN_ERR, EDAC_DEVICE, "PortB SBERR IRQ error\n"); | ||
1460 | goto err_release_group_1; | ||
1461 | } | ||
1462 | |||
1463 | altdev->db_irq = irq_of_parse_and_map(np, 3); | ||
1464 | if (!altdev->db_irq) { | ||
1465 | edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n"); | ||
1466 | rc = -ENODEV; | ||
1467 | goto err_release_group_1; | ||
1468 | } | ||
1469 | rc = devm_request_irq(&altdev->ddev, altdev->db_irq, | ||
1470 | prv->ecc_irq_handler, | ||
1471 | IRQF_SHARED, ecc_name, altdev); | ||
1472 | if (rc) { | ||
1473 | edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n"); | ||
1474 | goto err_release_group_1; | ||
1475 | } | ||
1476 | |||
1477 | rc = edac_device_add_device(dci); | ||
1478 | if (rc) { | ||
1479 | edac_printk(KERN_ERR, EDAC_DEVICE, | ||
1480 | "edac_device_add_device portB failed\n"); | ||
1481 | rc = -ENOMEM; | ||
1482 | goto err_release_group_1; | ||
1483 | } | ||
1484 | altr_create_edacdev_dbgfs(dci, prv); | ||
1485 | |||
1486 | list_add(&altdev->next, &altdev->edac->a10_ecc_devices); | ||
1487 | |||
1488 | devres_remove_group(&altdev->ddev, altr_portb_setup); | ||
1489 | |||
1490 | return 0; | ||
1491 | |||
1492 | err_release_group_1: | ||
1493 | edac_device_free_ctl_info(dci); | ||
1494 | devres_release_group(&altdev->ddev, altr_portb_setup); | ||
1495 | edac_printk(KERN_ERR, EDAC_DEVICE, | ||
1496 | "%s:Error setting up EDAC device: %d\n", ecc_name, rc); | ||
1497 | return rc; | ||
1498 | } | ||
1499 | |||
1500 | static irqreturn_t altr_edac_a10_ecc_irq_portb(int irq, void *dev_id) | ||
1501 | { | ||
1502 | struct altr_edac_device_dev *ad = dev_id; | ||
1503 | void __iomem *base = ad->base; | ||
1504 | const struct edac_device_prv_data *priv = ad->data; | ||
1505 | |||
1506 | if (irq == ad->sb_irq) { | ||
1507 | writel(priv->ce_clear_mask, | ||
1508 | base + ALTR_A10_ECC_INTSTAT_OFST); | ||
1509 | edac_device_handle_ce(ad->edac_dev, 0, 0, ad->edac_dev_name); | ||
1510 | return IRQ_HANDLED; | ||
1511 | } else if (irq == ad->db_irq) { | ||
1512 | writel(priv->ue_clear_mask, | ||
1513 | base + ALTR_A10_ECC_INTSTAT_OFST); | ||
1514 | edac_device_handle_ue(ad->edac_dev, 0, 0, ad->edac_dev_name); | ||
1515 | return IRQ_HANDLED; | ||
1516 | } | ||
1517 | |||
1518 | WARN_ONCE(1, "Unhandled IRQ%d on Port B.", irq); | ||
1519 | |||
1520 | return IRQ_NONE; | ||
1521 | } | ||
1522 | |||
1523 | static const struct edac_device_prv_data a10_sdmmcecca_data = { | ||
1524 | .setup = altr_portb_setup, | ||
1525 | .ce_clear_mask = ALTR_A10_ECC_SERRPENA, | ||
1526 | .ue_clear_mask = ALTR_A10_ECC_DERRPENA, | ||
1527 | .dbgfs_name = "altr_trigger", | ||
1528 | .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL, | ||
1529 | .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST, | ||
1530 | .ce_set_mask = ALTR_A10_ECC_SERRPENA, | ||
1531 | .ue_set_mask = ALTR_A10_ECC_DERRPENA, | ||
1532 | .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST, | ||
1533 | .ecc_irq_handler = altr_edac_a10_ecc_irq, | ||
1534 | .inject_fops = &altr_edac_a10_device_inject_fops, | ||
1535 | }; | ||
1536 | |||
1537 | static const struct edac_device_prv_data a10_sdmmceccb_data = { | ||
1538 | .setup = altr_portb_setup, | ||
1539 | .ce_clear_mask = ALTR_A10_ECC_SERRPENB, | ||
1540 | .ue_clear_mask = ALTR_A10_ECC_DERRPENB, | ||
1541 | .dbgfs_name = "altr_trigger", | ||
1542 | .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL, | ||
1543 | .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST, | ||
1544 | .ce_set_mask = ALTR_A10_ECC_TSERRB, | ||
1545 | .ue_set_mask = ALTR_A10_ECC_TDERRB, | ||
1546 | .set_err_ofst = ALTR_A10_ECC_INTTEST_OFST, | ||
1547 | .ecc_irq_handler = altr_edac_a10_ecc_irq_portb, | ||
1548 | .inject_fops = &altr_edac_a10_device_inject_fops, | ||
1549 | }; | ||
1550 | |||
1551 | static int __init socfpga_init_sdmmc_ecc(void) | ||
1552 | { | ||
1553 | int rc = -ENODEV; | ||
1554 | struct device_node *child = of_find_compatible_node(NULL, NULL, | ||
1555 | "altr,socfpga-sdmmc-ecc"); | ||
1556 | if (!child) { | ||
1557 | edac_printk(KERN_WARNING, EDAC_DEVICE, "SDMMC node not found\n"); | ||
1558 | return -ENODEV; | ||
1559 | } | ||
1560 | |||
1561 | if (!of_device_is_available(child)) | ||
1562 | goto exit; | ||
1563 | |||
1564 | if (validate_parent_available(child)) | ||
1565 | goto exit; | ||
1566 | |||
1567 | rc = altr_init_a10_ecc_block(child, ALTR_A10_SDMMC_IRQ_MASK, | ||
1568 | a10_sdmmcecca_data.ecc_enable_mask, 1); | ||
1569 | exit: | ||
1570 | of_node_put(child); | ||
1571 | return rc; | ||
1572 | } | ||
1573 | |||
1574 | early_initcall(socfpga_init_sdmmc_ecc); | ||
1575 | |||
1576 | #endif /* CONFIG_EDAC_ALTERA_SDMMC */ | ||
1577 | |||
1396 | /********************* Arria10 EDAC Device Functions *************************/ | 1578 | /********************* Arria10 EDAC Device Functions *************************/ |
1397 | static const struct of_device_id altr_edac_a10_device_of_match[] = { | 1579 | static const struct of_device_id altr_edac_a10_device_of_match[] = { |
1398 | #ifdef CONFIG_EDAC_ALTERA_L2C | 1580 | #ifdef CONFIG_EDAC_ALTERA_L2C |
@@ -1418,6 +1600,9 @@ static const struct of_device_id altr_edac_a10_device_of_match[] = { | |||
1418 | #ifdef CONFIG_EDAC_ALTERA_QSPI | 1600 | #ifdef CONFIG_EDAC_ALTERA_QSPI |
1419 | { .compatible = "altr,socfpga-qspi-ecc", .data = &a10_qspiecc_data }, | 1601 | { .compatible = "altr,socfpga-qspi-ecc", .data = &a10_qspiecc_data }, |
1420 | #endif | 1602 | #endif |
1603 | #ifdef CONFIG_EDAC_ALTERA_SDMMC | ||
1604 | { .compatible = "altr,socfpga-sdmmc-ecc", .data = &a10_sdmmcecca_data }, | ||
1605 | #endif | ||
1421 | {}, | 1606 | {}, |
1422 | }; | 1607 | }; |
1423 | MODULE_DEVICE_TABLE(of, altr_edac_a10_device_of_match); | 1608 | MODULE_DEVICE_TABLE(of, altr_edac_a10_device_of_match); |
@@ -1711,7 +1896,8 @@ static int altr_edac_a10_probe(struct platform_device *pdev) | |||
1711 | of_device_is_compatible(child, "altr,socfpga-nand-ecc") || | 1896 | of_device_is_compatible(child, "altr,socfpga-nand-ecc") || |
1712 | of_device_is_compatible(child, "altr,socfpga-dma-ecc") || | 1897 | of_device_is_compatible(child, "altr,socfpga-dma-ecc") || |
1713 | of_device_is_compatible(child, "altr,socfpga-usb-ecc") || | 1898 | of_device_is_compatible(child, "altr,socfpga-usb-ecc") || |
1714 | of_device_is_compatible(child, "altr,socfpga-qspi-ecc")) | 1899 | of_device_is_compatible(child, "altr,socfpga-qspi-ecc") || |
1900 | of_device_is_compatible(child, "altr,socfpga-sdmmc-ecc")) | ||
1715 | 1901 | ||
1716 | altr_edac_a10_device_add(edac, child); | 1902 | altr_edac_a10_device_add(edac, child); |
1717 | 1903 | ||