On Tue, Aug 02, 2016 at 10:56:20AM -0500, ttha...@opensource.altera.com wrote:
> From: Thor Thayer <ttha...@opensource.altera.com>
> 
> 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 <ttha...@opensource.altera.com>
> ---
>  drivers/edac/Kconfig       |    7 ++
>  drivers/edac/altera_edac.c |  188 
> +++++++++++++++++++++++++++++++++++++++++++-
>  drivers/edac/altera_edac.h |    5 ++
>  3 files changed, 199 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
> index 72752f4..394cd16 100644
> --- a/drivers/edac/Kconfig
> +++ b/drivers/edac/Kconfig
> @@ -426,6 +426,13 @@ config EDAC_ALTERA_QSPI
>         Support for error detection and correction on the
>         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
>       tristate "Synopsys DDR Memory Controller"
>       depends on EDAC_MM_EDAC && ARCH_ZYNQ
> diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
> index 28247f8..8b5177e 100644
> --- a/drivers/edac/altera_edac.c
> +++ b/drivers/edac/altera_edac.c
> @@ -1393,6 +1393,188 @@ 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;
> +
> +     /* 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;
> +
> +     /* Find the SD/MMC device tree Node then update the IRQs for PortB */
> +     np = of_find_compatible_node(NULL, NULL, "altr,socfpga-sdmmc-ecc");

So why aren't you doing this thing first in the function so that...

> +     if (!np) {
> +             rc = -ENODEV;
> +             goto err_release_group_1;

... you can save yourself the unwind work in err_release_group_1?

In general, make sure you're doing all the work of poking at the
hardware so that you make sure you have the right resources *before* you
go and allocate and init stuff here.

Should make the error paths simpler and the function body smaller.

> +     }
> +
> +     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_ON(1);

WARN(1, "Strange IRQ%d on Port B... 

or something like that which is more informative.

> +
> +     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_ERR, EDAC_DEVICE, "SDMMC node not found\n");

Are you sure you want to issue this error each time the driver loads? Is
that even an error condition?

> +             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
-- 
Regards/Gruss,
    Boris.

ECO tip #101: Trim your mails when you reply.
--
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to