Blame view
drivers/bus/omap_l3_noc.c
10.5 KB
2722e56de OMAP4: l3: Introd... |
1 |
/* |
c10d5c9e1 bus: omap_l3_noc:... |
2 |
* OMAP L3 Interconnect error handling driver |
ed0e35207 OMAP: Fix indenta... |
3 |
* |
e7309c267 bus: omap_l3_noc:... |
4 |
* Copyright (C) 2011-2015 Texas Instruments Incorporated - http://www.ti.com/ |
ed0e35207 OMAP: Fix indenta... |
5 6 7 8 |
* Santosh Shilimkar <santosh.shilimkar@ti.com> * Sricharan <r.sricharan@ti.com> * * This program is free software; you can redistribute it and/or modify |
c5f2aea0e bus: omap_l3_noc:... |
9 10 |
* it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. |
ed0e35207 OMAP: Fix indenta... |
11 |
* |
c5f2aea0e bus: omap_l3_noc:... |
12 13 14 |
* 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 |
ed0e35207 OMAP: Fix indenta... |
15 |
* GNU General Public License for more details. |
ed0e35207 OMAP: Fix indenta... |
16 |
*/ |
2722e56de OMAP4: l3: Introd... |
17 |
#include <linux/init.h> |
2722e56de OMAP4: l3: Introd... |
18 |
#include <linux/interrupt.h> |
0659452dd bus: omap_l3_noc:... |
19 |
#include <linux/io.h> |
2722e56de OMAP4: l3: Introd... |
20 |
#include <linux/kernel.h> |
0659452dd bus: omap_l3_noc:... |
21 22 23 24 |
#include <linux/module.h> #include <linux/of_device.h> #include <linux/of.h> #include <linux/platform_device.h> |
2722e56de OMAP4: l3: Introd... |
25 26 27 |
#include <linux/slab.h> #include "omap_l3_noc.h" |
e4be3f3a0 bus: omap_l3_noc:... |
28 29 30 31 32 33 |
/** * l3_handle_target() - Handle Target specific parse and reporting * @l3: pointer to l3 struct * @base: base address of clkdm * @flag_mux: flagmux corresponding to the event * @err_src: error source index of the slave (target) |
2722e56de OMAP4: l3: Introd... |
34 |
* |
e4be3f3a0 bus: omap_l3_noc:... |
35 36 37 38 39 40 41 |
* This does the second part of the error interrupt handling: * 3) Parse in the slave information * 4) Print the logged information. * 5) Add dump stack to provide kernel trace. * 6) Clear the source if known. * * This handles two types of errors: |
2722e56de OMAP4: l3: Introd... |
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
* 1) Custom errors in L3 : * Target like DMM/FW/EMIF generates SRESP=ERR error * 2) Standard L3 error: * - Unsupported CMD. * L3 tries to access target while it is idle * - OCP disconnect. * - Address hole error: * If DSS/ISS/FDIF/USBHOSTFS access a target where they * do not have connectivity, the error is logged in * their default target which is DMM2. * * On High Secure devices, firewall errors are possible and those * can be trapped as well. But the trapping is implemented as part * secure software and hence need not be implemented here. */ |
e4be3f3a0 bus: omap_l3_noc:... |
57 58 |
static int l3_handle_target(struct omap_l3 *l3, void __iomem *base, struct l3_flagmux_data *flag_mux, int err_src) |
2722e56de OMAP4: l3: Introd... |
59 |
{ |
e4be3f3a0 bus: omap_l3_noc:... |
60 61 |
int k; u32 std_err_main, clear, masterid; |
cf52b2ecd bus: omap_l3_noc:... |
62 |
u8 op_code, m_req_info; |
e4be3f3a0 bus: omap_l3_noc:... |
63 |
void __iomem *l3_targ_base; |
9e224c8ff bus: omap_l3_noc:... |
64 |
void __iomem *l3_targ_stderr, *l3_targ_slvofslsb, *l3_targ_mstaddr; |
cf52b2ecd bus: omap_l3_noc:... |
65 |
void __iomem *l3_targ_hdr, *l3_targ_info; |
3ae9af7c9 bus: omap_l3_noc:... |
66 |
struct l3_target_data *l3_targ_inst; |
0659452dd bus: omap_l3_noc:... |
67 |
struct l3_masters_data *master; |
e4be3f3a0 bus: omap_l3_noc:... |
68 |
char *target_name, *master_name = "UN IDENTIFIED"; |
c98aa7aaa bus: omap_l3_noc:... |
69 70 |
char *err_description; char err_string[30] = { 0 }; |
cf52b2ecd bus: omap_l3_noc:... |
71 |
char info_string[60] = { 0 }; |
2722e56de OMAP4: l3: Introd... |
72 |
|
e4be3f3a0 bus: omap_l3_noc:... |
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
/* 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 (target_name == L3_TARGET_NOT_SUPPORTED) return -ENODEV; /* Read the stderrlog_main_source from clk domain */ 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: 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; |
7f9de02d6 bus: omap_l3_noc:... |
101 |
l3_targ_hdr = l3_targ_base + L3_TARG_STDERRLOG_HDR; |
cf52b2ecd bus: omap_l3_noc:... |
102 |
l3_targ_info = l3_targ_base + L3_TARG_STDERRLOG_INFO; |
e4be3f3a0 bus: omap_l3_noc:... |
103 104 105 106 107 108 109 |
break; case CUSTOM_ERROR: err_description = "Custom"; l3_targ_mstaddr = l3_targ_base + L3_TARG_STDERRLOG_CINFO_MSTADDR; |
7f9de02d6 bus: omap_l3_noc:... |
110 |
l3_targ_hdr = l3_targ_base + L3_TARG_STDERRLOG_CINFO_OPCODE; |
cf52b2ecd bus: omap_l3_noc:... |
111 |
l3_targ_info = l3_targ_base + L3_TARG_STDERRLOG_CINFO_INFO; |
e4be3f3a0 bus: omap_l3_noc:... |
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
break; default: /* Nothing to be handled here as of now */ return 0; } /* 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; } } |
7f9de02d6 bus: omap_l3_noc:... |
130 |
op_code = readl_relaxed(l3_targ_hdr) & 0x7; |
cf52b2ecd bus: omap_l3_noc:... |
131 132 133 134 135 136 |
m_req_info = readl_relaxed(l3_targ_info) & 0xF; snprintf(info_string, sizeof(info_string), ": %s in %s mode during %s access", (m_req_info & BIT(0)) ? "Opcode Fetch" : "Data Access", (m_req_info & BIT(1)) ? "Supervisor" : "User", (m_req_info & BIT(3)) ? "Debug" : "Functional"); |
e4be3f3a0 bus: omap_l3_noc:... |
137 |
WARN(true, |
cf52b2ecd bus: omap_l3_noc:... |
138 139 |
"%s:L3 %s Error: MASTER %s TARGET %s (%s)%s%s ", |
e4be3f3a0 bus: omap_l3_noc:... |
140 141 142 |
dev_name(l3->dev), err_description, master_name, target_name, |
7f9de02d6 bus: omap_l3_noc:... |
143 |
l3_transaction_type[op_code], |
cf52b2ecd bus: omap_l3_noc:... |
144 |
err_string, info_string); |
e4be3f3a0 bus: omap_l3_noc:... |
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
/* clear the std error log*/ clear = std_err_main | CLEAR_STDERR_LOG; writel_relaxed(clear, l3_targ_stderr); return 0; } /** * l3_interrupt_handler() - interrupt handler for l3 events * @irq: irq number * @_l3: pointer to l3 structure * * Interrupt Handler for L3 error detection. * 1) Identify the L3 clockdomain partition to which the error belongs to. * 2) Identify the slave where the error information is logged * ... handle the slave event.. * 7) if the slave is unknown, mask out the slave. */ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) { struct omap_l3 *l3 = _l3; int inttype, i, ret; int err_src = 0; u32 err_reg, mask_val; void __iomem *base, *mask_reg; struct l3_flagmux_data *flag_mux; |
2722e56de OMAP4: l3: Introd... |
172 |
/* Get the Type of interrupt */ |
35f7b9610 OMAP3/4: l3: mino... |
173 |
inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR; |
2722e56de OMAP4: l3: Introd... |
174 |
|
0659452dd bus: omap_l3_noc:... |
175 |
for (i = 0; i < l3->num_modules; i++) { |
2722e56de OMAP4: l3: Introd... |
176 177 178 179 |
/* * Read the regerr register of the clock domain * to determine the source */ |
6616aac66 OMAP: Fix sparse ... |
180 |
base = l3->l3_base[i]; |
97708c08c bus: omap_l3_noc:... |
181 182 |
flag_mux = l3->l3_flagmux[i]; err_reg = readl_relaxed(base + flag_mux->offset + |
9e224c8ff bus: omap_l3_noc:... |
183 |
L3_FLAGMUX_REGERR0 + (inttype << 3)); |
2722e56de OMAP4: l3: Introd... |
184 |
|
2100b595b bus: omap_l3_noc:... |
185 186 |
err_reg &= ~(inttype ? flag_mux->mask_app_bits : flag_mux->mask_dbg_bits); |
2722e56de OMAP4: l3: Introd... |
187 188 189 |
/* Get the corresponding error and analyse */ if (err_reg) { /* Identify the source from control status register */ |
342fd1442 OMAP: Improve reg... |
190 |
err_src = __ffs(err_reg); |
3340d739f bus: omap_l3_noc:... |
191 |
|
e4be3f3a0 bus: omap_l3_noc:... |
192 |
ret = l3_handle_target(l3, base, flag_mux, err_src); |
2722e56de OMAP4: l3: Introd... |
193 |
|
3340d739f bus: omap_l3_noc:... |
194 |
/* |
e4be3f3a0 bus: omap_l3_noc:... |
195 196 197 198 |
* Certain plaforms may have "undocumented" status * pending on boot. So dont generate a severe warning * here. Just mask it off to prevent the error from * reoccuring and locking up the system. |
3340d739f bus: omap_l3_noc:... |
199 |
*/ |
e4be3f3a0 bus: omap_l3_noc:... |
200 |
if (ret) { |
3340d739f bus: omap_l3_noc:... |
201 202 203 204 205 |
dev_err(l3->dev, "L3 %s error: target %d mod:%d %s ", inttype ? "debug" : "application", err_src, i, "(unclearable)"); |
97708c08c bus: omap_l3_noc:... |
206 |
mask_reg = base + flag_mux->offset + |
3340d739f bus: omap_l3_noc:... |
207 208 209 210 |
L3_FLAGMUX_MASK0 + (inttype << 3); mask_val = readl_relaxed(mask_reg); mask_val &= ~(1 << err_src); writel_relaxed(mask_val, mask_reg); |
2100b595b bus: omap_l3_noc:... |
211 212 213 214 215 216 |
/* Mark these bits as to be ignored */ if (inttype) flag_mux->mask_app_bits |= 1 << err_src; else flag_mux->mask_dbg_bits |= 1 << err_src; |
3340d739f bus: omap_l3_noc:... |
217 |
} |
c98aa7aaa bus: omap_l3_noc:... |
218 |
/* Error found so break the for loop */ |
c4cf0935a bus: omap_l3_noc:... |
219 |
return IRQ_HANDLED; |
2722e56de OMAP4: l3: Introd... |
220 221 |
} } |
c4cf0935a bus: omap_l3_noc:... |
222 223 224 225 226 227 |
dev_err(l3->dev, "L3 %s IRQ not handled!! ", inttype ? "debug" : "application"); return IRQ_NONE; |
2722e56de OMAP4: l3: Introd... |
228 |
} |
0659452dd bus: omap_l3_noc:... |
229 |
static const struct of_device_id l3_noc_match[] = { |
e7309c267 bus: omap_l3_noc:... |
230 231 |
{.compatible = "ti,omap4-l3-noc", .data = &omap4_l3_data}, {.compatible = "ti,omap5-l3-noc", .data = &omap5_l3_data}, |
53a848be0 bus: omap_l3_noc:... |
232 |
{.compatible = "ti,dra7-l3-noc", .data = &dra_l3_data}, |
27b7d5f3c bus: omap_l3_noc:... |
233 |
{.compatible = "ti,am4372-l3-noc", .data = &am4372_l3_data}, |
0659452dd bus: omap_l3_noc:... |
234 235 236 |
{}, }; MODULE_DEVICE_TABLE(of, l3_noc_match); |
c10d5c9e1 bus: omap_l3_noc:... |
237 |
static int omap_l3_probe(struct platform_device *pdev) |
2722e56de OMAP4: l3: Introd... |
238 |
{ |
0659452dd bus: omap_l3_noc:... |
239 |
const struct of_device_id *of_id; |
c10d5c9e1 bus: omap_l3_noc:... |
240 |
static struct omap_l3 *l3; |
f33ddf745 bus: omap_l3_noc:... |
241 |
int ret, i, res_idx; |
2722e56de OMAP4: l3: Introd... |
242 |
|
0659452dd bus: omap_l3_noc:... |
243 244 245 246 247 248 |
of_id = of_match_device(l3_noc_match, &pdev->dev); if (!of_id) { dev_err(&pdev->dev, "OF data missing "); return -EINVAL; } |
bae745101 drivers: bus: oma... |
249 |
l3 = devm_kzalloc(&pdev->dev, sizeof(*l3), GFP_KERNEL); |
2722e56de OMAP4: l3: Introd... |
250 |
if (!l3) |
7529b7038 OMAP3/4: l3: fix ... |
251 |
return -ENOMEM; |
2722e56de OMAP4: l3: Introd... |
252 |
|
0659452dd bus: omap_l3_noc:... |
253 |
memcpy(l3, of_id->data, sizeof(*l3)); |
ca6a34935 bus: omap_l3_noc:... |
254 |
l3->dev = &pdev->dev; |
2722e56de OMAP4: l3: Introd... |
255 |
platform_set_drvdata(pdev, l3); |
2722e56de OMAP4: l3: Introd... |
256 |
|
56c4a0224 drivers: bus: oma... |
257 |
/* Get mem resources */ |
f33ddf745 bus: omap_l3_noc:... |
258 259 260 261 262 263 264 265 266 267 |
for (i = 0, res_idx = 0; i < l3->num_modules; i++) { struct resource *res; if (l3->l3_base[i] == L3_BASE_IS_SUBMODULE) { /* First entry cannot be submodule */ BUG_ON(i == 0); l3->l3_base[i] = l3->l3_base[i - 1]; continue; } res = platform_get_resource(pdev, IORESOURCE_MEM, res_idx); |
56c4a0224 drivers: bus: oma... |
268 269 |
l3->l3_base[i] = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(l3->l3_base[i])) { |
ca6a34935 bus: omap_l3_noc:... |
270 271 |
dev_err(l3->dev, "ioremap %d failed ", i); |
56c4a0224 drivers: bus: oma... |
272 273 |
return PTR_ERR(l3->l3_base[i]); } |
f33ddf745 bus: omap_l3_noc:... |
274 |
res_idx++; |
2722e56de OMAP4: l3: Introd... |
275 276 277 278 279 |
} /* * Setup interrupt Handlers */ |
c1df2dcc9 OMAP: Fix a BUG i... |
280 |
l3->debug_irq = platform_get_irq(pdev, 0); |
ca6a34935 bus: omap_l3_noc:... |
281 |
ret = devm_request_irq(l3->dev, l3->debug_irq, l3_interrupt_handler, |
d8bf368d0 genirq: Remove th... |
282 |
0x0, "l3-dbg-irq", l3); |
2722e56de OMAP4: l3: Introd... |
283 |
if (ret) { |
ca6a34935 bus: omap_l3_noc:... |
284 285 |
dev_err(l3->dev, "request_irq failed for %d ", |
ae22598a1 drivers: bus: oma... |
286 |
l3->debug_irq); |
56c4a0224 drivers: bus: oma... |
287 |
return ret; |
2722e56de OMAP4: l3: Introd... |
288 |
} |
2722e56de OMAP4: l3: Introd... |
289 |
|
c1df2dcc9 OMAP: Fix a BUG i... |
290 |
l3->app_irq = platform_get_irq(pdev, 1); |
ca6a34935 bus: omap_l3_noc:... |
291 |
ret = devm_request_irq(l3->dev, l3->app_irq, l3_interrupt_handler, |
d8bf368d0 genirq: Remove th... |
292 |
0x0, "l3-app-irq", l3); |
a0ef78f35 drivers: bus: oma... |
293 |
if (ret) |
ca6a34935 bus: omap_l3_noc:... |
294 295 |
dev_err(l3->dev, "request_irq failed for %d ", l3->app_irq); |
7529b7038 OMAP3/4: l3: fix ... |
296 |
|
2722e56de OMAP4: l3: Introd... |
297 298 |
return ret; } |
258d2a109 bus: omap_l3_noc:... |
299 |
#ifdef CONFIG_PM_SLEEP |
61b43d4e9 bus: omap_l3_noc:... |
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
/** * l3_resume_noirq() - resume function for l3_noc * @dev: pointer to l3_noc device structure * * We only have the resume handler only since we * have already maintained the delta register * configuration as part of configuring the system */ static int l3_resume_noirq(struct device *dev) { struct omap_l3 *l3 = dev_get_drvdata(dev); int i; struct l3_flagmux_data *flag_mux; void __iomem *base, *mask_regx = NULL; u32 mask_val; for (i = 0; i < l3->num_modules; i++) { base = l3->l3_base[i]; flag_mux = l3->l3_flagmux[i]; if (!flag_mux->mask_app_bits && !flag_mux->mask_dbg_bits) continue; mask_regx = base + flag_mux->offset + L3_FLAGMUX_MASK0 + (L3_APPLICATION_ERROR << 3); mask_val = readl_relaxed(mask_regx); mask_val &= ~(flag_mux->mask_app_bits); writel_relaxed(mask_val, mask_regx); mask_regx = base + flag_mux->offset + L3_FLAGMUX_MASK0 + (L3_DEBUG_ERROR << 3); mask_val = readl_relaxed(mask_regx); mask_val &= ~(flag_mux->mask_dbg_bits); writel_relaxed(mask_val, mask_regx); } /* Dummy read to force OCP barrier */ if (mask_regx) (void)readl(mask_regx); return 0; } static const struct dev_pm_ops l3_dev_pm_ops = { |
258d2a109 bus: omap_l3_noc:... |
345 |
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, l3_resume_noirq) |
61b43d4e9 bus: omap_l3_noc:... |
346 347 348 349 350 351 |
}; #define L3_DEV_PM_OPS (&l3_dev_pm_ops) #else #define L3_DEV_PM_OPS NULL #endif |
c10d5c9e1 bus: omap_l3_noc:... |
352 353 |
static struct platform_driver omap_l3_driver = { .probe = omap_l3_probe, |
d039c5b9f ARM: OMAP2+: l3-n... |
354 355 |
.driver = { .name = "omap_l3_noc", |
61b43d4e9 bus: omap_l3_noc:... |
356 |
.pm = L3_DEV_PM_OPS, |
0659452dd bus: omap_l3_noc:... |
357 |
.of_match_table = of_match_ptr(l3_noc_match), |
2722e56de OMAP4: l3: Introd... |
358 359 |
}, }; |
c10d5c9e1 bus: omap_l3_noc:... |
360 |
static int __init omap_l3_init(void) |
2722e56de OMAP4: l3: Introd... |
361 |
{ |
c10d5c9e1 bus: omap_l3_noc:... |
362 |
return platform_driver_register(&omap_l3_driver); |
2722e56de OMAP4: l3: Introd... |
363 |
} |
c10d5c9e1 bus: omap_l3_noc:... |
364 |
postcore_initcall_sync(omap_l3_init); |
2722e56de OMAP4: l3: Introd... |
365 |
|
c10d5c9e1 bus: omap_l3_noc:... |
366 |
static void __exit omap_l3_exit(void) |
2722e56de OMAP4: l3: Introd... |
367 |
{ |
c10d5c9e1 bus: omap_l3_noc:... |
368 |
platform_driver_unregister(&omap_l3_driver); |
2722e56de OMAP4: l3: Introd... |
369 |
} |
c10d5c9e1 bus: omap_l3_noc:... |
370 |
module_exit(omap_l3_exit); |