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