uts: make emu10k non-verbose
[unleashed.git] / kernel / os / ddi_intr_impl.c
blob215be73722ac7fa246985eaa765c58d2e3c7b8d9
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
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 */
38 #if defined(__i386) || defined(__amd64)
40 * MSI-X allocation limit.
42 uint_t ddi_msix_alloc_limit = DDI_DEFAULT_MSIX_ALLOC;
43 #endif
46 * New DDI interrupt framework
48 void
49 i_ddi_intr_devi_init(dev_info_t *dip)
51 int supported_types;
53 DDI_INTR_APIDBG((CE_CONT, "i_ddi_intr_devi_init: dip %p\n",
54 (void *)dip));
56 if (DEVI(dip)->devi_intr_p)
57 return;
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);
67 void
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",
73 (void *)dip));
75 if ((intr_p == NULL) || i_ddi_intr_get_current_nintrs(dip))
76 return;
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;
102 uint_t
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;
107 int ret, intr_types;
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));
113 hdl.ih_dip = dip;
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().
124 void
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;
129 if (intr_p)
130 intr_p->devi_intr_sup_types = intr_types;
133 uint_t
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;
138 int ret, nintrs;
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));
145 hdl.ih_dip = dip;
146 hdl.ih_type = intr_type;
148 ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_NINTRS, &hdl,
149 (void *)&nintrs);
151 return ((ret == DDI_SUCCESS) ? nintrs : 0);
155 * NOTE: This function is only called by ddi_intr_alloc().
157 void
158 i_ddi_intr_set_supported_nintrs(dev_info_t *dip, int nintrs)
160 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
162 if (intr_p)
163 intr_p->devi_intr_sup_nintrs = nintrs;
166 uint_t
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().
178 void
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;
183 if (intr_p)
184 intr_p->devi_intr_curr_type = intr_type;
187 uint_t
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().
199 void
200 i_ddi_intr_set_current_nintrs(dev_info_t *dip, int nintrs)
202 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
204 if (intr_p)
205 intr_p->devi_intr_curr_nintrs = nintrs;
208 uint_t
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);
216 void
217 i_ddi_intr_set_current_nenables(dev_info_t *dip, int nintrs)
219 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
221 if (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().
232 uint_t
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;
238 uint_t navail;
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);
252 return (navail);
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.
264 uint_t
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 */
271 if (pool_p == NULL)
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;
277 } else {
278 bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
279 hdl.ih_dip = dip;
280 hdl.ih_type = type;
281 if (i_ddi_intr_ops(dip, dip, DDI_INTROP_NAVAIL, &hdl,
282 (void *)&limit) != DDI_SUCCESS)
283 return (0);
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))
291 return (nintrs);
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);
300 #endif
302 /* Impose a global MSI limit on all platforms */
303 if (type == DDI_INTR_TYPE_MSI)
304 limit = MIN(limit, DDI_MAX_MSI_ALLOC);
306 return (limit);
309 ddi_intr_msix_t *
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);
317 void
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;
322 if (intr_p)
323 intr_p->devi_msix_p = msix_p;
326 ddi_intr_handle_t
327 i_ddi_get_intr_handle(dev_info_t *dip, int inum)
329 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
331 if (intr_p == NULL)
332 return (NULL);
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))
339 return (NULL);
341 return ((intr_p->devi_intr_handle_p) ?
342 intr_p->devi_intr_handle_p[inum] : NULL);
345 void
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;
350 if (intr_p == NULL)
351 return;
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))
358 return;
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.
383 int32_t
384 i_ddi_get_intr_weight(dev_info_t *dip)
386 int32_t weight;
388 weight = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
389 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "ddi-intr-weight", -1);
390 if (weight < -1)
391 weight = -1; /* undefined */
392 return (weight);
395 int32_t
396 i_ddi_set_intr_weight(dev_info_t *dip, int32_t weight)
398 int32_t oweight;
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);
404 return (oweight);
408 * Old DDI interrupt framework
410 * NOTE:
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.
419 /* ARGSUSED */
420 ddi_intrspec_t
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));
430 return (NULL);
433 /* ARGSUSED */
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);
451 /* ARGSUSED */
452 void
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));
464 /* ARGSUSED */
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;
486 int ret;
488 DDI_INTR_APIDBG((CE_CONT, "get_intr_affinity: hdlp = %p\n",
489 (void *)hdlp));
491 if ((hdlp == NULL) || (tgt_p == NULL))
492 return (DDI_EINVAL);
494 rw_enter(&hdlp->ih_rwlock, RW_READER);
495 if (hdlp->ih_state != DDI_IHDL_STATE_ENABLE) {
496 rw_exit(&hdlp->ih_rwlock);
497 return (DDI_EINVAL);
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",
504 *tgt_p));
506 if (ret == DDI_SUCCESS)
507 hdlp->ih_target = *tgt_p;
509 rw_exit(&hdlp->ih_rwlock);
510 return (ret);
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;
517 int ret;
519 DDI_INTR_APIDBG((CE_CONT, "set_intr_affinity: hdlp = %p "
520 "target %x\n", (void *)hdlp, tgt));
522 if (hdlp == NULL)
523 return (DDI_EINVAL);
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);
529 return (DDI_EINVAL);
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);
539 return (ret);
542 #if defined(__i386) || defined(__amd64)
543 ddi_acc_handle_t
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);
551 void
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;
556 if (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);
569 void
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;
574 if (intr_p)
575 intr_p->devi_cap_ptr = cap_ptr;
577 #endif