diff options
Diffstat (limited to 'drivers/bus/omap_l3_noc.c')
-rw-r--r-- | drivers/bus/omap_l3_noc.c | 211 |
1 files changed, 113 insertions, 98 deletions
diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 42e411457494..0691e6d9c1e4 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c | |||
@@ -26,14 +26,20 @@ | |||
26 | 26 | ||
27 | #include "omap_l3_noc.h" | 27 | #include "omap_l3_noc.h" |
28 | 28 | ||
29 | /* | 29 | /** |
30 | * Interrupt Handler for L3 error detection. | 30 | * l3_handle_target() - Handle Target specific parse and reporting |
31 | * 1) Identify the L3 clockdomain partition to which the error belongs to. | 31 | * @l3: pointer to l3 struct |
32 | * 2) Identify the slave where the error information is logged | 32 | * @base: base address of clkdm |
33 | * 3) Print the logged information. | 33 | * @flag_mux: flagmux corresponding to the event |
34 | * 4) Add dump stack to provide kernel trace. | 34 | * @err_src: error source index of the slave (target) |
35 | * | 35 | * |
36 | * Two Types of errors : | 36 | * This does the second part of the error interrupt handling: |
37 | * 3) Parse in the slave information | ||
38 | * 4) Print the logged information. | ||
39 | * 5) Add dump stack to provide kernel trace. | ||
40 | * 6) Clear the source if known. | ||
41 | * | ||
42 | * This handles two types of errors: | ||
37 | * 1) Custom errors in L3 : | 43 | * 1) Custom errors in L3 : |
38 | * Target like DMM/FW/EMIF generates SRESP=ERR error | 44 | * Target like DMM/FW/EMIF generates SRESP=ERR error |
39 | * 2) Standard L3 error: | 45 | * 2) Standard L3 error: |
@@ -49,22 +55,107 @@ | |||
49 | * can be trapped as well. But the trapping is implemented as part | 55 | * can be trapped as well. But the trapping is implemented as part |
50 | * secure software and hence need not be implemented here. | 56 | * secure software and hence need not be implemented here. |
51 | */ | 57 | */ |
52 | static irqreturn_t l3_interrupt_handler(int irq, void *_l3) | 58 | static int l3_handle_target(struct omap_l3 *l3, void __iomem *base, |
59 | struct l3_flagmux_data *flag_mux, int err_src) | ||
53 | { | 60 | { |
54 | 61 | int k; | |
55 | struct omap_l3 *l3 = _l3; | 62 | u32 std_err_main, clear, masterid; |
56 | int inttype, i, k; | 63 | void __iomem *l3_targ_base; |
57 | int err_src = 0; | ||
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; | 64 | 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; | 65 | struct l3_target_data *l3_targ_inst; |
63 | struct l3_flagmux_data *flag_mux; | ||
64 | struct l3_masters_data *master; | 66 | struct l3_masters_data *master; |
67 | char *target_name, *master_name = "UN IDENTIFIED"; | ||
65 | char *err_description; | 68 | char *err_description; |
66 | char err_string[30] = { 0 }; | 69 | char err_string[30] = { 0 }; |
67 | 70 | ||
71 | /* We DONOT expect err_src to go out of bounds */ | ||
72 | BUG_ON(err_src > MAX_CLKDM_TARGETS); | ||
73 | |||
74 | if (err_src < flag_mux->num_targ_data) { | ||
75 | l3_targ_inst = &flag_mux->l3_targ[err_src]; | ||
76 | target_name = l3_targ_inst->name; | ||
77 | l3_targ_base = base + l3_targ_inst->offset; | ||
78 | } else { | ||
79 | target_name = L3_TARGET_NOT_SUPPORTED; | ||
80 | } | ||
81 | |||
82 | if (target_name == L3_TARGET_NOT_SUPPORTED) | ||
83 | return -ENODEV; | ||
84 | |||
85 | /* Read the stderrlog_main_source from clk domain */ | ||
86 | l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN; | ||
87 | l3_targ_slvofslsb = l3_targ_base + L3_TARG_STDERRLOG_SLVOFSLSB; | ||
88 | |||
89 | std_err_main = readl_relaxed(l3_targ_stderr); | ||
90 | |||
91 | switch (std_err_main & CUSTOM_ERROR) { | ||
92 | case STANDARD_ERROR: | ||
93 | err_description = "Standard"; | ||
94 | snprintf(err_string, sizeof(err_string), | ||
95 | ": At Address: 0x%08X ", | ||
96 | readl_relaxed(l3_targ_slvofslsb)); | ||
97 | |||
98 | l3_targ_mstaddr = l3_targ_base + L3_TARG_STDERRLOG_MSTADDR; | ||
99 | break; | ||
100 | |||
101 | case CUSTOM_ERROR: | ||
102 | err_description = "Custom"; | ||
103 | |||
104 | l3_targ_mstaddr = l3_targ_base + | ||
105 | L3_TARG_STDERRLOG_CINFO_MSTADDR; | ||
106 | break; | ||
107 | |||
108 | default: | ||
109 | /* Nothing to be handled here as of now */ | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | /* STDERRLOG_MSTADDR Stores the NTTP master address. */ | ||
114 | masterid = (readl_relaxed(l3_targ_mstaddr) & | ||
115 | l3->mst_addr_mask) >> __ffs(l3->mst_addr_mask); | ||
116 | |||
117 | for (k = 0, master = l3->l3_masters; k < l3->num_masters; | ||
118 | k++, master++) { | ||
119 | if (masterid == master->id) { | ||
120 | master_name = master->name; | ||
121 | break; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | WARN(true, | ||
126 | "%s:L3 %s Error: MASTER %s TARGET %s%s\n", | ||
127 | dev_name(l3->dev), | ||
128 | err_description, | ||
129 | master_name, target_name, | ||
130 | err_string); | ||
131 | |||
132 | /* clear the std error log*/ | ||
133 | clear = std_err_main | CLEAR_STDERR_LOG; | ||
134 | writel_relaxed(clear, l3_targ_stderr); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * l3_interrupt_handler() - interrupt handler for l3 events | ||
141 | * @irq: irq number | ||
142 | * @_l3: pointer to l3 structure | ||
143 | * | ||
144 | * Interrupt Handler for L3 error detection. | ||
145 | * 1) Identify the L3 clockdomain partition to which the error belongs to. | ||
146 | * 2) Identify the slave where the error information is logged | ||
147 | * ... handle the slave event.. | ||
148 | * 7) if the slave is unknown, mask out the slave. | ||
149 | */ | ||
150 | static irqreturn_t l3_interrupt_handler(int irq, void *_l3) | ||
151 | { | ||
152 | struct omap_l3 *l3 = _l3; | ||
153 | int inttype, i, ret; | ||
154 | int err_src = 0; | ||
155 | u32 err_reg, mask_val; | ||
156 | void __iomem *base, *mask_reg; | ||
157 | struct l3_flagmux_data *flag_mux; | ||
158 | |||
68 | /* Get the Type of interrupt */ | 159 | /* Get the Type of interrupt */ |
69 | inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR; | 160 | inttype = irq == l3->app_irq ? L3_APPLICATION_ERROR : L3_DEBUG_ERROR; |
70 | 161 | ||
@@ -80,35 +171,18 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) | |||
80 | 171 | ||
81 | /* Get the corresponding error and analyse */ | 172 | /* Get the corresponding error and analyse */ |
82 | if (err_reg) { | 173 | if (err_reg) { |
83 | bool std_err = true; | ||
84 | |||
85 | /* Identify the source from control status register */ | 174 | /* Identify the source from control status register */ |
86 | err_src = __ffs(err_reg); | 175 | err_src = __ffs(err_reg); |
87 | 176 | ||
88 | /* We DONOT expect err_src to go out of bounds */ | 177 | ret = l3_handle_target(l3, base, flag_mux, err_src); |
89 | BUG_ON(err_src > MAX_CLKDM_TARGETS); | ||
90 | |||
91 | if (err_src < flag_mux->num_targ_data) { | ||
92 | l3_targ_inst = &flag_mux->l3_targ[err_src]; | ||
93 | target_name = l3_targ_inst->name; | ||
94 | l3_targ_base = base + l3_targ_inst->offset; | ||
95 | } else { | ||
96 | target_name = L3_TARGET_NOT_SUPPORTED; | ||
97 | } | ||
98 | 178 | ||
99 | /* | 179 | /* |
100 | * If we do not know of a register offset to decode | 180 | * Certain plaforms may have "undocumented" status |
101 | * and clear, then mask. | 181 | * pending on boot. So dont generate a severe warning |
182 | * here. Just mask it off to prevent the error from | ||
183 | * reoccuring and locking up the system. | ||
102 | */ | 184 | */ |
103 | if (target_name == L3_TARGET_NOT_SUPPORTED) { | 185 | if (ret) { |
104 | u32 mask_val; | ||
105 | void __iomem *mask_reg; | ||
106 | |||
107 | /* | ||
108 | * Certain plaforms may have "undocumented" | ||
109 | * status pending on boot.. So dont generate | ||
110 | * a severe warning here. | ||
111 | */ | ||
112 | dev_err(l3->dev, | 186 | dev_err(l3->dev, |
113 | "L3 %s error: target %d mod:%d %s\n", | 187 | "L3 %s error: target %d mod:%d %s\n", |
114 | inttype ? "debug" : "application", | 188 | inttype ? "debug" : "application", |
@@ -119,67 +193,8 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) | |||
119 | mask_val = readl_relaxed(mask_reg); | 193 | mask_val = readl_relaxed(mask_reg); |
120 | mask_val &= ~(1 << err_src); | 194 | mask_val &= ~(1 << err_src); |
121 | writel_relaxed(mask_val, mask_reg); | 195 | writel_relaxed(mask_val, mask_reg); |
122 | |||
123 | break; | ||
124 | } | 196 | } |
125 | 197 | ||
126 | /* Read the stderrlog_main_source from clk domain */ | ||
127 | l3_targ_stderr = l3_targ_base + L3_TARG_STDERRLOG_MAIN; | ||
128 | l3_targ_slvofslsb = l3_targ_base + | ||
129 | L3_TARG_STDERRLOG_SLVOFSLSB; | ||
130 | |||
131 | std_err_main = readl_relaxed(l3_targ_stderr); | ||
132 | |||
133 | switch (std_err_main & CUSTOM_ERROR) { | ||
134 | case STANDARD_ERROR: | ||
135 | err_description = "Standard"; | ||
136 | snprintf(err_string, sizeof(err_string), | ||
137 | ": At Address: 0x%08X ", | ||
138 | readl_relaxed(l3_targ_slvofslsb)); | ||
139 | |||
140 | l3_targ_mstaddr = l3_targ_base + | ||
141 | L3_TARG_STDERRLOG_MSTADDR; | ||
142 | break; | ||
143 | |||
144 | case CUSTOM_ERROR: | ||
145 | err_description = "Custom"; | ||
146 | |||
147 | l3_targ_mstaddr = l3_targ_base + | ||
148 | L3_TARG_STDERRLOG_CINFO_MSTADDR; | ||
149 | break; | ||
150 | |||
151 | default: | ||
152 | std_err = false; | ||
153 | /* Nothing to be handled here as of now */ | ||
154 | break; | ||
155 | } | ||
156 | |||
157 | if (!std_err) | ||
158 | break; | ||
159 | |||
160 | /* STDERRLOG_MSTADDR Stores the NTTP master address. */ | ||
161 | masterid = (readl_relaxed(l3_targ_mstaddr) & | ||
162 | l3->mst_addr_mask) >> | ||
163 | __ffs(l3->mst_addr_mask); | ||
164 | |||
165 | for (k = 0, master = l3->l3_masters; | ||
166 | k < l3->num_masters; k++, master++) { | ||
167 | if (masterid == master->id) { | ||
168 | master_name = master->name; | ||
169 | break; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | WARN(true, | ||
174 | "%s:L3 %s Error: MASTER %s TARGET %s%s\n", | ||
175 | dev_name(l3->dev), | ||
176 | err_description, | ||
177 | master_name, target_name, | ||
178 | err_string); | ||
179 | /* clear the std error log*/ | ||
180 | clear = std_err_main | CLEAR_STDERR_LOG; | ||
181 | writel_relaxed(clear, l3_targ_stderr); | ||
182 | |||
183 | /* Error found so break the for loop */ | 198 | /* Error found so break the for loop */ |
184 | break; | 199 | break; |
185 | } | 200 | } |