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