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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/note.h>
29 #include <sys/sysmacros.h>
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kmem.h>
34 #include <sys/cmn_err.h>
35 #include <sys/debug.h>
36 #include <sys/avintr.h>
37 #include <sys/autoconf.h>
38 #include <sys/sunndi.h>
39 #include <sys/ndi_impldefs.h>	/* include prototypes */
40 
41 extern uint_t		ddi_msix_alloc_limit;
42 
43 /*
44  * New DDI interrupt framework
45  */
46 void
47 i_ddi_intr_devi_init(dev_info_t *dip)
48 {
49 	int	supported_types;
50 
51 	DDI_INTR_APIDBG((CE_CONT, "i_ddi_intr_devi_init: dip %p\n",
52 	    (void *)dip));
53 
54 	if (DEVI(dip)->devi_intr_p)
55 		return;
56 
57 	DEVI(dip)->devi_intr_p = kmem_zalloc(sizeof (devinfo_intr_t),
58 	    KM_SLEEP);
59 
60 	supported_types = i_ddi_intr_get_supported_types(dip);
61 
62 	/* Save supported interrupt types information */
63 	i_ddi_intr_set_supported_types(dip, supported_types);
64 }
65 
66 void
67 i_ddi_intr_devi_fini(dev_info_t *dip)
68 {
69 	devinfo_intr_t	*intr_p = DEVI(dip)->devi_intr_p;
70 
71 	DDI_INTR_APIDBG((CE_CONT, "i_ddi_intr_devi_fini: dip %p\n",
72 	    (void *)dip));
73 
74 	if ((DEVI(dip)->devi_intr_p == NULL) ||
75 	    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 	kmem_free(DEVI(dip)->devi_intr_p, sizeof (devinfo_intr_t));
92 	DEVI(dip)->devi_intr_p = NULL;
93 }
94 
95 uint_t
96 i_ddi_intr_get_supported_types(dev_info_t *dip)
97 {
98 	devinfo_intr_t		*intr_p = DEVI(dip)->devi_intr_p;
99 	ddi_intr_handle_impl_t	hdl;
100 	int			ret, intr_types;
101 
102 	if ((intr_p) && (intr_p->devi_intr_sup_types))
103 		return (intr_p->devi_intr_sup_types);
104 
105 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
106 	hdl.ih_dip = dip;
107 
108 	ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_SUPPORTED_TYPES, &hdl,
109 	    (void *)&intr_types);
110 
111 	return ((ret == DDI_SUCCESS) ? intr_types : 0);
112 }
113 
114 /*
115  * NOTE: This function is only called by i_ddi_dev_init().
116  */
117 void
118 i_ddi_intr_set_supported_types(dev_info_t *dip, int intr_types)
119 {
120 	devinfo_intr_t		*intr_p = DEVI(dip)->devi_intr_p;
121 
122 	if (intr_p)
123 		intr_p->devi_intr_sup_types = intr_types;
124 }
125 
126 uint_t
127 i_ddi_intr_get_supported_nintrs(dev_info_t *dip, int intr_type)
128 {
129 	devinfo_intr_t		*intr_p = DEVI(dip)->devi_intr_p;
130 	ddi_intr_handle_impl_t	hdl;
131 	int			ret, nintrs;
132 
133 	if ((intr_p) && (intr_p->devi_intr_curr_type == intr_type) &&
134 	    (intr_p->devi_intr_sup_nintrs))
135 		return (intr_p->devi_intr_sup_nintrs);
136 
137 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
138 	hdl.ih_dip = dip;
139 	hdl.ih_type = intr_type;
140 
141 	ret = i_ddi_intr_ops(dip, dip, DDI_INTROP_NINTRS, &hdl,
142 	    (void *)&nintrs);
143 
144 	return ((ret == DDI_SUCCESS) ? nintrs : 0);
145 }
146 
147 /*
148  * NOTE: This function is only called by ddi_intr_alloc().
149  */
150 void
151 i_ddi_intr_set_supported_nintrs(dev_info_t *dip, int nintrs)
152 {
153 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
154 
155 	if (intr_p)
156 		intr_p->devi_intr_sup_nintrs = nintrs;
157 }
158 
159 uint_t
160 i_ddi_intr_get_current_type(dev_info_t *dip)
161 {
162 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
163 
164 	return (intr_p ? intr_p->devi_intr_curr_type : 0);
165 }
166 
167 /*
168  * NOTE: This function is only called by
169  *       ddi_intr_alloc() and ddi_intr_free().
170  */
171 void
172 i_ddi_intr_set_current_type(dev_info_t *dip, int intr_type)
173 {
174 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
175 
176 	if (intr_p)
177 		intr_p->devi_intr_curr_type = intr_type;
178 }
179 
180 uint_t
181 i_ddi_intr_get_current_nintrs(dev_info_t *dip)
182 {
183 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
184 
185 	return (intr_p ? intr_p->devi_intr_curr_nintrs : 0);
186 }
187 
188 /*
189  * NOTE: This function is only called by
190  *       ddi_intr_alloc() and ddi_intr_free().
191  */
192 void
193 i_ddi_intr_set_current_nintrs(dev_info_t *dip, int nintrs)
194 {
195 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
196 
197 	if (intr_p)
198 		intr_p->devi_intr_curr_nintrs = nintrs;
199 }
200 
201 ddi_intr_msix_t *
202 i_ddi_get_msix(dev_info_t *dip)
203 {
204 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
205 
206 	return (intr_p ? intr_p->devi_msix_p : NULL);
207 }
208 
209 void
210 i_ddi_set_msix(dev_info_t *dip, ddi_intr_msix_t *msix_p)
211 {
212 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
213 
214 	if (intr_p)
215 		intr_p->devi_msix_p = msix_p;
216 }
217 
218 ddi_intr_handle_t *
219 i_ddi_get_intr_handle(dev_info_t *dip, int inum)
220 {
221 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
222 
223 	if (intr_p == NULL)
224 		return (NULL);
225 
226 	/*
227 	 * Changed this to a check and return NULL if an invalid inum
228 	 * is passed to retrieve a handle
229 	 */
230 	if ((inum < 0) || (inum >= intr_p->devi_intr_sup_nintrs))
231 		return (NULL);
232 
233 	return ((intr_p->devi_intr_handle_p) ?
234 	    intr_p->devi_intr_handle_p[inum] : NULL);
235 }
236 
237 void
238 i_ddi_set_intr_handle(dev_info_t *dip, int inum,
239     ddi_intr_handle_t *intr_hdlp)
240 {
241 	devinfo_intr_t	*intr_p = DEVI(dip)->devi_intr_p;
242 
243 	if (intr_p == NULL)
244 		return;
245 
246 	/*
247 	 * Changed this to a check and return if an invalid inum
248 	 * is passed to set a handle
249 	 */
250 	if ((inum < 0) || (inum >= intr_p->devi_intr_sup_nintrs))
251 		return;
252 
253 	if (intr_hdlp && (intr_p->devi_intr_handle_p == NULL)) {
254 		/* nintrs could be zero; so check for it first */
255 		if (intr_p->devi_intr_sup_nintrs)
256 			intr_p->devi_intr_handle_p = kmem_zalloc(
257 			    sizeof (ddi_intr_handle_t) *
258 			    intr_p->devi_intr_sup_nintrs, KM_SLEEP);
259 	}
260 
261 	if (intr_p->devi_intr_handle_p)
262 		intr_p->devi_intr_handle_p[inum] = intr_hdlp;
263 }
264 
265 /*
266  * The "ddi-intr-weight" property contains the weight of each interrupt
267  * associated with a dev_info node. For devices with multiple interrupts per
268  * dev_info node, the total load of the device is "devi_intr_weight * nintr",
269  * possibly spread out over multiple CPUs.
270  *
271  * Maintaining this as a property permits possible tweaking in the product
272  * in response to customer problems via driver.conf property definitions at
273  * the driver or the instance level.  This does not mean that "ddi-intr_weight"
274  * is a formal or committed interface.
275  */
276 int32_t
277 i_ddi_get_intr_weight(dev_info_t *dip)
278 {
279 	int32_t	weight;
280 
281 	weight = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
282 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "ddi-intr-weight", -1);
283 	if (weight < -1)
284 		weight = -1;			/* undefined */
285 	return (weight);
286 }
287 
288 int32_t
289 i_ddi_set_intr_weight(dev_info_t *dip, int32_t weight)
290 {
291 	int32_t oweight;
292 
293 	oweight = i_ddi_get_intr_weight(dip);
294 	if ((weight > 0) && (oweight != weight))
295 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
296 		    "ddi-intr-weight", weight);
297 	return (oweight);
298 }
299 
300 /*
301  * Old DDI interrupt framework
302  *
303  * NOTE:
304  *	The following 4 busops entry points are obsoleted with version
305  *	9 or greater. Use i_ddi_intr_op interface in place of these
306  *	obsolete interfaces.
307  *
308  *	Remove these busops entry points and all related data structures
309  *	in future major/minor solaris release.
310  */
311 
312 /* ARGSUSED */
313 ddi_intrspec_t
314 i_ddi_get_intrspec(dev_info_t *dip, dev_info_t *rdip, uint_t inumber)
315 {
316 	dev_info_t	*pdip = ddi_get_parent(dip);
317 
318 	cmn_err(CE_WARN, "Failed to process interrupt "
319 	    "for %s%d due to down-rev nexus driver %s%d",
320 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
321 	    ddi_driver_name(pdip), ddi_get_instance(pdip));
322 
323 	return (NULL);
324 }
325 
326 /* ARGSUSED */
327 int
328 i_ddi_add_intrspec(dev_info_t *dip, dev_info_t *rdip, ddi_intrspec_t intrspec,
329     ddi_iblock_cookie_t *iblock_cookiep,
330     ddi_idevice_cookie_t *idevice_cookiep,
331     uint_t (*int_handler)(caddr_t int_handler_arg),
332     caddr_t int_handler_arg, int kind)
333 {
334 	dev_info_t	*pdip = ddi_get_parent(dip);
335 
336 	cmn_err(CE_WARN, "Failed to process interrupt "
337 	    "for %s%d due to down-rev nexus driver %s%d",
338 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
339 	    ddi_driver_name(pdip), ddi_get_instance(pdip));
340 
341 	return (DDI_ENOTSUP);
342 }
343 
344 /* ARGSUSED */
345 void
346 i_ddi_remove_intrspec(dev_info_t *dip, dev_info_t *rdip,
347     ddi_intrspec_t intrspec, ddi_iblock_cookie_t iblock_cookie)
348 {
349 	dev_info_t	*pdip = ddi_get_parent(dip);
350 
351 	cmn_err(CE_WARN, "Failed to process interrupt "
352 	    "for %s%d due to down-rev nexus driver %s%d",
353 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
354 	    ddi_driver_name(pdip), ddi_get_instance(pdip));
355 }
356 
357 /* ARGSUSED */
358 int
359 i_ddi_intr_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_ctlop_t op,
360     void *arg, void *val)
361 {
362 	dev_info_t	*pdip = ddi_get_parent(dip);
363 
364 	cmn_err(CE_WARN, "Failed to process interrupt "
365 	    "for %s%d due to down-rev nexus driver %s%d",
366 	    ddi_driver_name(rdip), ddi_get_instance(rdip),
367 	    ddi_driver_name(pdip), ddi_get_instance(pdip));
368 
369 	return (DDI_ENOTSUP);
370 }
371 
372 #if defined(__i386) || defined(__amd64)
373 ddi_acc_handle_t
374 i_ddi_get_pci_config_handle(dev_info_t *dip)
375 {
376 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
377 
378 	return (intr_p ? intr_p->devi_cfg_handle : NULL);
379 }
380 
381 void
382 i_ddi_set_pci_config_handle(dev_info_t *dip, ddi_acc_handle_t handle)
383 {
384 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
385 
386 	if (intr_p)
387 		intr_p->devi_cfg_handle = handle;
388 }
389 
390 
391 int
392 i_ddi_get_msi_msix_cap_ptr(dev_info_t *dip)
393 {
394 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
395 
396 	return (intr_p ? intr_p->devi_cap_ptr : 0);
397 }
398 
399 void
400 i_ddi_set_msi_msix_cap_ptr(dev_info_t *dip, int cap_ptr)
401 {
402 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
403 
404 	if (intr_p)
405 		intr_p->devi_cap_ptr = cap_ptr;
406 }
407 #endif
408 
409 /* ARGSUSED */
410 uint_t
411 i_ddi_get_msix_alloc_limit(dev_info_t *dip)
412 {
413 	uint_t	msix_alloc_limit = ddi_msix_alloc_limit;
414 
415 #if defined(__sparc)
416 	if (ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_NOTPROM |
417 	    DDI_PROP_DONTPASS, "#msix-request")) {
418 		msix_alloc_limit = MAX(DDI_MAX_MSIX_ALLOC,
419 		    ddi_msix_alloc_limit);
420 	}
421 #endif
422 
423 	return (msix_alloc_limit);
424 }
425