bus: omap_l3_noc: make error reporting and handling common
[cascardo/linux.git] / drivers / bus / omap_l3_noc.c
index feeecae..42e4114 100644 (file)
@@ -1,31 +1,27 @@
 /*
- * OMAP4XXX L3 Interconnect error handling driver
+ * OMAP L3 Interconnect error handling driver
  *
- * Copyright (C) 2011 Texas Corporation
+ * Copyright (C) 2011-2014 Texas Instruments Incorporated - http://www.ti.com/
  *     Santosh Shilimkar <santosh.shilimkar@ti.com>
  *     Sricharan <r.sricharan@ti.com>
  *
  * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
  */
-#include <linux/module.h>
 #include <linux/init.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
 #include <linux/interrupt.h>
+#include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 
 #include "omap_l3_noc.h"
 static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
 {
 
-       struct omap4_l3 *l3 = _l3;
+       struct omap_l3 *l3 = _l3;
        int inttype, i, k;
        int err_src = 0;
        u32 std_err_main, err_reg, clear, masterid;
        void __iomem *base, *l3_targ_base;
+       void __iomem *l3_targ_stderr, *l3_targ_slvofslsb, *l3_targ_mstaddr;
        char *target_name, *master_name = "UN IDENTIFIED";
+       struct l3_target_data *l3_targ_inst;
+       struct l3_flagmux_data *flag_mux;
+       struct l3_masters_data *master;
+       char *err_description;
+       char err_string[30] = { 0 };
 
        /* Get the Type of interrupt */
        inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR;
 
-       for (i = 0; i < L3_MODULES; i++) {
+       for (i = 0; i < l3->num_modules; i++) {
                /*
                 * Read the regerr register of the clock domain
                 * to determine the source
                 */
                base = l3->l3_base[i];
-               err_reg = __raw_readl(base + l3_flagmux[i] +
-                                       + L3_FLAGMUX_REGERR0 + (inttype << 3));
+               flag_mux = l3->l3_flagmux[i];
+               err_reg = readl_relaxed(base + flag_mux->offset +
+                                       L3_FLAGMUX_REGERR0 + (inttype << 3));
 
                /* Get the corresponding error and analyse */
                if (err_reg) {
+                       bool std_err = true;
+
                        /* Identify the source from control status register */
                        err_src = __ffs(err_reg);
 
+                       /* We DONOT expect err_src to go out of bounds */
+                       BUG_ON(err_src > MAX_CLKDM_TARGETS);
+
+                       if (err_src < flag_mux->num_targ_data) {
+                               l3_targ_inst = &flag_mux->l3_targ[err_src];
+                               target_name = l3_targ_inst->name;
+                               l3_targ_base = base + l3_targ_inst->offset;
+                       } else {
+                               target_name = L3_TARGET_NOT_SUPPORTED;
+                       }
+
+                       /*
+                        * If we do not know of a register offset to decode
+                        * and clear, then mask.
+                        */
+                       if (target_name == L3_TARGET_NOT_SUPPORTED) {
+                               u32 mask_val;
+                               void __iomem *mask_reg;
+
+                               /*
+                                * Certain plaforms may have "undocumented"
+                                * status pending on boot.. So dont generate
+                                * a severe warning here.
+                                */
+                               dev_err(l3->dev,
+                                       "L3 %s error: target %d mod:%d %s\n",
+                                       inttype ? "debug" : "application",
+                                       err_src, i, "(unclearable)");
+
+                               mask_reg = base + flag_mux->offset +
+                                          L3_FLAGMUX_MASK0 + (inttype << 3);
+                               mask_val = readl_relaxed(mask_reg);
+                               mask_val &= ~(1 << err_src);
+                               writel_relaxed(mask_val, mask_reg);
+
+                               break;
+                       }
+
                        /* Read the stderrlog_main_source from clk domain */
-                       l3_targ_base = base + *(l3_targ[i] + err_src);
-                       std_err_main =  __raw_readl(l3_targ_base +
-                                       L3_TARG_STDERRLOG_MAIN);
-                       masterid = __raw_readl(l3_targ_base +
-                                       L3_TARG_STDERRLOG_MSTADDR);
+                       l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN;
+                       l3_targ_slvofslsb = l3_targ_base +
+                                           L3_TARG_STDERRLOG_SLVOFSLSB;
+
+                       std_err_main = readl_relaxed(l3_targ_stderr);
 
                        switch (std_err_main & CUSTOM_ERROR) {
                        case STANDARD_ERROR:
-                               target_name =
-                                       l3_targ_inst_name[i][err_src];
-                               WARN(true, "L3 standard error: TARGET:%s at address 0x%x\n",
-                                       target_name,
-                                       __raw_readl(l3_targ_base +
-                                               L3_TARG_STDERRLOG_SLVOFSLSB));
-                               /* clear the std error log*/
-                               clear = std_err_main | CLEAR_STDERR_LOG;
-                               writel(clear, l3_targ_base +
-                                       L3_TARG_STDERRLOG_MAIN);
+                               err_description = "Standard";
+                               snprintf(err_string, sizeof(err_string),
+                                        ": At Address: 0x%08X ",
+                                        readl_relaxed(l3_targ_slvofslsb));
+
+                               l3_targ_mstaddr = l3_targ_base +
+                                               L3_TARG_STDERRLOG_MSTADDR;
                                break;
 
                        case CUSTOM_ERROR:
-                               target_name =
-                                       l3_targ_inst_name[i][err_src];
-                               for (k = 0; k < NUM_OF_L3_MASTERS; k++) {
-                                       if (masterid == l3_masters[k].id)
-                                               master_name =
-                                                       l3_masters[k].name;
-                               }
-                               WARN(true, "L3 custom error: MASTER:%s TARGET:%s\n",
-                                       master_name, target_name);
-                               /* clear the std error log*/
-                               clear = std_err_main | CLEAR_STDERR_LOG;
-                               writel(clear, l3_targ_base +
-                                       L3_TARG_STDERRLOG_MAIN);
+                               err_description = "Custom";
+
+                               l3_targ_mstaddr = l3_targ_base +
+                                               L3_TARG_STDERRLOG_CINFO_MSTADDR;
                                break;
 
                        default:
+                               std_err = false;
                                /* Nothing to be handled here as of now */
                                break;
                        }
-               /* Error found so break the for loop */
-               break;
+
+                       if (!std_err)
+                               break;
+
+                       /* STDERRLOG_MSTADDR Stores the NTTP master address. */
+                       masterid = (readl_relaxed(l3_targ_mstaddr) &
+                                   l3->mst_addr_mask) >>
+                                       __ffs(l3->mst_addr_mask);
+
+                       for (k = 0, master = l3->l3_masters;
+                            k < l3->num_masters; k++, master++) {
+                               if (masterid == master->id) {
+                                       master_name = master->name;
+                                       break;
+                               }
+                       }
+
+                       WARN(true,
+                            "%s:L3 %s Error: MASTER %s TARGET %s%s\n",
+                            dev_name(l3->dev),
+                            err_description,
+                            master_name, target_name,
+                            err_string);
+                       /* clear the std error log*/
+                       clear = std_err_main | CLEAR_STDERR_LOG;
+                       writel_relaxed(clear, l3_targ_stderr);
+
+                       /* Error found so break the for loop */
+                       break;
                }
        }
        return IRQ_HANDLED;
 }
 
-static int omap4_l3_probe(struct platform_device *pdev)
+static const struct of_device_id l3_noc_match[] = {
+       {.compatible = "ti,omap4-l3-noc", .data = &omap_l3_data},
+       {},
+};
+MODULE_DEVICE_TABLE(of, l3_noc_match);
+
+static int omap_l3_probe(struct platform_device *pdev)
 {
-       static struct omap4_l3 *l3;
-       struct resource *res;
-       int ret;
+       const struct of_device_id *of_id;
+       static struct omap_l3 *l3;
+       int ret, i;
+
+       of_id = of_match_device(l3_noc_match, &pdev->dev);
+       if (!of_id) {
+               dev_err(&pdev->dev, "OF data missing\n");
+               return -EINVAL;
+       }
 
-       l3 = kzalloc(sizeof(*l3), GFP_KERNEL);
+       l3 = devm_kzalloc(&pdev->dev, sizeof(*l3), GFP_KERNEL);
        if (!l3)
                return -ENOMEM;
 
+       memcpy(l3, of_id->data, sizeof(*l3));
+       l3->dev = &pdev->dev;
        platform_set_drvdata(pdev, l3);
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(&pdev->dev, "couldn't find resource 0\n");
-               ret = -ENODEV;
-               goto err0;
-       }
-
-       l3->l3_base[0] = ioremap(res->start, resource_size(res));
-       if (!l3->l3_base[0]) {
-               dev_err(&pdev->dev, "ioremap failed\n");
-               ret = -ENOMEM;
-               goto err0;
-       }
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       if (!res) {
-               dev_err(&pdev->dev, "couldn't find resource 1\n");
-               ret = -ENODEV;
-               goto err1;
-       }
-
-       l3->l3_base[1] = ioremap(res->start, resource_size(res));
-       if (!l3->l3_base[1]) {
-               dev_err(&pdev->dev, "ioremap failed\n");
-               ret = -ENOMEM;
-               goto err1;
-       }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
-       if (!res) {
-               dev_err(&pdev->dev, "couldn't find resource 2\n");
-               ret = -ENODEV;
-               goto err2;
-       }
+       /* Get mem resources */
+       for (i = 0; i < l3->num_modules; i++) {
+               struct resource *res = platform_get_resource(pdev,
+                                                            IORESOURCE_MEM, i);
 
-       l3->l3_base[2] = ioremap(res->start, resource_size(res));
-       if (!l3->l3_base[2]) {
-               dev_err(&pdev->dev, "ioremap failed\n");
-               ret = -ENOMEM;
-               goto err2;
+               l3->l3_base[i] = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(l3->l3_base[i])) {
+                       dev_err(l3->dev, "ioremap %d failed\n", i);
+                       return PTR_ERR(l3->l3_base[i]);
+               }
        }
 
        /*
         * Setup interrupt Handlers
         */
        l3->debug_irq = platform_get_irq(pdev, 0);
-       ret = request_irq(l3->debug_irq,
-                       l3_interrupt_handler,
-                       IRQF_DISABLED, "l3-dbg-irq", l3);
+       ret = devm_request_irq(l3->dev, l3->debug_irq, l3_interrupt_handler,
+                              IRQF_DISABLED, "l3-dbg-irq", l3);
        if (ret) {
-               pr_crit("L3: request_irq failed to register for 0x%x\n",
-                                               l3->debug_irq);
-               goto err3;
+               dev_err(l3->dev, "request_irq failed for %d\n",
+                       l3->debug_irq);
+               return ret;
        }
 
        l3->app_irq = platform_get_irq(pdev, 1);
-       ret = request_irq(l3->app_irq,
-                       l3_interrupt_handler,
-                       IRQF_DISABLED, "l3-app-irq", l3);
-       if (ret) {
-               pr_crit("L3: request_irq failed to register for 0x%x\n",
-                                               l3->app_irq);
-               goto err4;
-       }
+       ret = devm_request_irq(l3->dev, l3->app_irq, l3_interrupt_handler,
+                              IRQF_DISABLED, "l3-app-irq", l3);
+       if (ret)
+               dev_err(l3->dev, "request_irq failed for %d\n", l3->app_irq);
 
-       return 0;
-
-err4:
-       free_irq(l3->debug_irq, l3);
-err3:
-       iounmap(l3->l3_base[2]);
-err2:
-       iounmap(l3->l3_base[1]);
-err1:
-       iounmap(l3->l3_base[0]);
-err0:
-       kfree(l3);
        return ret;
 }
 
-static int omap4_l3_remove(struct platform_device *pdev)
-{
-       struct omap4_l3 *l3 = platform_get_drvdata(pdev);
-
-       free_irq(l3->app_irq, l3);
-       free_irq(l3->debug_irq, l3);
-       iounmap(l3->l3_base[0]);
-       iounmap(l3->l3_base[1]);
-       iounmap(l3->l3_base[2]);
-       kfree(l3);
-
-       return 0;
-}
-
-#if defined(CONFIG_OF)
-static const struct of_device_id l3_noc_match[] = {
-       {.compatible = "ti,omap4-l3-noc", },
-       {},
-};
-MODULE_DEVICE_TABLE(of, l3_noc_match);
-#else
-#define l3_noc_match NULL
-#endif
-
-static struct platform_driver omap4_l3_driver = {
-       .probe          = omap4_l3_probe,
-       .remove         = omap4_l3_remove,
+static struct platform_driver omap_l3_driver = {
+       .probe          = omap_l3_probe,
        .driver         = {
                .name           = "omap_l3_noc",
                .owner          = THIS_MODULE,
-               .of_match_table = l3_noc_match,
+               .of_match_table = of_match_ptr(l3_noc_match),
        },
 };
 
-static int __init omap4_l3_init(void)
+static int __init omap_l3_init(void)
 {
-       return platform_driver_register(&omap4_l3_driver);
+       return platform_driver_register(&omap_l3_driver);
 }
-postcore_initcall_sync(omap4_l3_init);
+postcore_initcall_sync(omap_l3_init);
 
-static void __exit omap4_l3_exit(void)
+static void __exit omap_l3_exit(void)
 {
-       platform_driver_unregister(&omap4_l3_driver);
+       platform_driver_unregister(&omap_l3_driver);
 }
-module_exit(omap4_l3_exit);
+module_exit(omap_l3_exit);