2 * OMAP L3 Interconnect error handling driver
4 * Copyright (C) 2011-2014 Texas Instruments Incorporated - http://www.ti.com/
5 * Santosh Shilimkar <santosh.shilimkar@ti.com>
6 * Sricharan <r.sricharan@ti.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
12 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
13 * kind, whether express or implied; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 #include <linux/init.h>
18 #include <linux/interrupt.h>
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/of_device.h>
24 #include <linux/platform_device.h>
25 #include <linux/slab.h>
27 #include "omap_l3_noc.h"
30 * Interrupt Handler for L3 error detection.
31 * 1) Identify the L3 clockdomain partition to which the error belongs to.
32 * 2) Identify the slave where the error information is logged
33 * 3) Print the logged information.
34 * 4) Add dump stack to provide kernel trace.
36 * Two Types of errors :
37 * 1) Custom errors in L3 :
38 * Target like DMM/FW/EMIF generates SRESP=ERR error
39 * 2) Standard L3 error:
41 * L3 tries to access target while it is idle
43 * - Address hole error:
44 * If DSS/ISS/FDIF/USBHOSTFS access a target where they
45 * do not have connectivity, the error is logged in
46 * their default target which is DMM2.
48 * On High Secure devices, firewall errors are possible and those
49 * can be trapped as well. But the trapping is implemented as part
50 * secure software and hence need not be implemented here.
52 static irqreturn_t
l3_interrupt_handler(int irq
, void *_l3
)
55 struct omap_l3
*l3
= _l3
;
58 u32 std_err_main
, err_reg
, clear
, masterid
;
59 void __iomem
*base
, *l3_targ_base
;
60 void __iomem
*l3_targ_stderr
, *l3_targ_slvofslsb
, *l3_targ_mstaddr
;
61 char *target_name
, *master_name
= "UN IDENTIFIED";
62 struct l3_target_data
*l3_targ_inst
;
63 struct l3_flagmux_data
*flag_mux
;
64 struct l3_masters_data
*master
;
66 /* Get the Type of interrupt */
67 inttype
= irq
== l3
->app_irq
? L3_APPLICATION_ERROR
: L3_DEBUG_ERROR
;
69 for (i
= 0; i
< l3
->num_modules
; i
++) {
71 * Read the regerr register of the clock domain
72 * to determine the source
74 base
= l3
->l3_base
[i
];
75 flag_mux
= l3
->l3_flagmux
[i
];
76 err_reg
= readl_relaxed(base
+ flag_mux
->offset
+
77 L3_FLAGMUX_REGERR0
+ (inttype
<< 3));
79 /* Get the corresponding error and analyse */
81 /* Identify the source from control status register */
82 err_src
= __ffs(err_reg
);
84 /* We DONOT expect err_src to go out of bounds */
85 BUG_ON(err_src
> MAX_CLKDM_TARGETS
);
87 if (err_src
< flag_mux
->num_targ_data
) {
88 l3_targ_inst
= &flag_mux
->l3_targ
[err_src
];
89 target_name
= l3_targ_inst
->name
;
90 l3_targ_base
= base
+ l3_targ_inst
->offset
;
92 target_name
= L3_TARGET_NOT_SUPPORTED
;
96 * If we do not know of a register offset to decode
97 * and clear, then mask.
99 if (target_name
== L3_TARGET_NOT_SUPPORTED
) {
101 void __iomem
*mask_reg
;
104 * Certain plaforms may have "undocumented"
105 * status pending on boot.. So dont generate
106 * a severe warning here.
109 "L3 %s error: target %d mod:%d %s\n",
110 inttype
? "debug" : "application",
111 err_src
, i
, "(unclearable)");
113 mask_reg
= base
+ flag_mux
->offset
+
114 L3_FLAGMUX_MASK0
+ (inttype
<< 3);
115 mask_val
= readl_relaxed(mask_reg
);
116 mask_val
&= ~(1 << err_src
);
117 writel_relaxed(mask_val
, mask_reg
);
122 /* Read the stderrlog_main_source from clk domain */
123 l3_targ_stderr
= l3_targ_base
+ L3_TARG_STDERRLOG_MAIN
;
124 l3_targ_slvofslsb
= l3_targ_base
+
125 L3_TARG_STDERRLOG_SLVOFSLSB
;
126 l3_targ_mstaddr
= l3_targ_base
+
127 L3_TARG_STDERRLOG_MSTADDR
;
129 std_err_main
= readl_relaxed(l3_targ_stderr
);
130 masterid
= readl_relaxed(l3_targ_mstaddr
);
132 switch (std_err_main
& CUSTOM_ERROR
) {
134 WARN(true, "L3 standard error: TARGET:%s at address 0x%x\n",
136 readl_relaxed(l3_targ_slvofslsb
));
137 /* clear the std error log*/
138 clear
= std_err_main
| CLEAR_STDERR_LOG
;
139 writel_relaxed(clear
, l3_targ_stderr
);
143 for (k
= 0, master
= l3
->l3_masters
;
144 k
< l3
->num_masters
; k
++, master
++) {
145 if (masterid
== master
->id
) {
146 master_name
= master
->name
;
150 WARN(true, "L3 custom error: MASTER:%s TARGET:%s\n",
151 master_name
, target_name
);
152 /* clear the std error log*/
153 clear
= std_err_main
| CLEAR_STDERR_LOG
;
154 writel_relaxed(clear
, l3_targ_stderr
);
158 /* Nothing to be handled here as of now */
161 /* Error found so break the for loop */
168 static const struct of_device_id l3_noc_match
[] = {
169 {.compatible
= "ti,omap4-l3-noc", .data
= &omap_l3_data
},
172 MODULE_DEVICE_TABLE(of
, l3_noc_match
);
174 static int omap_l3_probe(struct platform_device
*pdev
)
176 const struct of_device_id
*of_id
;
177 static struct omap_l3
*l3
;
180 of_id
= of_match_device(l3_noc_match
, &pdev
->dev
);
182 dev_err(&pdev
->dev
, "OF data missing\n");
186 l3
= devm_kzalloc(&pdev
->dev
, sizeof(*l3
), GFP_KERNEL
);
190 memcpy(l3
, of_id
->data
, sizeof(*l3
));
191 l3
->dev
= &pdev
->dev
;
192 platform_set_drvdata(pdev
, l3
);
194 /* Get mem resources */
195 for (i
= 0; i
< l3
->num_modules
; i
++) {
196 struct resource
*res
= platform_get_resource(pdev
,
199 l3
->l3_base
[i
] = devm_ioremap_resource(&pdev
->dev
, res
);
200 if (IS_ERR(l3
->l3_base
[i
])) {
201 dev_err(l3
->dev
, "ioremap %d failed\n", i
);
202 return PTR_ERR(l3
->l3_base
[i
]);
207 * Setup interrupt Handlers
209 l3
->debug_irq
= platform_get_irq(pdev
, 0);
210 ret
= devm_request_irq(l3
->dev
, l3
->debug_irq
, l3_interrupt_handler
,
211 IRQF_DISABLED
, "l3-dbg-irq", l3
);
213 dev_err(l3
->dev
, "request_irq failed for %d\n",
218 l3
->app_irq
= platform_get_irq(pdev
, 1);
219 ret
= devm_request_irq(l3
->dev
, l3
->app_irq
, l3_interrupt_handler
,
220 IRQF_DISABLED
, "l3-app-irq", l3
);
222 dev_err(l3
->dev
, "request_irq failed for %d\n", l3
->app_irq
);
227 static struct platform_driver omap_l3_driver
= {
228 .probe
= omap_l3_probe
,
230 .name
= "omap_l3_noc",
231 .owner
= THIS_MODULE
,
232 .of_match_table
= of_match_ptr(l3_noc_match
),
236 static int __init
omap_l3_init(void)
238 return platform_driver_register(&omap_l3_driver
);
240 postcore_initcall_sync(omap_l3_init
);
242 static void __exit
omap_l3_exit(void)
244 platform_driver_unregister(&omap_l3_driver
);
246 module_exit(omap_l3_exit
);