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.
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.
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]
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <sys/sysmacros.h>
27 #include <sys/types.h>
28 #include <sys/param.h>
29 #include <sys/systm.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 */
38 #if defined(__i386) || defined(__amd64)
40 * MSI-X allocation limit.
42 uint_t ddi_msix_alloc_limit
= DDI_DEFAULT_MSIX_ALLOC
;
46 * New DDI interrupt framework
49 i_ddi_intr_devi_init(dev_info_t
*dip
)
53 DDI_INTR_APIDBG((CE_CONT
, "i_ddi_intr_devi_init: dip %p\n",
56 if (DEVI(dip
)->devi_intr_p
)
59 DEVI(dip
)->devi_intr_p
= kmem_zalloc(sizeof (devinfo_intr_t
), KM_SLEEP
);
61 supported_types
= i_ddi_intr_get_supported_types(dip
);
63 /* Save supported interrupt types information */
64 i_ddi_intr_set_supported_types(dip
, supported_types
);
68 i_ddi_intr_devi_fini(dev_info_t
*dip
)
70 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
72 DDI_INTR_APIDBG((CE_CONT
, "i_ddi_intr_devi_fini: dip %p\n",
75 if ((intr_p
== NULL
) || i_ddi_intr_get_current_nintrs(dip
))
79 * devi_intr_handle_p will only be used for devices
80 * which are using the legacy DDI Interrupt interfaces.
82 if (intr_p
->devi_intr_handle_p
) {
83 /* nintrs could be zero; so check for it first */
84 if (intr_p
->devi_intr_sup_nintrs
) {
85 kmem_free(intr_p
->devi_intr_handle_p
,
86 intr_p
->devi_intr_sup_nintrs
*
87 sizeof (ddi_intr_handle_t
));
92 * devi_irm_req_p will only be used for devices which
93 * are mapped to an Interrupt Resource Management pool.
95 if (intr_p
->devi_irm_req_p
)
96 (void) i_ddi_irm_remove(dip
);
98 kmem_free(DEVI(dip
)->devi_intr_p
, sizeof (devinfo_intr_t
));
99 DEVI(dip
)->devi_intr_p
= NULL
;
103 i_ddi_intr_get_supported_types(dev_info_t
*dip
)
105 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
106 ddi_intr_handle_impl_t hdl
;
109 if ((intr_p
) && (intr_p
->devi_intr_sup_types
))
110 return (intr_p
->devi_intr_sup_types
);
112 bzero(&hdl
, sizeof (ddi_intr_handle_impl_t
));
115 ret
= i_ddi_intr_ops(dip
, dip
, DDI_INTROP_SUPPORTED_TYPES
, &hdl
,
116 (void *)&intr_types
);
118 return ((ret
== DDI_SUCCESS
) ? intr_types
: 0);
122 * NOTE: This function is only called by i_ddi_dev_init().
125 i_ddi_intr_set_supported_types(dev_info_t
*dip
, int intr_types
)
127 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
130 intr_p
->devi_intr_sup_types
= intr_types
;
134 i_ddi_intr_get_supported_nintrs(dev_info_t
*dip
, int intr_type
)
136 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
137 ddi_intr_handle_impl_t hdl
;
140 if ((intr_p
) && (intr_p
->devi_intr_curr_type
== intr_type
) &&
141 (intr_p
->devi_intr_sup_nintrs
))
142 return (intr_p
->devi_intr_sup_nintrs
);
144 bzero(&hdl
, sizeof (ddi_intr_handle_impl_t
));
146 hdl
.ih_type
= intr_type
;
148 ret
= i_ddi_intr_ops(dip
, dip
, DDI_INTROP_NINTRS
, &hdl
,
151 return ((ret
== DDI_SUCCESS
) ? nintrs
: 0);
155 * NOTE: This function is only called by ddi_intr_alloc().
158 i_ddi_intr_set_supported_nintrs(dev_info_t
*dip
, int nintrs
)
160 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
163 intr_p
->devi_intr_sup_nintrs
= nintrs
;
167 i_ddi_intr_get_current_type(dev_info_t
*dip
)
169 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
171 return (intr_p
? intr_p
->devi_intr_curr_type
: 0);
175 * NOTE: This function is only called by
176 * ddi_intr_alloc() and ddi_intr_free().
179 i_ddi_intr_set_current_type(dev_info_t
*dip
, int intr_type
)
181 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
184 intr_p
->devi_intr_curr_type
= intr_type
;
188 i_ddi_intr_get_current_nintrs(dev_info_t
*dip
)
190 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
192 return (intr_p
? intr_p
->devi_intr_curr_nintrs
: 0);
196 * NOTE: This function is only called by
197 * ddi_intr_alloc() and ddi_intr_free().
200 i_ddi_intr_set_current_nintrs(dev_info_t
*dip
, int nintrs
)
202 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
205 intr_p
->devi_intr_curr_nintrs
= nintrs
;
209 i_ddi_intr_get_current_nenables(dev_info_t
*dip
)
211 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
213 return (intr_p
? intr_p
->devi_intr_curr_nenables
: 0);
217 i_ddi_intr_set_current_nenables(dev_info_t
*dip
, int nintrs
)
219 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
222 intr_p
->devi_intr_curr_nenables
= nintrs
;
226 * i_ddi_intr_get_current_navail:
228 * Return the number of interrupts currently available.
229 * If a precise number set by IRM is not available, then
230 * return the limit determined by i_ddi_intr_get_limit().
233 i_ddi_intr_get_current_navail(dev_info_t
*dip
, int type
)
235 devinfo_intr_t
*intr_p
;
236 ddi_irm_pool_t
*pool_p
;
237 ddi_irm_req_t
*req_p
;
240 /* Check for a precise number from IRM */
241 if (((intr_p
= DEVI(dip
)->devi_intr_p
) != NULL
) &&
242 ((req_p
= intr_p
->devi_irm_req_p
) != NULL
) &&
243 (type
== req_p
->ireq_type
) &&
244 ((pool_p
= req_p
->ireq_pool_p
) != NULL
)) {
246 * Lock to be sure a rebalance is not in progress.
247 * (Should be changed to a rwlock.)
249 mutex_enter(&pool_p
->ipool_navail_lock
);
250 navail
= req_p
->ireq_navail
;
251 mutex_exit(&pool_p
->ipool_navail_lock
);
255 /* Otherwise, return the limit */
256 return (i_ddi_intr_get_limit(dip
, type
, NULL
));
260 * i_ddi_intr_get_limit:
262 * Return the limit of how many interrupts a driver can allocate.
265 i_ddi_intr_get_limit(dev_info_t
*dip
, int type
, ddi_irm_pool_t
*pool_p
)
267 ddi_intr_handle_impl_t hdl
;
268 uint_t limit
, nintrs
;
270 /* Check for interrupt pool */
272 pool_p
= i_ddi_intr_get_pool(dip
, type
);
274 /* Get default limit, from interrupt pool or by INTROP method */
275 if (pool_p
!= NULL
) {
276 limit
= pool_p
->ipool_defsz
;
278 bzero(&hdl
, sizeof (ddi_intr_handle_impl_t
));
281 if (i_ddi_intr_ops(dip
, dip
, DDI_INTROP_NAVAIL
, &hdl
,
282 (void *)&limit
) != DDI_SUCCESS
)
286 /* Get maximum supported by the device */
287 nintrs
= i_ddi_intr_get_supported_nintrs(dip
, type
);
289 /* No limit if device and system both support IRM */
290 if ((pool_p
!= NULL
) && (i_ddi_irm_supported(dip
, type
) == DDI_SUCCESS
))
293 /* Limit cannot exceed what device supports */
294 limit
= MIN(limit
, nintrs
);
296 /* Impose a global MSI-X limit on x86 */
297 #if defined(__i386) || defined(__amd64)
298 if (type
== DDI_INTR_TYPE_MSIX
)
299 limit
= MIN(limit
, ddi_msix_alloc_limit
);
302 /* Impose a global MSI limit on all platforms */
303 if (type
== DDI_INTR_TYPE_MSI
)
304 limit
= MIN(limit
, DDI_MAX_MSI_ALLOC
);
310 i_ddi_get_msix(dev_info_t
*dip
)
312 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
314 return (intr_p
? intr_p
->devi_msix_p
: NULL
);
318 i_ddi_set_msix(dev_info_t
*dip
, ddi_intr_msix_t
*msix_p
)
320 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
323 intr_p
->devi_msix_p
= msix_p
;
327 i_ddi_get_intr_handle(dev_info_t
*dip
, int inum
)
329 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
335 * Changed this to a check and return NULL if an invalid inum
336 * is passed to retrieve a handle
338 if ((inum
< 0) || (inum
>= intr_p
->devi_intr_sup_nintrs
))
341 return ((intr_p
->devi_intr_handle_p
) ?
342 intr_p
->devi_intr_handle_p
[inum
] : NULL
);
346 i_ddi_set_intr_handle(dev_info_t
*dip
, int inum
, ddi_intr_handle_t intr_hdl
)
348 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
354 * Changed this to a check and return if an invalid inum
355 * is passed to set a handle
357 if ((inum
< 0) || (inum
>= intr_p
->devi_intr_sup_nintrs
))
360 if (intr_hdl
&& (intr_p
->devi_intr_handle_p
== NULL
)) {
361 /* nintrs could be zero; so check for it first */
362 if (intr_p
->devi_intr_sup_nintrs
)
363 intr_p
->devi_intr_handle_p
= kmem_zalloc(
364 sizeof (ddi_intr_handle_t
) *
365 intr_p
->devi_intr_sup_nintrs
, KM_SLEEP
);
368 if (intr_p
->devi_intr_handle_p
)
369 intr_p
->devi_intr_handle_p
[inum
] = intr_hdl
;
373 * The "ddi-intr-weight" property contains the weight of each interrupt
374 * associated with a dev_info node. For devices with multiple interrupts per
375 * dev_info node, the total load of the device is "devi_intr_weight * nintr",
376 * possibly spread out over multiple CPUs.
378 * Maintaining this as a property permits possible tweaking in the product
379 * in response to customer problems via driver.conf property definitions at
380 * the driver or the instance level. This does not mean that "ddi-intr_weight"
381 * is a formal or committed interface.
384 i_ddi_get_intr_weight(dev_info_t
*dip
)
388 weight
= ddi_prop_get_int(DDI_DEV_T_ANY
, dip
,
389 DDI_PROP_DONTPASS
| DDI_PROP_NOTPROM
, "ddi-intr-weight", -1);
391 weight
= -1; /* undefined */
396 i_ddi_set_intr_weight(dev_info_t
*dip
, int32_t weight
)
400 oweight
= i_ddi_get_intr_weight(dip
);
401 if ((weight
> 0) && (oweight
!= weight
))
402 (void) ndi_prop_update_int(DDI_DEV_T_NONE
, dip
,
403 "ddi-intr-weight", weight
);
408 * Old DDI interrupt framework
411 * The following 4 busops entry points are obsoleted with version
412 * 9 or greater. Use i_ddi_intr_op interface in place of these
413 * obsolete interfaces.
415 * Remove these busops entry points and all related data structures
416 * in future major/minor solaris release.
421 i_ddi_get_intrspec(dev_info_t
*dip
, dev_info_t
*rdip
, uint_t inumber
)
423 dev_info_t
*pdip
= ddi_get_parent(dip
);
425 cmn_err(CE_WARN
, "Failed to process interrupt "
426 "for %s%d due to down-rev nexus driver %s%d",
427 ddi_driver_name(rdip
), ddi_get_instance(rdip
),
428 ddi_driver_name(pdip
), ddi_get_instance(pdip
));
435 i_ddi_add_intrspec(dev_info_t
*dip
, dev_info_t
*rdip
, ddi_intrspec_t intrspec
,
436 ddi_iblock_cookie_t
*iblock_cookiep
,
437 ddi_idevice_cookie_t
*idevice_cookiep
,
438 uint_t (*int_handler
)(caddr_t int_handler_arg
),
439 caddr_t int_handler_arg
, int kind
)
441 dev_info_t
*pdip
= ddi_get_parent(dip
);
443 cmn_err(CE_WARN
, "Failed to process interrupt "
444 "for %s%d due to down-rev nexus driver %s%d",
445 ddi_driver_name(rdip
), ddi_get_instance(rdip
),
446 ddi_driver_name(pdip
), ddi_get_instance(pdip
));
448 return (DDI_ENOTSUP
);
453 i_ddi_remove_intrspec(dev_info_t
*dip
, dev_info_t
*rdip
,
454 ddi_intrspec_t intrspec
, ddi_iblock_cookie_t iblock_cookie
)
456 dev_info_t
*pdip
= ddi_get_parent(dip
);
458 cmn_err(CE_WARN
, "Failed to process interrupt "
459 "for %s%d due to down-rev nexus driver %s%d",
460 ddi_driver_name(rdip
), ddi_get_instance(rdip
),
461 ddi_driver_name(pdip
), ddi_get_instance(pdip
));
466 i_ddi_intr_ctlops(dev_info_t
*dip
, dev_info_t
*rdip
, ddi_intr_ctlop_t op
,
467 void *arg
, void *val
)
469 dev_info_t
*pdip
= ddi_get_parent(dip
);
471 cmn_err(CE_WARN
, "Failed to process interrupt "
472 "for %s%d due to down-rev nexus driver %s%d",
473 ddi_driver_name(rdip
), ddi_get_instance(rdip
),
474 ddi_driver_name(pdip
), ddi_get_instance(pdip
));
476 return (DDI_ENOTSUP
);
480 * Interrupt target get/set functions
483 get_intr_affinity(ddi_intr_handle_t h
, processorid_t
*tgt_p
)
485 ddi_intr_handle_impl_t
*hdlp
= (ddi_intr_handle_impl_t
*)h
;
488 DDI_INTR_APIDBG((CE_CONT
, "get_intr_affinity: hdlp = %p\n",
491 if ((hdlp
== NULL
) || (tgt_p
== NULL
))
494 rw_enter(&hdlp
->ih_rwlock
, RW_READER
);
495 if (hdlp
->ih_state
!= DDI_IHDL_STATE_ENABLE
) {
496 rw_exit(&hdlp
->ih_rwlock
);
500 ret
= i_ddi_intr_ops(hdlp
->ih_dip
, hdlp
->ih_dip
,
501 DDI_INTROP_GETTARGET
, hdlp
, (void *)tgt_p
);
503 DDI_INTR_APIDBG((CE_CONT
, "get_intr_affinity: target %x\n",
506 if (ret
== DDI_SUCCESS
)
507 hdlp
->ih_target
= *tgt_p
;
509 rw_exit(&hdlp
->ih_rwlock
);
514 set_intr_affinity(ddi_intr_handle_t h
, processorid_t tgt
)
516 ddi_intr_handle_impl_t
*hdlp
= (ddi_intr_handle_impl_t
*)h
;
519 DDI_INTR_APIDBG((CE_CONT
, "set_intr_affinity: hdlp = %p "
520 "target %x\n", (void *)hdlp
, tgt
));
525 rw_enter(&hdlp
->ih_rwlock
, RW_WRITER
);
526 if ((hdlp
->ih_state
!= DDI_IHDL_STATE_ENABLE
) ||
527 (hdlp
->ih_type
!= DDI_INTR_TYPE_MSIX
)) {
528 rw_exit(&hdlp
->ih_rwlock
);
532 ret
= i_ddi_intr_ops(hdlp
->ih_dip
, hdlp
->ih_dip
,
533 DDI_INTROP_SETTARGET
, hdlp
, &tgt
);
535 if (ret
== DDI_SUCCESS
)
536 hdlp
->ih_target
= tgt
;
538 rw_exit(&hdlp
->ih_rwlock
);
542 #if defined(__i386) || defined(__amd64)
544 i_ddi_get_pci_config_handle(dev_info_t
*dip
)
546 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
548 return (intr_p
? intr_p
->devi_cfg_handle
: NULL
);
552 i_ddi_set_pci_config_handle(dev_info_t
*dip
, ddi_acc_handle_t handle
)
554 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
557 intr_p
->devi_cfg_handle
= handle
;
562 i_ddi_get_msi_msix_cap_ptr(dev_info_t
*dip
)
564 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
566 return (intr_p
? intr_p
->devi_cap_ptr
: 0);
570 i_ddi_set_msi_msix_cap_ptr(dev_info_t
*dip
, int cap_ptr
)
572 devinfo_intr_t
*intr_p
= DEVI(dip
)->devi_intr_p
;
575 intr_p
->devi_cap_ptr
= cap_ptr
;