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