1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <sys/note.h>
26 #include <sys/sysmacros.h>
27 #include <sys/types.h>
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/kmem.h>
31 #include <sys/cmn_err.h>
32 #include <sys/debug.h>
33 #include <sys/avintr.h>
34 #include <sys/autoconf.h>
35 #include <sys/sunndi.h>
36 #include <sys/ndi_impldefs.h> /* include prototypes */
37 #include <sys/atomic.h>
38
39 /*
40 * New DDI interrupt framework
41 */
42
43 /*
44 * ddi_intr_get_supported_types:
45 * Return, as a bit mask, the hardware interrupt types supported by
46 * both the device and by the host in the integer pointed
47 * to be the 'typesp' argument.
48 */
49 int
50 ddi_intr_get_supported_types(dev_info_t *dip, int *typesp)
51 {
52 int ret;
53 ddi_intr_handle_impl_t hdl;
54
55 if (dip == NULL)
56 return (DDI_EINVAL);
57
58 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_supported_types: dip %p\n",
59 (void *)dip));
60
61 if (*typesp = i_ddi_intr_get_supported_types(dip))
62 return (DDI_SUCCESS);
63
64 bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
65 hdl.ih_dip = dip;
66
67 ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_SUPPORTED_TYPES, &hdl,
68 (void *)typesp);
69
70 if (ret != DDI_SUCCESS)
71 return (DDI_INTR_NOTFOUND);
72
73 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_supported_types: types %x\n",
74 *typesp));
75
76 return (ret);
77 }
78
79 /*
80 * ddi_intr_get_nintrs:
81 * Return as an integer in the integer pointed to by the argument
82 * *nintrsp*, the number of interrupts the device supports for the
83 * given interrupt type.
84 */
85 int
86 ddi_intr_get_nintrs(dev_info_t *dip, int type, int *nintrsp)
87 {
88 int ret;
89 ddi_intr_handle_impl_t hdl;
90
91 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_nintrs: dip %p, type: %d\n",
92 (void *)dip, type));
93
94 if ((dip == NULL) || (nintrsp == NULL) ||
95 !DDI_INTR_TYPE_FLAG_VALID(type) ||
96 !(i_ddi_intr_get_supported_types(dip) & type)) {
97 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_nintrs: "
98 "Invalid input args\n"));
99 return (DDI_EINVAL);
100 }
101
102 if (*nintrsp = i_ddi_intr_get_supported_nintrs(dip, type))
103 return (DDI_SUCCESS);
104
105 bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
106 hdl.ih_dip = dip;
107 hdl.ih_type = type;
108
109 ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_NINTRS, &hdl,
110 (void *)nintrsp);
111
112 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_nintrs:: nintrs %x\n",
113 *nintrsp));
114
115 return (ret);
116 }
117
118 /*
119 * ddi_intr_get_navail:
120 * Bus nexus driver will return availble interrupt count value for
121 * a given interrupt type.
122 *
123 * Return as an integer in the integer pointed to by the argument
124 * *navailp*, the number of interrupts currently available for the
125 * given interrupt type.
126 */
127 int
128 ddi_intr_get_navail(dev_info_t *dip, int type, int *navailp)
129 {
130 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_navail: dip %p, type: %d\n",
131 (void *)dip, type));
132
133 if ((dip == NULL) || (navailp == NULL) ||
134 !DDI_INTR_TYPE_FLAG_VALID(type) ||
135 !(i_ddi_intr_get_supported_types(dip) & type)) {
136 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_navail: "
137 "Invalid input args\n"));
138 return (DDI_EINVAL);
139 }
140
141 if ((*navailp = i_ddi_intr_get_current_navail(dip, type)) == 0)
142 return (DDI_INTR_NOTFOUND);
143
144 return (DDI_SUCCESS);
145 }
146
147 /*
148 * Interrupt allocate/free functions
149 */
150 int
151 ddi_intr_alloc(dev_info_t *dip, ddi_intr_handle_t *h_array, int type, int inum,
152 int count, int *actualp, int behavior)
153 {
154 ddi_intr_handle_impl_t *hdlp, tmp_hdl;
155 int i, ret, cap = 0, curr_type, nintrs;
156 uint_t pri, navail, curr_nintrs = 0;
157
158 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: name %s dip 0x%p "
159 "type %x inum %x count %x behavior %x\n", ddi_driver_name(dip),
160 (void *)dip, type, inum, count, behavior));
161
162 /* Validate parameters */
163 if ((dip == NULL) || (h_array == NULL) || (inum < 0) || (count < 1) ||
164 (actualp == NULL) || !DDI_INTR_BEHAVIOR_FLAG_VALID(behavior)) {
165 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: "
166 "Invalid input args\n"));
167 return (DDI_EINVAL);
168 }
169
170 /* Validate interrupt type */
171 if (!DDI_INTR_TYPE_FLAG_VALID(type) ||
172 !(i_ddi_intr_get_supported_types(dip) & type)) {
173 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: type %x not "
174 "supported\n", type));
175 return (DDI_EINVAL);
176 }
177
178 /* Validate inum not previously allocated */
179 if ((type == DDI_INTR_TYPE_FIXED) &&
180 (i_ddi_get_intr_handle(dip, inum) != NULL)) {
181 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: inum %d is already "
182 "in use, cannot allocate again!!\n", inum));
183 return (DDI_EINVAL);
184 }
185
186 /* Get how many interrupts the device supports */
187 if ((nintrs = i_ddi_intr_get_supported_nintrs(dip, type)) == 0) {
188 if (ddi_intr_get_nintrs(dip, type, &nintrs) != DDI_SUCCESS) {
189 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: no "
190 "interrupts found of type %d\n", type));
191 return (DDI_INTR_NOTFOUND);
192 }
193 }
194
195 /* Get how many interrupts the device is already using */
196 if ((curr_type = i_ddi_intr_get_current_type(dip)) != 0) {
197 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: type %x "
198 "is already being used\n", curr_type));
199 curr_nintrs = i_ddi_intr_get_current_nintrs(dip);
200 }
201
202 /* Validate interrupt type consistency */
203 if ((curr_type != 0) && (type != curr_type)) {
204 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: Requested "
205 "interrupt type %x is different from interrupt type %x"
206 "already in use\n", type, curr_type));
207 return (DDI_EINVAL);
208 }
209
210 /* Validate count does not exceed what device supports */
211 if (count > nintrs) {
212 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: no of interrupts "
213 "requested %d is more than supported %d\n", count, nintrs));
214 return (DDI_EINVAL);
215 } else if ((count + curr_nintrs) > nintrs) {
216 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: count %d "
217 "+ intrs in use %d exceeds supported %d intrs\n",
218 count, curr_nintrs, nintrs));
219 return (DDI_EINVAL);
220 }
221
222 /* Validate power of 2 requirements for MSI */
223 if ((type == DDI_INTR_TYPE_MSI) && !ISP2(curr_nintrs + count)) {
224 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: "
225 "MSI count %d is not a power of two\n", count));
226 return (DDI_EINVAL);
227 }
228
229 /*
230 * Initialize the device's interrupt information structure,
231 * and establish an association with IRM if it is supported.
232 *
233 * NOTE: IRM checks minimum support, and can return DDI_EAGAIN.
234 */
235 if (curr_nintrs == 0) {
236 i_ddi_intr_devi_init(dip);
237 if (i_ddi_irm_insert(dip, type, count) == DDI_EAGAIN) {
238 cmn_err(CE_WARN, "ddi_intr_alloc: "
239 "cannot fit into interrupt pool\n");
240 return (DDI_EAGAIN);
241 }
242 }
243
244 /* Synchronously adjust IRM associations for non-IRM aware drivers */
245 if (curr_nintrs && (i_ddi_irm_supported(dip, type) != DDI_SUCCESS))
246 (void) i_ddi_irm_modify(dip, count + curr_nintrs);
247
248 /* Get how many interrupts are currently available */
249 navail = i_ddi_intr_get_current_navail(dip, type);
250
251 /* Validate that requested number of interrupts are available */
252 if (curr_nintrs == navail) {
253 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: max # of intrs %d "
254 "already allocated\n", navail));
255 return (DDI_EAGAIN);
256 }
257 if ((count + curr_nintrs) > navail) {
258 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: requested # of "
259 "intrs %d exceeds # of available intrs %d\n", count,
260 navail - curr_nintrs));
261 if (behavior == DDI_INTR_ALLOC_STRICT) {
262 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: "
263 "DDI_INTR_ALLOC_STRICT flag is passed, "
264 "return failure\n"));
265 if (curr_nintrs == 0)
266 i_ddi_intr_devi_fini(dip);
267 else if (i_ddi_irm_supported(dip, type) != DDI_SUCCESS)
268 (void) i_ddi_irm_modify(dip, curr_nintrs);
269 return (DDI_EAGAIN);
270 }
271 count = navail - curr_nintrs;
272 }
273
274 /* Now allocate required number of interrupts */
275 bzero(&tmp_hdl, sizeof (ddi_intr_handle_impl_t));
276 tmp_hdl.ih_type = type;
277 tmp_hdl.ih_inum = inum;
278 tmp_hdl.ih_scratch1 = count;
279 tmp_hdl.ih_scratch2 = (void *)(uintptr_t)behavior;
280 tmp_hdl.ih_dip = dip;
281 tmp_hdl.ih_irq = -1;
282
283 if (i_ddi_intr_ops(dip, dip, DDI_INTROP_ALLOC,
284 &tmp_hdl, (void *)actualp) != DDI_SUCCESS) {
285 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: allocation "
286 "failed\n"));
287 i_ddi_intr_devi_fini(dip);
288 return (*actualp ? DDI_EAGAIN : DDI_INTR_NOTFOUND);
289 }
290
291 if ((ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_GETPRI,
292 &tmp_hdl, (void *)&pri)) != DDI_SUCCESS) {
293 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: get priority "
294 "failed\n"));
295 goto fail;
296 }
297
298 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: getting capability\n"));
299
300 if ((ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_GETCAP,
301 &tmp_hdl, (void *)&cap)) != DDI_SUCCESS) {
302 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: get capability "
303 "failed\n"));
304 goto fail;
305 }
306
307 /*
308 * Save current interrupt type, supported and current intr count.
309 */
310 i_ddi_intr_set_current_type(dip, type);
311 i_ddi_intr_set_supported_nintrs(dip, nintrs);
312 i_ddi_intr_set_current_nintrs(dip,
313 i_ddi_intr_get_current_nintrs(dip) + *actualp);
314
315 /* Now, go and handle each "handle" */
316 for (i = inum; i < (inum + *actualp); i++) {
317 hdlp = (ddi_intr_handle_impl_t *)kmem_zalloc(
318 (sizeof (ddi_intr_handle_impl_t)), KM_SLEEP);
319 rw_init(&hdlp->ih_rwlock, NULL, RW_DRIVER, NULL);
320 h_array[i] = (struct __ddi_intr_handle *)hdlp;
321 hdlp->ih_type = type;
322 hdlp->ih_pri = pri;
323 hdlp->ih_cap = cap;
324 hdlp->ih_ver = DDI_INTR_VERSION;
325 hdlp->ih_state = DDI_IHDL_STATE_ALLOC;
326 hdlp->ih_dip = dip;
327 hdlp->ih_inum = i;
328 hdlp->ih_irq = -1;
329 i_ddi_alloc_intr_phdl(hdlp);
330 if (type & DDI_INTR_TYPE_FIXED) {
331 if (tmp_hdl.ih_irq != -1)
332 hdlp->ih_irq = tmp_hdl.ih_irq;
333 i_ddi_set_intr_handle(dip, hdlp->ih_inum,
334 (ddi_intr_handle_t)hdlp);
335 }
336
337 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_alloc: hdlp = 0x%p\n",
338 (void *)h_array[i]));
339 }
340
341 return (DDI_SUCCESS);
342
343 fail:
344 (void) i_ddi_intr_ops(tmp_hdl.ih_dip, tmp_hdl.ih_dip,
345 DDI_INTROP_FREE, &tmp_hdl, NULL);
346 i_ddi_intr_devi_fini(dip);
347
348 return (ret);
349 }
350
351 int
352 ddi_intr_free(ddi_intr_handle_t h)
353 {
354 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
355 int ret;
356
357 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_free: hdlp = %p\n", (void *)hdlp));
358
359 if (hdlp == NULL)
360 return (DDI_EINVAL);
361
362 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
363 if (((hdlp->ih_flags & DDI_INTR_MSIX_DUP) &&
364 (hdlp->ih_state != DDI_IHDL_STATE_ADDED)) ||
365 ((hdlp->ih_state != DDI_IHDL_STATE_ALLOC) &&
366 (!(hdlp->ih_flags & DDI_INTR_MSIX_DUP)))) {
367 rw_exit(&hdlp->ih_rwlock);
368 return (DDI_EINVAL);
369 }
370
371 /* Set the number of interrupts to free */
372 hdlp->ih_scratch1 = 1;
373
374 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
375 DDI_INTROP_FREE, hdlp, NULL);
376
377 rw_exit(&hdlp->ih_rwlock);
378 if (ret == DDI_SUCCESS) {
379 /* This would be the dup vector */
380 if (hdlp->ih_flags & DDI_INTR_MSIX_DUP)
381 atomic_dec_32(&hdlp->ih_main->ih_dup_cnt);
382 else {
383 int n, curr_type;
384
385 n = i_ddi_intr_get_current_nintrs(hdlp->ih_dip) - 1;
386 curr_type = i_ddi_intr_get_current_type(hdlp->ih_dip);
387
388 i_ddi_intr_set_current_nintrs(hdlp->ih_dip, n);
389
390 if ((i_ddi_irm_supported(hdlp->ih_dip, curr_type)
391 != DDI_SUCCESS) && (n > 0))
392 (void) i_ddi_irm_modify(hdlp->ih_dip, n);
393
394 if (hdlp->ih_type & DDI_INTR_TYPE_FIXED)
395 i_ddi_set_intr_handle(hdlp->ih_dip,
396 hdlp->ih_inum, NULL);
397
398 i_ddi_intr_devi_fini(hdlp->ih_dip);
399 i_ddi_free_intr_phdl(hdlp);
400 }
401 rw_destroy(&hdlp->ih_rwlock);
402 kmem_free(hdlp, sizeof (ddi_intr_handle_impl_t));
403 }
404
405 return (ret);
406 }
407
408 /*
409 * Interrupt get/set capacity functions
410 *
411 * The logic used to figure this out is shown here:
412 *
413 * Device level Platform level Intr source
414 * 1. Fixed interrupts
415 * (non-PCI)
416 * o Flags supported N/A Maskable/Pending/ rootnex
417 * No Block Enable
418 * o navail 1
419 *
420 * 2. PCI Fixed interrupts
421 * o Flags supported pending/Maskable Maskable/pending/ pci
422 * No Block enable
423 * o navail N/A 1
424 *
425 * 3. PCI MSI
426 * o Flags supported Maskable/Pending Maskable/Pending pci
427 * Block Enable (if drvr doesn't) Block Enable
428 * o navail N/A #vectors - #used N/A
429 *
430 * 4. PCI MSI-X
431 * o Flags supported Maskable/Pending Maskable/Pending pci
432 * Block Enable Block Enable
433 * o navail N/A #vectors - #used N/A
434 *
435 * where:
436 * #vectors - Total numbers of vectors available
437 * #used - Total numbers of vectors currently being used
438 *
439 * For devices complying to PCI2.3 or greater, see bit10 of Command Register
440 * 0 - enables assertion of INTx
441 * 1 - disables assertion of INTx
442 *
443 * For non MSI/X interrupts; if the IRQ is shared then all ddi_intr_set_*()
444 * operations return failure.
445 */
446 int
447 ddi_intr_get_cap(ddi_intr_handle_t h, int *flagsp)
448 {
449 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
450 int ret;
451
452 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_cap: hdlp = %p\n",
453 (void *)hdlp));
454
455 *flagsp = 0;
456 if (hdlp == NULL)
457 return (DDI_EINVAL);
458
459 rw_enter(&hdlp->ih_rwlock, RW_READER);
460
461 if (hdlp->ih_cap) {
462 *flagsp = hdlp->ih_cap & ~DDI_INTR_FLAG_MSI64;
463 rw_exit(&hdlp->ih_rwlock);
464 return (DDI_SUCCESS);
465 }
466
467 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
468 DDI_INTROP_GETCAP, hdlp, (void *)flagsp);
469
470 if (ret == DDI_SUCCESS) {
471 hdlp->ih_cap = *flagsp;
472
473 /* Mask out MSI/X 64-bit support to the consumer */
474 *flagsp &= ~DDI_INTR_FLAG_MSI64;
475 }
476
477 rw_exit(&hdlp->ih_rwlock);
478 return (ret);
479 }
480
481 int
482 ddi_intr_set_cap(ddi_intr_handle_t h, int flags)
483 {
484 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
485 int ret;
486
487 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_cap: hdlp = %p", (void *)hdlp));
488
489 if (hdlp == NULL)
490 return (DDI_EINVAL);
491
492 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
493 if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
494 rw_exit(&hdlp->ih_rwlock);
495 return (DDI_EINVAL);
496 }
497
498 /* Only DDI_INTR_FLAG_LEVEL or DDI_INTR_FLAG_EDGE are allowed */
499 if (!(flags & (DDI_INTR_FLAG_EDGE | DDI_INTR_FLAG_LEVEL))) {
500 DDI_INTR_APIDBG((CE_CONT, "%s%d: only LEVEL or EDGE capability "
501 "can be set\n", ddi_driver_name(hdlp->ih_dip),
502 ddi_get_instance(hdlp->ih_dip)));
503 rw_exit(&hdlp->ih_rwlock);
504 return (DDI_EINVAL);
505 }
506
507 /* Both level/edge flags must be currently supported */
508 if (!(hdlp->ih_cap & (DDI_INTR_FLAG_EDGE | DDI_INTR_FLAG_LEVEL))) {
509 DDI_INTR_APIDBG((CE_CONT, "%s%d: Both LEVEL and EDGE capability"
510 " must be supported\n", ddi_driver_name(hdlp->ih_dip),
511 ddi_get_instance(hdlp->ih_dip)));
512 rw_exit(&hdlp->ih_rwlock);
513 return (DDI_ENOTSUP);
514 }
515
516 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
517 DDI_INTROP_SETCAP, hdlp, &flags);
518
519 rw_exit(&hdlp->ih_rwlock);
520 return (ret);
521 }
522
523 /*
524 * Priority related functions
525 */
526
527 /*
528 * ddi_intr_get_hilevel_pri:
529 * Returns the minimum priority level for a
530 * high-level interrupt on a platform.
531 */
532 uint_t
533 ddi_intr_get_hilevel_pri(void)
534 {
535 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_hilevel_pri:\n"));
536 return (LOCK_LEVEL + 1);
537 }
538
539 int
540 ddi_intr_get_pri(ddi_intr_handle_t h, uint_t *prip)
541 {
542 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
543 int ret;
544
545 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_pri: hdlp = %p\n",
546 (void *)hdlp));
547
548 *prip = 0;
549 if (hdlp == NULL)
550 return (DDI_EINVAL);
551
552 rw_enter(&hdlp->ih_rwlock, RW_READER);
553 /* Already initialized, just return that */
554 if (hdlp->ih_pri) {
555 *prip = hdlp->ih_pri;
556 rw_exit(&hdlp->ih_rwlock);
557 return (DDI_SUCCESS);
558 }
559
560 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
561 DDI_INTROP_GETPRI, hdlp, (void *)prip);
562
563 if (ret == DDI_SUCCESS)
564 hdlp->ih_pri = *prip;
565
566 rw_exit(&hdlp->ih_rwlock);
567 return (ret);
568 }
569
570 int
571 ddi_intr_set_pri(ddi_intr_handle_t h, uint_t pri)
572 {
573 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
574 int ret;
575
576 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_pri: hdlp = %p", (void *)hdlp));
577
578 if (hdlp == NULL)
579 return (DDI_EINVAL);
580
581 /* Validate priority argument */
582 if (pri < DDI_INTR_PRI_MIN || pri > DDI_INTR_PRI_MAX) {
583 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_pri: invalid priority "
584 "specified = %x\n", pri));
585 return (DDI_EINVAL);
586 }
587
588 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
589 if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
590 rw_exit(&hdlp->ih_rwlock);
591 return (DDI_EINVAL);
592 }
593
594 /* If the passed priority is same as existing priority; do nothing */
595 if (pri == hdlp->ih_pri) {
596 rw_exit(&hdlp->ih_rwlock);
597 return (DDI_SUCCESS);
598 }
599
600 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
601 DDI_INTROP_SETPRI, hdlp, &pri);
602
603 if (ret == DDI_SUCCESS)
604 hdlp->ih_pri = pri;
605
606 rw_exit(&hdlp->ih_rwlock);
607 return (ret);
608 }
609
610 /*
611 * Interrupt add/duplicate/remove handlers
612 */
613 int
614 ddi_intr_add_handler(ddi_intr_handle_t h, ddi_intr_handler_t inthandler,
615 void *arg1, void *arg2)
616 {
617 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
618 int ret;
619
620 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_handler: hdlp = 0x%p\n",
621 (void *)hdlp));
622
623 if ((hdlp == NULL) || (inthandler == NULL))
624 return (DDI_EINVAL);
625
626 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
627 if (hdlp->ih_state != DDI_IHDL_STATE_ALLOC) {
628 rw_exit(&hdlp->ih_rwlock);
629 return (DDI_EINVAL);
630 }
631
632 hdlp->ih_cb_func = inthandler;
633 hdlp->ih_cb_arg1 = arg1;
634 hdlp->ih_cb_arg2 = arg2;
635
636 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
637 DDI_INTROP_ADDISR, hdlp, NULL);
638
639 if (ret != DDI_SUCCESS) {
640 hdlp->ih_cb_func = NULL;
641 hdlp->ih_cb_arg1 = NULL;
642 hdlp->ih_cb_arg2 = NULL;
643 } else
644 hdlp->ih_state = DDI_IHDL_STATE_ADDED;
645
646 rw_exit(&hdlp->ih_rwlock);
647 return (ret);
648 }
649
650 int
651 ddi_intr_dup_handler(ddi_intr_handle_t org, int dup_inum,
652 ddi_intr_handle_t *dup)
653 {
654 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)org;
655 ddi_intr_handle_impl_t *dup_hdlp;
656 int ret;
657
658 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_dup_handler: hdlp = 0x%p\n",
659 (void *)hdlp));
660
661 /* Do some input argument checking ("dup" handle is not allocated) */
662 if ((hdlp == NULL) || (*dup != NULL) || (dup_inum < 0)) {
663 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_dup_handler: Invalid "
664 "input args\n"));
665 return (DDI_EINVAL);
666 }
667
668 rw_enter(&hdlp->ih_rwlock, RW_READER);
669
670 /* Do some input argument checking */
671 if ((hdlp->ih_state == DDI_IHDL_STATE_ALLOC) || /* intr handle alloc? */
672 (hdlp->ih_type != DDI_INTR_TYPE_MSIX) || /* only MSI-X allowed */
673 (hdlp->ih_flags & DDI_INTR_MSIX_DUP)) { /* only dup original */
674 rw_exit(&hdlp->ih_rwlock);
675 return (DDI_EINVAL);
676 }
677
678 hdlp->ih_scratch1 = dup_inum;
679 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
680 DDI_INTROP_DUPVEC, hdlp, NULL);
681
682 if (ret == DDI_SUCCESS) {
683 dup_hdlp = (ddi_intr_handle_impl_t *)
684 kmem_alloc(sizeof (ddi_intr_handle_impl_t), KM_SLEEP);
685
686 atomic_inc_32(&hdlp->ih_dup_cnt);
687
688 *dup = (ddi_intr_handle_t)dup_hdlp;
689 bcopy(hdlp, dup_hdlp, sizeof (ddi_intr_handle_impl_t));
690
691 /* These fields are unique to each dupped msi-x vector */
692 rw_init(&dup_hdlp->ih_rwlock, NULL, RW_DRIVER, NULL);
693 dup_hdlp->ih_state = DDI_IHDL_STATE_ADDED;
694 dup_hdlp->ih_inum = dup_inum;
695 dup_hdlp->ih_flags |= DDI_INTR_MSIX_DUP;
696 dup_hdlp->ih_dup_cnt = 0;
697
698 /* Point back to original vector */
699 dup_hdlp->ih_main = hdlp;
700 }
701
702 rw_exit(&hdlp->ih_rwlock);
703 return (ret);
704 }
705
706 int
707 ddi_intr_remove_handler(ddi_intr_handle_t h)
708 {
709 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
710 int ret = DDI_SUCCESS;
711
712 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_remove_handler: hdlp = %p\n",
713 (void *)hdlp));
714
715 if (hdlp == NULL)
716 return (DDI_EINVAL);
717
718 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
719
720 if (hdlp->ih_state != DDI_IHDL_STATE_ADDED) {
721 ret = DDI_EINVAL;
722 goto done;
723 } else if (hdlp->ih_flags & DDI_INTR_MSIX_DUP)
724 goto done;
725
726 ASSERT(hdlp->ih_dup_cnt == 0);
727 if (hdlp->ih_dup_cnt > 0) {
728 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_remove_handler: MSI-X "
729 "dup_cnt %d is not 0\n", hdlp->ih_dup_cnt));
730 ret = DDI_FAILURE;
731 goto done;
732 }
733
734 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
735 DDI_INTROP_REMISR, hdlp, NULL);
736
737 if (ret == DDI_SUCCESS) {
738 hdlp->ih_state = DDI_IHDL_STATE_ALLOC;
739 hdlp->ih_cb_func = NULL;
740 hdlp->ih_cb_arg1 = NULL;
741 hdlp->ih_cb_arg2 = NULL;
742 }
743
744 done:
745 rw_exit(&hdlp->ih_rwlock);
746 return (ret);
747 }
748
749
750 /*
751 * Interrupt enable/disable/block_enable/block_disable handlers
752 */
753 int
754 ddi_intr_enable(ddi_intr_handle_t h)
755 {
756 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
757 int ret;
758
759 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_enable: hdlp = %p\n",
760 (void *)hdlp));
761
762 if (hdlp == NULL)
763 return (DDI_EINVAL);
764
765 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
766 if ((hdlp->ih_state != DDI_IHDL_STATE_ADDED) ||
767 ((hdlp->ih_type == DDI_INTR_TYPE_MSI) &&
768 (hdlp->ih_cap & DDI_INTR_FLAG_BLOCK))) {
769 rw_exit(&hdlp->ih_rwlock);
770 return (DDI_EINVAL);
771 }
772
773 I_DDI_VERIFY_MSIX_HANDLE(hdlp);
774
775 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
776 DDI_INTROP_ENABLE, hdlp, NULL);
777
778 if (ret == DDI_SUCCESS) {
779 hdlp->ih_state = DDI_IHDL_STATE_ENABLE;
780 i_ddi_intr_set_current_nenables(hdlp->ih_dip,
781 i_ddi_intr_get_current_nenables(hdlp->ih_dip) + 1);
782 }
783
784 rw_exit(&hdlp->ih_rwlock);
785 return (ret);
786 }
787
788 int
789 ddi_intr_disable(ddi_intr_handle_t h)
790 {
791 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
792 int ret;
793
794 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_disable: hdlp = %p\n",
795 (void *)hdlp));
796
797 if (hdlp == NULL)
798 return (DDI_EINVAL);
799
800 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
801 if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) ||
802 ((hdlp->ih_type == DDI_INTR_TYPE_MSI) &&
803 (hdlp->ih_cap & DDI_INTR_FLAG_BLOCK))) {
804 rw_exit(&hdlp->ih_rwlock);
805 return (DDI_EINVAL);
806 }
807
808 I_DDI_VERIFY_MSIX_HANDLE(hdlp);
809
810 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
811 DDI_INTROP_DISABLE, hdlp, NULL);
812
813 if (ret == DDI_SUCCESS) {
814 hdlp->ih_state = DDI_IHDL_STATE_ADDED;
815 i_ddi_intr_set_current_nenables(hdlp->ih_dip,
816 i_ddi_intr_get_current_nenables(hdlp->ih_dip) - 1);
817 }
818
819 rw_exit(&hdlp->ih_rwlock);
820 return (ret);
821 }
822
823 int
824 ddi_intr_block_enable(ddi_intr_handle_t *h_array, int count)
825 {
826 ddi_intr_handle_impl_t *hdlp;
827 int i, ret;
828
829 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_block_enable: h_array = %p\n",
830 (void *)h_array));
831
832 if (h_array == NULL)
833 return (DDI_EINVAL);
834
835 for (i = 0; i < count; i++) {
836 hdlp = (ddi_intr_handle_impl_t *)h_array[i];
837 rw_enter(&hdlp->ih_rwlock, RW_READER);
838
839 if (hdlp->ih_state != DDI_IHDL_STATE_ADDED ||
840 hdlp->ih_type != DDI_INTR_TYPE_MSI ||
841 !(hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) {
842 rw_exit(&hdlp->ih_rwlock);
843 return (DDI_EINVAL);
844 }
845 rw_exit(&hdlp->ih_rwlock);
846 }
847
848 hdlp = (ddi_intr_handle_impl_t *)h_array[0];
849 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
850 hdlp->ih_scratch1 = count;
851 hdlp->ih_scratch2 = (void *)h_array;
852
853 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
854 DDI_INTROP_BLOCKENABLE, hdlp, NULL);
855
856 rw_exit(&hdlp->ih_rwlock);
857
858 if (ret == DDI_SUCCESS) {
859 for (i = 0; i < count; i++) {
860 hdlp = (ddi_intr_handle_impl_t *)h_array[i];
861 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
862 hdlp->ih_state = DDI_IHDL_STATE_ENABLE;
863 rw_exit(&hdlp->ih_rwlock);
864 }
865 i_ddi_intr_set_current_nenables(hdlp->ih_dip, 1);
866 }
867
868 return (ret);
869 }
870
871 int
872 ddi_intr_block_disable(ddi_intr_handle_t *h_array, int count)
873 {
874 ddi_intr_handle_impl_t *hdlp;
875 int i, ret;
876
877 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_block_disable: h_array = %p\n",
878 (void *)h_array));
879
880 if (h_array == NULL)
881 return (DDI_EINVAL);
882
883 for (i = 0; i < count; i++) {
884 hdlp = (ddi_intr_handle_impl_t *)h_array[i];
885 rw_enter(&hdlp->ih_rwlock, RW_READER);
886 if (hdlp->ih_state != DDI_IHDL_STATE_ENABLE ||
887 hdlp->ih_type != DDI_INTR_TYPE_MSI ||
888 !(hdlp->ih_cap & DDI_INTR_FLAG_BLOCK)) {
889 rw_exit(&hdlp->ih_rwlock);
890 return (DDI_EINVAL);
891 }
892 rw_exit(&hdlp->ih_rwlock);
893 }
894
895 hdlp = (ddi_intr_handle_impl_t *)h_array[0];
896 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
897 hdlp->ih_scratch1 = count;
898 hdlp->ih_scratch2 = (void *)h_array;
899
900 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
901 DDI_INTROP_BLOCKDISABLE, hdlp, NULL);
902
903 rw_exit(&hdlp->ih_rwlock);
904
905 if (ret == DDI_SUCCESS) {
906 for (i = 0; i < count; i++) {
907 hdlp = (ddi_intr_handle_impl_t *)h_array[i];
908 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
909 hdlp->ih_state = DDI_IHDL_STATE_ADDED;
910 rw_exit(&hdlp->ih_rwlock);
911 }
912 i_ddi_intr_set_current_nenables(hdlp->ih_dip, 0);
913 }
914
915 return (ret);
916 }
917
918 /*
919 * Interrupt set/clr mask handlers
920 */
921 int
922 ddi_intr_set_mask(ddi_intr_handle_t h)
923 {
924 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
925 int ret;
926
927 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_mask: hdlp = %p\n",
928 (void *)hdlp));
929
930 if (hdlp == NULL)
931 return (DDI_EINVAL);
932
933 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
934 if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) ||
935 (!(hdlp->ih_cap & DDI_INTR_FLAG_MASKABLE))) {
936 rw_exit(&hdlp->ih_rwlock);
937 return (DDI_EINVAL);
938 }
939
940 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
941 DDI_INTROP_SETMASK, hdlp, NULL);
942
943 rw_exit(&hdlp->ih_rwlock);
944 return (ret);
945 }
946
947 int
948 ddi_intr_clr_mask(ddi_intr_handle_t h)
949 {
950 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
951 int ret;
952
953 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_clr_mask: hdlp = %p\n",
954 (void *)hdlp));
955
956 if (hdlp == NULL)
957 return (DDI_EINVAL);
958
959 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
960 if ((hdlp->ih_state != DDI_IHDL_STATE_ENABLE) ||
961 (!(hdlp->ih_cap & DDI_INTR_FLAG_MASKABLE))) {
962 rw_exit(&hdlp->ih_rwlock);
963 return (DDI_EINVAL);
964 }
965
966 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
967 DDI_INTROP_CLRMASK, hdlp, NULL);
968
969 rw_exit(&hdlp->ih_rwlock);
970 return (ret);
971 }
972
973 /*
974 * Interrupt get_pending handler
975 */
976 int
977 ddi_intr_get_pending(ddi_intr_handle_t h, int *pendingp)
978 {
979 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
980 int ret;
981
982 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_pending: hdlp = %p\n",
983 (void *)hdlp));
984
985 if (hdlp == NULL)
986 return (DDI_EINVAL);
987
988 rw_enter(&hdlp->ih_rwlock, RW_READER);
989 if (!(hdlp->ih_cap & DDI_INTR_FLAG_PENDING)) {
990 rw_exit(&hdlp->ih_rwlock);
991 return (DDI_EINVAL);
992 }
993
994 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
995 DDI_INTROP_GETPENDING, hdlp, (void *)pendingp);
996
997 rw_exit(&hdlp->ih_rwlock);
998 return (ret);
999 }
1000
1001 /*
1002 * Set the number of interrupts requested from IRM
1003 */
1004 int
1005 ddi_intr_set_nreq(dev_info_t *dip, int nreq)
1006 {
1007 int curr_type, nintrs;
1008
1009 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_nreq: dip %p, nreq %d\n",
1010 (void *)dip, nreq));
1011
1012 ASSERT(dip != NULL);
1013 ASSERT(nreq > 0);
1014
1015 /* Sanity check inputs */
1016 if ((dip == NULL) || (nreq < 1))
1017 return (DDI_EINVAL);
1018
1019 curr_type = i_ddi_intr_get_current_type(dip);
1020
1021 /* Only valid for IRM drivers actively using interrupts */
1022 if ((curr_type == 0) ||
1023 (i_ddi_irm_supported(dip, curr_type) != DDI_SUCCESS))
1024 return (DDI_ENOTSUP);
1025
1026 /* Range check */
1027 if (ddi_intr_get_nintrs(dip, curr_type, &nintrs) != DDI_SUCCESS)
1028 return (DDI_FAILURE);
1029 if (nreq > nintrs)
1030 return (DDI_EINVAL);
1031
1032 return (i_ddi_irm_modify(dip, nreq));
1033 }
1034
1035 /*
1036 * Soft interrupt handlers
1037 */
1038 /*
1039 * Add a soft interrupt and register its handler
1040 */
1041 /* ARGSUSED */
1042 int
1043 ddi_intr_add_softint(dev_info_t *dip, ddi_softint_handle_t *h_p, int soft_pri,
1044 ddi_intr_handler_t handler, void *arg1)
1045 {
1046 ddi_softint_hdl_impl_t *hdlp;
1047 int ret;
1048
1049 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: dip = %p, "
1050 "softpri = 0x%x\n", (void *)dip, soft_pri));
1051
1052 if ((dip == NULL) || (h_p == NULL) || (handler == NULL)) {
1053 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: "
1054 "invalid arguments"));
1055
1056 return (DDI_EINVAL);
1057 }
1058
1059 /* Validate input arguments */
1060 if (soft_pri < DDI_INTR_SOFTPRI_MIN ||
1061 soft_pri > DDI_INTR_SOFTPRI_MAX) {
1062 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: invalid "
1063 "soft_pri input given = %x\n", soft_pri));
1064 return (DDI_EINVAL);
1065 }
1066
1067 hdlp = (ddi_softint_hdl_impl_t *)kmem_zalloc(
1068 sizeof (ddi_softint_hdl_impl_t), KM_SLEEP);
1069
1070 /* fill up internally */
1071 rw_init(&hdlp->ih_rwlock, NULL, RW_DRIVER, NULL);
1072 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
1073 hdlp->ih_pri = soft_pri;
1074 hdlp->ih_dip = dip;
1075 hdlp->ih_cb_func = handler;
1076 hdlp->ih_cb_arg1 = arg1;
1077 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_add_softint: hdlp = %p\n",
1078 (void *)hdlp));
1079
1080 /* do the platform specific calls */
1081 if ((ret = i_ddi_add_softint(hdlp)) != DDI_SUCCESS) {
1082 rw_exit(&hdlp->ih_rwlock);
1083 rw_destroy(&hdlp->ih_rwlock);
1084 kmem_free(hdlp, sizeof (ddi_softint_hdl_impl_t));
1085 return (ret);
1086 }
1087
1088 *h_p = (ddi_softint_handle_t)hdlp;
1089 rw_exit(&hdlp->ih_rwlock);
1090 return (ret);
1091 }
1092
1093 /*
1094 * Remove the soft interrupt
1095 */
1096 int
1097 ddi_intr_remove_softint(ddi_softint_handle_t h)
1098 {
1099 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)h;
1100
1101 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_remove_softint: hdlp = %p\n",
1102 (void *)hdlp));
1103
1104 if (hdlp == NULL)
1105 return (DDI_EINVAL);
1106
1107 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
1108 i_ddi_remove_softint(hdlp);
1109 rw_exit(&hdlp->ih_rwlock);
1110 rw_destroy(&hdlp->ih_rwlock);
1111
1112 /* kmem_free the hdl impl_t structure allocated earlier */
1113 kmem_free(hdlp, sizeof (ddi_softint_hdl_impl_t));
1114 return (DDI_SUCCESS);
1115 }
1116
1117 /*
1118 * Trigger a soft interrupt
1119 */
1120 int
1121 ddi_intr_trigger_softint(ddi_softint_handle_t h, void *arg2)
1122 {
1123 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)h;
1124 int ret;
1125
1126 if (hdlp == NULL)
1127 return (DDI_EINVAL);
1128
1129 if ((ret = i_ddi_trigger_softint(hdlp, arg2)) != DDI_SUCCESS) {
1130 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_trigger_softint: failed, "
1131 " ret 0%x\n", ret));
1132
1133 return (ret);
1134 }
1135
1136 hdlp->ih_cb_arg2 = arg2;
1137 return (DDI_SUCCESS);
1138 }
1139
1140 /*
1141 * Get the soft interrupt priority
1142 */
1143 int
1144 ddi_intr_get_softint_pri(ddi_softint_handle_t h, uint_t *soft_prip)
1145 {
1146 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)h;
1147
1148 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_get_softint_pri: h = %p\n",
1149 (void *)h));
1150
1151 if (hdlp == NULL)
1152 return (DDI_EINVAL);
1153
1154 rw_enter(&hdlp->ih_rwlock, RW_READER);
1155 *soft_prip = hdlp->ih_pri;
1156 rw_exit(&hdlp->ih_rwlock);
1157 return (DDI_SUCCESS);
1158 }
1159
1160 /*
1161 * Set the soft interrupt priority
1162 */
1163 int
1164 ddi_intr_set_softint_pri(ddi_softint_handle_t h, uint_t soft_pri)
1165 {
1166 ddi_softint_hdl_impl_t *hdlp = (ddi_softint_hdl_impl_t *)h;
1167 int ret;
1168 uint_t orig_soft_pri;
1169
1170 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: h = %p\n",
1171 (void *)h));
1172
1173 if (hdlp == NULL)
1174 return (DDI_EINVAL);
1175
1176 /* Validate priority argument */
1177 if (soft_pri < DDI_INTR_SOFTPRI_MIN ||
1178 soft_pri > DDI_INTR_SOFTPRI_MAX) {
1179 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: invalid "
1180 "soft_pri input given = %x\n", soft_pri));
1181 return (DDI_EINVAL);
1182 }
1183
1184 rw_enter(&hdlp->ih_rwlock, RW_WRITER);
1185 orig_soft_pri = hdlp->ih_pri;
1186 hdlp->ih_pri = soft_pri;
1187
1188 if ((ret = i_ddi_set_softint_pri(hdlp, orig_soft_pri)) != DDI_SUCCESS) {
1189 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_set_softint_pri: failed, "
1190 " ret 0%x\n", ret));
1191 hdlp->ih_pri = orig_soft_pri;
1192 }
1193
1194 rw_exit(&hdlp->ih_rwlock);
1195 return (ret);
1196 }
1197
1198 /*
1199 * Old DDI interrupt framework
1200 *
1201 * The following DDI interrupt interfaces are obsolete.
1202 * Use the above new DDI interrupt interfaces instead.
1203 */
1204
1205 int
1206 ddi_intr_hilevel(dev_info_t *dip, uint_t inumber)
1207 {
1208 ddi_intr_handle_t hdl;
1209 ddi_intr_handle_t *hdl_p;
1210 size_t hdl_sz = 0;
1211 int actual, ret;
1212 uint_t high_pri, pri;
1213
1214 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: name=%s%d dip=0x%p "
1215 "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1216 (void *)dip, inumber));
1217
1218 /*
1219 * The device driver may have already registed with the
1220 * framework. If so, first try to get the existing interrupt handle
1221 * for that given inumber and use that handle.
1222 */
1223 if ((hdl = i_ddi_get_intr_handle(dip, inumber)) == NULL) {
1224 hdl_sz = sizeof (ddi_intr_handle_t) * (inumber + 1);
1225 hdl_p = kmem_zalloc(hdl_sz, KM_SLEEP);
1226 if ((ret = ddi_intr_alloc(dip, hdl_p, DDI_INTR_TYPE_FIXED,
1227 inumber, 1, &actual,
1228 DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) {
1229 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: "
1230 "ddi_intr_alloc failed, ret 0x%x\n", ret));
1231 kmem_free(hdl_p, hdl_sz);
1232 return (0);
1233 }
1234 hdl = hdl_p[inumber];
1235 }
1236
1237 if ((ret = ddi_intr_get_pri(hdl, &pri)) != DDI_SUCCESS) {
1238 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: "
1239 "ddi_intr_get_pri failed, ret 0x%x\n", ret));
1240 (void) ddi_intr_free(hdl);
1241 if (hdl_sz)
1242 kmem_free(hdl_p, hdl_sz);
1243 return (0);
1244 }
1245
1246 high_pri = ddi_intr_get_hilevel_pri();
1247
1248 DDI_INTR_APIDBG((CE_CONT, "ddi_intr_hilevel: pri = %x, "
1249 "high_pri = %x\n", pri, high_pri));
1250
1251 /* Free the handle allocated here only if no existing handle exists */
1252 if (hdl_sz) {
1253 (void) ddi_intr_free(hdl);
1254 kmem_free(hdl_p, hdl_sz);
1255 }
1256
1257 return (pri >= high_pri);
1258 }
1259
1260 int
1261 ddi_dev_nintrs(dev_info_t *dip, int *result)
1262 {
1263 DDI_INTR_APIDBG((CE_CONT, "ddi_dev_nintrs: name=%s%d dip=0x%p\n",
1264 ddi_driver_name(dip), ddi_get_instance(dip), (void *)dip));
1265
1266 if (ddi_intr_get_nintrs(dip, DDI_INTR_TYPE_FIXED,
1267 result) != DDI_SUCCESS) {
1268 DDI_INTR_APIDBG((CE_CONT, "ddi_dev_nintrs: "
1269 "ddi_intr_get_nintrs failed\n"));
1270 *result = 0;
1271 }
1272
1273 return (DDI_SUCCESS);
1274 }
1275
1276 int
1277 ddi_get_iblock_cookie(dev_info_t *dip, uint_t inumber,
1278 ddi_iblock_cookie_t *iblock_cookiep)
1279 {
1280 ddi_intr_handle_t hdl;
1281 ddi_intr_handle_t *hdl_p;
1282 size_t hdl_sz = 0;
1283 int actual, ret;
1284 uint_t pri;
1285
1286 DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: name=%s%d dip=0x%p "
1287 "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1288 (void *)dip, inumber));
1289
1290 ASSERT(iblock_cookiep != NULL);
1291
1292 /*
1293 * The device driver may have already registed with the
1294 * framework. If so, first try to get the existing interrupt handle
1295 * for that given inumber and use that handle.
1296 */
1297 if ((hdl = i_ddi_get_intr_handle(dip, inumber)) == NULL) {
1298 hdl_sz = sizeof (ddi_intr_handle_t) * (inumber + 1);
1299 hdl_p = kmem_zalloc(hdl_sz, KM_SLEEP);
1300 if ((ret = ddi_intr_alloc(dip, hdl_p,
1301 DDI_INTR_TYPE_FIXED, inumber, 1, &actual,
1302 DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) {
1303 DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: "
1304 "ddi_intr_alloc failed, ret 0x%x\n", ret));
1305 kmem_free(hdl_p, hdl_sz);
1306 return (DDI_INTR_NOTFOUND);
1307 }
1308 hdl = hdl_p[inumber];
1309 }
1310
1311 if ((ret = ddi_intr_get_pri(hdl, &pri)) != DDI_SUCCESS) {
1312 DDI_INTR_APIDBG((CE_CONT, "ddi_get_iblock_cookie: "
1313 "ddi_intr_get_pri failed, ret 0x%x\n", ret));
1314 (void) ddi_intr_free(hdl);
1315 if (hdl_sz)
1316 kmem_free(hdl_p, hdl_sz);
1317 return (DDI_FAILURE);
1318 }
1319
1320 *iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)pri;
1321 /* Free the handle allocated here only if no existing handle exists */
1322 if (hdl_sz) {
1323 (void) ddi_intr_free(hdl);
1324 kmem_free(hdl_p, hdl_sz);
1325 }
1326
1327 return (DDI_SUCCESS);
1328 }
1329
1330 int
1331 ddi_add_intr(dev_info_t *dip, uint_t inumber,
1332 ddi_iblock_cookie_t *iblock_cookiep,
1333 ddi_idevice_cookie_t *idevice_cookiep,
1334 uint_t (*int_handler)(caddr_t int_handler_arg),
1335 caddr_t int_handler_arg)
1336 {
1337 ddi_intr_handle_t *hdl_p;
1338 size_t hdl_sz;
1339 int actual, ret;
1340 uint_t pri;
1341
1342 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: name=%s%d dip=0x%p "
1343 "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1344 (void *)dip, inumber));
1345
1346 hdl_sz = sizeof (ddi_intr_handle_t) * (inumber + 1);
1347 hdl_p = kmem_zalloc(hdl_sz, KM_SLEEP);
1348
1349 if ((ret = ddi_intr_alloc(dip, hdl_p, DDI_INTR_TYPE_FIXED,
1350 inumber, 1, &actual, DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) {
1351 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: "
1352 "ddi_intr_alloc failed, ret 0x%x\n", ret));
1353 kmem_free(hdl_p, hdl_sz);
1354 return (DDI_INTR_NOTFOUND);
1355 }
1356
1357 if ((ret = ddi_intr_get_pri(hdl_p[inumber], &pri)) != DDI_SUCCESS) {
1358 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: "
1359 "ddi_intr_get_pri failed, ret 0x%x\n", ret));
1360 (void) ddi_intr_free(hdl_p[inumber]);
1361 kmem_free(hdl_p, hdl_sz);
1362 return (DDI_FAILURE);
1363 }
1364
1365 if ((ret = ddi_intr_add_handler(hdl_p[inumber], (ddi_intr_handler_t *)
1366 int_handler, int_handler_arg, NULL)) != DDI_SUCCESS) {
1367 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: "
1368 "ddi_intr_add_handler failed, ret 0x%x\n", ret));
1369 (void) ddi_intr_free(hdl_p[inumber]);
1370 kmem_free(hdl_p, hdl_sz);
1371 return (DDI_FAILURE);
1372 }
1373
1374 if ((ret = ddi_intr_enable(hdl_p[inumber])) != DDI_SUCCESS) {
1375 DDI_INTR_APIDBG((CE_CONT, "ddi_add_intr: "
1376 "ddi_intr_enable failed, ret 0x%x\n", ret));
1377 (void) ddi_intr_remove_handler(hdl_p[inumber]);
1378 (void) ddi_intr_free(hdl_p[inumber]);
1379 kmem_free(hdl_p, hdl_sz);
1380 return (DDI_FAILURE);
1381 }
1382
1383 if (iblock_cookiep)
1384 *iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)pri;
1385
1386 if (idevice_cookiep) {
1387 idevice_cookiep->idev_vector = 0;
1388 idevice_cookiep->idev_priority = pri;
1389 }
1390
1391 kmem_free(hdl_p, hdl_sz);
1392
1393 return (DDI_SUCCESS);
1394 }
1395
1396 /* ARGSUSED */
1397 int
1398 ddi_add_fastintr(dev_info_t *dip, uint_t inumber,
1399 ddi_iblock_cookie_t *iblock_cookiep,
1400 ddi_idevice_cookie_t *idevice_cookiep,
1401 uint_t (*hi_int_handler)(void))
1402 {
1403 DDI_INTR_APIDBG((CE_CONT, "ddi_add_fastintr: name=%s%d dip=0x%p "
1404 "inum=0x%x: Not supported, return failure\n", ddi_driver_name(dip),
1405 ddi_get_instance(dip), (void *)dip, inumber));
1406
1407 return (DDI_FAILURE);
1408 }
1409
1410 /* ARGSUSED */
1411 void
1412 ddi_remove_intr(dev_info_t *dip, uint_t inum, ddi_iblock_cookie_t iblock_cookie)
1413 {
1414 ddi_intr_handle_t hdl;
1415 int ret;
1416
1417 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: name=%s%d dip=0x%p "
1418 "inum=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1419 (void *)dip, inum));
1420
1421 if ((hdl = i_ddi_get_intr_handle(dip, inum)) == NULL) {
1422 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: no handle "
1423 "found\n"));
1424 return;
1425 }
1426
1427 if ((ret = ddi_intr_disable(hdl)) != DDI_SUCCESS) {
1428 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: "
1429 "ddi_intr_disable failed, ret 0x%x\n", ret));
1430 return;
1431 }
1432
1433 if ((ret = ddi_intr_remove_handler(hdl)) != DDI_SUCCESS) {
1434 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: "
1435 "ddi_intr_remove_handler failed, ret 0x%x\n", ret));
1436 return;
1437 }
1438
1439 if ((ret = ddi_intr_free(hdl)) != DDI_SUCCESS) {
1440 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_intr: "
1441 "ddi_intr_free failed, ret 0x%x\n", ret));
1442 return;
1443 }
1444 }
1445
1446 /* ARGSUSED */
1447 int
1448 ddi_get_soft_iblock_cookie(dev_info_t *dip, int preference,
1449 ddi_iblock_cookie_t *iblock_cookiep)
1450 {
1451 DDI_INTR_APIDBG((CE_CONT, "ddi_get_soft_iblock_cookie: name=%s%d "
1452 "dip=0x%p pref=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1453 (void *)dip, preference));
1454
1455 ASSERT(iblock_cookiep != NULL);
1456
1457 if (preference == DDI_SOFTINT_FIXED)
1458 return (DDI_FAILURE);
1459
1460 *iblock_cookiep = (ddi_iblock_cookie_t)((uintptr_t)
1461 ((preference > DDI_SOFTINT_MED) ? DDI_SOFT_INTR_PRI_H :
1462 DDI_SOFT_INTR_PRI_M));
1463
1464 return (DDI_SUCCESS);
1465 }
1466
1467 int
1468 ddi_add_softintr(dev_info_t *dip, int preference, ddi_softintr_t *idp,
1469 ddi_iblock_cookie_t *iblock_cookiep,
1470 ddi_idevice_cookie_t *idevice_cookiep,
1471 uint_t (*int_handler)(caddr_t int_handler_arg),
1472 caddr_t int_handler_arg)
1473 {
1474 ddi_softint_handle_t *hdl_p;
1475 uint64_t softpri;
1476 int ret;
1477
1478 DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: name=%s%d dip=0x%p "
1479 "pref=0x%x\n", ddi_driver_name(dip), ddi_get_instance(dip),
1480 (void *)dip, preference));
1481
1482 if ((idp == NULL) || ((preference == DDI_SOFTINT_FIXED) &&
1483 (iblock_cookiep == NULL)))
1484 return (DDI_FAILURE);
1485
1486 /* Translate the priority preference */
1487 if (preference == DDI_SOFTINT_FIXED) {
1488 softpri = (uint64_t)(uintptr_t)*iblock_cookiep;
1489 softpri = MIN(softpri, DDI_SOFT_INTR_PRI_H);
1490 } else {
1491 softpri = (uint64_t)((preference > DDI_SOFTINT_MED) ?
1492 DDI_SOFT_INTR_PRI_H : DDI_SOFT_INTR_PRI_M);
1493 }
1494
1495 DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: preference 0x%x "
1496 "softpri 0x%lx\n", preference, (long)softpri));
1497
1498 hdl_p = kmem_zalloc(sizeof (ddi_softint_handle_t), KM_SLEEP);
1499 if ((ret = ddi_intr_add_softint(dip, hdl_p, softpri,
1500 (ddi_intr_handler_t *)int_handler, int_handler_arg)) !=
1501 DDI_SUCCESS) {
1502 DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: "
1503 "ddi_intr_add_softint failed, ret 0x%x\n", ret));
1504
1505 kmem_free(hdl_p, sizeof (ddi_softint_handle_t));
1506 return (DDI_FAILURE);
1507 }
1508
1509 if (iblock_cookiep)
1510 *iblock_cookiep = (ddi_iblock_cookie_t)(uintptr_t)softpri;
1511
1512 if (idevice_cookiep) {
1513 idevice_cookiep->idev_vector = 0;
1514 idevice_cookiep->idev_priority = softpri;
1515 }
1516
1517 *idp = (ddi_softintr_t)hdl_p;
1518
1519 DDI_INTR_APIDBG((CE_CONT, "ddi_add_softintr: dip = 0x%p, "
1520 "idp = 0x%p, ret = %x\n", (void *)dip, (void *)*idp, ret));
1521
1522 return (DDI_SUCCESS);
1523 }
1524
1525 void
1526 ddi_remove_softintr(ddi_softintr_t id)
1527 {
1528 ddi_softint_handle_t *h_p = (ddi_softint_handle_t *)id;
1529
1530 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_softintr: id=0x%p\n",
1531 (void *)id));
1532
1533 if (h_p == NULL)
1534 return;
1535
1536 DDI_INTR_APIDBG((CE_CONT, "ddi_remove_softintr: handle 0x%p\n",
1537 (void *)h_p));
1538
1539 (void) ddi_intr_remove_softint(*h_p);
1540 kmem_free(h_p, sizeof (ddi_softint_handle_t));
1541 }
1542
1543 void
1544 ddi_trigger_softintr(ddi_softintr_t id)
1545 {
1546 ddi_softint_handle_t *h_p = (ddi_softint_handle_t *)id;
1547 int ret;
1548
1549 if (h_p == NULL)
1550 return;
1551
1552 if ((ret = ddi_intr_trigger_softint(*h_p, NULL)) != DDI_SUCCESS) {
1553 DDI_INTR_APIDBG((CE_CONT, "ddi_trigger_softintr: "
1554 "ddi_intr_trigger_softint failed, hdlp 0x%p "
1555 "ret 0x%x\n", (void *)h_p, ret));
1556 }
1557 }