On Wed, Jan 27, 2016 at 10:13:20AM -0600, ttha...@opensource.altera.com wrote:
> From: Thor Thayer <ttha...@opensource.altera.com>
> 
> Adding L2 Cache and On-Chip RAM EDAC support for the
> Altera SoCs using the EDAC device  model. The SDRAM
> controller is using the Memory Controller model.
> 
> Each type of ECC is individually configurable.
> 
> Signed-off-by: Thor Thayer <ttha...@opensource.altera.com>
> ---
> v9: Improve device tree node release. Free managed resources
>     on error path. Fix ocram memory leak.
> v8: Remove MASK from single bit mask names.
>     s/altr,edac/altr,socfpga-ecc-manager
>     Use debugfs instead of sysfs.
>     Add chip family name to match string.
>     Fix header year.
>     Fix build dependencies & change commit accordingly.
>     s/CONFIG_EDAC_ALTERA_MC/CONFIG_EDAC_ALTERA
> v7: s/of_get_named_gen_pool/of_gen_pool_get
>     Remove #ifdef for EDAC_DEBUG
>     Use -ENODEV instead of EPROBE_DEFER
> v6: Convert to nested EDAC in device tree. Force L2 cache
>     on for L2Cache ECC & remove L2 cache syscon for checking
>     enable bit. Update year in header.
> v5: No change.
> v4: Change mask defines to use BIT().
>     Fix comment style to agree with kernel coding style.
>     Better printk description for read != write in trigger.
>     Remove SysFS debugging message.
>     Better dci->mod_name
>     Move gen_pool pointer assignment to end of function.
>     Invert logic to reduce indent in ocram depenency check.
>     Change from dev_err() to edac_printk()
>     Replace magic numbers with defines & comments.
>     Improve error injection test.
>     Change Makefile intermediary name to altr (from alt)
> v3: Move OCRAM and L2 cache EDAC functions into altera_edac.c
>     instead of separate files.
> v2: Fix L2 dependency comments.
> ---
>  drivers/edac/Kconfig       |   26 ++-
>  drivers/edac/Makefile      |    2 +-
>  drivers/edac/altera_edac.c |  487 
> +++++++++++++++++++++++++++++++++++++++++++-
>  3 files changed, 507 insertions(+), 8 deletions(-)

I'm still waiting for the people on CC to confirm the DT changes. A
couple of comments on the EDAC bits below.


....


> +static irqreturn_t altr_edac_device_handler(int irq, void *dev_id)
> +{
> +     struct edac_device_ctl_info *dci = dev_id;
> +     struct altr_edac_device_dev *drvdata = dci->pvt_info;
> +     const struct edac_device_prv_data *priv = drvdata->data;
> +
> +     if (irq == drvdata->sb_irq) {
> +             if (priv->ce_clear_mask)
> +                     writel(priv->ce_clear_mask, drvdata->base);
> +             edac_device_handle_ce(dci, 0, 0, drvdata->edac_dev_name);
> +     }
> +     if (irq == drvdata->db_irq) {
> +             if (priv->ue_clear_mask)
> +                     writel(priv->ue_clear_mask, drvdata->base);
> +             edac_device_handle_ue(dci, 0, 0, drvdata->edac_dev_name);
> +             panic("\nEDAC:ECC_DEVICE[Uncorrectable errors]\n");
> +     }

And irq is guaranteed to always be ->sb_irq or ->db_irq?

Otherwise, you can do

        else
                WARN_ON(1);

just in case.

> +
> +     return IRQ_HANDLED;
> +}

...

> +/*
> + * altr_edac_device_probe()
> + *   This is a generic EDAC device driver that will support
> + *   various Altera memory devices such as the L2 cache ECC and
> + *   OCRAM ECC as well as the memories for other peripherals.
> + *   Module specific initialization is done by passing the
> + *   function index in the device tree.
> + */
> +static int altr_edac_device_probe(struct platform_device *pdev)
> +{
> +     struct edac_device_ctl_info *dci;
> +     struct altr_edac_device_dev *drvdata;
> +     struct resource *r;
> +     int res = 0;
> +     struct device_node *np = pdev->dev.of_node;
> +     char *ecc_name = (char *)np->name;
> +     static int dev_instance;
> +     struct dentry *debugfs;
> +
> +     if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
> +             edac_printk(KERN_ERR, EDAC_DEVICE,
> +                         "Unable to open devm\n");
> +             return -ENOMEM;
> +     }
> +
> +     r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +     if (!r) {
> +             edac_printk(KERN_ERR, EDAC_DEVICE,
> +                         "Unable to get mem resource\n");
> +             res = -ENODEV;
> +             goto fail;
> +     }
> +
> +     if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r),
> +                                  dev_name(&pdev->dev))) {
> +             edac_printk(KERN_ERR, EDAC_DEVICE,
> +                         "%s:Error requesting mem region\n", ecc_name);
> +             res = -EBUSY;
> +             goto fail;
> +     }
> +
> +     dci = edac_device_alloc_ctl_info(sizeof(*drvdata), ecc_name,
> +                                      1, ecc_name, 1, 0, NULL, 0,
> +                                      dev_instance++);
> +
> +     if (!dci) {
> +             edac_printk(KERN_ERR, EDAC_DEVICE,
> +                         "%s: Unable to allocate EDAC device\n", ecc_name);
> +             res = -ENOMEM;
> +             goto fail;
> +     }
> +
> +     drvdata = dci->pvt_info;
> +     dci->dev = &pdev->dev;
> +     platform_set_drvdata(pdev, dci);
> +     drvdata->edac_dev_name = ecc_name;
> +
> +     drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
> +     if (!drvdata->base)
> +             goto fail1;
> +
> +     /* Get driver specific data for this EDAC device */
> +     drvdata->data = of_match_node(altr_edac_device_of_match, np)->data;
> +
> +     /* Check specific dependencies for the module */
> +     if (drvdata->data->setup) {
> +             res = drvdata->data->setup(pdev, drvdata->base);
> +             if (res < 0)
> +                     goto fail1;
> +     }
> +
> +     drvdata->sb_irq = platform_get_irq(pdev, 0);
> +     res = devm_request_irq(&pdev->dev, drvdata->sb_irq,
> +                            altr_edac_device_handler,
> +                            0, dev_name(&pdev->dev), dci);
> +     if (res < 0)
> +             goto fail1;
> +
> +     drvdata->db_irq = platform_get_irq(pdev, 1);
> +     res = devm_request_irq(&pdev->dev, drvdata->db_irq,
> +                            altr_edac_device_handler,
> +                            0, dev_name(&pdev->dev), dci);
> +     if (res < 0)
> +             goto fail1;
> +
> +     dci->mod_name = "Altera ECC Manager";
> +     dci->dev_name = drvdata->edac_dev_name;
> +
> +     debugfs = edac_debugfs_create_dir(ecc_name);
> +     if (debugfs)
> +             altr_create_edacdev_dbgfs(dci, drvdata->data, debugfs);
> +
> +     if (edac_device_add_device(dci))

<--- if you end up here and debugfs nodes have been created, you need to
destroy them here. You probably could change edac_debugfs_exit() to call
debugfs_remove_recursive() and make sure your driver calls it.

Right now we're calling edac_debugfs_exit() in edac_exit() and I *think*
that is ok for a platform device driver as this one but I haven't
actually *verified* that.

> +             goto fail1;
> +
> +     devres_close_group(&pdev->dev, NULL);
> +
> +     return 0;
> +
> +fail1:
> +     edac_device_free_ctl_info(dci);
> +fail:
> +     devres_release_group(&pdev->dev, NULL);
> +     edac_printk(KERN_ERR, EDAC_DEVICE,
> +                 "%s:Error setting up EDAC device: %d\n", ecc_name, res);
> +
> +     return res;
> +}
> +
> +static int altr_edac_device_remove(struct platform_device *pdev)
> +{
> +     struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);
> +
> +     edac_device_del_device(&pdev->dev);
> +     edac_device_free_ctl_info(dci);
> +
> +     return 0;
> +}
> +
> +static struct platform_driver altr_edac_device_driver = {
> +     .probe =  altr_edac_device_probe,
> +     .remove = altr_edac_device_remove,
> +     .driver = {
> +             .name = "altr_edac_device",
> +             .of_match_table = altr_edac_device_of_match,
> +     },
> +};
> +module_platform_driver(altr_edac_device_driver);
> +
> +/*********************** OCRAM EDAC Device Functions *********************/
> +
> +#ifdef CONFIG_EDAC_ALTERA_OCRAM
> +
> +static void *ocram_alloc_mem(size_t size, void **other)
> +{
> +     struct device_node *np;
> +     struct gen_pool *gp;
> +     void *sram_addr;
> +
> +     np = of_find_compatible_node(NULL, NULL, "altr,socfpga-ocram-ecc");
> +     if (!np)
> +             return NULL;
> +
> +     gp = of_gen_pool_get(np, "iram", 0);
> +     of_node_put(np);
> +     if (!gp)
> +             return NULL;
> +
> +     sram_addr = (void *)gen_pool_alloc(gp, size);
> +     if (!sram_addr)
> +             return NULL;
> +
> +     memset(sram_addr, 0, size);
> +     wmb();  /* Ensure data is written out */
> +
> +     *other = gp;    /* Remember this handle for freeing  later */

Please put comments ontop of the line they're referring to. Those
side-things are cluttering the code unnecessarily.

> +
> +     return sram_addr;
> +}
> +
> +static void ocram_free_mem(void *p, size_t size, void *other)
> +{
> +     gen_pool_free((struct gen_pool *)other, (u32)p, size);
> +}
> +
> +/*
> + * altr_ocram_dependencies()
> + *   Test for OCRAM cache ECC dependencies upon entry because
> + *   platform specific startup should have initialized the
> + *   On-Chip RAM memory and enabled the ECC.
> + *   Can't turn on ECC here because accessing un-initialized
> + *   memory will cause CE/UE errors possibly causing an ABORT.
> + */
> +static int altr_ocram_dependencies(struct platform_device *pdev,

A verb in the name?

        altr_ocram_test_deps

or
        altr_ocram_check_deps

or so?

> +                                void __iomem *base)
> +{
> +     u32 control;
> +
> +     control = readl(base) & ALTR_OCR_ECC_EN;
> +     if (control)
> +             return 0;
> +
> +     edac_printk(KERN_ERR, EDAC_DEVICE,
> +                 "OCRAM: No ECC present or ECC disabled.\n");
> +     return -ENODEV;
> +}
> +
> +const struct edac_device_prv_data ocramecc_data = {
> +     .setup = altr_ocram_dependencies,
> +     .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,
> +     .ce_set_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_INJS),
> +     .ue_set_mask = (ALTR_OCR_ECC_EN | ALTR_OCR_ECC_INJD),
> +     .trig_alloc_sz = ALTR_TRIG_OCRAM_BYTE_SIZE,
> +};
> +
> +#endif       /* CONFIG_EDAC_ALTERA_OCRAM */
> +
> +/********************* L2 Cache EDAC Device Functions ********************/
> +
> +#ifdef CONFIG_EDAC_ALTERA_L2C
> +
> +static void *l2_alloc_mem(size_t size, void **other)
> +{
> +     struct device *dev = *other;
> +     void *ptemp = devm_kzalloc(dev, size, GFP_KERNEL);
> +
> +     if (!ptemp)
> +             return NULL;
> +
> +     /* Make sure everything is written out */
> +     wmb();

See, it reads much better with the comment ontop. :)

> +
> +     /*
> +      * Clean all cache levels up to LoC (includes L2)
> +      * This ensures the corrupted data is written into
> +      * L2 cache for readback test (which causes ECC error).
> +      */
> +     flush_cache_all();
> +
> +     return ptemp;
> +}
> +
> +static void l2_free_mem(void *p, size_t size, void *other)
> +{
> +     struct device *dev = other;
> +
> +     if (dev && p)
> +             devm_kfree(dev, p);
> +}
> +
> +/*
> + * altr_l2_dependencies()
> + *   Test for L2 cache ECC dependencies upon entry because
> + *   platform specific startup should have initialized the L2
> + *   memory and enabled the ECC.
> + *   Bail if ECC is not enabled.
> + *   Note that L2 Cache Enable is forced at build time.
> + */
> +static int altr_l2_dependencies(struct platform_device *pdev,

Here too, pls add a verb in the function name.

> +                             void __iomem *base)
> +{
> +     u32 control;
> +
> +     control = readl(base) & ALTR_L2_ECC_EN;
> +     if (!control) {
> +             edac_printk(KERN_ERR, EDAC_DEVICE,
> +                         "L2: No ECC present, or ECC disabled\n");
> +             return -ENODEV;
> +     }
> +
> +     return 0;
> +}

-- 
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