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