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 /*
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * File that has code which is common between pci(7d) and npe(7d)
28 * It shares the following:
29 * - interrupt code
30 * - pci_tools ioctl code
31 * - name_child code
32 * - set_parent_private_data code
33 */
34
35 #include <sys/conf.h>
36 #include <sys/pci.h>
37 #include <sys/sunndi.h>
38 #include <sys/mach_intr.h>
39 #include <sys/pci_intr_lib.h>
40 #include <sys/psm.h>
41 #include <sys/policy.h>
42 #include <sys/sysmacros.h>
43 #include <sys/clock.h>
183
184 static int pcieb_intr_pri_counter = 0;
185
186 /*
187 * pci_common_intr_ops: bus_intr_op() function for interrupt support
188 */
189 int
190 pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
191 ddi_intr_handle_impl_t *hdlp, void *result)
192 {
193 int priority = 0;
194 int psm_status = 0;
195 int pci_status = 0;
196 int pci_rval, psm_rval = PSM_FAILURE;
197 int types = 0;
198 int pciepci = 0;
199 int i, j, count;
200 int rv;
201 int behavior;
202 int cap_ptr;
203 uint16_t msi_cap_base, msix_cap_base, cap_ctrl;
204 char *prop;
205 ddi_intrspec_t isp;
206 struct intrspec *ispec;
207 ddi_intr_handle_impl_t tmp_hdl;
208 ddi_intr_msix_t *msix_p;
209 ihdl_plat_t *ihdl_plat_datap;
210 ddi_intr_handle_t *h_array;
211 ddi_acc_handle_t handle;
212 apic_get_intr_t intrinfo;
213
214 DDI_INTR_NEXDBG((CE_CONT,
215 "pci_common_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
216 (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
217
218 /* Process the request */
219 switch (intr_op) {
220 case DDI_INTROP_SUPPORTED_TYPES:
221 /*
222 * First we determine the interrupt types supported by the
323 * Following check is a special case for 'pcieb'.
324 * This makes sure vectors with the right priority
325 * are allocated for pcieb during ALLOC time.
326 */
327 if (strcmp(ddi_driver_name(rdip), "pcieb") == 0) {
328 hdlp->ih_pri =
329 (pcieb_intr_pri_counter % 2) ? 4 : 7;
330 pciepci = 1;
331 } else
332 hdlp->ih_pri = priority;
333 behavior = (int)(uintptr_t)hdlp->ih_scratch2;
334
335 /*
336 * Cache in the config handle and cap_ptr
337 */
338 if (i_ddi_get_pci_config_handle(rdip) == NULL) {
339 if (pci_config_setup(rdip, &handle) !=
340 DDI_SUCCESS)
341 return (DDI_FAILURE);
342 i_ddi_set_pci_config_handle(rdip, handle);
343 }
344
345 prop = NULL;
346 cap_ptr = 0;
347 if (hdlp->ih_type == DDI_INTR_TYPE_MSI)
348 prop = "pci-msi-capid-pointer";
349 else if (hdlp->ih_type == DDI_INTR_TYPE_MSIX)
350 prop = "pci-msix-capid-pointer";
351
352 /*
353 * Enforce the calling of DDI_INTROP_SUPPORTED_TYPES
354 * for MSI(X) before allocation
355 */
356 if (prop != NULL) {
357 cap_ptr = ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
358 DDI_PROP_DONTPASS, prop, 0);
359 if (cap_ptr == 0) {
360 DDI_INTR_NEXDBG((CE_CONT,
361 "pci_common_intr_ops: rdip: 0x%p "
362 "attempted MSI(X) alloc without "
363 "cap property\n", (void *)rdip));
364 return (DDI_FAILURE);
365 }
366 }
367 i_ddi_set_msi_msix_cap_ptr(rdip, cap_ptr);
368
369 /*
370 * Allocate interrupt vectors
371 */
372 (void) (*psm_intr_ops)(rdip, hdlp,
373 PSM_INTR_OP_ALLOC_VECTORS, result);
374
375 if (*(int *)result == 0)
376 return (DDI_INTR_NOTFOUND);
377
378 /* verify behavior flag and take appropriate action */
379 if ((behavior == DDI_INTR_ALLOC_STRICT) &&
380 (*(int *)result < hdlp->ih_scratch1)) {
381 DDI_INTR_NEXDBG((CE_CONT,
382 "pci_common_intr_ops: behavior %x, "
383 "couldn't get enough intrs\n", behavior));
384 hdlp->ih_scratch1 = *(int *)result;
385 (void) (*psm_intr_ops)(rdip, hdlp,
386 PSM_INTR_OP_FREE_VECTORS, NULL);
387 return (DDI_EAGAIN);
388 }
389
390 if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
391 if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) {
392 msix_p = pci_msix_init(hdlp->ih_dip);
393 if (msix_p) {
394 i_ddi_set_msix(hdlp->ih_dip,
395 msix_p);
396 } else {
397 DDI_INTR_NEXDBG((CE_CONT,
398 "pci_common_intr_ops: MSI-X"
399 "table initilization failed"
400 ", rdip 0x%p inum 0x%x\n",
401 (void *)rdip,
402 hdlp->ih_inum));
403
404 (void) (*psm_intr_ops)(rdip,
405 hdlp,
406 PSM_INTR_OP_FREE_VECTORS,
407 NULL);
408
409 return (DDI_FAILURE);
410 }
411 }
412 }
413
414 if (pciepci) {
415 /* update priority in ispec */
416 isp = pci_intx_get_ispec(pdip, rdip,
417 (int)hdlp->ih_inum);
418 ispec = (struct intrspec *)isp;
419 if (ispec)
420 ispec->intrspec_pri = hdlp->ih_pri;
421 ++pcieb_intr_pri_counter;
422 }
423
424 } else
425 return (DDI_FAILURE);
426 break;
427 case DDI_INTROP_FREE:
428 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
429 (psm_intr_ops != NULL)) {
430 if (i_ddi_intr_get_current_nintrs(hdlp->ih_dip) - 1 ==
431 0) {
432 if (handle = i_ddi_get_pci_config_handle(
433 rdip)) {
434 (void) pci_config_teardown(&handle);
435 i_ddi_set_pci_config_handle(rdip, NULL);
436 }
437 if (cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip))
438 i_ddi_set_msi_msix_cap_ptr(rdip, 0);
439 }
440
441 (void) (*psm_intr_ops)(rdip, hdlp,
442 PSM_INTR_OP_FREE_VECTORS, NULL);
443
444 if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
445 msix_p = i_ddi_get_msix(hdlp->ih_dip);
446 if (msix_p &&
|
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 /*
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 *
25 * Copyright (c) 2012, Nexenta Systems, Inc. All rights reserved.
26 */
27
28 /*
29 * File that has code which is common between pci(7d) and npe(7d)
30 * It shares the following:
31 * - interrupt code
32 * - pci_tools ioctl code
33 * - name_child code
34 * - set_parent_private_data code
35 */
36
37 #include <sys/conf.h>
38 #include <sys/pci.h>
39 #include <sys/sunndi.h>
40 #include <sys/mach_intr.h>
41 #include <sys/pci_intr_lib.h>
42 #include <sys/psm.h>
43 #include <sys/policy.h>
44 #include <sys/sysmacros.h>
45 #include <sys/clock.h>
185
186 static int pcieb_intr_pri_counter = 0;
187
188 /*
189 * pci_common_intr_ops: bus_intr_op() function for interrupt support
190 */
191 int
192 pci_common_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op,
193 ddi_intr_handle_impl_t *hdlp, void *result)
194 {
195 int priority = 0;
196 int psm_status = 0;
197 int pci_status = 0;
198 int pci_rval, psm_rval = PSM_FAILURE;
199 int types = 0;
200 int pciepci = 0;
201 int i, j, count;
202 int rv;
203 int behavior;
204 int cap_ptr;
205 boolean_t did_pci_config_setup = B_FALSE;
206 boolean_t did_intr_vec_alloc = B_FALSE;
207 boolean_t did_msi_cap_set = B_FALSE;
208 uint16_t msi_cap_base, msix_cap_base, cap_ctrl;
209 char *prop;
210 ddi_intrspec_t isp;
211 struct intrspec *ispec;
212 ddi_intr_handle_impl_t tmp_hdl;
213 ddi_intr_msix_t *msix_p;
214 ihdl_plat_t *ihdl_plat_datap;
215 ddi_intr_handle_t *h_array;
216 ddi_acc_handle_t handle;
217 apic_get_intr_t intrinfo;
218
219 DDI_INTR_NEXDBG((CE_CONT,
220 "pci_common_intr_ops: pdip 0x%p, rdip 0x%p, op %x handle 0x%p\n",
221 (void *)pdip, (void *)rdip, intr_op, (void *)hdlp));
222
223 /* Process the request */
224 switch (intr_op) {
225 case DDI_INTROP_SUPPORTED_TYPES:
226 /*
227 * First we determine the interrupt types supported by the
328 * Following check is a special case for 'pcieb'.
329 * This makes sure vectors with the right priority
330 * are allocated for pcieb during ALLOC time.
331 */
332 if (strcmp(ddi_driver_name(rdip), "pcieb") == 0) {
333 hdlp->ih_pri =
334 (pcieb_intr_pri_counter % 2) ? 4 : 7;
335 pciepci = 1;
336 } else
337 hdlp->ih_pri = priority;
338 behavior = (int)(uintptr_t)hdlp->ih_scratch2;
339
340 /*
341 * Cache in the config handle and cap_ptr
342 */
343 if (i_ddi_get_pci_config_handle(rdip) == NULL) {
344 if (pci_config_setup(rdip, &handle) !=
345 DDI_SUCCESS)
346 return (DDI_FAILURE);
347 i_ddi_set_pci_config_handle(rdip, handle);
348 did_pci_config_setup = B_TRUE;
349 }
350
351 prop = NULL;
352 cap_ptr = 0;
353 if (hdlp->ih_type == DDI_INTR_TYPE_MSI)
354 prop = "pci-msi-capid-pointer";
355 else if (hdlp->ih_type == DDI_INTR_TYPE_MSIX)
356 prop = "pci-msix-capid-pointer";
357
358 /*
359 * Enforce the calling of DDI_INTROP_SUPPORTED_TYPES
360 * for MSI(X) before allocation
361 */
362 if (prop != NULL) {
363 cap_ptr = ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
364 DDI_PROP_DONTPASS, prop, 0);
365 if (cap_ptr == 0) {
366 DDI_INTR_NEXDBG((CE_CONT,
367 "pci_common_intr_ops: rdip: 0x%p "
368 "attempted MSI(X) alloc without "
369 "cap property\n", (void *)rdip));
370 return (DDI_FAILURE);
371 }
372 }
373 i_ddi_set_msi_msix_cap_ptr(rdip, cap_ptr);
374 did_msi_cap_set = B_TRUE;
375
376 /*
377 * Allocate interrupt vectors
378 */
379 (void) (*psm_intr_ops)(rdip, hdlp,
380 PSM_INTR_OP_ALLOC_VECTORS, result);
381
382 if (*(int *)result == 0) {
383 rv = DDI_INTR_NOTFOUND;
384 goto HANDLE_ALLOC_FAILURE;
385 }
386 did_intr_vec_alloc = B_TRUE;
387
388 /* verify behavior flag and take appropriate action */
389 if ((behavior == DDI_INTR_ALLOC_STRICT) &&
390 (*(int *)result < hdlp->ih_scratch1)) {
391 DDI_INTR_NEXDBG((CE_CONT,
392 "pci_common_intr_ops: behavior %x, "
393 "couldn't get enough intrs\n", behavior));
394 hdlp->ih_scratch1 = *(int *)result;
395 rv = DDI_EAGAIN;
396 goto HANDLE_ALLOC_FAILURE;
397 }
398
399 if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
400 if (!(msix_p = i_ddi_get_msix(hdlp->ih_dip))) {
401 msix_p = pci_msix_init(hdlp->ih_dip);
402 if (msix_p) {
403 i_ddi_set_msix(hdlp->ih_dip,
404 msix_p);
405 } else {
406 DDI_INTR_NEXDBG((CE_CONT,
407 "pci_common_intr_ops: MSI-X"
408 "table initilization failed"
409 ", rdip 0x%p inum 0x%x\n",
410 (void *)rdip,
411 hdlp->ih_inum));
412
413 rv = DDI_FAILURE;
414 goto HANDLE_ALLOC_FAILURE;
415 }
416 }
417 }
418
419 if (pciepci) {
420 /* update priority in ispec */
421 isp = pci_intx_get_ispec(pdip, rdip,
422 (int)hdlp->ih_inum);
423 ispec = (struct intrspec *)isp;
424 if (ispec)
425 ispec->intrspec_pri = hdlp->ih_pri;
426 ++pcieb_intr_pri_counter;
427 }
428
429 } else
430 return (DDI_FAILURE);
431 break;
432
433 HANDLE_ALLOC_FAILURE:
434 if (did_intr_vec_alloc == B_TRUE)
435 (void) (*psm_intr_ops)(rdip, hdlp,
436 PSM_INTR_OP_FREE_VECTORS, NULL);
437 if (did_msi_cap_set == B_TRUE)
438 i_ddi_set_msi_msix_cap_ptr(rdip, 0);
439 if (did_pci_config_setup == B_TRUE) {
440 (void) pci_config_teardown(&handle);
441 i_ddi_set_pci_config_handle(rdip, NULL);
442 }
443 return (rv);
444
445 case DDI_INTROP_FREE:
446 if (DDI_INTR_IS_MSI_OR_MSIX(hdlp->ih_type) &&
447 (psm_intr_ops != NULL)) {
448 if (i_ddi_intr_get_current_nintrs(hdlp->ih_dip) - 1 ==
449 0) {
450 if (handle = i_ddi_get_pci_config_handle(
451 rdip)) {
452 (void) pci_config_teardown(&handle);
453 i_ddi_set_pci_config_handle(rdip, NULL);
454 }
455 if (cap_ptr = i_ddi_get_msi_msix_cap_ptr(rdip))
456 i_ddi_set_msi_msix_cap_ptr(rdip, 0);
457 }
458
459 (void) (*psm_intr_ops)(rdip, hdlp,
460 PSM_INTR_OP_FREE_VECTORS, NULL);
461
462 if (hdlp->ih_type == DDI_INTR_TYPE_MSIX) {
463 msix_p = i_ddi_get_msix(hdlp->ih_dip);
464 if (msix_p &&
|