xref: /illumos-gate/usr/src/uts/common/os/ddi_intr_impl.c (revision 956e8222f10bf55e45b41d8b56084f72ebc113c9)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/note.h>
30 #include <sys/sysmacros.h>
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kmem.h>
35 #include <sys/cmn_err.h>
36 #include <sys/debug.h>
37 #include <sys/avintr.h>
38 #include <sys/autoconf.h>
39 #include <sys/sunndi.h>
40 #include <sys/ndi_impldefs.h>	/* include prototypes */
41 
42 /*
43  * New DDI interrupt framework
44  */
45 void
46 i_ddi_intr_devi_init(dev_info_t *dip)
47 {
48 	int	supported_types;
49 
50 	DDI_INTR_APIDBG((CE_CONT, "i_ddi_intr_devi_init: dip %p\n",
51 	    (void *)dip));
52 
53 	if (DEVI(dip)->devi_intr_p)
54 		return;
55 
56 	DEVI(dip)->devi_intr_p = kmem_zalloc(sizeof (devinfo_intr_t),
57 	    KM_SLEEP);
58 
59 	supported_types = i_ddi_intr_get_supported_types(dip);
60 
61 	/* Save supported interrupt types information */
62 	i_ddi_intr_set_supported_types(dip, supported_types);
63 }
64 
65 void
66 i_ddi_intr_devi_fini(dev_info_t *dip)
67 {
68 	devinfo_intr_t	*intr_p = DEVI(dip)->devi_intr_p;
69 
70 	/*
71 	 * devi_intr_handle_p will only be used for devices
72 	 * which are using the legacy DDI Interrupt interfaces.
73 	 */
74 	if (intr_p->devi_intr_handle_p) {
75 		/* nintrs could be zero; so check for it first */
76 		if (intr_p->devi_intr_sup_nintrs) {
77 			kmem_free(intr_p->devi_intr_handle_p,
78 			    intr_p->devi_intr_sup_nintrs *
79 			    sizeof (ddi_intr_handle_t));
80 		}
81 	}
82 	kmem_free(DEVI(dip)->devi_intr_p, sizeof (devinfo_intr_t));
83 	DEVI(dip)->devi_intr_p = NULL;
84 }
85 
86 uint_t
87 i_ddi_intr_get_supported_types(dev_info_t *dip)
88 {
89 	devinfo_intr_t		*intr_p = DEVI(dip)->devi_intr_p;
90 	ddi_intr_handle_impl_t	hdl;
91 	int			ret, intr_types;
92 
93 	if ((intr_p) && (intr_p->devi_intr_sup_types))
94 		return (intr_p->devi_intr_sup_types);
95 
96 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
97 	hdl.ih_dip = dip;
98 
99 	ret = i_ddi_handle_intr_ops(dip, dip, DDI_INTROP_SUPPORTED_TYPES, &hdl,
100 	    (void *)&intr_types);
101 
102 	return ((ret == DDI_SUCCESS) ? intr_types : 0);
103 }
104 
105 /*
106  * NOTE: This function is only called by i_ddi_dev_init().
107  */
108 void
109 i_ddi_intr_set_supported_types(dev_info_t *dip, int intr_types)
110 {
111 	devinfo_intr_t		*intr_p = DEVI(dip)->devi_intr_p;
112 
113 	if (intr_p)
114 		intr_p->devi_intr_sup_types = intr_types;
115 }
116 
117 uint_t
118 i_ddi_intr_get_supported_nintrs(dev_info_t *dip, int intr_type)
119 {
120 	devinfo_intr_t		*intr_p = DEVI(dip)->devi_intr_p;
121 	ddi_intr_handle_impl_t	hdl;
122 	int			ret, nintrs;
123 
124 	if ((intr_p) && (intr_p->devi_intr_curr_type == intr_type) &&
125 	    (intr_p->devi_intr_sup_nintrs))
126 		return (intr_p->devi_intr_sup_nintrs);
127 
128 	bzero(&hdl, sizeof (ddi_intr_handle_impl_t));
129 	hdl.ih_dip = dip;
130 	hdl.ih_type = intr_type;
131 
132 	ret = i_ddi_handle_intr_ops(dip, dip, DDI_INTROP_NINTRS, &hdl,
133 	    (void *)&nintrs);
134 
135 	return ((ret == DDI_SUCCESS) ? nintrs : 0);
136 }
137 
138 /*
139  * NOTE: This function is only called by ddi_intr_alloc().
140  */
141 void
142 i_ddi_intr_set_supported_nintrs(dev_info_t *dip, int nintrs)
143 {
144 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
145 
146 	if (intr_p)
147 		intr_p->devi_intr_sup_nintrs = nintrs;
148 }
149 
150 uint_t
151 i_ddi_intr_get_current_type(dev_info_t *dip)
152 {
153 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
154 
155 	return (intr_p ? intr_p->devi_intr_curr_type : 0);
156 }
157 
158 /*
159  * NOTE: This function is only called by
160  *       ddi_intr_alloc() and ddi_intr_free().
161  */
162 void
163 i_ddi_intr_set_current_type(dev_info_t *dip, int intr_type)
164 {
165 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
166 
167 	if (intr_p)
168 		intr_p->devi_intr_curr_type = intr_type;
169 }
170 
171 uint_t
172 i_ddi_intr_get_current_nintrs(dev_info_t *dip)
173 {
174 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
175 
176 	return (intr_p ? intr_p->devi_intr_curr_nintrs : 0);
177 }
178 
179 /*
180  * NOTE: This function is only called by
181  *       ddi_intr_alloc() and ddi_intr_free().
182  */
183 void
184 i_ddi_intr_set_current_nintrs(dev_info_t *dip, int nintrs)
185 {
186 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
187 
188 	if (intr_p)
189 		intr_p->devi_intr_curr_nintrs = nintrs;
190 }
191 
192 ddi_intr_msix_t *
193 i_ddi_get_msix(dev_info_t *dip)
194 {
195 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
196 
197 	return (intr_p ? intr_p->devi_msix_p : NULL);
198 }
199 
200 void
201 i_ddi_set_msix(dev_info_t *dip, ddi_intr_msix_t *msix_p)
202 {
203 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
204 
205 	if (intr_p)
206 		intr_p->devi_msix_p = msix_p;
207 }
208 
209 ddi_intr_handle_t *
210 i_ddi_get_intr_handle(dev_info_t *dip, int inum)
211 {
212 	devinfo_intr_t *intr_p = DEVI(dip)->devi_intr_p;
213 
214 	if (intr_p == NULL)
215 		return (NULL);
216 
217 	/*
218 	 * Changed this to a check and return NULL if an invalid inum
219 	 * is passed to retrieve a handle
220 	 */
221 	if ((inum < 0) || (inum >= intr_p->devi_intr_sup_nintrs))
222 		return (NULL);
223 
224 	return ((intr_p->devi_intr_handle_p) ?
225 	    intr_p->devi_intr_handle_p[inum] : NULL);
226 }
227 
228 void
229 i_ddi_set_intr_handle(dev_info_t *dip, int inum,
230     ddi_intr_handle_t *intr_hdlp)
231 {
232 	devinfo_intr_t	*intr_p = DEVI(dip)->devi_intr_p;
233 
234 	if (intr_p == NULL)
235 		return;
236 
237 	/*
238 	 * Changed this to a check and return if an invalid inum
239 	 * is passed to set a handle
240 	 */
241 	if ((inum < 0) || (inum >= intr_p->devi_intr_sup_nintrs))
242 		return;
243 
244 	if (intr_p->devi_intr_handle_p == NULL) {
245 		/* nintrs could be zero; so check for it first */
246 		if (intr_p->devi_intr_sup_nintrs)
247 			intr_p->devi_intr_handle_p = kmem_zalloc(
248 			    sizeof (ddi_intr_handle_t) *
249 			    intr_p->devi_intr_sup_nintrs, KM_SLEEP);
250 	}
251 
252 	intr_p->devi_intr_handle_p[inum] = intr_hdlp;
253 }
254 
255 /*
256  * The "ddi-intr-weight" property contains the weight of each interrupt
257  * associated with a dev_info node. For devices with multiple interrupts per
258  * dev_info node, the total load of the device is "devi_intr_weight * nintr",
259  * possibly spread out over multiple CPUs.
260  *
261  * Maintaining this as a property permits possible tweaking in the product
262  * in response to customer problems via driver.conf property definitions at
263  * the driver or the instance level.  This does not mean that "ddi-intr_weight"
264  * is a formal or committed interface.
265  */
266 int32_t
267 i_ddi_get_intr_weight(dev_info_t *dip)
268 {
269 	int32_t	weight;
270 
271 	weight = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
272 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "ddi-intr-weight", -1);
273 	if (weight < -1)
274 		weight = -1;			/* undefined */
275 	return (weight);
276 }
277 
278 int32_t
279 i_ddi_set_intr_weight(dev_info_t *dip, int32_t weight)
280 {
281 	int32_t oweight;
282 
283 	oweight = i_ddi_get_intr_weight(dip);
284 	if ((weight > 0) && (oweight != weight))
285 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
286 		    "ddi-intr-weight", weight);
287 	return (oweight);
288 }
289 
290 /*
291  * Old DDI interrupt framework
292  *
293  * NOTE:
294  *	The following 4 busops entry points are obsoleted with version
295  *	9 or greater. Use i_ddi_intr_op interface in place of these
296  *	obsolete interfaces.
297  *
298  *	Remove these busops entry points and all related data structures
299  *	in future major/minor solaris release.
300  */
301 
302 /* ARGSUSED */
303 ddi_intrspec_t
304 i_ddi_get_intrspec(dev_info_t *dip, dev_info_t *rdip, uint_t inumber)
305 {
306 	dev_info_t	*pdip = ddi_get_parent(dip);
307 
308 	cmn_err(CE_WARN, "Failed to process interrupt "
309 	    "for %s%d due to down-rev nexus driver %s%d",
310 	    ddi_get_name(rdip), ddi_get_instance(rdip),
311 	    ddi_get_name(pdip), ddi_get_instance(pdip));
312 
313 	return (NULL);
314 }
315 
316 /* ARGSUSED */
317 int
318 i_ddi_add_intrspec(dev_info_t *dip, dev_info_t *rdip, ddi_intrspec_t intrspec,
319     ddi_iblock_cookie_t *iblock_cookiep,
320     ddi_idevice_cookie_t *idevice_cookiep,
321     uint_t (*int_handler)(caddr_t int_handler_arg),
322     caddr_t int_handler_arg, int kind)
323 {
324 	dev_info_t	*pdip = ddi_get_parent(dip);
325 
326 	cmn_err(CE_WARN, "Failed to process interrupt "
327 	    "for %s%d due to down-rev nexus driver %s%d",
328 	    ddi_get_name(rdip), ddi_get_instance(rdip),
329 	    ddi_get_name(pdip), ddi_get_instance(pdip));
330 
331 	return (DDI_ENOTSUP);
332 }
333 
334 /* ARGSUSED */
335 void
336 i_ddi_remove_intrspec(dev_info_t *dip, dev_info_t *rdip,
337     ddi_intrspec_t intrspec, ddi_iblock_cookie_t iblock_cookie)
338 {
339 	dev_info_t	*pdip = ddi_get_parent(dip);
340 
341 	cmn_err(CE_WARN, "Failed to process interrupt "
342 	    "for %s%d due to down-rev nexus driver %s%d",
343 	    ddi_get_name(rdip), ddi_get_instance(rdip),
344 	    ddi_get_name(pdip), ddi_get_instance(pdip));
345 }
346 
347 /* ARGSUSED */
348 int
349 i_ddi_intr_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_ctlop_t op,
350     void *arg, void *val)
351 {
352 	dev_info_t	*pdip = ddi_get_parent(dip);
353 
354 	cmn_err(CE_WARN, "Failed to process interrupt "
355 	    "for %s%d due to down-rev nexus driver %s%d",
356 	    ddi_get_name(rdip), ddi_get_instance(rdip),
357 	    ddi_get_name(pdip), ddi_get_instance(pdip));
358 
359 	return (DDI_ENOTSUP);
360 }
361