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 devclass_t al_msix_devclass; 111 112 DRIVER_MODULE(al_msix, ofwbus, al_msix_driver, al_msix_devclass, 0, 0); 113 DRIVER_MODULE(al_msix, simplebus, al_msix_driver, al_msix_devclass, 0, 0); 114 115 MALLOC_DECLARE(M_AL_MSIX); 116 MALLOC_DEFINE(M_AL_MSIX, "al_msix", "Alpine MSIX"); 117 118 static int 119 al_msix_probe(device_t dev) 120 { 121 122 if (!ofw_bus_status_okay(dev)) 123 return (ENXIO); 124 125 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 126 return (ENXIO); 127 128 device_set_desc(dev, "Annapurna-Labs MSI-X Controller"); 129 return (BUS_PROBE_DEFAULT); 130 } 131 132 static int 133 al_msix_attach(device_t dev) 134 { 135 struct al_msix_softc *sc; 136 device_t gic_dev; 137 phandle_t iparent; 138 phandle_t node; 139 intptr_t xref; 140 int interrupts[INTR_RANGE_COUNT]; 141 int nintr, i, rid; 142 uint32_t icells, *intr; 143 144 sc = device_get_softc(dev); 145 146 node = ofw_bus_get_node(dev); 147 xref = OF_xref_from_node(node); 148 OF_device_register_xref(xref, dev); 149 150 rid = 0; 151 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 152 if (sc->res == NULL) { 153 device_printf(dev, "Failed to allocate resource\n"); 154 return (ENXIO); 155 } 156 157 sc->base_addr = (bus_addr_t)rman_get_start(sc->res); 158 159 /* Register this device to handle MSI interrupts */ 160 if (intr_msi_register(dev, xref) != 0) { 161 device_printf(dev, "could not register MSI-X controller\n"); 162 return (ENXIO); 163 } 164 else 165 device_printf(dev, "MSI-X controller registered\n"); 166 167 /* Find root interrupt controller */ 168 iparent = ofw_bus_find_iparent(node); 169 if (iparent == 0) { 170 device_printf(dev, "No interrupt-parrent found. " 171 "Error in DTB\n"); 172 return (ENXIO); 173 } else { 174 /* While at parent - store interrupt cells prop */ 175 if (OF_searchencprop(OF_node_from_xref(iparent), 176 "#interrupt-cells", &icells, sizeof(icells)) == -1) { 177 device_printf(dev, "DTB: Missing #interrupt-cells " 178 "property in GIC node\n"); 179 return (ENXIO); 180 } 181 } 182 183 gic_dev = OF_device_from_xref(iparent); 184 if (gic_dev == NULL) { 185 device_printf(dev, "Cannot find GIC device\n"); 186 return (ENXIO); 187 } 188 sc->gic_dev = gic_dev; 189 190 /* Manually read range of interrupts from DTB */ 191 nintr = OF_getencprop_alloc_multi(node, "interrupts", sizeof(*intr), 192 (void **)&intr); 193 if (nintr == 0) { 194 device_printf(dev, "Cannot read interrupts prop from DTB\n"); 195 return (ENXIO); 196 } else if ((nintr / icells) != INTR_RANGE_COUNT) { 197 /* Supposed to have min and max value only */ 198 device_printf(dev, "Unexpected count of interrupts " 199 "in DTB node\n"); 200 return (EINVAL); 201 } 202 203 /* Read interrupt range values */ 204 for (i = 0; i < INTR_RANGE_COUNT; i++) 205 interrupts[i] = intr[(i * icells) + IRQ_OFFSET]; 206 207 sc->irq_min = interrupts[0]; 208 sc->irq_max = interrupts[1]; 209 sc->irq_count = (sc->irq_max - sc->irq_min + 1); 210 211 if (sc->irq_count > MAX_MSIX_COUNT) { 212 device_printf(dev, "Available MSI-X count exceeds buffer size." 213 " Capping to %d\n", MAX_MSIX_COUNT); 214 sc->irq_count = MAX_MSIX_COUNT; 215 } 216 217 mtx_init(&sc->msi_mtx, "msi_mtx", NULL, MTX_DEF); 218 219 sc->irq_alloc = vmem_create("Alpine MSI-X IRQs", 0, sc->irq_count, 220 1, 0, M_FIRSTFIT | M_WAITOK); 221 222 device_printf(dev, "MSI-X SPI IRQ %d-%d\n", sc->irq_min, sc->irq_max); 223 224 return (bus_generic_attach(dev)); 225 } 226 227 static int 228 al_find_intr_pos_in_map(device_t dev, struct intr_irqsrc *isrc) 229 { 230 struct al_msix_softc *sc; 231 int i; 232 233 sc = device_get_softc(dev); 234 for (i = 0; i < MAX_MSIX_COUNT; i++) 235 if (sc->isrcs[i] == isrc) 236 return (i); 237 return (ERR_NOT_IN_MAP); 238 } 239 240 static int 241 al_msix_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, 242 uint64_t *addr, uint32_t *data) 243 { 244 struct al_msix_softc *sc; 245 int i, spi; 246 247 sc = device_get_softc(dev); 248 249 i = al_find_intr_pos_in_map(dev, isrc); 250 if (i == ERR_NOT_IN_MAP) 251 return (EINVAL); 252 253 spi = sc->irq_min + i; 254 255 /* 256 * MSIX message address format: 257 * [63:20] - MSIx TBAR 258 * Same value as the MSIx Translation Base Address Register 259 * [19] - WFE_EXIT 260 * Once set by MSIx message, an EVENTI is signal to the CPUs 261 * cluster specified by ‘Local GIC Target List’ 262 * [18:17] - Target GIC ID 263 * Specifies which IO-GIC (external shared GIC) is targeted 264 * 0: Local GIC, as specified by the Local GIC Target List 265 * 1: IO-GIC 0 266 * 2: Reserved 267 * 3: Reserved 268 * [16:13] - Local GIC Target List 269 * Specifies the Local GICs list targeted by this MSIx 270 * message. 271 * [16] If set, SPIn is set in Cluster 0 local GIC 272 * [15:13] Reserved 273 * [15] If set, SPIn is set in Cluster 1 local GIC 274 * [14] If set, SPIn is set in Cluster 2 local GIC 275 * [13] If set, SPIn is set in Cluster 3 local GIC 276 * [12:3] - SPIn 277 * Specifies the SPI (Shared Peripheral Interrupt) index to 278 * be set in target GICs 279 * Notes: 280 * If targeting any local GIC than only SPI[249:0] are valid 281 * [2] - Function vector 282 * MSI Data vector extension hint 283 * [1:0] - Reserved 284 * Must be set to zero 285 */ 286 *addr = (uint64_t)sc->base_addr + (uint64_t)((1 << 16) + (spi << 3)); 287 *data = 0; 288 289 if (bootverbose) 290 device_printf(dev, "MSI mapping: SPI: %d addr: %jx data: %x\n", 291 spi, (uintmax_t)*addr, *data); 292 return (0); 293 } 294 295 static int 296 al_msix_alloc_msi(device_t dev, device_t child, int count, int maxcount, 297 device_t *pic, struct intr_irqsrc **srcs) 298 { 299 struct intr_map_data_fdt *fdt_data; 300 struct al_msix_softc *sc; 301 vmem_addr_t irq_base; 302 int error; 303 u_int i, j; 304 305 sc = device_get_softc(dev); 306 307 if ((powerof2(count) == 0) || (count > 8)) 308 return (EINVAL); 309 310 if (vmem_alloc(sc->irq_alloc, count, M_FIRSTFIT | M_NOWAIT, 311 &irq_base) != 0) 312 return (ENOMEM); 313 314 /* Fabricate OFW data to get ISRC from GIC and return it */ 315 fdt_data = malloc(sizeof(*fdt_data) + 316 GIC_INTR_CELL_CNT * sizeof(pcell_t), M_AL_MSIX, M_WAITOK); 317 fdt_data->hdr.type = INTR_MAP_DATA_FDT; 318 fdt_data->iparent = 0; 319 fdt_data->ncells = GIC_INTR_CELL_CNT; 320 fdt_data->cells[0] = AL_SPI_INTR; /* code for SPI interrupt */ 321 fdt_data->cells[1] = 0; /* SPI number (uninitialized) */ 322 fdt_data->cells[2] = AL_EDGE_HIGH; /* trig = edge, pol = high */ 323 324 mtx_lock(&sc->msi_mtx); 325 326 for (i = irq_base; i < irq_base + count; i++) { 327 fdt_data->cells[1] = sc->irq_min + i; 328 error = PIC_MAP_INTR(sc->gic_dev, 329 (struct intr_map_data *)fdt_data, srcs); 330 if (error) { 331 for (j = irq_base; j < i; j++) 332 sc->isrcs[j] = NULL; 333 mtx_unlock(&sc->msi_mtx); 334 vmem_free(sc->irq_alloc, irq_base, count); 335 free(fdt_data, M_AL_MSIX); 336 return (error); 337 } 338 339 sc->isrcs[i] = *srcs; 340 srcs++; 341 } 342 343 mtx_unlock(&sc->msi_mtx); 344 free(fdt_data, M_AL_MSIX); 345 346 if (bootverbose) 347 device_printf(dev, 348 "MSI-X allocation: start SPI %d, count %d\n", 349 (int)irq_base + sc->irq_min, count); 350 351 *pic = sc->gic_dev; 352 353 return (0); 354 } 355 356 static int 357 al_msix_release_msi(device_t dev, device_t child, int count, 358 struct intr_irqsrc **srcs) 359 { 360 struct al_msix_softc *sc; 361 int i, pos; 362 363 sc = device_get_softc(dev); 364 365 mtx_lock(&sc->msi_mtx); 366 367 pos = al_find_intr_pos_in_map(dev, *srcs); 368 vmem_free(sc->irq_alloc, pos, count); 369 for (i = 0; i < count; i++) { 370 pos = al_find_intr_pos_in_map(dev, *srcs); 371 if (pos != ERR_NOT_IN_MAP) 372 sc->isrcs[pos] = NULL; 373 srcs++; 374 } 375 376 mtx_unlock(&sc->msi_mtx); 377 378 return (0); 379 } 380 381 static int 382 al_msix_alloc_msix(device_t dev, device_t child, device_t *pic, 383 struct intr_irqsrc **isrcp) 384 { 385 386 return (al_msix_alloc_msi(dev, child, 1, 1, pic, isrcp)); 387 } 388 389 static int 390 al_msix_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc) 391 { 392 393 return (al_msix_release_msi(dev, child, 1, &isrc)); 394 } 395