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