Merge tag 'armsoc-late' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[cascardo/linux.git] / drivers / edac / altera_edac.c
index 2398d07..58d3e2b 100644 (file)
@@ -203,7 +203,7 @@ static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
        if (!mci->debugfs)
                return;
 
-       edac_debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
+       edac_debugfs_create_file("altr_trigger", S_IWUSR, mci->debugfs, mci,
                                 &altr_sdr_mc_debug_inject_fops);
 }
 
@@ -680,7 +680,7 @@ static void altr_create_edacdev_dbgfs(struct edac_device_ctl_info *edac_dci,
        if (!drvdata->debugfs_dir)
                return;
 
-       if (!edac_debugfs_create_file(priv->dbgfs_name, S_IWUSR,
+       if (!edac_debugfs_create_file("altr_trigger", S_IWUSR,
                                      drvdata->debugfs_dir, edac_dci,
                                      priv->inject_fops))
                debugfs_remove_recursive(drvdata->debugfs_dir);
@@ -1108,7 +1108,6 @@ static const struct edac_device_prv_data ocramecc_data = {
        .setup = altr_check_ecc_deps,
        .ce_clear_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_SERR),
        .ue_clear_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_DERR),
-       .dbgfs_name = "altr_ocram_trigger",
        .alloc_mem = ocram_alloc_mem,
        .free_mem = ocram_free_mem,
        .ecc_enable_mask = ALTR_OCR_ECC_EN,
@@ -1125,7 +1124,6 @@ static const struct edac_device_prv_data a10_ocramecc_data = {
        .ce_clear_mask = ALTR_A10_ECC_SERRPENA,
        .ue_clear_mask = ALTR_A10_ECC_DERRPENA,
        .irq_status_mask = A10_SYSMGR_ECC_INTSTAT_OCRAM,
-       .dbgfs_name = "altr_ocram_trigger",
        .ecc_enable_mask = ALTR_A10_OCRAM_ECC_EN_CTL,
        .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST,
        .ce_set_mask = ALTR_A10_ECC_TSERRA,
@@ -1228,7 +1226,6 @@ static const struct edac_device_prv_data l2ecc_data = {
        .setup = altr_l2_check_deps,
        .ce_clear_mask = 0,
        .ue_clear_mask = 0,
-       .dbgfs_name = "altr_l2_trigger",
        .alloc_mem = l2_alloc_mem,
        .free_mem = l2_free_mem,
        .ecc_enable_mask = ALTR_L2_ECC_EN,
@@ -1244,7 +1241,6 @@ static const struct edac_device_prv_data a10_l2ecc_data = {
        .ce_clear_mask = ALTR_A10_L2_ECC_SERR_CLR,
        .ue_clear_mask = ALTR_A10_L2_ECC_MERR_CLR,
        .irq_status_mask = A10_SYSMGR_ECC_INTSTAT_L2,
-       .dbgfs_name = "altr_l2_trigger",
        .alloc_mem = l2_alloc_mem,
        .free_mem = l2_free_mem,
        .ecc_enable_mask = ALTR_A10_L2_ECC_EN_CTL,
@@ -1266,7 +1262,6 @@ static const struct edac_device_prv_data a10_enetecc_data = {
        .setup = altr_check_ecc_deps,
        .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_TSERRA,
@@ -1285,6 +1280,292 @@ early_initcall(socfpga_init_ethernet_ecc);
 
 #endif /* CONFIG_EDAC_ALTERA_ETHERNET */
 
+/********************** NAND Device Functions **********************/
+
+#ifdef CONFIG_EDAC_ALTERA_NAND
+
+static const struct edac_device_prv_data a10_nandecc_data = {
+       .setup = altr_check_ecc_deps,
+       .ce_clear_mask = ALTR_A10_ECC_SERRPENA,
+       .ue_clear_mask = ALTR_A10_ECC_DERRPENA,
+       .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL,
+       .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST,
+       .ce_set_mask = ALTR_A10_ECC_TSERRA,
+       .ue_set_mask = ALTR_A10_ECC_TDERRA,
+       .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 int __init socfpga_init_nand_ecc(void)
+{
+       return altr_init_a10_ecc_device_type("altr,socfpga-nand-ecc");
+}
+
+early_initcall(socfpga_init_nand_ecc);
+
+#endif /* CONFIG_EDAC_ALTERA_NAND */
+
+/********************** DMA Device Functions **********************/
+
+#ifdef CONFIG_EDAC_ALTERA_DMA
+
+static const struct edac_device_prv_data a10_dmaecc_data = {
+       .setup = altr_check_ecc_deps,
+       .ce_clear_mask = ALTR_A10_ECC_SERRPENA,
+       .ue_clear_mask = ALTR_A10_ECC_DERRPENA,
+       .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL,
+       .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST,
+       .ce_set_mask = ALTR_A10_ECC_TSERRA,
+       .ue_set_mask = ALTR_A10_ECC_TDERRA,
+       .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 int __init socfpga_init_dma_ecc(void)
+{
+       return altr_init_a10_ecc_device_type("altr,socfpga-dma-ecc");
+}
+
+early_initcall(socfpga_init_dma_ecc);
+
+#endif /* CONFIG_EDAC_ALTERA_DMA */
+
+/********************** USB Device Functions **********************/
+
+#ifdef CONFIG_EDAC_ALTERA_USB
+
+static const struct edac_device_prv_data a10_usbecc_data = {
+       .setup = altr_check_ecc_deps,
+       .ce_clear_mask = ALTR_A10_ECC_SERRPENA,
+       .ue_clear_mask = ALTR_A10_ECC_DERRPENA,
+       .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL,
+       .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST,
+       .ce_set_mask = ALTR_A10_ECC_TSERRA,
+       .ue_set_mask = ALTR_A10_ECC_TDERRA,
+       .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 int __init socfpga_init_usb_ecc(void)
+{
+       return altr_init_a10_ecc_device_type("altr,socfpga-usb-ecc");
+}
+
+early_initcall(socfpga_init_usb_ecc);
+
+#endif /* CONFIG_EDAC_ALTERA_USB */
+
+/********************** QSPI Device Functions **********************/
+
+#ifdef CONFIG_EDAC_ALTERA_QSPI
+
+static const struct edac_device_prv_data a10_qspiecc_data = {
+       .setup = altr_check_ecc_deps,
+       .ce_clear_mask = ALTR_A10_ECC_SERRPENA,
+       .ue_clear_mask = ALTR_A10_ECC_DERRPENA,
+       .ecc_enable_mask = ALTR_A10_COMMON_ECC_EN_CTL,
+       .ecc_en_ofst = ALTR_A10_ECC_CTRL_OFST,
+       .ce_set_mask = ALTR_A10_ECC_TSERRA,
+       .ue_set_mask = ALTR_A10_ECC_TDERRA,
+       .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 int __init socfpga_init_qspi_ecc(void)
+{
+       return altr_init_a10_ecc_device_type("altr,socfpga-qspi-ecc");
+}
+
+early_initcall(socfpga_init_qspi_ecc);
+
+#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_ONESHOT | IRQF_TRIGGER_HIGH,
+                             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_ONESHOT | IRQF_TRIGGER_HIGH,
+                             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,
+       .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,
+       .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 *************************/
 static const struct of_device_id altr_edac_a10_device_of_match[] = {
 #ifdef CONFIG_EDAC_ALTERA_L2C
@@ -1297,6 +1578,21 @@ static const struct of_device_id altr_edac_a10_device_of_match[] = {
 #ifdef CONFIG_EDAC_ALTERA_ETHERNET
        { .compatible = "altr,socfpga-eth-mac-ecc",
          .data = &a10_enetecc_data },
+#endif
+#ifdef CONFIG_EDAC_ALTERA_NAND
+       { .compatible = "altr,socfpga-nand-ecc", .data = &a10_nandecc_data },
+#endif
+#ifdef CONFIG_EDAC_ALTERA_DMA
+       { .compatible = "altr,socfpga-dma-ecc", .data = &a10_dmaecc_data },
+#endif
+#ifdef CONFIG_EDAC_ALTERA_USB
+       { .compatible = "altr,socfpga-usb-ecc", .data = &a10_usbecc_data },
+#endif
+#ifdef CONFIG_EDAC_ALTERA_QSPI
+       { .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
        {},
 };
@@ -1451,11 +1747,11 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
                rc = -ENODEV;
                goto err_release_group1;
        }
-       rc = devm_request_irq(edac->dev, altdev->sb_irq,
-                             prv->ecc_irq_handler,
-                             IRQF_SHARED, ecc_name, altdev);
+       rc = devm_request_irq(edac->dev, altdev->sb_irq, prv->ecc_irq_handler,
+                             IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
+                             ecc_name, altdev);
        if (rc) {
-               edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
+               edac_printk(KERN_ERR, EDAC_DEVICE, "No SBERR IRQ resource\n");
                goto err_release_group1;
        }
 
@@ -1465,9 +1761,9 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
                rc = -ENODEV;
                goto err_release_group1;
        }
-       rc = devm_request_irq(edac->dev, altdev->db_irq,
-                             prv->ecc_irq_handler,
-                             IRQF_SHARED, ecc_name, altdev);
+       rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler,
+                             IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
+                             ecc_name, altdev);
        if (rc) {
                edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
                goto err_release_group1;
@@ -1526,7 +1822,7 @@ static int a10_eccmgr_irqdomain_map(struct irq_domain *d, unsigned int irq,
        return 0;
 }
 
-struct irq_domain_ops a10_eccmgr_ic_ops = {
+static struct irq_domain_ops a10_eccmgr_ic_ops = {
        .map = a10_eccmgr_irqdomain_map,
        .xlate = irq_domain_xlate_twocell,
 };
@@ -1584,15 +1880,19 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
        for_each_child_of_node(pdev->dev.of_node, child) {
                if (!of_device_is_available(child))
                        continue;
-               if (of_device_is_compatible(child, "altr,socfpga-a10-l2-ecc"))
-                       altr_edac_a10_device_add(edac, child);
-               else if ((of_device_is_compatible(child,
-                                       "altr,socfpga-a10-ocram-ecc")) ||
-                        (of_device_is_compatible(child,
-                                       "altr,socfpga-eth-mac-ecc")))
+
+               if (of_device_is_compatible(child, "altr,socfpga-a10-l2-ecc") || 
+                   of_device_is_compatible(child, "altr,socfpga-a10-ocram-ecc") ||
+                   of_device_is_compatible(child, "altr,socfpga-eth-mac-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-usb-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);
-               else if (of_device_is_compatible(child,
-                                                "altr,sdram-edac-a10"))
+
+               else if (of_device_is_compatible(child, "altr,sdram-edac-a10"))
                        of_platform_populate(pdev->dev.of_node,
                                             altr_sdram_ctrl_of_match,
                                             NULL, &pdev->dev);