xref: /titanic_44/usr/src/uts/common/os/ddi_intr_impl.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 	ASSERT((inum >= 0) && (inum < intr_p->devi_intr_sup_nintrs));
218 
219 	return ((intr_p->devi_intr_handle_p) ?
220 	    intr_p->devi_intr_handle_p[inum] : NULL);
221 }
222 
223 void
224 i_ddi_set_intr_handle(dev_info_t *dip, int inum,
225     ddi_intr_handle_t *intr_hdlp)
226 {
227 	devinfo_intr_t	*intr_p = DEVI(dip)->devi_intr_p;
228 
229 	if (intr_p == NULL)
230 		return;
231 
232 	ASSERT((inum >= 0) && (inum < intr_p->devi_intr_sup_nintrs));
233 
234 	if (intr_p->devi_intr_handle_p == NULL) {
235 		/* nintrs could be zero; so check for it first */
236 		if (intr_p->devi_intr_sup_nintrs)
237 			intr_p->devi_intr_handle_p = kmem_zalloc(
238 			    sizeof (ddi_intr_handle_t) *
239 			    intr_p->devi_intr_sup_nintrs, KM_SLEEP);
240 	}
241 
242 	intr_p->devi_intr_handle_p[inum] = intr_hdlp;
243 }
244 
245 /*
246  * The "ddi-intr-weight" property contains the weight of each interrupt
247  * associated with a dev_info node. For devices with multiple interrupts per
248  * dev_info node, the total load of the device is "devi_intr_weight * nintr",
249  * possibly spread out over multiple CPUs.
250  *
251  * Maintaining this as a property permits possible tweaking in the product
252  * in response to customer problems via driver.conf property definitions at
253  * the driver or the instance level.  This does not mean that "ddi-intr_weight"
254  * is a formal or committed interface.
255  */
256 int32_t
257 i_ddi_get_intr_weight(dev_info_t *dip)
258 {
259 	int32_t	weight;
260 
261 	weight = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
262 	    DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "ddi-intr-weight", -1);
263 	if (weight < -1)
264 		weight = -1;			/* undefined */
265 	return (weight);
266 }
267 
268 int32_t
269 i_ddi_set_intr_weight(dev_info_t *dip, int32_t weight)
270 {
271 	int32_t oweight;
272 
273 	oweight = i_ddi_get_intr_weight(dip);
274 	if ((weight > 0) && (oweight != weight))
275 		(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
276 		    "ddi-intr-weight", weight);
277 	return (oweight);
278 }
279 
280 /*
281  * Old DDI interrupt framework
282  *
283  * NOTE:
284  *	The following 4 busops entry points are obsoleted with version
285  *	9 or greater. Use i_ddi_intr_op interface in place of these
286  *	obsolete interfaces.
287  *
288  *	Remove these busops entry points and all related data structures
289  *	in future major/minor solaris release.
290  */
291 
292 /* ARGSUSED */
293 ddi_intrspec_t
294 i_ddi_get_intrspec(dev_info_t *dip, dev_info_t *rdip, uint_t inumber)
295 {
296 	dev_info_t	*pdip = ddi_get_parent(dip);
297 
298 	cmn_err(CE_WARN, "Failed to process interrupt "
299 	    "for %s%d due to down-rev nexus driver %s%d",
300 	    ddi_get_name(rdip), ddi_get_instance(rdip),
301 	    ddi_get_name(pdip), ddi_get_instance(pdip));
302 
303 	return (NULL);
304 }
305 
306 /* ARGSUSED */
307 int
308 i_ddi_add_intrspec(dev_info_t *dip, dev_info_t *rdip, ddi_intrspec_t intrspec,
309     ddi_iblock_cookie_t *iblock_cookiep,
310     ddi_idevice_cookie_t *idevice_cookiep,
311     uint_t (*int_handler)(caddr_t int_handler_arg),
312     caddr_t int_handler_arg, int kind)
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_get_name(rdip), ddi_get_instance(rdip),
319 	    ddi_get_name(pdip), ddi_get_instance(pdip));
320 
321 	return (DDI_ENOTSUP);
322 }
323 
324 /* ARGSUSED */
325 void
326 i_ddi_remove_intrspec(dev_info_t *dip, dev_info_t *rdip,
327     ddi_intrspec_t intrspec, ddi_iblock_cookie_t iblock_cookie)
328 {
329 	dev_info_t	*pdip = ddi_get_parent(dip);
330 
331 	cmn_err(CE_WARN, "Failed to process interrupt "
332 	    "for %s%d due to down-rev nexus driver %s%d",
333 	    ddi_get_name(rdip), ddi_get_instance(rdip),
334 	    ddi_get_name(pdip), ddi_get_instance(pdip));
335 }
336 
337 /* ARGSUSED */
338 int
339 i_ddi_intr_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_ctlop_t op,
340     void *arg, void *val)
341 {
342 	dev_info_t	*pdip = ddi_get_parent(dip);
343 
344 	cmn_err(CE_WARN, "Failed to process interrupt "
345 	    "for %s%d due to down-rev nexus driver %s%d",
346 	    ddi_get_name(rdip), ddi_get_instance(rdip),
347 	    ddi_get_name(pdip), ddi_get_instance(pdip));
348 
349 	return (DDI_ENOTSUP);
350 }
351