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