1 /*- 2 * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates 3 * All rights reserved. 4 * 5 * Developed by Semihalf. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/lock.h> 36 #include <sys/malloc.h> 37 #include <sys/module.h> 38 #include <sys/mutex.h> 39 #include <sys/bus.h> 40 #include <sys/rman.h> 41 #include <sys/vmem.h> 42 43 #include <dev/ofw/ofw_bus.h> 44 #include <dev/ofw/ofw_bus_subr.h> 45 46 #include "msi_if.h" 47 #include "pic_if.h" 48 49 #define AL_SPI_INTR 0 50 #define AL_EDGE_HIGH 1 51 #define ERR_NOT_IN_MAP -1 52 #define IRQ_OFFSET 1 53 #define GIC_INTR_CELL_CNT 3 54 #define INTR_RANGE_COUNT 2 55 #define MAX_MSIX_COUNT 160 56 57 static int al_msix_attach(device_t); 58 static int al_msix_probe(device_t); 59 60 static msi_alloc_msi_t al_msix_alloc_msi; 61 static msi_release_msi_t al_msix_release_msi; 62 static msi_alloc_msix_t al_msix_alloc_msix; 63 static msi_release_msix_t al_msix_release_msix; 64 static msi_map_msi_t al_msix_map_msi; 65 66 static int al_find_intr_pos_in_map(device_t, struct intr_irqsrc *); 67 68 static struct ofw_compat_data compat_data[] = { 69 {"annapurna-labs,al-msix", true}, 70 {"annapurna-labs,alpine-msix", true}, 71 {NULL, false} 72 }; 73 74 /* 75 * Bus interface definitions. 76 */ 77 static device_method_t al_msix_methods[] = { 78 DEVMETHOD(device_probe, al_msix_probe), 79 DEVMETHOD(device_attach, al_msix_attach), 80 81 /* Interrupt controller interface */ 82 DEVMETHOD(msi_alloc_msi, al_msix_alloc_msi), 83 DEVMETHOD(msi_release_msi, al_msix_release_msi), 84 DEVMETHOD(msi_alloc_msix, al_msix_alloc_msix), 85 DEVMETHOD(msi_release_msix, al_msix_release_msix), 86 DEVMETHOD(msi_map_msi, al_msix_map_msi), 87 88 DEVMETHOD_END 89 }; 90 91 struct al_msix_softc { 92 bus_addr_t base_addr; 93 struct resource *res; 94 uint32_t irq_min; 95 uint32_t irq_max; 96 uint32_t irq_count; 97 struct mtx msi_mtx; 98 vmem_t *irq_alloc; 99 device_t gic_dev; 100 /* Table of isrcs maps isrc pointer to vmem_alloc'd irq number */ 101 struct intr_irqsrc *isrcs[MAX_MSIX_COUNT]; 102 }; 103 104 static driver_t al_msix_driver = { 105 "al_msix", 106 al_msix_methods, 107 sizeof(struct al_msix_softc), 108 }; 109 110 DRIVER_MODULE(al_msix, ofwbus, al_msix_driver, 0, 0); 111 DRIVER_MODULE(al_msix, simplebus, al_msix_driver, 0, 0); 112 113 MALLOC_DECLARE(M_AL_MSIX); 114 MALLOC_DEFINE(M_AL_MSIX, "al_msix", "Alpine MSIX"); 115 116 static int 117 al_msix_probe(device_t dev) 118 { 119 120 if (!ofw_bus_status_okay(dev)) 121 return (ENXIO); 122 123 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 124 return (ENXIO); 125 126 device_set_desc(dev, "Annapurna-Labs MSI-X Controller"); 127 return (BUS_PROBE_DEFAULT); 128 } 129 130 static int 131 al_msix_attach(device_t dev) 132 { 133 struct al_msix_softc *sc; 134 device_t gic_dev; 135 phandle_t iparent; 136 phandle_t node; 137 intptr_t xref; 138 int interrupts[INTR_RANGE_COUNT]; 139 int nintr, i, rid; 140 uint32_t icells, *intr; 141 142 sc = device_get_softc(dev); 143 144 node = ofw_bus_get_node(dev); 145 xref = OF_xref_from_node(node); 146 OF_device_register_xref(xref, dev); 147 148 rid = 0; 149 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 150 if (sc->res == NULL) { 151 device_printf(dev, "Failed to allocate resource\n"); 152 return (ENXIO); 153 } 154 155 sc->base_addr = (bus_addr_t)rman_get_start(sc->res); 156 157 /* Register this device to handle MSI interrupts */ 158 if (intr_msi_register(dev, xref) != 0) { 159 device_printf(dev, "could not register MSI-X controller\n"); 160 return (ENXIO); 161 } 162 else 163 device_printf(dev, "MSI-X controller registered\n"); 164 165 /* Find root interrupt controller */ 166 iparent = ofw_bus_find_iparent(node); 167 if (iparent == 0) { 168 device_printf(dev, "No interrupt-parrent found. " 169 "Error in DTB\n"); 170 return (ENXIO); 171 } else { 172 /* While at parent - store interrupt cells prop */ 173 if (OF_searchencprop(OF_node_from_xref(iparent), 174 "#interrupt-cells", &icells, sizeof(icells)) == -1) { 175 device_printf(dev, "DTB: Missing #interrupt-cells " 176 "property in GIC node\n"); 177 return (ENXIO); 178 } 179 } 180 181 gic_dev = OF_device_from_xref(iparent); 182 if (gic_dev == NULL) { 183 device_printf(dev, "Cannot find GIC device\n"); 184 return (ENXIO); 185 } 186 sc->gic_dev = gic_dev; 187 188 /* Manually read range of interrupts from DTB */ 189 nintr = OF_getencprop_alloc_multi(node, "interrupts", sizeof(*intr), 190 (void **)&intr); 191 if (nintr == 0) { 192 device_printf(dev, "Cannot read interrupts prop from DTB\n"); 193 return (ENXIO); 194 } else if ((nintr / icells) != INTR_RANGE_COUNT) { 195 /* Supposed to have min and max value only */ 196 device_printf(dev, "Unexpected count of interrupts " 197 "in DTB node\n"); 198 return (EINVAL); 199 } 200 201 /* Read interrupt range values */ 202 for (i = 0; i < INTR_RANGE_COUNT; i++) 203 interrupts[i] = intr[(i * icells) + IRQ_OFFSET]; 204 205 sc->irq_min = interrupts[0]; 206 sc->irq_max = interrupts[1]; 207 sc->irq_count = (sc->irq_max - sc->irq_min + 1); 208 209 if (sc->irq_count > MAX_MSIX_COUNT) { 210 device_printf(dev, "Available MSI-X count exceeds buffer size." 211 " Capping to %d\n", MAX_MSIX_COUNT); 212 sc->irq_count = MAX_MSIX_COUNT; 213 } 214 215 mtx_init(&sc->msi_mtx, "msi_mtx", NULL, MTX_DEF); 216 217 sc->irq_alloc = vmem_create("Alpine MSI-X IRQs", 0, sc->irq_count, 218 1, 0, M_FIRSTFIT | M_WAITOK); 219 220 device_printf(dev, "MSI-X SPI IRQ %d-%d\n", sc->irq_min, sc->irq_max); 221 222 return (bus_generic_attach(dev)); 223 } 224 225 static int 226 al_find_intr_pos_in_map(device_t dev, struct intr_irqsrc *isrc) 227 { 228 struct al_msix_softc *sc; 229 int i; 230 231 sc = device_get_softc(dev); 232 for (i = 0; i < MAX_MSIX_COUNT; i++) 233 if (sc->isrcs[i] == isrc) 234 return (i); 235 return (ERR_NOT_IN_MAP); 236 } 237 238 static int 239 al_msix_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, 240 uint64_t *addr, uint32_t *data) 241 { 242 struct al_msix_softc *sc; 243 int i, spi; 244 245 sc = device_get_softc(dev); 246 247 i = al_find_intr_pos_in_map(dev, isrc); 248 if (i == ERR_NOT_IN_MAP) 249 return (EINVAL); 250 251 spi = sc->irq_min + i; 252 253 /* 254 * MSIX message address format: 255 * [63:20] - MSIx TBAR 256 * Same value as the MSIx Translation Base Address Register 257 * [19] - WFE_EXIT 258 * Once set by MSIx message, an EVENTI is signal to the CPUs 259 * cluster specified by ‘Local GIC Target List’ 260 * [18:17] - Target GIC ID 261 * Specifies which IO-GIC (external shared GIC) is targeted 262 * 0: Local GIC, as specified by the Local GIC Target List 263 * 1: IO-GIC 0 264 * 2: Reserved 265 * 3: Reserved 266 * [16:13] - Local GIC Target List 267 * Specifies the Local GICs list targeted by this MSIx 268 * message. 269 * [16] If set, SPIn is set in Cluster 0 local GIC 270 * [15:13] Reserved 271 * [15] If set, SPIn is set in Cluster 1 local GIC 272 * [14] If set, SPIn is set in Cluster 2 local GIC 273 * [13] If set, SPIn is set in Cluster 3 local GIC 274 * [12:3] - SPIn 275 * Specifies the SPI (Shared Peripheral Interrupt) index to 276 * be set in target GICs 277 * Notes: 278 * If targeting any local GIC than only SPI[249:0] are valid 279 * [2] - Function vector 280 * MSI Data vector extension hint 281 * [1:0] - Reserved 282 * Must be set to zero 283 */ 284 *addr = (uint64_t)sc->base_addr + (uint64_t)((1 << 16) + (spi << 3)); 285 *data = 0; 286 287 if (bootverbose) 288 device_printf(dev, "MSI mapping: SPI: %d addr: %jx data: %x\n", 289 spi, (uintmax_t)*addr, *data); 290 return (0); 291 } 292 293 static int 294 al_msix_alloc_msi(device_t dev, device_t child, int count, int maxcount, 295 device_t *pic, struct intr_irqsrc **srcs) 296 { 297 struct intr_map_data_fdt *fdt_data; 298 struct al_msix_softc *sc; 299 vmem_addr_t irq_base; 300 int error; 301 u_int i, j; 302 303 sc = device_get_softc(dev); 304 305 if ((powerof2(count) == 0) || (count > 8)) 306 return (EINVAL); 307 308 if (vmem_alloc(sc->irq_alloc, count, M_FIRSTFIT | M_NOWAIT, 309 &irq_base) != 0) 310 return (ENOMEM); 311 312 /* Fabricate OFW data to get ISRC from GIC and return it */ 313 fdt_data = malloc(sizeof(*fdt_data) + 314 GIC_INTR_CELL_CNT * sizeof(pcell_t), M_AL_MSIX, M_WAITOK); 315 fdt_data->hdr.type = INTR_MAP_DATA_FDT; 316 fdt_data->iparent = 0; 317 fdt_data->ncells = GIC_INTR_CELL_CNT; 318 fdt_data->cells[0] = AL_SPI_INTR; /* code for SPI interrupt */ 319 fdt_data->cells[1] = 0; /* SPI number (uninitialized) */ 320 fdt_data->cells[2] = AL_EDGE_HIGH; /* trig = edge, pol = high */ 321 322 mtx_lock(&sc->msi_mtx); 323 324 for (i = irq_base; i < irq_base + count; i++) { 325 fdt_data->cells[1] = sc->irq_min + i; 326 error = PIC_MAP_INTR(sc->gic_dev, 327 (struct intr_map_data *)fdt_data, srcs); 328 if (error) { 329 for (j = irq_base; j < i; j++) 330 sc->isrcs[j] = NULL; 331 mtx_unlock(&sc->msi_mtx); 332 vmem_free(sc->irq_alloc, irq_base, count); 333 free(fdt_data, M_AL_MSIX); 334 return (error); 335 } 336 337 sc->isrcs[i] = *srcs; 338 srcs++; 339 } 340 341 mtx_unlock(&sc->msi_mtx); 342 free(fdt_data, M_AL_MSIX); 343 344 if (bootverbose) 345 device_printf(dev, 346 "MSI-X allocation: start SPI %d, count %d\n", 347 (int)irq_base + sc->irq_min, count); 348 349 *pic = sc->gic_dev; 350 351 return (0); 352 } 353 354 static int 355 al_msix_release_msi(device_t dev, device_t child, int count, 356 struct intr_irqsrc **srcs) 357 { 358 struct al_msix_softc *sc; 359 int i, pos; 360 361 sc = device_get_softc(dev); 362 363 mtx_lock(&sc->msi_mtx); 364 365 pos = al_find_intr_pos_in_map(dev, *srcs); 366 vmem_free(sc->irq_alloc, pos, count); 367 for (i = 0; i < count; i++) { 368 pos = al_find_intr_pos_in_map(dev, *srcs); 369 if (pos != ERR_NOT_IN_MAP) 370 sc->isrcs[pos] = NULL; 371 srcs++; 372 } 373 374 mtx_unlock(&sc->msi_mtx); 375 376 return (0); 377 } 378 379 static int 380 al_msix_alloc_msix(device_t dev, device_t child, device_t *pic, 381 struct intr_irqsrc **isrcp) 382 { 383 384 return (al_msix_alloc_msi(dev, child, 1, 1, pic, isrcp)); 385 } 386 387 static int 388 al_msix_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc) 389 { 390 391 return (al_msix_release_msi(dev, child, 1, &isrc)); 392 } 393