Home | History | Annotate | Line # | Download | only in pci
      1 /* $NetBSD: pci_msi_machdep.c,v 1.11 2026/09/20 11:00:32 skrll Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2018 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jared McNeill <jmcneill (at) invisible.ca>.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: pci_msi_machdep.c,v 1.11 2026/09/20 11:00:32 skrll Exp $");
     34 
     35 #include <sys/kernel.h>
     36 #include <sys/kmem.h>
     37 
     38 #include <dev/pci/pcireg.h>
     39 #include <dev/pci/pcivar.h>
     40 
     41 #include <arm/pic/picvar.h>
     42 
     43 static SIMPLEQ_HEAD(, md_pci_msi) md_pci_msi_list =
     44     SIMPLEQ_HEAD_INITIALIZER(md_pci_msi_list);
     45 
     46 static struct md_pci_msi *
     47 md_pci_msi_find_frame(pci_intr_handle_t ih)
     48 {
     49 	struct md_pci_msi *msip;
     50 
     51 	const int id = __SHIFTOUT(ih, MD_PCI_INTR_FRAME);
     52 
     53 	SIMPLEQ_FOREACH(msip, &md_pci_msi_list, msi_link)
     54 		if (id == msip->msi_id)
     55 			return msip;
     56 
     57 	return NULL;
     58 }
     59 
     60 static struct md_pci_msi *
     61 md_pci_msi_lookup(const struct pci_attach_args *pa)
     62 {
     63 	struct md_pci_msi *msip;
     64 	uint32_t rid, frameid;
     65 	int b, d, f;
     66 
     67 	pci_decompose_tag(pa->pa_pc, pa->pa_tag, &b, &d, &f);
     68 
     69 	rid = (b << 8) | (d << 3) | f;
     70 	frameid = pci_get_frameid(pa->pa_pc, rid);
     71 
     72 	SIMPLEQ_FOREACH(msip, &md_pci_msi_list, msi_link)
     73 		if (frameid == msip->msi_id)
     74 			return msip;
     75 
     76 	return NULL;
     77 }
     78 
     79 static int
     80 md_pci_msi_alloc_common(pci_intr_handle_t **ihps, int *count, const struct pci_attach_args *pa, bool exact)
     81 {
     82 	pci_intr_handle_t *vectors;
     83 	struct md_pci_msi *msi;
     84 
     85 	if ((pa->pa_flags & PCI_FLAGS_MSI_OKAY) == 0)
     86 		return ENODEV;
     87 
     88 	msi = md_pci_msi_lookup(pa);
     89 	if (msi == NULL || msi->msi_alloc == NULL)
     90 		return EINVAL;
     91 
     92 	vectors = msi->msi_alloc(msi, count, pa, exact);
     93 	if (vectors == NULL)
     94 		return ENOMEM;
     95 
     96 	*ihps = vectors;
     97 
     98 	return 0;
     99 }
    100 
    101 static int
    102 md_pci_msix_alloc_common(pci_intr_handle_t **ihps, u_int *table_indexes, int *count, const struct pci_attach_args *pa, bool exact)
    103 {
    104 	pci_intr_handle_t *vectors;
    105 	struct md_pci_msi *msi;
    106 
    107 	if ((pa->pa_flags & PCI_FLAGS_MSIX_OKAY) == 0)
    108 		return ENODEV;
    109 
    110 	msi = md_pci_msi_lookup(pa);
    111 	if (msi == NULL || msi->msix_alloc == NULL)
    112 		return EINVAL;
    113 
    114 	vectors = msi->msix_alloc(msi, table_indexes, count, pa, exact);
    115 	if (vectors == NULL)
    116 		return ENOMEM;
    117 
    118 	*ihps = vectors;
    119 
    120 	return 0;
    121 }
    122 
    123 /*
    124  * md_pci_msi MD API
    125  */
    126 
    127 int
    128 md_pci_msi_add(struct md_pci_msi *msi)
    129 {
    130 	SIMPLEQ_INSERT_TAIL(&md_pci_msi_list, msi, msi_link);
    131 
    132 	return 0;
    133 }
    134 
    135 void *
    136 md_pci_msi_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t pih,
    137     int ipl, int (*func)(void *), void *arg, const char *xname)
    138 {
    139 	struct md_pci_msi *msi;
    140 
    141 	msi = md_pci_msi_find_frame(pih);
    142 	if (msi == NULL)
    143 		return NULL;
    144 
    145 	return msi->msi_intr_establish(msi, pih, ipl, func, arg, xname);
    146 }
    147 
    148 /*
    149  * pci_msi(9) implementation
    150  */
    151 
    152 int
    153 pci_msi_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count)
    154 {
    155 	return md_pci_msi_alloc_common(ihps, count, pa, false);
    156 }
    157 
    158 int
    159 pci_msi_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int count)
    160 {
    161 	return md_pci_msi_alloc_common(ihps, &count, pa, true);
    162 }
    163 
    164 int
    165 pci_msix_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count)
    166 {
    167 	return md_pci_msix_alloc_common(ihps, NULL, count, pa, false);
    168 }
    169 
    170 int
    171 pci_msix_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int count)
    172 {
    173 	return md_pci_msix_alloc_common(ihps, NULL, &count, pa, true);
    174 }
    175 
    176 int
    177 pci_msix_alloc_map(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, u_int *table_indexes, int count)
    178 {
    179 	return md_pci_msix_alloc_common(ihps, table_indexes, &count, pa, true);
    180 }
    181 
    182 int
    183 pci_intx_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihp)
    184 {
    185 	pci_intr_handle_t *pih;
    186 
    187 	if (ihp == NULL)
    188 		return EINVAL;
    189 
    190 	pih = kmem_alloc(sizeof(*pih), KM_SLEEP);
    191 	if (pci_intr_map(pa, pih) != 0) {
    192 		kmem_free(pih, sizeof(*pih));
    193 		return EINVAL;
    194 	}
    195 	*ihp = pih;
    196 
    197 	return 0;
    198 }
    199 
    200 int
    201 pci_intr_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *counts, pci_intr_type_t max_type)
    202 {
    203 	int intx_count, msi_count, msix_count, error;
    204 
    205 	error = EINVAL;
    206 
    207 	if (counts != NULL) {
    208 		intx_count = msi_count = msix_count = 0;
    209 
    210 		switch (max_type) {
    211 		case PCI_INTR_TYPE_MSIX:
    212 			msix_count = counts[PCI_INTR_TYPE_MSIX];
    213 			/* FALLTHROUGH */
    214 		case PCI_INTR_TYPE_MSI:
    215 			msi_count = counts[PCI_INTR_TYPE_MSI];
    216 			/* FALLTHROUGH */
    217 		case PCI_INTR_TYPE_INTX:
    218 			intx_count = counts[PCI_INTR_TYPE_INTX];
    219 			if (intx_count > 1)
    220 				return EINVAL;
    221 			break;
    222 		default:
    223 			return EINVAL;
    224 		}
    225 		memset(counts, 0, sizeof(*counts) * PCI_INTR_TYPE_SIZE);
    226 	} else {
    227 		intx_count = msi_count = msix_count = 1;
    228 	}
    229 
    230 	if (msix_count == -1)
    231 		msix_count = pci_msix_count(pa->pa_pc, pa->pa_tag);
    232 	if (msix_count > 0 && (error = pci_msix_alloc_exact(pa, ihps, msix_count)) == 0) {
    233 		if (counts != NULL)
    234 			counts[PCI_INTR_TYPE_MSIX] = msix_count;
    235 		return 0;
    236 	}
    237 
    238 	if (msi_count == -1)
    239 		msi_count = pci_msi_count(pa->pa_pc, pa->pa_tag);
    240 	if (msi_count > 0 && (error = pci_msi_alloc_exact(pa, ihps, msi_count)) == 0) {
    241 		if (counts != NULL)
    242 			counts[PCI_INTR_TYPE_MSI] = msi_count;
    243 		return 0;
    244 	}
    245 
    246 	if (intx_count > 0 && (error = pci_intx_alloc(pa, ihps)) == 0) {
    247 		if (counts != NULL)
    248 			counts[PCI_INTR_TYPE_INTX] = intx_count;
    249 		return 0;
    250 	}
    251 
    252 	return error;
    253 }
    254 
    255 void
    256 pci_intr_release(pci_chipset_tag_t pc, pci_intr_handle_t *pih, int count)
    257 {
    258 	struct md_pci_msi *msi = NULL;
    259 
    260 	if (pih == NULL || count == 0)
    261 		return;
    262 
    263 	if ((pih[0] & (MD_PCI_INTR_MSIX|MD_PCI_INTR_MSI)) != 0) {
    264 		msi = md_pci_msi_find_frame(pih[0]);
    265 		KASSERT(msi != NULL);
    266 		msi->msi_intr_release(msi, pih, count);
    267 	}
    268 
    269 	kmem_free(pih, sizeof(*pih) * count);
    270 }
    271 
    272 pci_intr_type_t
    273 pci_intr_type(pci_chipset_tag_t pc, pci_intr_handle_t ih)
    274 {
    275 	if (ih & MD_PCI_INTR_MSIX)
    276 		return PCI_INTR_TYPE_MSIX;
    277 
    278 	if (ih & MD_PCI_INTR_MSI)
    279 		return PCI_INTR_TYPE_MSI;
    280 
    281 	return PCI_INTR_TYPE_INTX;
    282 }
    283