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