linux/arch/arm/mach-omap2/omap_l3_noc.c
<<
>>
Prefs
   1/*
   2 * OMAP4XXX L3 Interconnect error handling driver
   3 *
   4 * Copyright (C) 2011 Texas Corporation
   5 *      Santosh Shilimkar <santosh.shilimkar@ti.com>
   6 *      Sricharan <r.sricharan@ti.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  21 * USA
  22 */
  23#include <linux/module.h>
  24#include <linux/init.h>
  25#include <linux/io.h>
  26#include <linux/platform_device.h>
  27#include <linux/interrupt.h>
  28#include <linux/kernel.h>
  29#include <linux/slab.h>
  30
  31#include "omap_l3_noc.h"
  32
  33/*
  34 * Interrupt Handler for L3 error detection.
  35 *      1) Identify the L3 clockdomain partition to which the error belongs to.
  36 *      2) Identify the slave where the error information is logged
  37 *      3) Print the logged information.
  38 *      4) Add dump stack to provide kernel trace.
  39 *
  40 * Two Types of errors :
  41 *      1) Custom errors in L3 :
  42 *              Target like DMM/FW/EMIF generates SRESP=ERR error
  43 *      2) Standard L3 error:
  44 *              - Unsupported CMD.
  45 *                      L3 tries to access target while it is idle
  46 *              - OCP disconnect.
  47 *              - Address hole error:
  48 *                      If DSS/ISS/FDIF/USBHOSTFS access a target where they
  49 *                      do not have connectivity, the error is logged in
  50 *                      their default target which is DMM2.
  51 *
  52 *      On High Secure devices, firewall errors are possible and those
  53 *      can be trapped as well. But the trapping is implemented as part
  54 *      secure software and hence need not be implemented here.
  55 */
  56static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
  57{
  58
  59        struct omap4_l3 *l3 = _l3;
  60        int inttype, i, k;
  61        int err_src = 0;
  62        u32 std_err_main, err_reg, clear, masterid;
  63        void __iomem *base, *l3_targ_base;
  64        char *target_name, *master_name = "UN IDENTIFIED";
  65
  66        /* Get the Type of interrupt */
  67        inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR;
  68
  69        for (i = 0; i < L3_MODULES; i++) {
  70                /*
  71                 * Read the regerr register of the clock domain
  72                 * to determine the source
  73                 */
  74                base = l3->l3_base[i];
  75                err_reg = __raw_readl(base + l3_flagmux[i] +
  76                                        + L3_FLAGMUX_REGERR0 + (inttype << 3));
  77
  78                /* Get the corresponding error and analyse */
  79                if (err_reg) {
  80                        /* Identify the source from control status register */
  81                        err_src = __ffs(err_reg);
  82
  83                        /* Read the stderrlog_main_source from clk domain */
  84                        l3_targ_base = base + *(l3_targ[i] + err_src);
  85                        std_err_main =  __raw_readl(l3_targ_base +
  86                                        L3_TARG_STDERRLOG_MAIN);
  87                        masterid = __raw_readl(l3_targ_base +
  88                                        L3_TARG_STDERRLOG_MSTADDR);
  89
  90                        switch (std_err_main & CUSTOM_ERROR) {
  91                        case STANDARD_ERROR:
  92                                target_name =
  93                                        l3_targ_inst_name[i][err_src];
  94                                WARN(true, "L3 standard error: TARGET:%s at address 0x%x\n",
  95                                        target_name,
  96                                        __raw_readl(l3_targ_base +
  97                                                L3_TARG_STDERRLOG_SLVOFSLSB));
  98                                /* clear the std error log*/
  99                                clear = std_err_main | CLEAR_STDERR_LOG;
 100                                writel(clear, l3_targ_base +
 101                                        L3_TARG_STDERRLOG_MAIN);
 102                                break;
 103
 104                        case CUSTOM_ERROR:
 105                                target_name =
 106                                        l3_targ_inst_name[i][err_src];
 107                                for (k = 0; k < NUM_OF_L3_MASTERS; k++) {
 108                                        if (masterid == l3_masters[k].id)
 109                                                master_name =
 110                                                        l3_masters[k].name;
 111                                }
 112                                WARN(true, "L3 custom error: MASTER:%s TARGET:%s\n",
 113                                        master_name, target_name);
 114                                /* clear the std error log*/
 115                                clear = std_err_main | CLEAR_STDERR_LOG;
 116                                writel(clear, l3_targ_base +
 117                                        L3_TARG_STDERRLOG_MAIN);
 118                                break;
 119
 120                        default:
 121                                /* Nothing to be handled here as of now */
 122                                break;
 123                        }
 124                /* Error found so break the for loop */
 125                break;
 126                }
 127        }
 128        return IRQ_HANDLED;
 129}
 130
 131static int __devinit omap4_l3_probe(struct platform_device *pdev)
 132{
 133        static struct omap4_l3 *l3;
 134        struct resource *res;
 135        int ret;
 136
 137        l3 = kzalloc(sizeof(*l3), GFP_KERNEL);
 138        if (!l3)
 139                return -ENOMEM;
 140
 141        platform_set_drvdata(pdev, l3);
 142        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 143        if (!res) {
 144                dev_err(&pdev->dev, "couldn't find resource 0\n");
 145                ret = -ENODEV;
 146                goto err0;
 147        }
 148
 149        l3->l3_base[0] = ioremap(res->start, resource_size(res));
 150        if (!l3->l3_base[0]) {
 151                dev_err(&pdev->dev, "ioremap failed\n");
 152                ret = -ENOMEM;
 153                goto err0;
 154        }
 155
 156        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 157        if (!res) {
 158                dev_err(&pdev->dev, "couldn't find resource 1\n");
 159                ret = -ENODEV;
 160                goto err1;
 161        }
 162
 163        l3->l3_base[1] = ioremap(res->start, resource_size(res));
 164        if (!l3->l3_base[1]) {
 165                dev_err(&pdev->dev, "ioremap failed\n");
 166                ret = -ENOMEM;
 167                goto err1;
 168        }
 169
 170        res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
 171        if (!res) {
 172                dev_err(&pdev->dev, "couldn't find resource 2\n");
 173                ret = -ENODEV;
 174                goto err2;
 175        }
 176
 177        l3->l3_base[2] = ioremap(res->start, resource_size(res));
 178        if (!l3->l3_base[2]) {
 179                dev_err(&pdev->dev, "ioremap failed\n");
 180                ret = -ENOMEM;
 181                goto err2;
 182        }
 183
 184        /*
 185         * Setup interrupt Handlers
 186         */
 187        l3->debug_irq = platform_get_irq(pdev, 0);
 188        ret = request_irq(l3->debug_irq,
 189                        l3_interrupt_handler,
 190                        IRQF_DISABLED, "l3-dbg-irq", l3);
 191        if (ret) {
 192                pr_crit("L3: request_irq failed to register for 0x%x\n",
 193                                                OMAP44XX_IRQ_L3_DBG);
 194                goto err3;
 195        }
 196
 197        l3->app_irq = platform_get_irq(pdev, 1);
 198        ret = request_irq(l3->app_irq,
 199                        l3_interrupt_handler,
 200                        IRQF_DISABLED, "l3-app-irq", l3);
 201        if (ret) {
 202                pr_crit("L3: request_irq failed to register for 0x%x\n",
 203                                                OMAP44XX_IRQ_L3_APP);
 204                goto err4;
 205        }
 206
 207        return 0;
 208
 209err4:
 210        free_irq(l3->debug_irq, l3);
 211err3:
 212        iounmap(l3->l3_base[2]);
 213err2:
 214        iounmap(l3->l3_base[1]);
 215err1:
 216        iounmap(l3->l3_base[0]);
 217err0:
 218        kfree(l3);
 219        return ret;
 220}
 221
 222static int __devexit omap4_l3_remove(struct platform_device *pdev)
 223{
 224        struct omap4_l3 *l3 = platform_get_drvdata(pdev);
 225
 226        free_irq(l3->app_irq, l3);
 227        free_irq(l3->debug_irq, l3);
 228        iounmap(l3->l3_base[0]);
 229        iounmap(l3->l3_base[1]);
 230        iounmap(l3->l3_base[2]);
 231        kfree(l3);
 232
 233        return 0;
 234}
 235
 236#if defined(CONFIG_OF)
 237static const struct of_device_id l3_noc_match[] = {
 238        {.compatible = "ti,omap4-l3-noc", },
 239        {},
 240};
 241MODULE_DEVICE_TABLE(of, l3_noc_match);
 242#else
 243#define l3_noc_match NULL
 244#endif
 245
 246static struct platform_driver omap4_l3_driver = {
 247        .probe          = omap4_l3_probe,
 248        .remove         = __devexit_p(omap4_l3_remove),
 249        .driver         = {
 250                .name           = "omap_l3_noc",
 251                .owner          = THIS_MODULE,
 252                .of_match_table = l3_noc_match,
 253        },
 254};
 255
 256static int __init omap4_l3_init(void)
 257{
 258        return platform_driver_register(&omap4_l3_driver);
 259}
 260postcore_initcall_sync(omap4_l3_init);
 261
 262static void __exit omap4_l3_exit(void)
 263{
 264        platform_driver_unregister(&omap4_l3_driver);
 265}
 266module_exit(omap4_l3_exit);
 267