bus: omap_l3_noc: make error reporting and handling common
[cascardo/linux.git] / drivers / bus / omap_l3_noc.c
index 7743e86..42e4114 100644 (file)
  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-#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"
@@ -58,30 +60,41 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
        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 = readl_relaxed(base + l3_flagmux[i] +
+               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);
 
-                       l3_targ_inst = &l3_targ[i][err_src];
-                       target_name = l3_targ_inst->name;
-                       l3_targ_base = base + l3_targ_inst->offset;
+                       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
@@ -101,7 +114,7 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
                                        inttype ? "debug" : "application",
                                        err_src, i, "(unclearable)");
 
-                               mask_reg = base + l3_flagmux[i] +
+                               mask_reg = base + flag_mux->offset +
                                           L3_FLAGMUX_MASK0 + (inttype << 3);
                                mask_val = readl_relaxed(mask_reg);
                                mask_val &= ~(1 << err_src);
@@ -114,60 +127,94 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
                        l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN;
                        l3_targ_slvofslsb = l3_targ_base +
                                            L3_TARG_STDERRLOG_SLVOFSLSB;
-                       l3_targ_mstaddr = l3_targ_base +
-                                         L3_TARG_STDERRLOG_MSTADDR;
 
                        std_err_main = readl_relaxed(l3_targ_stderr);
-                       masterid = readl_relaxed(l3_targ_mstaddr);
 
                        switch (std_err_main & CUSTOM_ERROR) {
                        case STANDARD_ERROR:
-                               WARN(true, "L3 standard error: TARGET:%s at address 0x%x\n",
-                                       target_name,
-                                       readl_relaxed(l3_targ_slvofslsb));
-                               /* clear the std error log*/
-                               clear = std_err_main | CLEAR_STDERR_LOG;
-                               writel_relaxed(clear, l3_targ_stderr);
+                               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:
-                               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_relaxed(clear, l3_targ_stderr);
+                               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 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)
 {
+       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 = 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);
 
        /* Get mem resources */
-       for (i = 0; i < L3_MODULES; i++) {
+       for (i = 0; i < l3->num_modules; i++) {
                struct resource *res = platform_get_resource(pdev,
                                                             IORESOURCE_MEM, i);
 
@@ -199,22 +246,12 @@ static int omap_l3_probe(struct platform_device *pdev)
        return ret;
 }
 
-#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 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),
        },
 };