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