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
38 #if defined(__x86)
39 /*
40 * MSI-X allocation limit.
41 */
42 uint_t ddi_msix_alloc_limit = DDI_DEFAULT_MSIX_ALLOC;
43 #endif
44
45 /*
46 * New DDI interrupt framework
47 */
48 void
i_ddi_intr_devi_init(dev_info_t * dip)49 i_ddi_intr_devi_init(dev_info_t *dip)
50 {
51 int supported_types;
52
53 DDI_INTR_APIDBG((CE_CONT, "i_ddi_intr_devi_init: dip %p\n",
54 (void *)dip));
55
56 if (DEVI(dip)->devi_intr_p)
57 return;
58
59 DEVI(dip)->devi_intr_p = kmem_zalloc(sizeof (devinfo_intr_t), KM_SLEEP);
60
61 supported_types = i_ddi_intr_get_supported_types(dip);
62
63 /* Save supported interrupt types information */
64 i_ddi_intr_set_supported_types(dip, supported_types);
65 }
66
67 void
i_ddi_intr_devi_fini(dev_info_t * dip)68 i_ddi_intr_devi_fini(dev_info_t *dip)
69 {
70 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
71
72 DDI_INTR_APIDBG((CE_CONT, "i_ddi_intr_devi_fini: dip %p\n",
73 (void *)dip));
74
75 if ((intr_p == NULL) || i_ddi_intr_get_current_nintrs(dip))
76 return;
77
78 /*
79 * devi_intr_handle_p will only be used for devices
80 * which are using the legacy DDI Interrupt interfaces.
81 */
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));
88 }
89 }
90
91 /*
92 * devi_irm_req_p will only be used for devices which
93 * are mapped to an Interrupt Resource Management pool.
94 */
95 if (intr_p->devi_irm_req_p)
96 (void) i_ddi_irm_remove(dip);
97
98 kmem_free(DEVI(dip)->devi_intr_p, sizeof (devinfo_intr_t));
99 DEVI(dip)->devi_intr_p = NULL;
100 }
101
102 uint_t
i_ddi_intr_get_supported_types(dev_info_t * dip)103 i_ddi_intr_get_supported_types(dev_info_t *dip)
104 {
105 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
106 ddi_intr_handle_impl_t hdl;
107 int ret, intr_types;
108
109 if ((intr_p) && (intr_p->devi_intr_sup_types))
110 return (intr_p->devi_intr_sup_types);
111
112 bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
113 hdl.ih_dip = dip;
114
115 ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_SUPPORTED_TYPES, &hdl,
116 (void *)&intr_types);
117
118 return ((ret == DDI_SUCCESS) ? intr_types : 0);
119 }
120
121 /*
122 * NOTE: This function is only called by i_ddi_dev_init().
123 */
124 void
i_ddi_intr_set_supported_types(dev_info_t * dip,int intr_types)125 i_ddi_intr_set_supported_types(dev_info_t *dip, int intr_types)
126 {
127 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
128
129 if (intr_p)
130 intr_p->devi_intr_sup_types = intr_types;
131 }
132
133 uint_t
i_ddi_intr_get_supported_nintrs(dev_info_t * dip,int intr_type)134 i_ddi_intr_get_supported_nintrs(dev_info_t *dip, int intr_type)
135 {
136 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
137 ddi_intr_handle_impl_t hdl;
138 int ret, nintrs;
139
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);
143
144 bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
145 hdl.ih_dip = dip;
146 hdl.ih_type = intr_type;
147
148 ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_NINTRS, &hdl,
149 (void *)&nintrs);
150
151 return ((ret == DDI_SUCCESS) ? nintrs : 0);
152 }
153
154 /*
155 * NOTE: This function is only called by ddi_intr_alloc().
156 */
157 void
i_ddi_intr_set_supported_nintrs(dev_info_t * dip,int nintrs)158 i_ddi_intr_set_supported_nintrs(dev_info_t *dip, int nintrs)
159 {
160 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
161
162 if (intr_p)
163 intr_p->devi_intr_sup_nintrs = nintrs;
164 }
165
166 uint_t
i_ddi_intr_get_current_type(dev_info_t * dip)167 i_ddi_intr_get_current_type(dev_info_t *dip)
168 {
169 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
170
171 return (intr_p ? intr_p->devi_intr_curr_type : 0);
172 }
173
174 /*
175 * NOTE: This function is only called by
176 * ddi_intr_alloc() and ddi_intr_free().
177 */
178 void
i_ddi_intr_set_current_type(dev_info_t * dip,int intr_type)179 i_ddi_intr_set_current_type(dev_info_t *dip, int intr_type)
180 {
181 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
182
183 if (intr_p)
184 intr_p->devi_intr_curr_type = intr_type;
185 }
186
187 uint_t
i_ddi_intr_get_current_nintrs(dev_info_t * dip)188 i_ddi_intr_get_current_nintrs(dev_info_t *dip)
189 {
190 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
191
192 return (intr_p ? intr_p->devi_intr_curr_nintrs : 0);
193 }
194
195 /*
196 * NOTE: This function is only called by
197 * ddi_intr_alloc() and ddi_intr_free().
198 */
199 void
i_ddi_intr_set_current_nintrs(dev_info_t * dip,int nintrs)200 i_ddi_intr_set_current_nintrs(dev_info_t *dip, int nintrs)
201 {
202 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
203
204 if (intr_p)
205 intr_p->devi_intr_curr_nintrs = nintrs;
206 }
207
208 uint_t
i_ddi_intr_get_current_nenables(dev_info_t * dip)209 i_ddi_intr_get_current_nenables(dev_info_t *dip)
210 {
211 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
212
213 return (intr_p ? intr_p->devi_intr_curr_nenables : 0);
214 }
215
216 void
i_ddi_intr_set_current_nenables(dev_info_t * dip,int nintrs)217 i_ddi_intr_set_current_nenables(dev_info_t *dip, int nintrs)
218 {
219 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
220
221 if (intr_p)
222 intr_p->devi_intr_curr_nenables = nintrs;
223 }
224
225 /*
226 * i_ddi_intr_get_current_navail:
227 *
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().
231 */
232 uint_t
i_ddi_intr_get_current_navail(dev_info_t * dip,int type)233 i_ddi_intr_get_current_navail(dev_info_t *dip, int type)
234 {
235 devinfo_intr_t *intr_p;
236 ddi_irm_pool_t *pool_p;
237 ddi_irm_req_t *req_p;
238 uint_t navail;
239
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)) {
245 /*
246 * Lock to be sure a rebalance is not in progress.
247 * (Should be changed to a rwlock.)
248 */
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);
253 }
254
255 /* Otherwise, return the limit */
256 return (i_ddi_intr_get_limit(dip, type, NULL));
257 }
258
259 /*
260 * i_ddi_intr_get_limit:
261 *
262 * Return the limit of how many interrupts a driver can allocate.
263 */
264 uint_t
i_ddi_intr_get_limit(dev_info_t * dip,int type,ddi_irm_pool_t * pool_p)265 i_ddi_intr_get_limit(dev_info_t *dip, int type, ddi_irm_pool_t *pool_p)
266 {
267 ddi_intr_handle_impl_t hdl;
268 uint_t limit, nintrs;
269
270 /* Check for interrupt pool */
271 if (pool_p == NULL)
272 pool_p = i_ddi_intr_get_pool(dip, type);
273
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);
284 }
285
286 /* Get maximum supported by the device */
287 nintrs = i_ddi_intr_get_supported_nintrs(dip, type);
288
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);
292
293 /* Limit cannot exceed what device supports */
294 limit = MIN(limit, nintrs);
295
296 /* Impose a global MSI-X limit on x86 */
297 #if defined(__x86)
298 if (type == DDI_INTR_TYPE_MSIX)
299 limit = MIN(limit, ddi_msix_alloc_limit);
300 #endif
301
302 /* Impose a global MSI limit on all platforms */
303 if (type == DDI_INTR_TYPE_MSI)
304 limit = MIN(limit, DDI_MAX_MSI_ALLOC);
305
306 return (limit);
307 }
308
309 ddi_intr_msix_t *
i_ddi_get_msix(dev_info_t * dip)310 i_ddi_get_msix(dev_info_t *dip)
311 {
312 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
313
314 return (intr_p ? intr_p->devi_msix_p : NULL);
315 }
316
317 void
i_ddi_set_msix(dev_info_t * dip,ddi_intr_msix_t * msix_p)318 i_ddi_set_msix(dev_info_t *dip, ddi_intr_msix_t *msix_p)
319 {
320 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
321
322 if (intr_p)
323 intr_p->devi_msix_p = msix_p;
324 }
325
326 ddi_intr_handle_t
i_ddi_get_intr_handle(dev_info_t * dip,int inum)327 i_ddi_get_intr_handle(dev_info_t *dip, int inum)
328 {
329 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
330
331 if (intr_p == NULL)
332 return (NULL);
333
334 /*
335 * Changed this to a check and return NULL if an invalid inum
336 * is passed to retrieve a handle
337 */
338 if ((inum < 0) || (inum >= intr_p->devi_intr_sup_nintrs))
339 return (NULL);
340
341 return ((intr_p->devi_intr_handle_p) ?
342 intr_p->devi_intr_handle_p[inum] : NULL);
343 }
344
345 void
i_ddi_set_intr_handle(dev_info_t * dip,int inum,ddi_intr_handle_t intr_hdl)346 i_ddi_set_intr_handle(dev_info_t *dip, int inum, ddi_intr_handle_t intr_hdl)
347 {
348 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
349
350 if (intr_p == NULL)
351 return;
352
353 /*
354 * Changed this to a check and return if an invalid inum
355 * is passed to set a handle
356 */
357 if ((inum < 0) || (inum >= intr_p->devi_intr_sup_nintrs))
358 return;
359
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);
366 }
367
368 if (intr_p->devi_intr_handle_p)
369 intr_p->devi_intr_handle_p[inum] = intr_hdl;
370 }
371
372 /*
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.
377 *
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.
382 */
383 int32_t
i_ddi_get_intr_weight(dev_info_t * dip)384 i_ddi_get_intr_weight(dev_info_t *dip)
385 {
386 int32_t weight;
387
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);
393 }
394
395 int32_t
i_ddi_set_intr_weight(dev_info_t * dip,int32_t weight)396 i_ddi_set_intr_weight(dev_info_t *dip, int32_t weight)
397 {
398 int32_t oweight;
399
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);
405 }
406
407 /*
408 * Old DDI interrupt framework
409 *
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.
414 *
415 * Remove these busops entry points and all related data structures
416 * in future major/minor solaris release.
417 */
418
419 /* ARGSUSED */
420 ddi_intrspec_t
i_ddi_get_intrspec(dev_info_t * dip,dev_info_t * rdip,uint_t inumber)421 i_ddi_get_intrspec(dev_info_t *dip, dev_info_t *rdip, uint_t inumber)
422 {
423 dev_info_t *pdip = ddi_get_parent(dip);
424
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));
429
430 return (NULL);
431 }
432
433 /* ARGSUSED */
434 int
i_ddi_add_intrspec(dev_info_t * dip,dev_info_t * rdip,ddi_intrspec_t intrspec,ddi_iblock_cookie_t * iblock_cookiep,ddi_idevice_cookie_t * idevice_cookiep,uint_t (* int_handler)(caddr_t int_handler_arg),caddr_t int_handler_arg,int kind)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)
440 {
441 dev_info_t *pdip = ddi_get_parent(dip);
442
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));
447
448 return (DDI_ENOTSUP);
449 }
450
451 /* ARGSUSED */
452 void
i_ddi_remove_intrspec(dev_info_t * dip,dev_info_t * rdip,ddi_intrspec_t intrspec,ddi_iblock_cookie_t iblock_cookie)453 i_ddi_remove_intrspec(dev_info_t *dip, dev_info_t *rdip,
454 ddi_intrspec_t intrspec, ddi_iblock_cookie_t iblock_cookie)
455 {
456 dev_info_t *pdip = ddi_get_parent(dip);
457
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));
462 }
463
464 /* ARGSUSED */
465 int
i_ddi_intr_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_intr_ctlop_t op,void * arg,void * val)466 i_ddi_intr_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_ctlop_t op,
467 void *arg, void *val)
468 {
469 dev_info_t *pdip = ddi_get_parent(dip);
470
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));
475
476 return (DDI_ENOTSUP);
477 }
478
479 /*
480 * Interrupt target get/set functions
481 */
482 int
get_intr_affinity(ddi_intr_handle_t h,processorid_t * tgt_p)483 get_intr_affinity(ddi_intr_handle_t h, processorid_t *tgt_p)
484 {
485 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
486 int ret;
487
488 DDI_INTR_APIDBG((CE_CONT, "get_intr_affinity: hdlp = %p\n",
489 (void *)hdlp));
490
491 if ((hdlp == NULL) || (tgt_p == NULL))
492 return (DDI_EINVAL);
493
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);
498 }
499
500 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
501 DDI_INTROP_GETTARGET, hdlp, (void *)tgt_p);
502
503 DDI_INTR_APIDBG((CE_CONT, "get_intr_affinity: target %x\n",
504 *tgt_p));
505
506 if (ret == DDI_SUCCESS)
507 hdlp->ih_target = *tgt_p;
508
509 rw_exit(&hdlp->ih_rwlock);
510 return (ret);
511 }
512
513 int
set_intr_affinity(ddi_intr_handle_t h,processorid_t tgt)514 set_intr_affinity(ddi_intr_handle_t h, processorid_t tgt)
515 {
516 ddi_intr_handle_impl_t *hdlp = (ddi_intr_handle_impl_t *)h;
517 int ret;
518
519 DDI_INTR_APIDBG((CE_CONT, "set_intr_affinity: hdlp = %p "
520 "target %x\n", (void *)hdlp, tgt));
521
522 if (hdlp == NULL)
523 return (DDI_EINVAL);
524
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);
530 }
531
532 ret = i_ddi_intr_ops(hdlp->ih_dip, hdlp->ih_dip,
533 DDI_INTROP_SETTARGET, hdlp, &tgt);
534
535 if (ret == DDI_SUCCESS)
536 hdlp->ih_target = tgt;
537
538 rw_exit(&hdlp->ih_rwlock);
539 return (ret);
540 }
541
542 #if defined(__x86)
543 ddi_acc_handle_t
i_ddi_get_pci_config_handle(dev_info_t * dip)544 i_ddi_get_pci_config_handle(dev_info_t *dip)
545 {
546 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
547
548 return (intr_p ? intr_p->devi_cfg_handle : NULL);
549 }
550
551 void
i_ddi_set_pci_config_handle(dev_info_t * dip,ddi_acc_handle_t handle)552 i_ddi_set_pci_config_handle(dev_info_t *dip, ddi_acc_handle_t handle)
553 {
554 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
555
556 if (intr_p)
557 intr_p->devi_cfg_handle = handle;
558 }
559
560
561 int
i_ddi_get_msi_msix_cap_ptr(dev_info_t * dip)562 i_ddi_get_msi_msix_cap_ptr(dev_info_t *dip)
563 {
564 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
565
566 return (intr_p ? intr_p->devi_cap_ptr : 0);
567 }
568
569 void
i_ddi_set_msi_msix_cap_ptr(dev_info_t * dip,int cap_ptr)570 i_ddi_set_msi_msix_cap_ptr(dev_info_t *dip, int cap_ptr)
571 {
572 devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
573
574 if (intr_p)
575 intr_p->devi_cap_ptr = cap_ptr;
576 }
577 #endif
578