xref: /illumos-gate/usr/src/uts/common/io/pci_intr_lib.c (revision f83ffe1aa13dc057aa65c36bacc83297b35f9be2)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Support for MSI, MSIX and INTx
28  */
29 
30 #include <sys/conf.h>
31 #include <sys/debug.h>
32 #include <sys/pci.h>
33 #include <sys/pci_cap.h>
34 #include <sys/pci_intr_lib.h>
35 #include <sys/sunddi.h>
36 #include <sys/bitmap.h>
37 
38 /*
39  * MSI-X BIR Index Table:
40  *
41  * BAR indicator register (BIR) to Base Address register.
42  */
43 static	uchar_t pci_msix_bir_index[8] = {0x10, 0x14, 0x18, 0x1c,
44 					0x20, 0x24, 0xff, 0xff};
45 
46 /* default class to pil value mapping */
47 pci_class_val_t pci_default_pil [] = {
48 	{0x000000, 0xff0000, 0x1},	/* Class code for pre-2.0 devices */
49 	{0x010000, 0xff0000, 0x5},	/* Mass Storage Controller */
50 	{0x020000, 0xff0000, 0x6},	/* Network Controller */
51 	{0x030000, 0xff0000, 0x9},	/* Display Controller */
52 	{0x040000, 0xff0000, 0x8},	/* Multimedia Controller */
53 	{0x050000, 0xff0000, 0x9},	/* Memory Controller */
54 	{0x060000, 0xff0000, 0x9},	/* Bridge Controller */
55 	{0x0c0000, 0xffff00, 0x9},	/* Serial Bus, FireWire (IEEE 1394) */
56 	{0x0c0100, 0xffff00, 0x4},	/* Serial Bus, ACCESS.bus */
57 	{0x0c0200, 0xffff00, 0x4},	/* Serial Bus, SSA */
58 	{0x0c0300, 0xffff00, 0x9},	/* Serial Bus Universal Serial Bus */
59 /*
60  * XXX - This is a temporary workaround and it will be removed
61  *       after x86 interrupt scalability support.
62  */
63 #if defined(__i386) || defined(__amd64)
64 	{0x0c0400, 0xffff00, 0x5},	/* Serial Bus, Fibre Channel */
65 #else
66 	{0x0c0400, 0xffff00, 0x6},	/* Serial Bus, Fibre Channel */
67 #endif
68 	{0x0c0600, 0xffff00, 0x6}	/* Serial Bus, Infiniband */
69 };
70 
71 /*
72  * Default class to intr_weight value mapping (% of CPU).  A driver.conf
73  * entry on or above the pci node like
74  *
75  *	pci-class-intr-weights= 0x020000, 0xff0000, 30;
76  *
77  * can be used to augment or override entries in the default table below.
78  *
79  * NB: The values below give NICs preference on redistribution, and provide
80  * NICs some isolation from other interrupt sources. We need better interfaces
81  * that allow the NIC driver to identify a specific NIC instance as high
82  * bandwidth, and thus deserving of separation from other low bandwidth
83  * NICs additional isolation from other interrupt sources.
84  *
85  * NB: We treat Infiniband like a NIC.
86  */
87 pci_class_val_t pci_default_intr_weight [] = {
88 	{0x020000, 0xff0000, 35},	/* Network Controller */
89 	{0x010000, 0xff0000, 10},	/* Mass Storage Controller */
90 	{0x0c0400, 0xffff00, 10},	/* Serial Bus, Fibre Channel */
91 	{0x0c0600, 0xffff00, 50}	/* Serial Bus, Infiniband */
92 };
93 
94 /*
95  * Library utility functions
96  */
97 
98 /*
99  * pci_get_msi_ctrl:
100  *
101  *	Helper function that returns with 'cfg_hdl', MSI/X ctrl pointer,
102  *	and caps_ptr for MSI/X if these are found.
103  */
104 static int
105 pci_get_msi_ctrl(dev_info_t *dip, int type, ushort_t *msi_ctrl,
106     ushort_t *caps_ptr, ddi_acc_handle_t *h)
107 {
108 	*msi_ctrl = *caps_ptr = 0;
109 
110 	if (pci_config_setup(dip, h) != DDI_SUCCESS) {
111 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: "
112 		    "%s%d can't get config handle",
113 		    ddi_driver_name(dip), ddi_get_instance(dip)));
114 
115 		return (DDI_FAILURE);
116 	}
117 
118 	if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI, caps_ptr) == DDI_SUCCESS) &&
119 	    (type == DDI_INTR_TYPE_MSI)) {
120 		if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr,
121 		    PCI_MSI_CTRL)) == PCI_CAP_EINVAL16)
122 			goto done;
123 
124 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI "
125 		    "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
126 
127 		return (DDI_SUCCESS);
128 	}
129 
130 	if ((PCI_CAP_LOCATE(*h, PCI_CAP_ID_MSI_X, caps_ptr) == DDI_SUCCESS) &&
131 	    (type == DDI_INTR_TYPE_MSIX)) {
132 		if ((*msi_ctrl = PCI_CAP_GET16(*h, NULL, *caps_ptr,
133 		    PCI_MSIX_CTRL)) == PCI_CAP_EINVAL16)
134 			goto done;
135 
136 		DDI_INTR_NEXDBG((CE_CONT, "pci_get_msi_ctrl: MSI-X "
137 		    "caps_ptr=%x msi_ctrl=%x\n", *caps_ptr, *msi_ctrl));
138 
139 		return (DDI_SUCCESS);
140 	}
141 
142 done:
143 	pci_config_teardown(h);
144 	return (DDI_FAILURE);
145 }
146 
147 
148 /*
149  * pci_msi_get_cap:
150  *
151  * Get the capabilities of the MSI/X interrupt
152  */
153 int
154 pci_msi_get_cap(dev_info_t *rdip, int type, int *flagsp)
155 {
156 	ushort_t		caps_ptr, msi_ctrl;
157 	ddi_acc_handle_t	cfg_hdle;
158 
159 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: rdip = 0x%p\n",
160 	    (void *)rdip));
161 
162 	*flagsp = 0;
163 
164 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
165 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
166 		return (DDI_FAILURE);
167 
168 	if (type == DDI_INTR_TYPE_MSI) {
169 		if (msi_ctrl &  PCI_MSI_64BIT_MASK)
170 			*flagsp |= DDI_INTR_FLAG_MSI64;
171 		if (msi_ctrl & PCI_MSI_PVM_MASK)
172 			*flagsp |= (DDI_INTR_FLAG_MASKABLE |
173 			    DDI_INTR_FLAG_PENDING);
174 		else
175 			*flagsp |= DDI_INTR_FLAG_BLOCK;
176 	} else if (type == DDI_INTR_TYPE_MSIX) {
177 		/* MSI-X supports PVM, 64bit by default */
178 		*flagsp |= (DDI_INTR_FLAG_MASKABLE | DDI_INTR_FLAG_MSI64 |
179 		    DDI_INTR_FLAG_PENDING);
180 	}
181 
182 	*flagsp |= DDI_INTR_FLAG_EDGE;
183 
184 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_cap: flags = 0x%x\n", *flagsp));
185 
186 	pci_config_teardown(&cfg_hdle);
187 	return (DDI_SUCCESS);
188 }
189 
190 
191 /*
192  * pci_msi_configure:
193  *
194  * Configure address/data and number MSI/Xs fields in the MSI/X
195  * capability structure.
196  */
197 /* ARGSUSED */
198 int
199 pci_msi_configure(dev_info_t *rdip, int type, int count, int inum,
200     uint64_t addr, uint64_t data)
201 {
202 	ushort_t		caps_ptr, msi_ctrl;
203 	ddi_acc_handle_t	h;
204 
205 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: rdip = 0x%p type 0x%x "
206 	    "count 0x%x inum 0x%x addr 0x%" PRIx64 " data 0x%" PRIx64 "\n",
207 	    (void *)rdip, type, count, inum, addr, data));
208 
209 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
210 	    &caps_ptr, &h) != DDI_SUCCESS)
211 		return (DDI_FAILURE);
212 
213 	if (type == DDI_INTR_TYPE_MSI) {
214 		/* Set the bits to inform how many MSIs are enabled */
215 		msi_ctrl |= ((highbit(count) -1) << PCI_MSI_MME_SHIFT);
216 		PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
217 
218 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_ctrl = %x\n",
219 		    PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL)));
220 
221 		/* Set the "data" and "addr" bits */
222 		PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, addr);
223 
224 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_addr = %x\n",
225 		    PCI_CAP_GET32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET)));
226 
227 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
228 			PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET
229 			    + 4, addr >> 32);
230 
231 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: upper "
232 			    "32bit msi_addr = %x\n", PCI_CAP_GET32(h, NULL,
233 			    caps_ptr, PCI_MSI_ADDR_OFFSET + 4)));
234 
235 			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA,
236 			    data);
237 
238 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
239 			    "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr,
240 			    PCI_MSI_64BIT_DATA)));
241 		} else {
242 			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA,
243 			    data);
244 
245 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: msi_data "
246 			    "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr,
247 			    PCI_MSI_32BIT_DATA)));
248 		}
249 	} else if (type == DDI_INTR_TYPE_MSIX) {
250 		uintptr_t	off;
251 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
252 
253 		/* Offset into the "inum"th entry in the MSI-X table */
254 		off = (uintptr_t)msix_p->msix_tbl_addr +
255 		    (inum * PCI_MSIX_VECTOR_SIZE);
256 
257 		/* Set the "data" and "addr" bits */
258 		ddi_put32(msix_p->msix_tbl_hdl,
259 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), data);
260 
261 		ddi_put64(msix_p->msix_tbl_hdl,
262 		    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), addr);
263 
264 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_configure: "
265 		    "msix_addr 0x%" PRIx64 " msix_data 0x%x\n",
266 		    ddi_get64(msix_p->msix_tbl_hdl,
267 		    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET)),
268 		    ddi_get32(msix_p->msix_tbl_hdl,
269 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET))));
270 	}
271 
272 	pci_config_teardown(&h);
273 	return (DDI_SUCCESS);
274 }
275 
276 
277 /*
278  * pci_msi_unconfigure:
279  *
280  * Unconfigure address/data and number MSI/Xs fields in the MSI/X
281  * capability structure.
282  */
283 /* ARGSUSED */
284 int
285 pci_msi_unconfigure(dev_info_t *rdip, int type, int inum)
286 {
287 	ushort_t		msi_ctrl, caps_ptr;
288 	ddi_acc_handle_t	h;
289 
290 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: rdip = 0x%p type 0x%x "
291 	    "inum 0x%x\n", (void *)rdip, type, inum));
292 
293 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl, &caps_ptr, &h) !=
294 	    DDI_SUCCESS)
295 		return (DDI_FAILURE);
296 
297 	if (type == DDI_INTR_TYPE_MSI) {
298 		msi_ctrl &= (~PCI_MSI_MME_MASK);
299 		PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
300 
301 		PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET, 0);
302 
303 		if (msi_ctrl &  PCI_MSI_64BIT_MASK) {
304 			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_64BIT_DATA,
305 			    0);
306 			PCI_CAP_PUT32(h, NULL, caps_ptr, PCI_MSI_ADDR_OFFSET
307 			    + 4, 0);
308 		} else {
309 			PCI_CAP_PUT16(h, NULL, caps_ptr, PCI_MSI_32BIT_DATA,
310 			    0);
311 		}
312 
313 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_unconfigure: msi_ctrl "
314 		    "= %x\n", PCI_CAP_GET16(h, NULL, caps_ptr, PCI_MSI_CTRL)));
315 
316 	} else if (type == DDI_INTR_TYPE_MSIX) {
317 		uintptr_t	off;
318 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
319 
320 		/* Offset into the "inum"th entry in the MSI-X table */
321 		off = (uintptr_t)msix_p->msix_tbl_addr +
322 		    (inum * PCI_MSIX_VECTOR_SIZE);
323 
324 		/* Reset the "data" and "addr" bits */
325 		ddi_put32(msix_p->msix_tbl_hdl,
326 		    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), 0);
327 
328 		ddi_put64(msix_p->msix_tbl_hdl,
329 		    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), 0);
330 	}
331 
332 	pci_config_teardown(&h);
333 	return (DDI_SUCCESS);
334 }
335 
336 
337 /*
338  * pci_is_msi_enabled:
339  *
340  * This function returns DDI_SUCCESS if MSI/X is already enabled, otherwise
341  * it returns DDI_FAILURE.
342  */
343 int
344 pci_is_msi_enabled(dev_info_t *rdip, int type)
345 {
346 	ushort_t		caps_ptr, msi_ctrl;
347 	ddi_acc_handle_t	cfg_hdle;
348 	int			ret = DDI_FAILURE;
349 
350 	DDI_INTR_NEXDBG((CE_CONT, "pci_is_msi_enabled: rdip = 0x%p, "
351 	    "type  = 0x%x\n", (void *)rdip, type));
352 
353 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
354 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
355 		return (DDI_FAILURE);
356 
357 	if ((type == DDI_INTR_TYPE_MSI) && (msi_ctrl & PCI_MSI_ENABLE_BIT))
358 		ret = DDI_SUCCESS;
359 
360 	if ((type == DDI_INTR_TYPE_MSIX) && (msi_ctrl & PCI_MSIX_ENABLE_BIT))
361 		ret = DDI_SUCCESS;
362 
363 	pci_config_teardown(&cfg_hdle);
364 	return (ret);
365 }
366 
367 
368 /*
369  * pci_msi_enable_mode:
370  *
371  * This function sets the MSI_ENABLE bit in the capability structure
372  * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
373  *
374  * NOTE: It is the nexus driver's responsibility to clear the MSI/X
375  * interrupt's mask bit in the MSI/X capability structure before the
376  * interrupt can be used.
377  */
378 int
379 pci_msi_enable_mode(dev_info_t *rdip, int type)
380 {
381 	ushort_t		caps_ptr, msi_ctrl;
382 	ddi_acc_handle_t	cfg_hdle;
383 
384 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: rdip = 0x%p\n",
385 	    (void *)rdip));
386 
387 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
388 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
389 		return (DDI_FAILURE);
390 
391 	if (type == DDI_INTR_TYPE_MSI) {
392 		if (msi_ctrl & PCI_MSI_ENABLE_BIT)
393 			goto finished;
394 
395 		msi_ctrl |= PCI_MSI_ENABLE_BIT;
396 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
397 
398 	} else if (type == DDI_INTR_TYPE_MSIX) {
399 		if (msi_ctrl & PCI_MSIX_ENABLE_BIT)
400 			goto finished;
401 
402 		msi_ctrl |= PCI_MSIX_ENABLE_BIT;
403 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL,
404 		    msi_ctrl);
405 	}
406 
407 finished:
408 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_enable_mode: msi_ctrl = %x\n",
409 	    msi_ctrl));
410 
411 	pci_config_teardown(&cfg_hdle);
412 	return (DDI_SUCCESS);
413 }
414 
415 
416 /*
417  * pci_msi_disable_mode:
418  *
419  * This function resets the MSI_ENABLE bit in the capability structure
420  * (for MSI) and MSIX_ENABLE bit in the MSI-X capability structure.
421  *
422  * NOTE: It is the nexus driver's responsibility to set the MSI/X
423  * interrupt's mask bit in the MSI/X capability structure before the
424  * interrupt can be disabled.
425  */
426 int
427 pci_msi_disable_mode(dev_info_t *rdip, int type, uint_t flags)
428 {
429 	ushort_t		caps_ptr, msi_ctrl;
430 	ddi_acc_handle_t	cfg_hdle;
431 
432 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: rdip = 0x%p "
433 	    "flags = 0x%x\n", (void *)rdip, flags));
434 
435 	/*
436 	 * Do not turn off the master enable bit if other interrupts are
437 	 * still active.
438 	 */
439 	if ((flags != DDI_INTR_FLAG_BLOCK) &&
440 	    (i_ddi_intr_get_current_nenables(rdip) > 1))
441 		return (DDI_SUCCESS);
442 
443 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
444 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
445 		return (DDI_FAILURE);
446 
447 	/* Reset the "enable" bit */
448 	if (type == DDI_INTR_TYPE_MSI) {
449 		if (!(msi_ctrl & PCI_MSI_ENABLE_BIT))
450 			goto finished;
451 		msi_ctrl &= ~PCI_MSI_ENABLE_BIT;
452 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
453 	} else if (type == DDI_INTR_TYPE_MSIX) {
454 		if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT))
455 			goto finished;
456 
457 		msi_ctrl &= ~PCI_MSIX_ENABLE_BIT;
458 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSIX_CTRL,
459 		    msi_ctrl);
460 	}
461 
462 finished:
463 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_disable_mode: msi_ctrl = %x\n",
464 	    msi_ctrl));
465 
466 	pci_config_teardown(&cfg_hdle);
467 	return (DDI_SUCCESS);
468 }
469 
470 
471 /*
472  * pci_msi_set_mask:
473  *
474  * Set the mask bit in the MSI/X capability structure
475  */
476 /* ARGSUSED */
477 int
478 pci_msi_set_mask(dev_info_t *rdip, int type, int inum)
479 {
480 	int			offset;
481 	int			ret = DDI_FAILURE;
482 	ushort_t		caps_ptr, msi_ctrl;
483 	ddi_acc_handle_t	cfg_hdle;
484 	uint32_t		mask_bits;
485 
486 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_mask: rdip = 0x%p, "
487 	    "type = 0x%x\n", (void *)rdip, type));
488 
489 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
490 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
491 		return (DDI_FAILURE);
492 
493 	if (type == DDI_INTR_TYPE_MSI) {
494 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
495 			goto done;
496 
497 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
498 		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
499 
500 		if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
501 		    offset)) == PCI_CAP_EINVAL32)
502 			goto done;
503 
504 		mask_bits |= (1 << inum);
505 
506 		PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits);
507 
508 	} else if (type == DDI_INTR_TYPE_MSIX) {
509 		uintptr_t		off;
510 		ddi_intr_msix_t		*msix_p;
511 
512 		/* Set function mask */
513 		if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
514 			ret = DDI_SUCCESS;
515 			goto done;
516 		}
517 
518 		msix_p = i_ddi_get_msix(rdip);
519 
520 		/* Offset into the "inum"th entry in the MSI-X table */
521 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
522 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
523 
524 		/* Set the Mask bit */
525 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x1);
526 	}
527 
528 	ret = DDI_SUCCESS;
529 done:
530 	pci_config_teardown(&cfg_hdle);
531 	return (ret);
532 }
533 
534 
535 /*
536  * pci_msi_clr_mask:
537  *
538  * Clear the mask bit in the MSI/X capability structure
539  */
540 /* ARGSUSED */
541 int
542 pci_msi_clr_mask(dev_info_t *rdip, int type, int inum)
543 {
544 	ushort_t		caps_ptr, msi_ctrl;
545 	ddi_acc_handle_t	cfg_hdle;
546 	int			offset;
547 	int			ret = DDI_FAILURE;
548 	uint32_t		mask_bits;
549 
550 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_clr_mask: rdip = 0x%p, "
551 	    "type = 0x%x\n", (void *)rdip, type));
552 
553 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
554 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
555 		return (DDI_FAILURE);
556 
557 	if (type == DDI_INTR_TYPE_MSI) {
558 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK))
559 			goto done;
560 
561 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
562 		    PCI_MSI_64BIT_MASKBITS : PCI_MSI_32BIT_MASK;
563 		if ((mask_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
564 		    offset)) == PCI_CAP_EINVAL32)
565 			goto done;
566 
567 		mask_bits &= ~(1 << inum);
568 
569 		PCI_CAP_PUT32(cfg_hdle, NULL, caps_ptr, offset, mask_bits);
570 
571 	} else if (type == DDI_INTR_TYPE_MSIX) {
572 		uintptr_t		off;
573 		ddi_intr_msix_t		*msix_p;
574 
575 		if (msi_ctrl & PCI_MSIX_FUNCTION_MASK) {
576 			ret = DDI_SUCCESS;
577 			goto done;
578 		}
579 
580 		msix_p = i_ddi_get_msix(rdip);
581 
582 		/* Offset into the "inum"th entry in the MSI-X table */
583 		off = (uintptr_t)msix_p->msix_tbl_addr + (inum *
584 		    PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET;
585 
586 		/* Clear the Mask bit */
587 		ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, 0x0);
588 	}
589 
590 	ret = DDI_SUCCESS;
591 done:
592 	pci_config_teardown(&cfg_hdle);
593 	return (ret);
594 }
595 
596 
597 /*
598  * pci_msi_get_pending:
599  *
600  * Get the pending bit from the MSI/X capability structure
601  */
602 /* ARGSUSED */
603 int
604 pci_msi_get_pending(dev_info_t *rdip, int type, int inum, int *pendingp)
605 {
606 	ushort_t		caps_ptr, msi_ctrl;
607 	ddi_acc_handle_t	cfg_hdle;
608 	int			offset;
609 	int			ret = DDI_FAILURE;
610 
611 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: rdip = 0x%p\n",
612 	    (void *)rdip));
613 
614 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
615 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
616 		return (DDI_FAILURE);
617 
618 	if (type == DDI_INTR_TYPE_MSI) {
619 		uint32_t	pending_bits;
620 
621 		if (!(msi_ctrl &  PCI_MSI_PVM_MASK)) {
622 			DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_pending: "
623 			    "PVM is not supported\n"));
624 			goto done;
625 		}
626 
627 		offset = (msi_ctrl &  PCI_MSI_64BIT_MASK) ?
628 		    PCI_MSI_64BIT_PENDING : PCI_MSI_32BIT_PENDING;
629 
630 		if ((pending_bits = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
631 		    offset)) == PCI_CAP_EINVAL32)
632 			goto done;
633 
634 		*pendingp = pending_bits & ~(1 >> inum);
635 
636 	} else if (type == DDI_INTR_TYPE_MSIX) {
637 		uintptr_t	off;
638 		uint64_t	pending_bits;
639 		ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
640 
641 		/* Offset into the PBA array which has entry for "inum" */
642 		off = (uintptr_t)msix_p->msix_pba_addr + (inum / 64);
643 
644 		/* Read the PBA array */
645 		pending_bits = ddi_get64(msix_p->msix_pba_hdl, (uint64_t *)off);
646 
647 		*pendingp = pending_bits & ~(1 >> inum);
648 	}
649 
650 	ret = DDI_SUCCESS;
651 done:
652 	pci_config_teardown(&cfg_hdle);
653 	return (ret);
654 }
655 
656 
657 /*
658  * pci_msi_get_nintrs:
659  *
660  * For a given type (MSI/X) returns the number of interrupts supported
661  */
662 int
663 pci_msi_get_nintrs(dev_info_t *rdip, int type, int *nintrs)
664 {
665 	ushort_t		caps_ptr, msi_ctrl;
666 	ddi_acc_handle_t	cfg_hdle;
667 
668 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: rdip = 0x%p\n",
669 	    (void *)rdip));
670 
671 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
672 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
673 		return (DDI_FAILURE);
674 
675 	if (type == DDI_INTR_TYPE_MSI) {
676 		*nintrs = 1 << ((msi_ctrl & PCI_MSI_MMC_MASK) >>
677 		    PCI_MSI_MMC_SHIFT);
678 	} else if (type == DDI_INTR_TYPE_MSIX) {
679 		if (msi_ctrl &  PCI_MSIX_TBL_SIZE_MASK)
680 			*nintrs = (msi_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1;
681 	}
682 
683 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_nintrs: "
684 	    "nintr = 0x%x\n", *nintrs));
685 
686 	pci_config_teardown(&cfg_hdle);
687 	return (DDI_SUCCESS);
688 }
689 
690 
691 /*
692  * pci_msi_set_nintrs:
693  *
694  * For a given type (MSI/X) sets the number of interrupts supported
695  * by the system.
696  * For MSI: Return an error if this func is called for navail > 32
697  * For MSI-X: Return an error if this func is called for navail > 2048
698  */
699 int
700 pci_msi_set_nintrs(dev_info_t *rdip, int type, int navail)
701 {
702 	ushort_t		caps_ptr, msi_ctrl;
703 	ddi_acc_handle_t	cfg_hdle;
704 
705 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: rdip = 0x%p, "
706 	    "navail = 0x%x\n", (void *)rdip, navail));
707 
708 	/* Check for valid input argument */
709 	if (((type == DDI_INTR_TYPE_MSI) && (navail > PCI_MSI_MAX_INTRS)) ||
710 	    ((type == DDI_INTR_TYPE_MSIX) && (navail >  PCI_MSIX_MAX_INTRS)))
711 		return (DDI_EINVAL);
712 
713 	if (pci_get_msi_ctrl(rdip, type, &msi_ctrl,
714 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
715 		return (DDI_FAILURE);
716 
717 	if (type == DDI_INTR_TYPE_MSI) {
718 		msi_ctrl |= ((highbit(navail) -1) << PCI_MSI_MME_SHIFT);
719 
720 		PCI_CAP_PUT16(cfg_hdle, NULL, caps_ptr, PCI_MSI_CTRL, msi_ctrl);
721 	} else if (type == DDI_INTR_TYPE_MSIX) {
722 		DDI_INTR_NEXDBG((CE_CONT, "pci_msi_set_nintrs: unsupported\n"));
723 	}
724 
725 	pci_config_teardown(&cfg_hdle);
726 	return (DDI_SUCCESS);
727 }
728 
729 
730 /*
731  * pci_msi_get_supported_type:
732  *
733  * Returns DDI_INTR_TYPE_MSI and/or DDI_INTR_TYPE_MSIX as supported
734  * types if device supports them. A DDI_FAILURE is returned otherwise.
735  */
736 int
737 pci_msi_get_supported_type(dev_info_t *rdip, int *typesp)
738 {
739 	ushort_t		caps_ptr, msi_ctrl;
740 	ddi_acc_handle_t	cfg_hdle;
741 
742 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
743 	    "rdip = 0x%p\n", (void *)rdip));
744 
745 	*typesp = 0;
746 
747 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSI, &msi_ctrl,
748 	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
749 		*typesp |= DDI_INTR_TYPE_MSI;
750 		pci_config_teardown(&cfg_hdle);
751 	}
752 
753 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msi_ctrl,
754 	    &caps_ptr, &cfg_hdle) == DDI_SUCCESS) {
755 		*typesp |= DDI_INTR_TYPE_MSIX;
756 		pci_config_teardown(&cfg_hdle);
757 	}
758 
759 	DDI_INTR_NEXDBG((CE_CONT, "pci_msi_get_supported_type: "
760 	    "rdip = 0x%p types 0x%x\n", (void *)rdip, *typesp));
761 
762 	return (*typesp == 0 ? DDI_FAILURE : DDI_SUCCESS);
763 }
764 
765 
766 /*
767  * pci_msix_init:
768  *	This function initializes the various handles/addrs etc.
769  *	needed for MSI-X support. It also allocates a private
770  *	structure to keep track of these.
771  */
772 ddi_intr_msix_t *
773 pci_msix_init(dev_info_t *rdip)
774 {
775 	uint_t			rnumber, breg, nregs;
776 	size_t			msix_tbl_size;
777 	size_t			pba_tbl_size;
778 	ushort_t		caps_ptr, msix_ctrl;
779 	ddi_intr_msix_t		*msix_p;
780 	ddi_acc_handle_t	cfg_hdle;
781 	pci_regspec_t		*rp;
782 	int			reg_size, addr_space, offset, *regs_list;
783 	int			i, ret;
784 
785 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: rdip = %p\n", (void *)rdip));
786 
787 	if (pci_get_msi_ctrl(rdip, DDI_INTR_TYPE_MSIX, &msix_ctrl,
788 	    &caps_ptr, &cfg_hdle) != DDI_SUCCESS)
789 		return (NULL);
790 
791 	msix_p = kmem_zalloc(sizeof (ddi_intr_msix_t), KM_SLEEP);
792 
793 	/*
794 	 * Initialize the devacc structure
795 	 */
796 	msix_p->msix_dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
797 	msix_p->msix_dev_attr.devacc_attr_endian_flags =
798 	    DDI_STRUCTURE_LE_ACC;
799 	msix_p->msix_dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
800 
801 	/* Map the entire MSI-X vector table */
802 	msix_p->msix_tbl_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
803 	    PCI_MSIX_TBL_OFFSET);
804 
805 	if ((breg = pci_msix_bir_index[msix_p->msix_tbl_offset &
806 	    PCI_MSIX_TBL_BIR_MASK]) == 0xff)
807 		goto fail1;
808 
809 	msix_p->msix_tbl_offset = msix_p->msix_tbl_offset &
810 	    ~PCI_MSIX_TBL_BIR_MASK;
811 	msix_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1) *
812 	    PCI_MSIX_VECTOR_SIZE;
813 
814 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X table offset 0x%x "
815 	    "breg 0x%x size 0x%lx\n", msix_p->msix_tbl_offset, breg,
816 	    msix_tbl_size));
817 
818 	if ((ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
819 	    DDI_PROP_DONTPASS, "reg", (int **)&regs_list, &nregs))
820 	    != DDI_PROP_SUCCESS) {
821 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
822 		    "ddi_prop_lookup_int_array failed %d\n", ret));
823 
824 		goto fail1;
825 	}
826 
827 	reg_size = sizeof (pci_regspec_t) / sizeof (int);
828 
829 	for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
830 		rp = (pci_regspec_t *)&regs_list[i * reg_size];
831 		addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
832 		offset = PCI_REG_REG_G(rp->pci_phys_hi);
833 
834 		if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
835 		    (addr_space == PCI_ADDR_MEM64))) {
836 			rnumber = i;
837 			break;
838 		}
839 	}
840 
841 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X rnum = %d\n", rnumber));
842 
843 	if (rnumber == 0) {
844 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
845 		    "no mtaching reg number for offset 0x%x\n", breg));
846 
847 		goto fail2;
848 	}
849 
850 	if ((ret = ddi_regs_map_setup(rdip, rnumber,
851 	    (caddr_t *)&msix_p->msix_tbl_addr, msix_p->msix_tbl_offset,
852 	    msix_tbl_size, &msix_p->msix_dev_attr,
853 	    &msix_p->msix_tbl_hdl)) != DDI_SUCCESS) {
854 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: MSI-X Table "
855 		    "ddi_regs_map_setup failed %d\n", ret));
856 
857 		goto fail2;
858 	}
859 
860 	/*
861 	 * Map in the MSI-X Pending Bit Array
862 	 */
863 	msix_p->msix_pba_offset = PCI_CAP_GET32(cfg_hdle, NULL, caps_ptr,
864 	    PCI_MSIX_PBA_OFFSET);
865 
866 	if ((breg = pci_msix_bir_index[msix_p->msix_pba_offset &
867 	    PCI_MSIX_PBA_BIR_MASK]) == 0xff)
868 		goto fail3;
869 
870 	msix_p->msix_pba_offset = msix_p->msix_pba_offset &
871 	    ~PCI_MSIX_PBA_BIR_MASK;
872 	pba_tbl_size = ((msix_ctrl & PCI_MSIX_TBL_SIZE_MASK) + 1)/8;
873 
874 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA table offset 0x%x "
875 	    "breg 0x%x size 0x%lx\n", msix_p->msix_pba_offset, breg,
876 	    pba_tbl_size));
877 
878 	for (i = 1, rnumber = 0; i < nregs/reg_size; i++) {
879 		rp = (pci_regspec_t *)&regs_list[i * reg_size];
880 		addr_space = rp->pci_phys_hi & PCI_ADDR_MASK;
881 		offset = PCI_REG_REG_G(rp->pci_phys_hi);
882 
883 		if ((offset == breg) && ((addr_space == PCI_ADDR_MEM32) ||
884 		    (addr_space == PCI_ADDR_MEM64))) {
885 			rnumber = i;
886 			break;
887 		}
888 	}
889 
890 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA rnum = %d\n", rnumber));
891 
892 	if (rnumber == 0) {
893 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: "
894 		    "no matching reg number for offset 0x%x\n", breg));
895 
896 		goto fail3;
897 	}
898 
899 	if ((ret = ddi_regs_map_setup(rdip, rnumber,
900 	    (caddr_t *)&msix_p->msix_pba_addr, msix_p->msix_pba_offset,
901 	    pba_tbl_size, &msix_p->msix_dev_attr,
902 	    &msix_p->msix_pba_hdl)) != DDI_SUCCESS) {
903 		DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: PBA "
904 		    "ddi_regs_map_setup failed %d\n", ret));
905 
906 		goto fail3;
907 	}
908 
909 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_init: msix_p = 0x%p DONE!!\n",
910 	    (void *)msix_p));
911 
912 	ddi_prop_free(regs_list);
913 	goto done;
914 
915 fail3:
916 	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
917 fail2:
918 	ddi_prop_free(regs_list);
919 fail1:
920 	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
921 	msix_p = NULL;
922 done:
923 	pci_config_teardown(&cfg_hdle);
924 	return (msix_p);
925 }
926 
927 
928 /*
929  * pci_msix_fini:
930  *	This function cleans up previously allocated handles/addrs etc.
931  *	It is only called if no more MSI-X interrupts are being used.
932  */
933 void
934 pci_msix_fini(ddi_intr_msix_t *msix_p)
935 {
936 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_fini: msix_p = 0x%p\n",
937 	    (void *)msix_p));
938 
939 	ddi_regs_map_free(&msix_p->msix_pba_hdl);
940 	ddi_regs_map_free(&msix_p->msix_tbl_hdl);
941 	kmem_free(msix_p, sizeof (ddi_intr_msix_t));
942 }
943 
944 
945 /*
946  * pci_msix_dup:
947  *	This function duplicates the address and data pair of one msi-x
948  *	vector to another msi-x vector.
949  */
950 int
951 pci_msix_dup(dev_info_t *rdip, int org_inum, int dup_inum)
952 {
953 	ddi_intr_msix_t	*msix_p = i_ddi_get_msix(rdip);
954 	uint64_t	addr;
955 	uint64_t	data;
956 	uintptr_t	off;
957 
958 	DDI_INTR_NEXDBG((CE_CONT, "pci_msix_dup: dip = %p, inum = 0x%x, "
959 	    "to_vector = 0x%x\n", (void *)rdip, org_inum, dup_inum));
960 
961 	/* Offset into the original inum's entry in the MSI-X table */
962 	off = (uintptr_t)msix_p->msix_tbl_addr +
963 	    (org_inum * PCI_MSIX_VECTOR_SIZE);
964 
965 	/* For the MSI-X number passed in, get the "data" and "addr" fields */
966 	addr = ddi_get64(msix_p->msix_tbl_hdl,
967 	    (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET));
968 
969 	data = ddi_get32(msix_p->msix_tbl_hdl,
970 	    (uint32_t *)(off + PCI_MSIX_DATA_OFFSET));
971 
972 	/* Program new vector with these existing values */
973 	return (pci_msi_configure(rdip, DDI_INTR_TYPE_MSIX, 1, dup_inum, addr,
974 	    data));
975 }
976 
977 
978 /*
979  * Next set of routines are for INTx (legacy) PCI interrupt
980  * support only.
981  */
982 
983 /*
984  * pci_intx_get_cap:
985  *	For non-MSI devices that comply to PCI v2.3 or greater;
986  *	read the command register. Bit 10 implies interrupt disable.
987  *	Set this bit and then read the status register bit 3.
988  *	Bit 3 of status register is Interrupt state.
989  *	If it is set; then the device supports 'Masking'
990  *
991  *	Reset the device back to the original state.
992  */
993 int
994 pci_intx_get_cap(dev_info_t *dip, int *flagsp)
995 {
996 	uint16_t		cmdreg, savereg;
997 	ddi_acc_handle_t	cfg_hdl;
998 #ifdef	DEBUG
999 	uint16_t		statreg;
1000 #endif /* DEBUG */
1001 
1002 	*flagsp = 0;
1003 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: %s%d: called\n",
1004 	    ddi_driver_name(dip), ddi_get_instance(dip)));
1005 
1006 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1007 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: can't get "
1008 		    "config handle\n"));
1009 		return (DDI_FAILURE);
1010 	}
1011 
1012 	savereg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1013 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
1014 	    "command register was 0x%x\n", savereg));
1015 
1016 	/* Disable the interrupts */
1017 	cmdreg = savereg | PCI_COMM_INTX_DISABLE;
1018 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
1019 
1020 #ifdef	DEBUG
1021 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
1022 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
1023 	    "status register is 0x%x\n", statreg));
1024 #endif /* DEBUG */
1025 
1026 	/* Read the bit back */
1027 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1028 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
1029 	    "command register is now 0x%x\n", cmdreg));
1030 
1031 	*flagsp = DDI_INTR_FLAG_LEVEL;
1032 
1033 	if (cmdreg & PCI_COMM_INTX_DISABLE) {
1034 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_cap: "
1035 		    "masking supported\n"));
1036 		*flagsp |= (DDI_INTR_FLAG_MASKABLE |
1037 		    DDI_INTR_FLAG_PENDING);
1038 	}
1039 
1040 	/* Restore the device back to the original state and return */
1041 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, savereg);
1042 
1043 	pci_config_teardown(&cfg_hdl);
1044 	return (DDI_SUCCESS);
1045 }
1046 
1047 
1048 /*
1049  * pci_intx_clr_mask:
1050  *	For non-MSI devices that comply to PCI v2.3 or greater;
1051  *	clear the bit10 in the command register.
1052  */
1053 int
1054 pci_intx_clr_mask(dev_info_t *dip)
1055 {
1056 	uint16_t		cmdreg;
1057 	ddi_acc_handle_t	cfg_hdl;
1058 
1059 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: %s%d: called\n",
1060 	    ddi_driver_name(dip), ddi_get_instance(dip)));
1061 
1062 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1063 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: can't get "
1064 		    "config handle\n"));
1065 		return (DDI_FAILURE);
1066 	}
1067 
1068 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1069 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_clr_mask: "
1070 	    "command register was 0x%x\n", cmdreg));
1071 
1072 	/* Enable the interrupts */
1073 	cmdreg &= ~PCI_COMM_INTX_DISABLE;
1074 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
1075 	pci_config_teardown(&cfg_hdl);
1076 	return (DDI_SUCCESS);
1077 }
1078 
1079 
1080 /*
1081  * pci_intx_set_mask:
1082  *	For non-MSI devices that comply to PCI v2.3 or greater;
1083  *	set the bit10 in the command register.
1084  */
1085 int
1086 pci_intx_set_mask(dev_info_t *dip)
1087 {
1088 	uint16_t		cmdreg;
1089 	ddi_acc_handle_t	cfg_hdl;
1090 
1091 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: %s%d: called\n",
1092 	    ddi_driver_name(dip), ddi_get_instance(dip)));
1093 
1094 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1095 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: can't get "
1096 		    "config handle\n"));
1097 		return (DDI_FAILURE);
1098 	}
1099 
1100 	cmdreg = pci_config_get16(cfg_hdl, PCI_CONF_COMM);
1101 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_set_mask: "
1102 	    "command register was 0x%x\n", cmdreg));
1103 
1104 	/* Disable the interrupts */
1105 	cmdreg |= PCI_COMM_INTX_DISABLE;
1106 	pci_config_put16(cfg_hdl, PCI_CONF_COMM, cmdreg);
1107 	pci_config_teardown(&cfg_hdl);
1108 	return (DDI_SUCCESS);
1109 }
1110 
1111 /*
1112  * pci_intx_get_pending:
1113  *	For non-MSI devices that comply to PCI v2.3 or greater;
1114  *	read the status register. Bit 3 of status register is
1115  *	Interrupt state. If it is set; then the interrupt is
1116  *	'Pending'.
1117  */
1118 int
1119 pci_intx_get_pending(dev_info_t *dip, int *pendingp)
1120 {
1121 	uint16_t		statreg;
1122 	ddi_acc_handle_t	cfg_hdl;
1123 
1124 	*pendingp = 0;
1125 	DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: %s%d: called\n",
1126 	    ddi_driver_name(dip), ddi_get_instance(dip)));
1127 
1128 	if (pci_config_setup(dip, &cfg_hdl) != DDI_SUCCESS) {
1129 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: can't get "
1130 		    "config handle\n"));
1131 		return (DDI_FAILURE);
1132 	}
1133 
1134 	statreg = pci_config_get16(cfg_hdl, PCI_CONF_STAT);
1135 
1136 	if (statreg & PCI_STAT_INTR) {
1137 		DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_pending: "
1138 		    "interrupt is pending\n"));
1139 		*pendingp = 1;
1140 	}
1141 
1142 	pci_config_teardown(&cfg_hdl);
1143 	return (DDI_SUCCESS);
1144 }
1145 
1146 
1147 /*
1148  * pci_intx_get_ispec:
1149  *	Get intrspec for PCI devices (legacy support)
1150  *	NOTE: This is moved here from x86 pci.c and is
1151  *	needed here as pci-ide.c uses it as well
1152  */
1153 /*ARGSUSED*/
1154 ddi_intrspec_t
1155 pci_intx_get_ispec(dev_info_t *dip, dev_info_t *rdip, int inum)
1156 {
1157 	int				*intpriorities;
1158 	uint_t				num_intpriorities;
1159 	struct intrspec			*ispec;
1160 	ddi_acc_handle_t		cfg_hdl;
1161 	struct ddi_parent_private_data	*pdptr;
1162 
1163 	if ((pdptr = ddi_get_parent_data(rdip)) == NULL)
1164 		return (NULL);
1165 
1166 	ispec = pdptr->par_intr;
1167 	ASSERT(ispec);
1168 
1169 	/* check if the intrspec_pri has been initialized */
1170 	if (!ispec->intrspec_pri) {
1171 		if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, rdip,
1172 		    DDI_PROP_DONTPASS, "interrupt-priorities",
1173 		    &intpriorities, &num_intpriorities) == DDI_PROP_SUCCESS) {
1174 			if (inum < num_intpriorities)
1175 				ispec->intrspec_pri = intpriorities[inum];
1176 			ddi_prop_free(intpriorities);
1177 		}
1178 
1179 		/* If still no priority, guess based on the class code */
1180 		if (ispec->intrspec_pri == 0)
1181 			ispec->intrspec_pri = pci_class_to_pil(rdip);
1182 	}
1183 
1184 	/* Get interrupt line value */
1185 	if (!ispec->intrspec_vec) {
1186 		if (pci_config_setup(rdip, &cfg_hdl) != DDI_SUCCESS) {
1187 			DDI_INTR_NEXDBG((CE_CONT, "pci_intx_get_iline: "
1188 			    "can't get config handle\n"));
1189 			return ((ddi_intrspec_t)ispec);
1190 		}
1191 
1192 		ispec->intrspec_vec = pci_config_get8(cfg_hdl, PCI_CONF_ILINE);
1193 		pci_config_teardown(&cfg_hdl);
1194 	}
1195 
1196 	return ((ddi_intrspec_t)ispec);
1197 }
1198 
1199 static uint32_t
1200 pci_match_class_val(uint32_t key, pci_class_val_t *rec_p, int nrec,
1201     uint32_t default_val)
1202 {
1203 	int i;
1204 
1205 	for (i = 0; i < nrec; rec_p++, i++) {
1206 		if ((rec_p->class_code & rec_p->class_mask) ==
1207 		    (key & rec_p->class_mask))
1208 			return (rec_p->class_val);
1209 	}
1210 
1211 	return (default_val);
1212 }
1213 
1214 /*
1215  * Return the configuration value, based on class code and sub class code,
1216  * from the specified property based or default pci_class_val_t table.
1217  */
1218 uint32_t
1219 pci_class_to_val(dev_info_t *rdip, char *property_name, pci_class_val_t *rec_p,
1220     int nrec, uint32_t default_val)
1221 {
1222 	int property_len;
1223 	uint32_t class_code;
1224 	pci_class_val_t *conf;
1225 	uint32_t val = default_val;
1226 
1227 	/*
1228 	 * Use the "class-code" property to get the base and sub class
1229 	 * codes for the requesting device.
1230 	 */
1231 	class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
1232 	    DDI_PROP_DONTPASS, "class-code", -1);
1233 
1234 	if (class_code == -1)
1235 		return (val);
1236 
1237 	/* look up the val from the default table */
1238 	val = pci_match_class_val(class_code, rec_p, nrec, val);
1239 
1240 
1241 	/* see if there is a more specific property specified value */
1242 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_NOTPROM,
1243 	    property_name, (caddr_t)&conf, &property_len))
1244 			return (val);
1245 
1246 	if ((property_len % sizeof (pci_class_val_t)) == 0)
1247 		val = pci_match_class_val(class_code, conf,
1248 		    property_len / sizeof (pci_class_val_t), val);
1249 	kmem_free(conf, property_len);
1250 	return (val);
1251 }
1252 
1253 /*
1254  * pci_class_to_pil:
1255  *
1256  * Return the pil for a given PCI device.
1257  */
1258 uint32_t
1259 pci_class_to_pil(dev_info_t *rdip)
1260 {
1261 	uint32_t pil;
1262 
1263 	/* Default pil is 1 */
1264 	pil = pci_class_to_val(rdip,
1265 	    "pci-class-priorities", pci_default_pil,
1266 	    sizeof (pci_default_pil) / sizeof (pci_class_val_t), 1);
1267 
1268 	/* Range check the result */
1269 	if (pil >= 0xf)
1270 		pil = 1;
1271 
1272 	return (pil);
1273 }
1274 
1275 /*
1276  * pci_class_to_intr_weight:
1277  *
1278  * Return the intr_weight for a given PCI device.
1279  */
1280 int32_t
1281 pci_class_to_intr_weight(dev_info_t *rdip)
1282 {
1283 	int32_t intr_weight;
1284 
1285 	/* default weight is 0% */
1286 	intr_weight = pci_class_to_val(rdip,
1287 	    "pci-class-intr-weights", pci_default_intr_weight,
1288 	    sizeof (pci_default_intr_weight) / sizeof (pci_class_val_t), 0);
1289 
1290 	/* range check the result */
1291 	if (intr_weight < 0)
1292 		intr_weight = 0;
1293 	if (intr_weight > 1000)
1294 		intr_weight = 1000;
1295 
1296 	return (intr_weight);
1297 }
1298