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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2013 Pluribus Networks, Inc.
24 */
25
26 /*
27 * apic_introp.c:
28 * Has code for Advanced DDI interrupt framework support.
29 */
30
31 #include <sys/cpuvar.h>
32 #include <sys/psm.h>
33 #include <sys/archsystm.h>
34 #include <sys/apic.h>
35 #include <sys/sunddi.h>
36 #include <sys/ddi_impldefs.h>
37 #include <sys/mach_intr.h>
38 #include <sys/sysmacros.h>
39 #include <sys/trap.h>
40 #include <sys/pci.h>
41 #include <sys/pci_intr_lib.h>
42 #include <sys/apic_common.h>
43
44 extern struct av_head autovect[];
45
46 /*
47 * Local Function Prototypes
48 */
49 apic_irq_t *apic_find_irq(dev_info_t *, struct intrspec *, int);
50
51 /*
52 * apic_pci_msi_enable_vector:
53 * Set the address/data fields in the MSI/X capability structure
54 * XXX: MSI-X support
55 */
56 /* ARGSUSED */
57 void
58 apic_pci_msi_enable_vector(apic_irq_t *irq_ptr, int type, int inum, int vector,
59 int count, int target_apic_id)
60 {
61 uint64_t msi_addr, msi_data;
62 ushort_t msi_ctrl;
63 dev_info_t *dip = irq_ptr->airq_dip;
209 * starting vector
210 */
211 msibits = count - 1;
212
213 /* It has to be contiguous */
214 for (i = lowest; i <= highest; i++) {
215 navail = 0;
216
217 /*
218 * starting vector has to be aligned accordingly for
219 * multiple MSIs
220 */
221 if (msibits)
222 i = (i + msibits) & ~msibits;
223 start = i;
224 while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) &&
225 (i <= highest)) {
226 if (APIC_CHECK_RESERVE_VECTORS(i))
227 break;
228 navail++;
229 if (navail >= count)
230 return (start);
231 i++;
232 }
233 }
234 return (0);
235 }
236
237
238 /*
239 * It finds the apic_irq_t associates with the dip, ispec and type.
240 */
241 apic_irq_t *
242 apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type)
243 {
244 apic_irq_t *irqp;
245 int i;
246
247 DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x "
248 "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec,
249 ispec->intrspec_pri, type));
250
488 return (PSM_FAILURE);
489 }
490 /*
491 * keep tracking the default interrupt cpu binding
492 */
493 irqp->airq_cpu = cpu;
494
495 *result = 0;
496 return (PSM_SUCCESS);
497 }
498
499 static int
500 apic_grp_set_cpu(int irqno, int new_cpu, int *result)
501 {
502 dev_info_t *orig_dip;
503 uint32_t orig_cpu;
504 ulong_t iflag;
505 apic_irq_t *irqps[PCI_MSI_MAX_INTRS];
506 int i;
507 int cap_ptr;
508 int msi_mask_off;
509 ushort_t msi_ctrl;
510 uint32_t msi_pvm;
511 ddi_acc_handle_t handle;
512 int num_vectors = 0;
513 uint32_t vector;
514
515 DDI_INTR_IMPLDBG((CE_CONT, "APIC_GRP_SET_CPU\n"));
516
517 /*
518 * Take mutex to insure that table doesn't change out from underneath
519 * us while we're playing with it.
520 */
521 mutex_enter(&airq_mutex);
522 irqps[0] = apic_irq_table[irqno];
523 orig_cpu = irqps[0]->airq_temp_cpu;
524 orig_dip = irqps[0]->airq_dip;
525 num_vectors = irqps[0]->airq_intin_no;
526 vector = irqps[0]->airq_vector;
527
528 /* A "group" of 1 */
529 if (num_vectors == 1) {
530 mutex_exit(&airq_mutex);
624 /* Reenable vectors if per vector masking is supported. */
625 if (msi_ctrl & PCI_MSI_PVM_MASK) {
626 pci_config_put32(handle, msi_mask_off, msi_pvm);
627 DDI_INTR_IMPLDBG((CE_CONT,
628 "set_grp: pvm supported. Mask restored to 0x%x\n",
629 pci_config_get32(handle, msi_mask_off)));
630 }
631
632 set_grp_intr_done:
633 if (*result != 0)
634 return (PSM_FAILURE);
635
636 return (PSM_SUCCESS);
637 }
638
639 int
640 apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p)
641 {
642 struct autovec *av_dev;
643 uchar_t irqno;
644 int i;
645 apic_irq_t *irq_p;
646
647 /* Sanity check the vector/irq argument. */
648 ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR));
649
650 mutex_enter(&airq_mutex);
651
652 /*
653 * Convert the vecirq arg to an irq using vector_to_irq table
654 * if the arg is a vector. Pass thru if already an irq.
655 */
656 if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) ==
657 PSMGI_INTRBY_VEC)
658 irqno = apic_vector_to_irq[vecirq];
659 else
660 irqno = vecirq;
661
662 irq_p = apic_irq_table[irqno];
663
664 if ((irq_p == NULL) ||
665 ((irq_p->airq_mps_intr_index != RESERVE_INDEX) &&
666 ((irq_p->airq_temp_cpu == IRQ_UNBOUND) ||
667 (irq_p->airq_temp_cpu == IRQ_UNINIT)))) {
668 mutex_exit(&airq_mutex);
669 return (PSM_FAILURE);
670 }
671
672 if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) {
673
674 /* Get the (temp) cpu from apic_irq table, indexed by irq. */
675 intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu;
676
677 /* Return user bound info for intrd. */
678 if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) {
679 intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND;
680 intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND;
683
684 if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR)
685 intr_params_p->avgi_vector = irq_p->airq_vector;
686
687 if (intr_params_p->avgi_req_flags &
688 (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS))
689 /* Get number of devices from apic_irq table shared field. */
690 intr_params_p->avgi_num_devs = irq_p->airq_share;
691
692 if (intr_params_p->avgi_req_flags & PSMGI_REQ_GET_DEVS) {
693
694 intr_params_p->avgi_req_flags |= PSMGI_REQ_NUM_DEVS;
695
696 /* Some devices have NULL dip. Don't count these. */
697 if (intr_params_p->avgi_num_devs > 0) {
698 for (i = 0, av_dev = autovect[irqno].avh_link;
699 av_dev; av_dev = av_dev->av_link)
700 if (av_dev->av_vector && av_dev->av_dip)
701 i++;
702 intr_params_p->avgi_num_devs =
703 MIN(intr_params_p->avgi_num_devs, i);
704 }
705
706 /* There are no viable dips to return. */
707 if (intr_params_p->avgi_num_devs == 0)
708 intr_params_p->avgi_dip_list = NULL;
709
710 else { /* Return list of dips */
711
712 /* Allocate space in array for that number of devs. */
713 intr_params_p->avgi_dip_list = kmem_zalloc(
714 intr_params_p->avgi_num_devs *
715 sizeof (dev_info_t *),
716 KM_SLEEP);
717
718 /*
719 * Loop through the device list of the autovec table
720 * filling in the dip array.
721 *
722 * Note that the autovect table may have some special
723 * entries which contain NULL dips. These will be
|
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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2013 Pluribus Networks, Inc.
24 * Copyright 2017 Joyent, Inc.
25 */
26
27 /*
28 * apic_introp.c:
29 * Has code for Advanced DDI interrupt framework support.
30 */
31
32 #include <sys/cpuvar.h>
33 #include <sys/psm.h>
34 #include <sys/archsystm.h>
35 #include <sys/apic.h>
36 #include <sys/sunddi.h>
37 #include <sys/ddi_impldefs.h>
38 #include <sys/mach_intr.h>
39 #include <sys/sysmacros.h>
40 #include <sys/trap.h>
41 #include <sys/pci.h>
42 #include <sys/pci_intr_lib.h>
43 #include <sys/apic_common.h>
44
45 #define UCHAR_MAX UINT8_MAX
46
47 extern struct av_head autovect[];
48
49 /*
50 * Local Function Prototypes
51 */
52 apic_irq_t *apic_find_irq(dev_info_t *, struct intrspec *, int);
53
54 /*
55 * apic_pci_msi_enable_vector:
56 * Set the address/data fields in the MSI/X capability structure
57 * XXX: MSI-X support
58 */
59 /* ARGSUSED */
60 void
61 apic_pci_msi_enable_vector(apic_irq_t *irq_ptr, int type, int inum, int vector,
62 int count, int target_apic_id)
63 {
64 uint64_t msi_addr, msi_data;
65 ushort_t msi_ctrl;
66 dev_info_t *dip = irq_ptr->airq_dip;
212 * starting vector
213 */
214 msibits = count - 1;
215
216 /* It has to be contiguous */
217 for (i = lowest; i <= highest; i++) {
218 navail = 0;
219
220 /*
221 * starting vector has to be aligned accordingly for
222 * multiple MSIs
223 */
224 if (msibits)
225 i = (i + msibits) & ~msibits;
226 start = i;
227 while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) &&
228 (i <= highest)) {
229 if (APIC_CHECK_RESERVE_VECTORS(i))
230 break;
231 navail++;
232 if (navail >= count) {
233 ASSERT(start >= 0 && start <= UCHAR_MAX);
234 return ((uchar_t)start);
235 }
236 i++;
237 }
238 }
239 return (0);
240 }
241
242
243 /*
244 * It finds the apic_irq_t associates with the dip, ispec and type.
245 */
246 apic_irq_t *
247 apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type)
248 {
249 apic_irq_t *irqp;
250 int i;
251
252 DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x "
253 "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec,
254 ispec->intrspec_pri, type));
255
493 return (PSM_FAILURE);
494 }
495 /*
496 * keep tracking the default interrupt cpu binding
497 */
498 irqp->airq_cpu = cpu;
499
500 *result = 0;
501 return (PSM_SUCCESS);
502 }
503
504 static int
505 apic_grp_set_cpu(int irqno, int new_cpu, int *result)
506 {
507 dev_info_t *orig_dip;
508 uint32_t orig_cpu;
509 ulong_t iflag;
510 apic_irq_t *irqps[PCI_MSI_MAX_INTRS];
511 int i;
512 int cap_ptr;
513 int msi_mask_off = 0;
514 ushort_t msi_ctrl;
515 uint32_t msi_pvm = 0;
516 ddi_acc_handle_t handle;
517 int num_vectors = 0;
518 uint32_t vector;
519
520 DDI_INTR_IMPLDBG((CE_CONT, "APIC_GRP_SET_CPU\n"));
521
522 /*
523 * Take mutex to insure that table doesn't change out from underneath
524 * us while we're playing with it.
525 */
526 mutex_enter(&airq_mutex);
527 irqps[0] = apic_irq_table[irqno];
528 orig_cpu = irqps[0]->airq_temp_cpu;
529 orig_dip = irqps[0]->airq_dip;
530 num_vectors = irqps[0]->airq_intin_no;
531 vector = irqps[0]->airq_vector;
532
533 /* A "group" of 1 */
534 if (num_vectors == 1) {
535 mutex_exit(&airq_mutex);
629 /* Reenable vectors if per vector masking is supported. */
630 if (msi_ctrl & PCI_MSI_PVM_MASK) {
631 pci_config_put32(handle, msi_mask_off, msi_pvm);
632 DDI_INTR_IMPLDBG((CE_CONT,
633 "set_grp: pvm supported. Mask restored to 0x%x\n",
634 pci_config_get32(handle, msi_mask_off)));
635 }
636
637 set_grp_intr_done:
638 if (*result != 0)
639 return (PSM_FAILURE);
640
641 return (PSM_SUCCESS);
642 }
643
644 int
645 apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p)
646 {
647 struct autovec *av_dev;
648 uchar_t irqno;
649 uint i;
650 apic_irq_t *irq_p;
651
652 /* Sanity check the vector/irq argument. */
653 ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR));
654
655 mutex_enter(&airq_mutex);
656
657 /*
658 * Convert the vecirq arg to an irq using vector_to_irq table
659 * if the arg is a vector. Pass thru if already an irq.
660 */
661 if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) ==
662 PSMGI_INTRBY_VEC)
663 irqno = apic_vector_to_irq[vecirq];
664 else
665 irqno = (uchar_t)vecirq;
666
667 irq_p = apic_irq_table[irqno];
668
669 if ((irq_p == NULL) ||
670 ((irq_p->airq_mps_intr_index != RESERVE_INDEX) &&
671 ((irq_p->airq_temp_cpu == IRQ_UNBOUND) ||
672 (irq_p->airq_temp_cpu == IRQ_UNINIT)))) {
673 mutex_exit(&airq_mutex);
674 return (PSM_FAILURE);
675 }
676
677 if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) {
678
679 /* Get the (temp) cpu from apic_irq table, indexed by irq. */
680 intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu;
681
682 /* Return user bound info for intrd. */
683 if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) {
684 intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND;
685 intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND;
688
689 if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR)
690 intr_params_p->avgi_vector = irq_p->airq_vector;
691
692 if (intr_params_p->avgi_req_flags &
693 (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS))
694 /* Get number of devices from apic_irq table shared field. */
695 intr_params_p->avgi_num_devs = irq_p->airq_share;
696
697 if (intr_params_p->avgi_req_flags & PSMGI_REQ_GET_DEVS) {
698
699 intr_params_p->avgi_req_flags |= PSMGI_REQ_NUM_DEVS;
700
701 /* Some devices have NULL dip. Don't count these. */
702 if (intr_params_p->avgi_num_devs > 0) {
703 for (i = 0, av_dev = autovect[irqno].avh_link;
704 av_dev; av_dev = av_dev->av_link)
705 if (av_dev->av_vector && av_dev->av_dip)
706 i++;
707 intr_params_p->avgi_num_devs =
708 (uchar_t)MIN(intr_params_p->avgi_num_devs, i);
709 }
710
711 /* There are no viable dips to return. */
712 if (intr_params_p->avgi_num_devs == 0)
713 intr_params_p->avgi_dip_list = NULL;
714
715 else { /* Return list of dips */
716
717 /* Allocate space in array for that number of devs. */
718 intr_params_p->avgi_dip_list = kmem_zalloc(
719 intr_params_p->avgi_num_devs *
720 sizeof (dev_info_t *),
721 KM_SLEEP);
722
723 /*
724 * Loop through the device list of the autovec table
725 * filling in the dip array.
726 *
727 * Note that the autovect table may have some special
728 * entries which contain NULL dips. These will be
|