| @@ -1,488 +1,488 @@ | | | @@ -1,488 +1,488 @@ |
1 | /* $NetBSD: fdt_intr.c,v 1.25 2020/02/16 20:28:18 thorpej Exp $ */ | | 1 | /* $NetBSD: fdt_intr.c,v 1.26 2020/03/08 08:25:36 skrll Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2015-2018 Jared McNeill <jmcneill@invisible.ca> | | 4 | * Copyright (c) 2015-2018 Jared McNeill <jmcneill@invisible.ca> |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
15 | * | | 15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | | 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | | 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | | 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | | 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | | 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | | 21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | | 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | | 23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 | * SUCH DAMAGE. | | 26 | * SUCH DAMAGE. |
27 | */ | | 27 | */ |
28 | | | 28 | |
29 | #include <sys/cdefs.h> | | 29 | #include <sys/cdefs.h> |
30 | __KERNEL_RCSID(0, "$NetBSD: fdt_intr.c,v 1.25 2020/02/16 20:28:18 thorpej Exp $"); | | 30 | __KERNEL_RCSID(0, "$NetBSD: fdt_intr.c,v 1.26 2020/03/08 08:25:36 skrll Exp $"); |
31 | | | 31 | |
32 | #include <sys/param.h> | | 32 | #include <sys/param.h> |
33 | #include <sys/atomic.h> | | 33 | #include <sys/atomic.h> |
34 | #include <sys/bus.h> | | 34 | #include <sys/bus.h> |
35 | #include <sys/kmem.h> | | 35 | #include <sys/kmem.h> |
36 | #include <sys/queue.h> | | 36 | #include <sys/queue.h> |
37 | #include <sys/mutex.h> | | 37 | #include <sys/mutex.h> |
38 | #include <sys/condvar.h> | | 38 | #include <sys/condvar.h> |
39 | | | 39 | |
40 | #include <libfdt.h> | | 40 | #include <libfdt.h> |
41 | #include <dev/fdt/fdtvar.h> | | 41 | #include <dev/fdt/fdtvar.h> |
42 | #include <dev/fdt/fdt_private.h> | | 42 | #include <dev/fdt/fdt_private.h> |
43 | | | 43 | |
44 | struct fdtbus_interrupt_controller { | | 44 | struct fdtbus_interrupt_controller { |
45 | device_t ic_dev; | | 45 | device_t ic_dev; |
46 | int ic_phandle; | | 46 | int ic_phandle; |
47 | const struct fdtbus_interrupt_controller_func *ic_funcs; | | 47 | const struct fdtbus_interrupt_controller_func *ic_funcs; |
48 | | | 48 | |
49 | LIST_ENTRY(fdtbus_interrupt_controller) ic_next; | | 49 | LIST_ENTRY(fdtbus_interrupt_controller) ic_next; |
50 | }; | | 50 | }; |
51 | | | 51 | |
52 | static LIST_HEAD(, fdtbus_interrupt_controller) fdtbus_interrupt_controllers = | | 52 | static LIST_HEAD(, fdtbus_interrupt_controller) fdtbus_interrupt_controllers = |
53 | LIST_HEAD_INITIALIZER(fdtbus_interrupt_controllers); | | 53 | LIST_HEAD_INITIALIZER(fdtbus_interrupt_controllers); |
54 | | | 54 | |
55 | struct fdtbus_interrupt_cookie { | | 55 | struct fdtbus_interrupt_cookie { |
56 | struct fdtbus_interrupt_controller *c_ic; | | 56 | struct fdtbus_interrupt_controller *c_ic; |
57 | void *c_ih; | | 57 | void *c_ih; |
58 | | | 58 | |
59 | LIST_ENTRY(fdtbus_interrupt_cookie) c_next; | | 59 | LIST_ENTRY(fdtbus_interrupt_cookie) c_next; |
60 | uint32_t c_refcnt; | | 60 | uint32_t c_refcnt; |
61 | }; | | 61 | }; |
62 | | | 62 | |
63 | static LIST_HEAD(, fdtbus_interrupt_cookie) fdtbus_interrupt_cookies = | | 63 | static LIST_HEAD(, fdtbus_interrupt_cookie) fdtbus_interrupt_cookies = |
64 | LIST_HEAD_INITIALIZER(fdtbus_interrupt_cookies); | | 64 | LIST_HEAD_INITIALIZER(fdtbus_interrupt_cookies); |
65 | static kmutex_t fdtbus_interrupt_cookie_mutex; | | 65 | static kmutex_t fdtbus_interrupt_cookie_mutex; |
66 | static kcondvar_t fdtbus_interrupt_cookie_wait; | | 66 | static kcondvar_t fdtbus_interrupt_cookie_wait; |
67 | static bool fdtbus_interrupt_cookies_wanted; | | 67 | static bool fdtbus_interrupt_cookies_wanted; |
68 | | | 68 | |
69 | static const u_int * get_specifier_by_index(int, int, int *); | | 69 | static const u_int * get_specifier_by_index(int, int, int *); |
70 | static const u_int * get_specifier_from_map(int, const u_int *, int *); | | 70 | static const u_int * get_specifier_from_map(int, const u_int *, int *); |
71 | | | 71 | |
72 | void | | 72 | void |
73 | fdtbus_intr_init(void) | | 73 | fdtbus_intr_init(void) |
74 | { | | 74 | { |
75 | | | 75 | |
76 | mutex_init(&fdtbus_interrupt_cookie_mutex, MUTEX_SPIN, IPL_HIGH); | | 76 | mutex_init(&fdtbus_interrupt_cookie_mutex, MUTEX_DEFAULT, IPL_HIGH); |
77 | cv_init(&fdtbus_interrupt_cookie_wait, "fdtintr"); | | 77 | cv_init(&fdtbus_interrupt_cookie_wait, "fdtintr"); |
78 | } | | 78 | } |
79 | | | 79 | |
80 | /* | | 80 | /* |
81 | * Find the interrupt controller for a given node. This function will either | | 81 | * Find the interrupt controller for a given node. This function will either |
82 | * return the phandle of the interrupt controller for this node, or the phandle | | 82 | * return the phandle of the interrupt controller for this node, or the phandle |
83 | * of a node containing an interrupt-map table that can be used to find the | | 83 | * of a node containing an interrupt-map table that can be used to find the |
84 | * real interrupt controller. | | 84 | * real interrupt controller. |
85 | */ | | 85 | */ |
86 | static int | | 86 | static int |
87 | fdtbus_get_interrupt_parent(int phandle) | | 87 | fdtbus_get_interrupt_parent(int phandle) |
88 | { | | 88 | { |
89 | int iparent = phandle; | | 89 | int iparent = phandle; |
90 | | | 90 | |
91 | do { | | 91 | do { |
92 | /* | | 92 | /* |
93 | * If the node is an interrupt-controller, we are done. Note that | | 93 | * If the node is an interrupt-controller, we are done. Note that |
94 | * a node cannot be an interrupt-controller for itself, so we skip | | 94 | * a node cannot be an interrupt-controller for itself, so we skip |
95 | * the leaf node here. | | 95 | * the leaf node here. |
96 | */ | | 96 | */ |
97 | if (phandle != iparent && of_hasprop(iparent, "interrupt-controller")) | | 97 | if (phandle != iparent && of_hasprop(iparent, "interrupt-controller")) |
98 | return iparent; | | 98 | return iparent; |
99 | | | 99 | |
100 | /* | | 100 | /* |
101 | * If the node has an explicit interrupt-parent, follow the reference. | | 101 | * If the node has an explicit interrupt-parent, follow the reference. |
102 | */ | | 102 | */ |
103 | if (of_hasprop(iparent, "interrupt-parent")) | | 103 | if (of_hasprop(iparent, "interrupt-parent")) |
104 | return fdtbus_get_phandle(iparent, "interrupt-parent"); | | 104 | return fdtbus_get_phandle(iparent, "interrupt-parent"); |
105 | | | 105 | |
106 | /* | | 106 | /* |
107 | * If the node has an interrupt-map, use it. The caller is responsible | | 107 | * If the node has an interrupt-map, use it. The caller is responsible |
108 | * for parsing the interrupt-map and finding the real interrupt parent. | | 108 | * for parsing the interrupt-map and finding the real interrupt parent. |
109 | */ | | 109 | */ |
110 | if (phandle != iparent && of_hasprop(iparent, "interrupt-map")) | | 110 | if (phandle != iparent && of_hasprop(iparent, "interrupt-map")) |
111 | return iparent; | | 111 | return iparent; |
112 | | | 112 | |
113 | /* | | 113 | /* |
114 | * Continue searching up the tree. | | 114 | * Continue searching up the tree. |
115 | */ | | 115 | */ |
116 | iparent = OF_parent(iparent); | | 116 | iparent = OF_parent(iparent); |
117 | } while (iparent > 0); | | 117 | } while (iparent > 0); |
118 | | | 118 | |
119 | return 0; | | 119 | return 0; |
120 | } | | 120 | } |
121 | | | 121 | |
122 | static struct fdtbus_interrupt_controller * | | 122 | static struct fdtbus_interrupt_controller * |
123 | fdtbus_get_interrupt_controller(int phandle) | | 123 | fdtbus_get_interrupt_controller(int phandle) |
124 | { | | 124 | { |
125 | struct fdtbus_interrupt_controller * ic; | | 125 | struct fdtbus_interrupt_controller * ic; |
126 | LIST_FOREACH(ic, &fdtbus_interrupt_controllers, ic_next) { | | 126 | LIST_FOREACH(ic, &fdtbus_interrupt_controllers, ic_next) { |
127 | if (ic->ic_phandle == phandle) | | 127 | if (ic->ic_phandle == phandle) |
128 | return ic; | | 128 | return ic; |
129 | } | | 129 | } |
130 | return NULL; | | 130 | return NULL; |
131 | } | | 131 | } |
132 | | | 132 | |
133 | int | | 133 | int |
134 | fdtbus_register_interrupt_controller(device_t dev, int phandle, | | 134 | fdtbus_register_interrupt_controller(device_t dev, int phandle, |
135 | const struct fdtbus_interrupt_controller_func *funcs) | | 135 | const struct fdtbus_interrupt_controller_func *funcs) |
136 | { | | 136 | { |
137 | struct fdtbus_interrupt_controller *ic; | | 137 | struct fdtbus_interrupt_controller *ic; |
138 | | | 138 | |
139 | ic = kmem_alloc(sizeof(*ic), KM_SLEEP); | | 139 | ic = kmem_alloc(sizeof(*ic), KM_SLEEP); |
140 | ic->ic_dev = dev; | | 140 | ic->ic_dev = dev; |
141 | ic->ic_phandle = phandle; | | 141 | ic->ic_phandle = phandle; |
142 | ic->ic_funcs = funcs; | | 142 | ic->ic_funcs = funcs; |
143 | | | 143 | |
144 | LIST_INSERT_HEAD(&fdtbus_interrupt_controllers, ic, ic_next); | | 144 | LIST_INSERT_HEAD(&fdtbus_interrupt_controllers, ic, ic_next); |
145 | | | 145 | |
146 | return 0; | | 146 | return 0; |
147 | } | | 147 | } |
148 | | | 148 | |
149 | static struct fdtbus_interrupt_cookie * | | 149 | static struct fdtbus_interrupt_cookie * |
150 | fdtbus_get_interrupt_cookie(void *cookie) | | 150 | fdtbus_get_interrupt_cookie(void *cookie) |
151 | { | | 151 | { |
152 | struct fdtbus_interrupt_cookie *c; | | 152 | struct fdtbus_interrupt_cookie *c; |
153 | | | 153 | |
154 | mutex_enter(&fdtbus_interrupt_cookie_mutex); | | 154 | mutex_enter(&fdtbus_interrupt_cookie_mutex); |
155 | LIST_FOREACH(c, &fdtbus_interrupt_cookies, c_next) { | | 155 | LIST_FOREACH(c, &fdtbus_interrupt_cookies, c_next) { |
156 | if (c->c_ih == cookie) { | | 156 | if (c->c_ih == cookie) { |
157 | c->c_refcnt++; | | 157 | c->c_refcnt++; |
158 | KASSERT(c->c_refcnt > 0); | | 158 | KASSERT(c->c_refcnt > 0); |
159 | break; | | 159 | break; |
160 | } | | 160 | } |
161 | } | | 161 | } |
162 | mutex_exit(&fdtbus_interrupt_cookie_mutex); | | 162 | mutex_exit(&fdtbus_interrupt_cookie_mutex); |
163 | return c; | | 163 | return c; |
164 | } | | 164 | } |
165 | | | 165 | |
166 | static void | | 166 | static void |
167 | fdtbus_put_interrupt_cookie(struct fdtbus_interrupt_cookie *c) | | 167 | fdtbus_put_interrupt_cookie(struct fdtbus_interrupt_cookie *c) |
168 | { | | 168 | { |
169 | | | 169 | |
170 | mutex_enter(&fdtbus_interrupt_cookie_mutex); | | 170 | mutex_enter(&fdtbus_interrupt_cookie_mutex); |
171 | KASSERT(c->c_refcnt > 0); | | 171 | KASSERT(c->c_refcnt > 0); |
172 | c->c_refcnt--; | | 172 | c->c_refcnt--; |
173 | if (fdtbus_interrupt_cookies_wanted) { | | 173 | if (fdtbus_interrupt_cookies_wanted) { |
174 | fdtbus_interrupt_cookies_wanted = false; | | 174 | fdtbus_interrupt_cookies_wanted = false; |
175 | cv_signal(&fdtbus_interrupt_cookie_wait); | | 175 | cv_signal(&fdtbus_interrupt_cookie_wait); |
176 | } | | 176 | } |
177 | mutex_exit(&fdtbus_interrupt_cookie_mutex); | | 177 | mutex_exit(&fdtbus_interrupt_cookie_mutex); |
178 | } | | 178 | } |
179 | | | 179 | |
180 | void * | | 180 | void * |
181 | fdtbus_intr_establish(int phandle, u_int index, int ipl, int flags, | | 181 | fdtbus_intr_establish(int phandle, u_int index, int ipl, int flags, |
182 | int (*func)(void *), void *arg) | | 182 | int (*func)(void *), void *arg) |
183 | { | | 183 | { |
184 | const u_int *specifier; | | 184 | const u_int *specifier; |
185 | int ihandle; | | 185 | int ihandle; |
186 | | | 186 | |
187 | specifier = get_specifier_by_index(phandle, index, &ihandle); | | 187 | specifier = get_specifier_by_index(phandle, index, &ihandle); |
188 | if (specifier == NULL) | | 188 | if (specifier == NULL) |
189 | return NULL; | | 189 | return NULL; |
190 | | | 190 | |
191 | return fdtbus_intr_establish_raw(ihandle, specifier, ipl, | | 191 | return fdtbus_intr_establish_raw(ihandle, specifier, ipl, |
192 | flags, func, arg); | | 192 | flags, func, arg); |
193 | } | | 193 | } |
194 | | | 194 | |
195 | void * | | 195 | void * |
196 | fdtbus_intr_establish_byname(int phandle, const char *name, int ipl, | | 196 | fdtbus_intr_establish_byname(int phandle, const char *name, int ipl, |
197 | int flags, int (*func)(void *), void *arg) | | 197 | int flags, int (*func)(void *), void *arg) |
198 | { | | 198 | { |
199 | u_int index; | | 199 | u_int index; |
200 | int err; | | 200 | int err; |
201 | | | 201 | |
202 | err = fdtbus_get_index(phandle, "interrupt-names", name, &index); | | 202 | err = fdtbus_get_index(phandle, "interrupt-names", name, &index); |
203 | if (err != 0) | | 203 | if (err != 0) |
204 | return NULL; | | 204 | return NULL; |
205 | | | 205 | |
206 | return fdtbus_intr_establish(phandle, index, ipl, flags, func, arg); | | 206 | return fdtbus_intr_establish(phandle, index, ipl, flags, func, arg); |
207 | } | | 207 | } |
208 | | | 208 | |
209 | void * | | 209 | void * |
210 | fdtbus_intr_establish_raw(int ihandle, const u_int *specifier, int ipl, | | 210 | fdtbus_intr_establish_raw(int ihandle, const u_int *specifier, int ipl, |
211 | int flags, int (*func)(void *), void *arg) | | 211 | int flags, int (*func)(void *), void *arg) |
212 | { | | 212 | { |
213 | struct fdtbus_interrupt_controller *ic; | | 213 | struct fdtbus_interrupt_controller *ic; |
214 | struct fdtbus_interrupt_cookie *c; | | 214 | struct fdtbus_interrupt_cookie *c; |
215 | void *ih; | | 215 | void *ih; |
216 | | | 216 | |
217 | ic = fdtbus_get_interrupt_controller(ihandle); | | 217 | ic = fdtbus_get_interrupt_controller(ihandle); |
218 | if (ic == NULL) { | | 218 | if (ic == NULL) { |
219 | printf("%s: ihandle %d is not a controller\n",__func__,ihandle); | | 219 | printf("%s: ihandle %d is not a controller\n",__func__,ihandle); |
220 | return NULL; | | 220 | return NULL; |
221 | } | | 221 | } |
222 | | | 222 | |
223 | c = kmem_zalloc(sizeof(*c), KM_SLEEP); | | 223 | c = kmem_zalloc(sizeof(*c), KM_SLEEP); |
224 | c->c_ic = ic; | | 224 | c->c_ic = ic; |
225 | mutex_enter(&fdtbus_interrupt_cookie_mutex); | | 225 | mutex_enter(&fdtbus_interrupt_cookie_mutex); |
226 | LIST_INSERT_HEAD(&fdtbus_interrupt_cookies, c, c_next); | | 226 | LIST_INSERT_HEAD(&fdtbus_interrupt_cookies, c, c_next); |
227 | mutex_exit(&fdtbus_interrupt_cookie_mutex); | | 227 | mutex_exit(&fdtbus_interrupt_cookie_mutex); |
228 | | | 228 | |
229 | /* | | 229 | /* |
230 | * XXX This leaves a small window where the handler is registered | | 230 | * XXX This leaves a small window where the handler is registered |
231 | * (and thus could be called) before the cookie on the list has a | | 231 | * (and thus could be called) before the cookie on the list has a |
232 | * valid lookup key (and thus can be found). This will cause a | | 232 | * valid lookup key (and thus can be found). This will cause a |
233 | * panic in fdt_intr_mask() if that is called from the handler before | | 233 | * panic in fdt_intr_mask() if that is called from the handler before |
234 | * this situation is resolved. For now we just cross our fingers | | 234 | * this situation is resolved. For now we just cross our fingers |
235 | * and hope that the device won't actually interrupt until we return. | | 235 | * and hope that the device won't actually interrupt until we return. |
236 | */ | | 236 | */ |
237 | ih = ic->ic_funcs->establish(ic->ic_dev, __UNCONST(specifier), | | 237 | ih = ic->ic_funcs->establish(ic->ic_dev, __UNCONST(specifier), |
238 | ipl, flags, func, arg); | | 238 | ipl, flags, func, arg); |
239 | if (ih != NULL) { | | 239 | if (ih != NULL) { |
240 | atomic_store_release(&c->c_ih, ih); | | 240 | atomic_store_release(&c->c_ih, ih); |
241 | } else { | | 241 | } else { |
242 | mutex_enter(&fdtbus_interrupt_cookie_mutex); | | 242 | mutex_enter(&fdtbus_interrupt_cookie_mutex); |
243 | LIST_REMOVE(c, c_next); | | 243 | LIST_REMOVE(c, c_next); |
244 | mutex_exit(&fdtbus_interrupt_cookie_mutex); | | 244 | mutex_exit(&fdtbus_interrupt_cookie_mutex); |
245 | kmem_free(c, sizeof(*c)); | | 245 | kmem_free(c, sizeof(*c)); |
246 | } | | 246 | } |
247 | | | 247 | |
248 | return ih; | | 248 | return ih; |
249 | } | | 249 | } |
250 | | | 250 | |
251 | void | | 251 | void |
252 | fdtbus_intr_disestablish(int phandle, void *cookie) | | 252 | fdtbus_intr_disestablish(int phandle, void *cookie) |
253 | { | | 253 | { |
254 | struct fdtbus_interrupt_cookie *c; | | 254 | struct fdtbus_interrupt_cookie *c; |
255 | | | 255 | |
256 | if ((c = fdtbus_get_interrupt_cookie(cookie)) == NULL) { | | 256 | if ((c = fdtbus_get_interrupt_cookie(cookie)) == NULL) { |
257 | panic("%s: interrupt handle not valid", __func__); | | 257 | panic("%s: interrupt handle not valid", __func__); |
258 | } | | 258 | } |
259 | | | 259 | |
260 | const struct fdtbus_interrupt_controller *ic = c->c_ic; | | 260 | const struct fdtbus_interrupt_controller *ic = c->c_ic; |
261 | ic->ic_funcs->disestablish(ic->ic_dev, cookie); | | 261 | ic->ic_funcs->disestablish(ic->ic_dev, cookie); |
262 | | | 262 | |
263 | /* | | 263 | /* |
264 | * Wait for any dangling references other than ours to | | 264 | * Wait for any dangling references other than ours to |
265 | * drain away. | | 265 | * drain away. |
266 | */ | | 266 | */ |
267 | mutex_enter(&fdtbus_interrupt_cookie_mutex); | | 267 | mutex_enter(&fdtbus_interrupt_cookie_mutex); |
268 | while (c->c_refcnt != 1) { | | 268 | while (c->c_refcnt != 1) { |
269 | KASSERT(c->c_refcnt > 0); | | 269 | KASSERT(c->c_refcnt > 0); |
270 | fdtbus_interrupt_cookies_wanted = true; | | 270 | fdtbus_interrupt_cookies_wanted = true; |
271 | cv_wait(&fdtbus_interrupt_cookie_wait, | | 271 | cv_wait(&fdtbus_interrupt_cookie_wait, |
272 | &fdtbus_interrupt_cookie_mutex); | | 272 | &fdtbus_interrupt_cookie_mutex); |
273 | } | | 273 | } |
274 | LIST_REMOVE(c, c_next); | | 274 | LIST_REMOVE(c, c_next); |
275 | mutex_exit(&fdtbus_interrupt_cookie_mutex); | | 275 | mutex_exit(&fdtbus_interrupt_cookie_mutex); |
276 | | | 276 | |
277 | kmem_free(c, sizeof(*c)); | | 277 | kmem_free(c, sizeof(*c)); |
278 | } | | 278 | } |
279 | | | 279 | |
280 | void | | 280 | void |
281 | fdtbus_intr_mask(int phandle, void *cookie) | | 281 | fdtbus_intr_mask(int phandle, void *cookie) |
282 | { | | 282 | { |
283 | struct fdtbus_interrupt_cookie *c; | | 283 | struct fdtbus_interrupt_cookie *c; |
284 | | | 284 | |
285 | if ((c = fdtbus_get_interrupt_cookie(cookie)) == NULL) { | | 285 | if ((c = fdtbus_get_interrupt_cookie(cookie)) == NULL) { |
286 | panic("%s: interrupt handle not valid", __func__); | | 286 | panic("%s: interrupt handle not valid", __func__); |
287 | } | | 287 | } |
288 | | | 288 | |
289 | struct fdtbus_interrupt_controller * const ic = c->c_ic; | | 289 | struct fdtbus_interrupt_controller * const ic = c->c_ic; |
290 | | | 290 | |
291 | if (ic->ic_funcs->mask == NULL) { | | 291 | if (ic->ic_funcs->mask == NULL) { |
292 | panic("%s: no 'mask' method for %s", __func__, | | 292 | panic("%s: no 'mask' method for %s", __func__, |
293 | device_xname(ic->ic_dev)); | | 293 | device_xname(ic->ic_dev)); |
294 | } | | 294 | } |
295 | | | 295 | |
296 | ic->ic_funcs->mask(ic->ic_dev, cookie); | | 296 | ic->ic_funcs->mask(ic->ic_dev, cookie); |
297 | fdtbus_put_interrupt_cookie(c); | | 297 | fdtbus_put_interrupt_cookie(c); |
298 | } | | 298 | } |
299 | | | 299 | |
300 | void | | 300 | void |
301 | fdtbus_intr_unmask(int phandle, void *cookie) | | 301 | fdtbus_intr_unmask(int phandle, void *cookie) |
302 | { | | 302 | { |
303 | struct fdtbus_interrupt_cookie *c; | | 303 | struct fdtbus_interrupt_cookie *c; |
304 | | | 304 | |
305 | if ((c = fdtbus_get_interrupt_cookie(cookie)) == NULL) { | | 305 | if ((c = fdtbus_get_interrupt_cookie(cookie)) == NULL) { |
306 | panic("%s: interrupt handle not valid", __func__); | | 306 | panic("%s: interrupt handle not valid", __func__); |
307 | } | | 307 | } |
308 | | | 308 | |
309 | struct fdtbus_interrupt_controller * const ic = c->c_ic; | | 309 | struct fdtbus_interrupt_controller * const ic = c->c_ic; |
310 | | | 310 | |
311 | if (ic->ic_funcs->unmask == NULL) { | | 311 | if (ic->ic_funcs->unmask == NULL) { |
312 | panic("%s: no 'unmask' method for %s", __func__, | | 312 | panic("%s: no 'unmask' method for %s", __func__, |
313 | device_xname(ic->ic_dev)); | | 313 | device_xname(ic->ic_dev)); |
314 | } | | 314 | } |
315 | | | 315 | |
316 | ic->ic_funcs->unmask(ic->ic_dev, cookie); | | 316 | ic->ic_funcs->unmask(ic->ic_dev, cookie); |
317 | fdtbus_put_interrupt_cookie(c); | | 317 | fdtbus_put_interrupt_cookie(c); |
318 | } | | 318 | } |
319 | | | 319 | |
320 | bool | | 320 | bool |
321 | fdtbus_intr_str(int phandle, u_int index, char *buf, size_t buflen) | | 321 | fdtbus_intr_str(int phandle, u_int index, char *buf, size_t buflen) |
322 | { | | 322 | { |
323 | const u_int *specifier; | | 323 | const u_int *specifier; |
324 | int ihandle; | | 324 | int ihandle; |
325 | | | 325 | |
326 | specifier = get_specifier_by_index(phandle, index, &ihandle); | | 326 | specifier = get_specifier_by_index(phandle, index, &ihandle); |
327 | if (specifier == NULL) | | 327 | if (specifier == NULL) |
328 | return false; | | 328 | return false; |
329 | | | 329 | |
330 | return fdtbus_intr_str_raw(ihandle, specifier, buf, buflen); | | 330 | return fdtbus_intr_str_raw(ihandle, specifier, buf, buflen); |
331 | } | | 331 | } |
332 | | | 332 | |
333 | bool | | 333 | bool |
334 | fdtbus_intr_str_raw(int ihandle, const u_int *specifier, char *buf, size_t buflen) | | 334 | fdtbus_intr_str_raw(int ihandle, const u_int *specifier, char *buf, size_t buflen) |
335 | { | | 335 | { |
336 | struct fdtbus_interrupt_controller *ic; | | 336 | struct fdtbus_interrupt_controller *ic; |
337 | | | 337 | |
338 | ic = fdtbus_get_interrupt_controller(ihandle); | | 338 | ic = fdtbus_get_interrupt_controller(ihandle); |
339 | if (ic == NULL) | | 339 | if (ic == NULL) |
340 | return false; | | 340 | return false; |
341 | | | 341 | |
342 | return ic->ic_funcs->intrstr(ic->ic_dev, __UNCONST(specifier), buf, buflen); | | 342 | return ic->ic_funcs->intrstr(ic->ic_dev, __UNCONST(specifier), buf, buflen); |
343 | } | | 343 | } |
344 | | | 344 | |
345 | static int | | 345 | static int |
346 | find_address_cells(int phandle) | | 346 | find_address_cells(int phandle) |
347 | { | | 347 | { |
348 | uint32_t cells; | | 348 | uint32_t cells; |
349 | | | 349 | |
350 | if (of_getprop_uint32(phandle, "#address-cells", &cells) != 0) | | 350 | if (of_getprop_uint32(phandle, "#address-cells", &cells) != 0) |
351 | cells = 2; | | 351 | cells = 2; |
352 | | | 352 | |
353 | return cells; | | 353 | return cells; |
354 | } | | 354 | } |
355 | | | 355 | |
356 | static int | | 356 | static int |
357 | find_interrupt_cells(int phandle) | | 357 | find_interrupt_cells(int phandle) |
358 | { | | 358 | { |
359 | uint32_t cells; | | 359 | uint32_t cells; |
360 | | | 360 | |
361 | while (phandle > 0) { | | 361 | while (phandle > 0) { |
362 | if (of_getprop_uint32(phandle, "#interrupt-cells", &cells) == 0) | | 362 | if (of_getprop_uint32(phandle, "#interrupt-cells", &cells) == 0) |
363 | return cells; | | 363 | return cells; |
364 | phandle = OF_parent(phandle); | | 364 | phandle = OF_parent(phandle); |
365 | } | | 365 | } |
366 | return 0; | | 366 | return 0; |
367 | } | | 367 | } |
368 | | | 368 | |
369 | static const u_int * | | 369 | static const u_int * |
370 | get_specifier_from_map(int phandle, const u_int *interrupt_spec, int *piphandle) | | 370 | get_specifier_from_map(int phandle, const u_int *interrupt_spec, int *piphandle) |
371 | { | | 371 | { |
372 | const u_int *result = NULL; | | 372 | const u_int *result = NULL; |
373 | int len, resid; | | 373 | int len, resid; |
374 | | | 374 | |
375 | const u_int *data = fdtbus_get_prop(phandle, "interrupt-map", &len); | | 375 | const u_int *data = fdtbus_get_prop(phandle, "interrupt-map", &len); |
376 | if (data == NULL || len <= 0) | | 376 | if (data == NULL || len <= 0) |
377 | return NULL; | | 377 | return NULL; |
378 | resid = len; | | 378 | resid = len; |
379 | | | 379 | |
380 | /* child unit address: #address-cells prop of child bus node */ | | 380 | /* child unit address: #address-cells prop of child bus node */ |
381 | const int cua_cells = find_address_cells(phandle); | | 381 | const int cua_cells = find_address_cells(phandle); |
382 | /* child interrupt specifier: #interrupt-cells of the nexus node */ | | 382 | /* child interrupt specifier: #interrupt-cells of the nexus node */ |
383 | const int cis_cells = find_interrupt_cells(phandle); | | 383 | const int cis_cells = find_interrupt_cells(phandle); |
384 | | | 384 | |
385 | /* Offset (in cells) from map entry to child unit address specifier */ | | 385 | /* Offset (in cells) from map entry to child unit address specifier */ |
386 | const u_int cua_off = 0; | | 386 | const u_int cua_off = 0; |
387 | /* Offset (in cells) from map entry to child interrupt specifier */ | | 387 | /* Offset (in cells) from map entry to child interrupt specifier */ |
388 | const u_int cis_off = cua_off + cua_cells; | | 388 | const u_int cis_off = cua_off + cua_cells; |
389 | /* Offset (in cells) from map entry to interrupt parent phandle */ | | 389 | /* Offset (in cells) from map entry to interrupt parent phandle */ |
390 | const u_int ip_off = cis_off + cis_cells; | | 390 | const u_int ip_off = cis_off + cis_cells; |
391 | /* Offset (in cells) from map entry to parent unit specifier */ | | 391 | /* Offset (in cells) from map entry to parent unit specifier */ |
392 | const u_int pus_off = ip_off + 1; | | 392 | const u_int pus_off = ip_off + 1; |
393 | | | 393 | |
394 | const u_int *p = (const u_int *)data; | | 394 | const u_int *p = (const u_int *)data; |
395 | while (resid > 0) { | | 395 | while (resid > 0) { |
396 | /* Interrupt parent phandle */ | | 396 | /* Interrupt parent phandle */ |
397 | const u_int iparent = fdtbus_get_phandle_from_native(be32toh(p[ip_off])); | | 397 | const u_int iparent = fdtbus_get_phandle_from_native(be32toh(p[ip_off])); |
398 | | | 398 | |
399 | /* parent unit specifier: #address-cells of the interrupt parent */ | | 399 | /* parent unit specifier: #address-cells of the interrupt parent */ |
400 | const u_int pus_cells = find_address_cells(iparent); | | 400 | const u_int pus_cells = find_address_cells(iparent); |
401 | /* parent interrupt specifier: #interrupt-cells of the interrupt parent */ | | 401 | /* parent interrupt specifier: #interrupt-cells of the interrupt parent */ |
402 | const u_int pis_cells = find_interrupt_cells(iparent); | | 402 | const u_int pis_cells = find_interrupt_cells(iparent); |
403 | | | 403 | |
404 | /* Offset (in cells) from map entry to parent interrupt specifier */ | | 404 | /* Offset (in cells) from map entry to parent interrupt specifier */ |
405 | const u_int pis_off = pus_off + pus_cells; | | 405 | const u_int pis_off = pus_off + pus_cells; |
406 | | | 406 | |
407 | #ifdef FDT_INTR_DEBUG | | 407 | #ifdef FDT_INTR_DEBUG |
408 | printf(" intr map (len %d):", pis_off + pis_cells); | | 408 | printf(" intr map (len %d):", pis_off + pis_cells); |
409 | for (int i = 0; i < pis_off + pis_cells; i++) | | 409 | for (int i = 0; i < pis_off + pis_cells; i++) |
410 | printf(" %08x", p[i]); | | 410 | printf(" %08x", p[i]); |
411 | printf("\n"); | | 411 | printf("\n"); |
412 | #endif | | 412 | #endif |
413 | | | 413 | |
414 | if (memcmp(&p[cis_off], interrupt_spec, cis_cells * 4) == 0) { | | 414 | if (memcmp(&p[cis_off], interrupt_spec, cis_cells * 4) == 0) { |
415 | #ifdef FDT_INTR_DEBUG | | 415 | #ifdef FDT_INTR_DEBUG |
416 | const int slen = pus_cells + pis_cells; | | 416 | const int slen = pus_cells + pis_cells; |
417 | printf(" intr map match iparent %08x slen %d:", iparent, slen); | | 417 | printf(" intr map match iparent %08x slen %d:", iparent, slen); |
418 | for (int i = 0; i < slen; i++) | | 418 | for (int i = 0; i < slen; i++) |
419 | printf(" %08x", p[pus_off + i]); | | 419 | printf(" %08x", p[pus_off + i]); |
420 | printf("\n"); | | 420 | printf("\n"); |
421 | #endif | | 421 | #endif |
422 | result = &p[pus_off]; | | 422 | result = &p[pus_off]; |
423 | *piphandle = iparent; | | 423 | *piphandle = iparent; |
424 | goto done; | | 424 | goto done; |
425 | } | | 425 | } |
426 | /* Determine the length of the entry and skip that many | | 426 | /* Determine the length of the entry and skip that many |
427 | * 32 bit words | | 427 | * 32 bit words |
428 | */ | | 428 | */ |
429 | const u_int reclen = pis_off + pis_cells; | | 429 | const u_int reclen = pis_off + pis_cells; |
430 | resid -= reclen * sizeof(u_int); | | 430 | resid -= reclen * sizeof(u_int); |
431 | p += reclen; | | 431 | p += reclen; |
432 | } | | 432 | } |
433 | | | 433 | |
434 | done: | | 434 | done: |
435 | return result; | | 435 | return result; |
436 | } | | 436 | } |
437 | | | 437 | |
438 | | | 438 | |
439 | static const u_int * | | 439 | static const u_int * |
440 | get_specifier_from_extended(int phandle, int pindex, int *piphandle) | | 440 | get_specifier_from_extended(int phandle, int pindex, int *piphandle) |
441 | { | | 441 | { |
442 | const u_int *result = NULL; | | 442 | const u_int *result = NULL; |
443 | struct fdt_phandle_data data; | | 443 | struct fdt_phandle_data data; |
444 | | | 444 | |
445 | if (fdtbus_get_phandle_with_data(phandle, "interrupts-extended", | | 445 | if (fdtbus_get_phandle_with_data(phandle, "interrupts-extended", |
446 | "#interrupt-cells", pindex, &data) == 0) { | | 446 | "#interrupt-cells", pindex, &data) == 0) { |
447 | *piphandle = data.phandle; | | 447 | *piphandle = data.phandle; |
448 | result = data.values; | | 448 | result = data.values; |
449 | } | | 449 | } |
450 | | | 450 | |
451 | return result; | | 451 | return result; |
452 | } | | 452 | } |
453 | | | 453 | |
454 | static const u_int * | | 454 | static const u_int * |
455 | get_specifier_by_index(int phandle, int pindex, int *piphandle) | | 455 | get_specifier_by_index(int phandle, int pindex, int *piphandle) |
456 | { | | 456 | { |
457 | const u_int *node_specifier; | | 457 | const u_int *node_specifier; |
458 | int interrupt_parent, interrupt_cells, len; | | 458 | int interrupt_parent, interrupt_cells, len; |
459 | | | 459 | |
460 | if (of_hasprop(phandle, "interrupts-extended")) | | 460 | if (of_hasprop(phandle, "interrupts-extended")) |
461 | return get_specifier_from_extended(phandle, pindex, piphandle); | | 461 | return get_specifier_from_extended(phandle, pindex, piphandle); |
462 | | | 462 | |
463 | interrupt_parent = fdtbus_get_interrupt_parent(phandle); | | 463 | interrupt_parent = fdtbus_get_interrupt_parent(phandle); |
464 | if (interrupt_parent <= 0) | | 464 | if (interrupt_parent <= 0) |
465 | return NULL; | | 465 | return NULL; |
466 | | | 466 | |
467 | node_specifier = fdtbus_get_prop(phandle, "interrupts", &len); | | 467 | node_specifier = fdtbus_get_prop(phandle, "interrupts", &len); |
468 | if (node_specifier == NULL) | | 468 | if (node_specifier == NULL) |
469 | return NULL; | | 469 | return NULL; |
470 | | | 470 | |
471 | interrupt_cells = find_interrupt_cells(interrupt_parent); | | 471 | interrupt_cells = find_interrupt_cells(interrupt_parent); |
472 | if (interrupt_cells <= 0) | | 472 | if (interrupt_cells <= 0) |
473 | return NULL; | | 473 | return NULL; |
474 | | | 474 | |
475 | const u_int spec_length = len / 4; | | 475 | const u_int spec_length = len / 4; |
476 | const u_int nintr = spec_length / interrupt_cells; | | 476 | const u_int nintr = spec_length / interrupt_cells; |
477 | if (pindex >= nintr) | | 477 | if (pindex >= nintr) |
478 | return NULL; | | 478 | return NULL; |
479 | | | 479 | |
480 | node_specifier += (interrupt_cells * pindex); | | 480 | node_specifier += (interrupt_cells * pindex); |
481 | | | 481 | |
482 | if (of_hasprop(interrupt_parent, "interrupt-map")) | | 482 | if (of_hasprop(interrupt_parent, "interrupt-map")) |
483 | return get_specifier_from_map(interrupt_parent, node_specifier, piphandle); | | 483 | return get_specifier_from_map(interrupt_parent, node_specifier, piphandle); |
484 | | | 484 | |
485 | *piphandle = interrupt_parent; | | 485 | *piphandle = interrupt_parent; |
486 | | | 486 | |
487 | return node_specifier; | | 487 | return node_specifier; |
488 | } | | 488 | } |