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