|
@@ -26,14 +26,20 @@
|
|
|
|
|
|
#include "omap_l3_noc.h"
|
|
|
|
|
|
-/*
|
|
|
- * 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
|
|
|
- * 3) Print the logged information.
|
|
|
- * 4) Add dump stack to provide kernel trace.
|
|
|
+/**
|
|
|
+ * 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)
|
|
|
*
|
|
|
- * Two Types of errors :
|
|
|
+ * 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:
|
|
|
* 1) Custom errors in L3 :
|
|
|
* Target like DMM/FW/EMIF generates SRESP=ERR error
|
|
|
* 2) Standard L3 error:
|
|
@@ -49,22 +55,107 @@
|
|
|
* can be trapped as well. But the trapping is implemented as part
|
|
|
* secure software and hence need not be implemented here.
|
|
|
*/
|
|
|
-static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
|
|
|
+static int l3_handle_target(struct omap_l3 *l3, void __iomem *base,
|
|
|
+ struct l3_flagmux_data *flag_mux, int err_src)
|
|
|
{
|
|
|
-
|
|
|
- struct omap_l3 *l3 = _l3;
|
|
|
- int inttype, i, k;
|
|
|
- int err_src = 0;
|
|
|
- u32 std_err_main, err_reg, clear, masterid;
|
|
|
- void __iomem *base, *l3_targ_base;
|
|
|
+ int k;
|
|
|
+ u32 std_err_main, clear, masterid;
|
|
|
+ void __iomem *l3_targ_base;
|
|
|
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 *target_name, *master_name = "UN IDENTIFIED";
|
|
|
char *err_description;
|
|
|
char err_string[30] = { 0 };
|
|
|
|
|
|
+ /* 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;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CUSTOM_ERROR:
|
|
|
+ err_description = "Custom";
|
|
|
+
|
|
|
+ l3_targ_mstaddr = l3_targ_base +
|
|
|
+ L3_TARG_STDERRLOG_CINFO_MSTADDR;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
/* Get the Type of interrupt */
|
|
|
inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR;
|
|
|
|
|
@@ -80,35 +171,18 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
|
|
|
|
|
|
/* 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);
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
+ ret = l3_handle_target(l3, base, flag_mux, err_src);
|
|
|
|
|
|
/*
|
|
|
- * If we do not know of a register offset to decode
|
|
|
- * and clear, then mask.
|
|
|
+ * 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.
|
|
|
*/
|
|
|
- if (target_name == L3_TARGET_NOT_SUPPORTED) {
|
|
|
- u32 mask_val;
|
|
|
- void __iomem *mask_reg;
|
|
|
-
|
|
|
- /*
|
|
|
- * Certain plaforms may have "undocumented"
|
|
|
- * status pending on boot.. So dont generate
|
|
|
- * a severe warning here.
|
|
|
- */
|
|
|
+ if (ret) {
|
|
|
dev_err(l3->dev,
|
|
|
"L3 %s error: target %d mod:%d %s\n",
|
|
|
inttype ? "debug" : "application",
|
|
@@ -119,67 +193,8 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
|
|
|
mask_val = readl_relaxed(mask_reg);
|
|
|
mask_val &= ~(1 << err_src);
|
|
|
writel_relaxed(mask_val, mask_reg);
|
|
|
-
|
|
|
- break;
|
|
|
}
|
|
|
|
|
|
- /* 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;
|
|
|
- break;
|
|
|
-
|
|
|
- case CUSTOM_ERROR:
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- 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;
|
|
|
}
|