| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: exynos_combiner.c,v 1.6 2016/01/05 21:53:48 marty Exp $ */ | | 1 | /* $NetBSD: exynos_combiner.c,v 1.7 2017/06/11 16:19:27 jmcneill Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2015 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 2015 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation | | 7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Marty Fouts | | 8 | * by Marty Fouts |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
| @@ -24,27 +24,27 @@ | | | @@ -24,27 +24,27 @@ |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. | | 29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | | | 31 | |
32 | #include "opt_exynos.h" | | 32 | #include "opt_exynos.h" |
33 | #include "opt_arm_debug.h" | | 33 | #include "opt_arm_debug.h" |
34 | #include "gpio.h" | | 34 | #include "gpio.h" |
35 | | | 35 | |
36 | #include <sys/cdefs.h> | | 36 | #include <sys/cdefs.h> |
37 | __KERNEL_RCSID(1, "$NetBSD: exynos_combiner.c,v 1.6 2016/01/05 21:53:48 marty Exp $"); | | 37 | __KERNEL_RCSID(1, "$NetBSD: exynos_combiner.c,v 1.7 2017/06/11 16:19:27 jmcneill Exp $"); |
38 | | | 38 | |
39 | #include <sys/param.h> | | 39 | #include <sys/param.h> |
40 | #include <sys/bus.h> | | 40 | #include <sys/bus.h> |
41 | #include <sys/device.h> | | 41 | #include <sys/device.h> |
42 | #include <sys/intr.h> | | 42 | #include <sys/intr.h> |
43 | #include <sys/systm.h> | | 43 | #include <sys/systm.h> |
44 | #include <sys/kmem.h> | | 44 | #include <sys/kmem.h> |
45 | | | 45 | |
46 | #include <arm/cortex/gic_intr.h> | | 46 | #include <arm/cortex/gic_intr.h> |
47 | | | 47 | |
48 | #include <arm/samsung/exynos_reg.h> | | 48 | #include <arm/samsung/exynos_reg.h> |
49 | #include <arm/samsung/exynos_intr.h> | | 49 | #include <arm/samsung/exynos_intr.h> |
50 | | | 50 | |
| @@ -56,33 +56,35 @@ __KERNEL_RCSID(1, "$NetBSD: exynos_combi | | | @@ -56,33 +56,35 @@ __KERNEL_RCSID(1, "$NetBSD: exynos_combi |
56 | #define COMBINER_IMSR_OFFSET 0x0C | | 56 | #define COMBINER_IMSR_OFFSET 0x0C |
57 | #define COMBINER_GROUP_SIZE 0x10 | | 57 | #define COMBINER_GROUP_SIZE 0x10 |
58 | #define COMBINER_IRQS_PER_BLOCK 8 | | 58 | #define COMBINER_IRQS_PER_BLOCK 8 |
59 | #define COMBINER_BLOCKS_PER_GROUP 4 | | 59 | #define COMBINER_BLOCKS_PER_GROUP 4 |
60 | #define COMBINER_N_BLOCKS 32 | | 60 | #define COMBINER_N_BLOCKS 32 |
61 | | | 61 | |
62 | struct exynos_combiner_softc; | | 62 | struct exynos_combiner_softc; |
63 | | | 63 | |
64 | struct exynos_combiner_irq_entry { | | 64 | struct exynos_combiner_irq_entry { |
65 | int irq_no; | | 65 | int irq_no; |
66 | int (*irq_handler)(void *); | | 66 | int (*irq_handler)(void *); |
67 | void * irq_arg; | | 67 | void * irq_arg; |
68 | struct exynos_combiner_irq_entry *irq_next; | | 68 | struct exynos_combiner_irq_entry *irq_next; |
| | | 69 | bool irq_mpsafe; |
69 | }; | | 70 | }; |
70 | | | 71 | |
71 | struct exynos_combiner_irq_block { | | 72 | struct exynos_combiner_irq_block { |
72 | int irq_block_no; | | 73 | int irq_block_no; |
73 | struct exynos_combiner_softc *irq_sc; | | 74 | struct exynos_combiner_softc *irq_sc; |
74 | struct exynos_combiner_irq_entry *irq_entries; | | 75 | struct exynos_combiner_irq_entry *irq_entries; |
75 | struct exynos_combiner_irq_block *irq_block_next; | | 76 | struct exynos_combiner_irq_block *irq_block_next; |
| | | 77 | void *irq_ih; |
76 | }; | | 78 | }; |
77 | | | 79 | |
78 | struct exynos_combiner_softc { | | 80 | struct exynos_combiner_softc { |
79 | device_t sc_dev; | | 81 | device_t sc_dev; |
80 | bus_space_tag_t sc_bst; | | 82 | bus_space_tag_t sc_bst; |
81 | bus_space_handle_t sc_bsh; | | 83 | bus_space_handle_t sc_bsh; |
82 | int sc_phandle; | | 84 | int sc_phandle; |
83 | struct exynos_combiner_irq_block *irq_blocks; | | 85 | struct exynos_combiner_irq_block *irq_blocks; |
84 | }; | | 86 | }; |
85 | | | 87 | |
86 | static int exynos_combiner_match(device_t, cfdata_t, void *); | | 88 | static int exynos_combiner_match(device_t, cfdata_t, void *); |
87 | static void exynos_combiner_attach(device_t, device_t, void *); | | 89 | static void exynos_combiner_attach(device_t, device_t, void *); |
88 | | | 90 | |
| @@ -163,116 +165,124 @@ exynos_combiner_new_block(struct exynos_ | | | @@ -163,116 +165,124 @@ exynos_combiner_new_block(struct exynos_ |
163 | static struct exynos_combiner_irq_block * | | 165 | static struct exynos_combiner_irq_block * |
164 | exynos_combiner_get_block(struct exynos_combiner_softc *sc, int block) | | 166 | exynos_combiner_get_block(struct exynos_combiner_softc *sc, int block) |
165 | { | | 167 | { |
166 | for (struct exynos_combiner_irq_block *b = sc->irq_blocks; | | 168 | for (struct exynos_combiner_irq_block *b = sc->irq_blocks; |
167 | b; b = b->irq_block_next) { | | 169 | b; b = b->irq_block_next) { |
168 | if (b->irq_block_no == block) | | 170 | if (b->irq_block_no == block) |
169 | return b; | | 171 | return b; |
170 | } | | 172 | } |
171 | return NULL; | | 173 | return NULL; |
172 | } | | 174 | } |
173 | | | 175 | |
174 | static struct exynos_combiner_irq_entry * | | 176 | static struct exynos_combiner_irq_entry * |
175 | exynos_combiner_new_irq(struct exynos_combiner_irq_block *block, | | 177 | exynos_combiner_new_irq(struct exynos_combiner_irq_block *block, |
176 | int irq, int (*func)(void *), void *arg) | | 178 | int irq, bool mpsafe, int (*func)(void *), void *arg) |
177 | { | | 179 | { |
178 | struct exynos_combiner_irq_entry * n = kmem_zalloc(sizeof(*n), | | 180 | struct exynos_combiner_irq_entry * n = kmem_zalloc(sizeof(*n), |
179 | KM_SLEEP); | | 181 | KM_SLEEP); |
180 | n->irq_no = irq; | | 182 | n->irq_no = irq; |
181 | n->irq_handler = func; | | 183 | n->irq_handler = func; |
182 | n->irq_next = block->irq_entries; | | 184 | n->irq_next = block->irq_entries; |
183 | n->irq_arg = arg; | | 185 | n->irq_arg = arg; |
| | | 186 | n->irq_mpsafe = mpsafe; |
184 | block->irq_entries = n; | | 187 | block->irq_entries = n; |
185 | return n; | | 188 | return n; |
186 | } | | 189 | } |
187 | | | 190 | |
188 | static struct exynos_combiner_irq_entry * | | 191 | static struct exynos_combiner_irq_entry * |
189 | exynos_combiner_get_irq(struct exynos_combiner_irq_block *b, int irq) | | 192 | exynos_combiner_get_irq(struct exynos_combiner_irq_block *b, int irq) |
190 | { | | 193 | { |
191 | for (struct exynos_combiner_irq_entry *p = b->irq_entries; p; | | 194 | for (struct exynos_combiner_irq_entry *p = b->irq_entries; p; |
192 | p = p->irq_next) { | | 195 | p = p->irq_next) { |
193 | if (p->irq_no == irq) | | 196 | if (p->irq_no == irq) |
194 | return p; | | 197 | return p; |
195 | } | | 198 | } |
196 | return NULL; | | 199 | return NULL; |
197 | } | | 200 | } |
198 | | | 201 | |
199 | static int exynos_combiner_irq(void *cookie) | | 202 | static int |
| | | 203 | exynos_combiner_irq(void *cookie) |
200 | { | | 204 | { |
201 | struct exynos_combiner_irq_block *blockp = cookie; | | 205 | struct exynos_combiner_irq_block *blockp = cookie; |
202 | struct exynos_combiner_softc *sc = blockp->irq_sc; | | 206 | struct exynos_combiner_softc *sc = blockp->irq_sc; |
203 | int intr = blockp->irq_block_no; | | 207 | int intr = blockp->irq_block_no; |
204 | int iblock = | | 208 | int iblock = |
205 | intr / COMBINER_BLOCKS_PER_GROUP * COMBINER_GROUP_SIZE | | 209 | intr / COMBINER_BLOCKS_PER_GROUP * COMBINER_GROUP_SIZE |
206 | + COMBINER_IESR_OFFSET; | | 210 | + COMBINER_IESR_OFFSET; |
207 | int istatus = | | 211 | int istatus = |
208 | bus_space_read_4(sc->sc_bst, sc->sc_bsh, iblock); | | 212 | bus_space_read_4(sc->sc_bst, sc->sc_bsh, iblock); |
209 | istatus >>= (intr % 4) *8; | | 213 | istatus >>= (intr % 4) *8; |
210 | for (int irq = 0; irq < 8; irq++) { | | 214 | for (int irq = 0; irq < 8; irq++) { |
211 | if (istatus & 1 << irq) { | | 215 | if (istatus & 1 << irq) { |
212 | struct exynos_combiner_irq_entry *e = | | 216 | struct exynos_combiner_irq_entry *e = |
213 | exynos_combiner_get_irq(blockp, irq); | | 217 | exynos_combiner_get_irq(blockp, irq); |
214 | if (e) | | 218 | if (e) { |
| | | 219 | if (!e->irq_mpsafe) |
| | | 220 | KERNEL_LOCK(1, curlwp); |
215 | e->irq_handler(e->irq_arg); | | 221 | e->irq_handler(e->irq_arg); |
216 | else | | 222 | if (!e->irq_mpsafe) |
| | | 223 | KERNEL_UNLOCK_ONE(curlwp); |
| | | 224 | } else |
217 | printf("%s: Unexpected irq %d, %d\n", __func__, | | 225 | printf("%s: Unexpected irq %d, %d\n", __func__, |
218 | intr, irq); | | 226 | intr, irq); |
219 | } | | 227 | } |
220 | } | | 228 | } |
221 | return 0; | | 229 | return 0; |
222 | } | | 230 | } |
223 | | | 231 | |
224 | static void * | | 232 | static void * |
225 | exynos_combiner_establish(device_t dev, u_int *specifier, | | 233 | exynos_combiner_establish(device_t dev, u_int *specifier, |
226 | int ipl, int flags, | | 234 | int ipl, int flags, |
227 | int (*func)(void *), void *arg) | | 235 | int (*func)(void *), void *arg) |
228 | { | | 236 | { |
229 | struct exynos_combiner_softc * const sc = device_private(dev); | | 237 | struct exynos_combiner_softc * const sc = device_private(dev); |
230 | struct exynos_combiner_irq_block *blockp; | | 238 | struct exynos_combiner_irq_block *blockp; |
231 | struct exynos_combiner_irq_entry *entryp; | | 239 | struct exynos_combiner_irq_entry *entryp; |
| | | 240 | const bool mpsafe = (flags & FDT_INTR_MPSAFE) != 0; |
232 | | | 241 | |
233 | const u_int intr = be32toh(specifier[0]); | | 242 | const u_int intr = be32toh(specifier[0]); |
234 | const u_int irq = be32toh(specifier[1]); | | 243 | const u_int irq = be32toh(specifier[1]); |
235 | | | 244 | |
236 | int iblock = | | 245 | int iblock = |
237 | intr / COMBINER_BLOCKS_PER_GROUP * COMBINER_GROUP_SIZE | | 246 | intr / COMBINER_BLOCKS_PER_GROUP * COMBINER_GROUP_SIZE |
238 | + COMBINER_IESR_OFFSET; | | 247 | + COMBINER_IESR_OFFSET; |
239 | | | 248 | |
240 | blockp = exynos_combiner_get_block(sc, intr); | | 249 | blockp = exynos_combiner_get_block(sc, intr); |
241 | if (!blockp) { | | 250 | if (!blockp) { |
242 | blockp = exynos_combiner_new_block(sc, intr); | | 251 | blockp = exynos_combiner_new_block(sc, intr); |
243 | KASSERT(blockp); | | 252 | KASSERT(blockp); |
244 | intr_establish(intr, ipl, IST_LEVEL, exynos_combiner_irq, | | 253 | blockp->irq_ih = fdtbus_intr_establish(sc->sc_phandle, intr, |
245 | blockp); | | 254 | IPL_VM /* XXX */, FDT_INTR_MPSAFE, exynos_combiner_irq, |
| | | 255 | blockp); |
246 | } | | 256 | } |
247 | | | 257 | |
248 | entryp = exynos_combiner_get_irq(blockp, irq); | | 258 | entryp = exynos_combiner_get_irq(blockp, irq); |
249 | if (entryp) | | 259 | if (entryp) |
250 | return NULL; | | 260 | return NULL; |
251 | entryp = exynos_combiner_new_irq(blockp, irq, func, arg); | | 261 | entryp = exynos_combiner_new_irq(blockp, irq, mpsafe, func, arg); |
252 | KASSERT(entryp); | | 262 | KASSERT(entryp); |
253 | | | 263 | |
254 | int istatus = | | 264 | int istatus = |
255 | bus_space_read_4(sc->sc_bst, sc->sc_bsh, iblock); | | 265 | bus_space_read_4(sc->sc_bst, sc->sc_bsh, iblock); |
256 | istatus |= 1 << (irq + ((intr % 4) * 8)); | | 266 | istatus |= 1 << (irq + ((intr % 4) * 8)); |
257 | bus_space_write_4(sc->sc_bst, sc->sc_bsh, iblock, istatus); | | 267 | bus_space_write_4(sc->sc_bst, sc->sc_bsh, iblock, istatus); |
258 | return (void *)istatus; | | 268 | return (void *)istatus; |
259 | } | | 269 | } |
260 | | | 270 | |
261 | static void | | 271 | static void |
262 | exynos_combiner_disestablish(device_t dev, void *ih) | | 272 | exynos_combiner_disestablish(device_t dev, void *ih) |
263 | { | | 273 | { |
264 | /* MJF: Find the ih and disable the handler. */ | | 274 | /* MJF: Find the ih and disable the handler. */ |
265 | intr_disestablish(ih); | | 275 | panic("exynos_combiner_disestablish not implemented"); |
266 | } | | 276 | } |
267 | | | 277 | |
268 | static bool | | 278 | static bool |
269 | exynos_combiner_intrstr(device_t dev, u_int *specifier, char *buf, | | 279 | exynos_combiner_intrstr(device_t dev, u_int *specifier, char *buf, |
270 | size_t buflen) | | 280 | size_t buflen) |
271 | { | | 281 | { |
272 | | | 282 | |
273 | /* 1st cell is the interrupt block */ | | 283 | /* 1st cell is the interrupt block */ |
274 | /* 2nd cell is the interrupt number */ | | 284 | /* 2nd cell is the interrupt number */ |
275 | | | 285 | |
276 | const u_int intr = be32toh(specifier[0]); | | 286 | const u_int intr = be32toh(specifier[0]); |
277 | const u_int irq = be32toh(specifier[1]); | | 287 | const u_int irq = be32toh(specifier[1]); |
278 | | | 288 | |