1 /* $NetBSD: pci_msi_machdep.c,v 1.10 2024/06/30 09:38:07 jmcneill 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@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.10 2024/06/30 09:38:07 jmcneill 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 #include <arm/pci/pci_msi_machdep.h> 44 45 static SIMPLEQ_HEAD(, arm_pci_msi) arm_pci_msi_list = 46 SIMPLEQ_HEAD_INITIALIZER(arm_pci_msi_list); 47 48 static struct arm_pci_msi * 49 arm_pci_msi_find_frame(pci_intr_handle_t ih) 50 { 51 struct arm_pci_msi *msip; 52 53 const int id = __SHIFTOUT(ih, ARM_PCI_INTR_FRAME); 54 55 SIMPLEQ_FOREACH(msip, &arm_pci_msi_list, msi_link) 56 if (id == msip->msi_id) 57 return msip; 58 59 return NULL; 60 } 61 62 static struct arm_pci_msi * 63 arm_pci_msi_lookup(const struct pci_attach_args *pa) 64 { 65 struct arm_pci_msi *msip; 66 uint32_t rid, frameid; 67 int b, d, f; 68 69 pci_decompose_tag(pa->pa_pc, pa->pa_tag, &b, &d, &f); 70 71 rid = (b << 8) | (d << 3) | f; 72 frameid = pci_get_frameid(pa->pa_pc, rid); 73 74 SIMPLEQ_FOREACH(msip, &arm_pci_msi_list, msi_link) 75 if (frameid == msip->msi_id) 76 return msip; 77 78 return NULL; 79 } 80 81 static int 82 arm_pci_msi_alloc_common(pci_intr_handle_t **ihps, int *count, const struct pci_attach_args *pa, bool exact) 83 { 84 pci_intr_handle_t *vectors; 85 struct arm_pci_msi *msi; 86 87 if ((pa->pa_flags & PCI_FLAGS_MSI_OKAY) == 0) 88 return ENODEV; 89 90 msi = arm_pci_msi_lookup(pa); 91 if (msi == NULL || msi->msi_alloc == NULL) 92 return EINVAL; 93 94 vectors = msi->msi_alloc(msi, count, pa, exact); 95 if (vectors == NULL) 96 return ENOMEM; 97 98 *ihps = vectors; 99 100 return 0; 101 } 102 103 static int 104 arm_pci_msix_alloc_common(pci_intr_handle_t **ihps, u_int *table_indexes, int *count, const struct pci_attach_args *pa, bool exact) 105 { 106 pci_intr_handle_t *vectors; 107 struct arm_pci_msi *msi; 108 109 if ((pa->pa_flags & PCI_FLAGS_MSIX_OKAY) == 0) 110 return ENODEV; 111 112 msi = arm_pci_msi_lookup(pa); 113 if (msi == NULL || msi->msix_alloc == NULL) 114 return EINVAL; 115 116 vectors = msi->msix_alloc(msi, table_indexes, count, pa, exact); 117 if (vectors == NULL) 118 return ENOMEM; 119 120 *ihps = vectors; 121 122 return 0; 123 } 124 125 /* 126 * arm_pci_msi MD API 127 */ 128 129 int 130 arm_pci_msi_add(struct arm_pci_msi *msi) 131 { 132 SIMPLEQ_INSERT_TAIL(&arm_pci_msi_list, msi, msi_link); 133 134 return 0; 135 } 136 137 void * 138 arm_pci_msi_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t pih, 139 int ipl, int (*func)(void *), void *arg, const char *xname) 140 { 141 struct arm_pci_msi *msi; 142 143 msi = arm_pci_msi_find_frame(pih); 144 if (msi == NULL) 145 return NULL; 146 147 return msi->msi_intr_establish(msi, pih, ipl, func, arg, xname); 148 } 149 150 /* 151 * pci_msi(9) implementation 152 */ 153 154 int 155 pci_msi_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count) 156 { 157 return arm_pci_msi_alloc_common(ihps, count, pa, false); 158 } 159 160 int 161 pci_msi_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int count) 162 { 163 return arm_pci_msi_alloc_common(ihps, &count, pa, true); 164 } 165 166 int 167 pci_msix_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count) 168 { 169 return arm_pci_msix_alloc_common(ihps, NULL, count, pa, false); 170 } 171 172 int 173 pci_msix_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int count) 174 { 175 return arm_pci_msix_alloc_common(ihps, NULL, &count, pa, true); 176 } 177 178 int 179 pci_msix_alloc_map(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, u_int *table_indexes, int count) 180 { 181 return arm_pci_msix_alloc_common(ihps, table_indexes, &count, pa, true); 182 } 183 184 int 185 pci_intx_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihp) 186 { 187 pci_intr_handle_t *pih; 188 189 if (ihp == NULL) 190 return EINVAL; 191 192 pih = kmem_alloc(sizeof(*pih), KM_SLEEP); 193 if (pci_intr_map(pa, pih) != 0) { 194 kmem_free(pih, sizeof(*pih)); 195 return EINVAL; 196 } 197 *ihp = pih; 198 199 return 0; 200 } 201 202 int 203 pci_intr_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *counts, pci_intr_type_t max_type) 204 { 205 int intx_count, msi_count, msix_count, error; 206 207 error = EINVAL; 208 209 if (counts != NULL) { 210 intx_count = msi_count = msix_count = 0; 211 212 switch (max_type) { 213 case PCI_INTR_TYPE_MSIX: 214 msix_count = counts[PCI_INTR_TYPE_MSIX]; 215 /* FALLTHROUGH */ 216 case PCI_INTR_TYPE_MSI: 217 msi_count = counts[PCI_INTR_TYPE_MSI]; 218 /* FALLTHROUGH */ 219 case PCI_INTR_TYPE_INTX: 220 intx_count = counts[PCI_INTR_TYPE_INTX]; 221 if (intx_count > 1) 222 return EINVAL; 223 break; 224 default: 225 return EINVAL; 226 } 227 memset(counts, 0, sizeof(*counts) * PCI_INTR_TYPE_SIZE); 228 } else { 229 intx_count = msi_count = msix_count = 1; 230 } 231 232 if (msix_count == -1) 233 msix_count = pci_msix_count(pa->pa_pc, pa->pa_tag); 234 if (msix_count > 0 && (error = pci_msix_alloc_exact(pa, ihps, msix_count)) == 0) { 235 if (counts != NULL) 236 counts[PCI_INTR_TYPE_MSIX] = msix_count; 237 return 0; 238 } 239 240 if (msi_count == -1) 241 msi_count = pci_msi_count(pa->pa_pc, pa->pa_tag); 242 if (msi_count > 0 && (error = pci_msi_alloc_exact(pa, ihps, msi_count)) == 0) { 243 if (counts != NULL) 244 counts[PCI_INTR_TYPE_MSI] = msi_count; 245 return 0; 246 } 247 248 if (intx_count > 0 && (error = pci_intx_alloc(pa, ihps)) == 0) { 249 if (counts != NULL) 250 counts[PCI_INTR_TYPE_INTX] = intx_count; 251 return 0; 252 } 253 254 return error; 255 } 256 257 void 258 pci_intr_release(pci_chipset_tag_t pc, pci_intr_handle_t *pih, int count) 259 { 260 struct arm_pci_msi *msi = NULL; 261 262 if (pih == NULL || count == 0) 263 return; 264 265 if ((pih[0] & (ARM_PCI_INTR_MSIX|ARM_PCI_INTR_MSI)) != 0) { 266 msi = arm_pci_msi_find_frame(pih[0]); 267 KASSERT(msi != NULL); 268 msi->msi_intr_release(msi, pih, count); 269 } 270 271 kmem_free(pih, sizeof(*pih) * count); 272 } 273 274 pci_intr_type_t 275 pci_intr_type(pci_chipset_tag_t pc, pci_intr_handle_t ih) 276 { 277 if (ih & ARM_PCI_INTR_MSIX) 278 return PCI_INTR_TYPE_MSIX; 279 280 if (ih & ARM_PCI_INTR_MSI) 281 return PCI_INTR_TYPE_MSI; 282 283 return PCI_INTR_TYPE_INTX; 284 } 285