1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2019 Michal Meloun <mmel@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/bus.h> 32 33 #include <sys/bitset.h> 34 #include <sys/kernel.h> 35 #include <sys/proc.h> 36 #include <sys/rman.h> 37 #include <sys/lock.h> 38 #include <sys/module.h> 39 #include <sys/mutex.h> 40 41 #include <machine/bus.h> 42 #include <machine/intr.h> 43 #include <machine/resource.h> 44 45 #include <dev/fdt/simplebus.h> 46 47 #include <dev/ofw/ofw_bus.h> 48 #include <dev/ofw/ofw_bus_subr.h> 49 50 #include "msi_if.h" 51 #include "pic_if.h" 52 53 #define MV_AP806_SEI_LOCK(_sc) mtx_lock(&(_sc)->mtx) 54 #define MV_AP806_SEI_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) 55 #define MV_AP806_SEI_LOCK_INIT(_sc) mtx_init(&_sc->mtx, \ 56 device_get_nameunit(_sc->dev), "mv_ap806_sei", MTX_DEF) 57 #define MV_AP806_SEI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx); 58 #define MV_AP806_SEI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED); 59 #define MV_AP806_SEI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED); 60 61 #define GICP_SECR0 0x00 62 #define GICP_SECR1 0x04 63 #define GICP_SECR(i) (0x00 + (((i)/32) * 0x4)) 64 #define GICP_SECR_BIT(i) ((i) % 32) 65 #define GICP_SEMR0 0x20 66 #define GICP_SEMR1 0x24 67 #define GICP_SEMR(i) (0x20 + (((i)/32) * 0x4)) 68 #define GICP_SEMR_BIT(i) ((i) % 32) 69 70 #define MV_AP806_SEI_AP_FIRST 0 71 #define MV_AP806_SEI_AP_SIZE 21 72 #define MV_AP806_SEI_CP_FIRST 21 73 #define MV_AP806_SEI_CP_SIZE 43 74 #define MV_AP806_SEI_MAX_NIRQS (MV_AP806_SEI_AP_SIZE + MV_AP806_SEI_CP_SIZE) 75 76 #define MV_AP806_SEI_SETSPI_OFFSET 0x30 77 78 BITSET_DEFINE(sei_msi_bitmap, MV_AP806_SEI_CP_SIZE); 79 80 struct mv_ap806_sei_irqsrc { 81 struct intr_irqsrc isrc; 82 u_int irq; 83 }; 84 85 struct mv_ap806_sei_softc { 86 device_t dev; 87 struct resource *mem_res; 88 struct resource *irq_res; 89 void *irq_ih; 90 struct mtx mtx; 91 92 struct mv_ap806_sei_irqsrc *isrcs; 93 94 struct sei_msi_bitmap msi_bitmap; 95 }; 96 97 static struct ofw_compat_data compat_data[] = { 98 {"marvell,ap806-sei", 1}, 99 {NULL, 0} 100 }; 101 102 #define RD4(sc, reg) bus_read_4((sc)->mem_res, (reg)) 103 #define WR4(sc, reg, val) bus_write_4((sc)->mem_res, (reg), (val)) 104 105 static msi_alloc_msi_t mv_ap806_sei_alloc_msi; 106 static msi_release_msi_t mv_ap806_sei_release_msi; 107 static msi_map_msi_t mv_ap806_sei_map_msi; 108 109 static inline void 110 mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc *sc, 111 struct mv_ap806_sei_irqsrc *sisrc, uint32_t val) 112 { 113 uint32_t tmp; 114 int bit; 115 116 bit = GICP_SEMR_BIT(sisrc->irq); 117 MV_AP806_SEI_LOCK(sc); 118 tmp = RD4(sc, GICP_SEMR(sisrc->irq)); 119 if (val != 0) 120 tmp |= 1 << bit; 121 else 122 tmp &= ~(1 << bit); 123 WR4(sc, GICP_SEMR(sisrc->irq), tmp); 124 MV_AP806_SEI_UNLOCK(sc); 125 } 126 127 static inline void 128 mv_ap806_sei_isrc_eoi(struct mv_ap806_sei_softc *sc, 129 struct mv_ap806_sei_irqsrc *sisrc) 130 { 131 132 WR4(sc, GICP_SECR(sisrc->irq), GICP_SECR_BIT(sisrc->irq)); 133 } 134 135 static void 136 mv_ap806_sei_enable_intr(device_t dev, struct intr_irqsrc *isrc) 137 { 138 struct mv_ap806_sei_softc *sc; 139 struct mv_ap806_sei_irqsrc *sisrc; 140 141 sc = device_get_softc(dev); 142 sisrc = (struct mv_ap806_sei_irqsrc *)isrc; 143 mv_ap806_sei_isrc_mask(sc, sisrc, 0); 144 } 145 146 static void 147 mv_ap806_sei_disable_intr(device_t dev, struct intr_irqsrc *isrc) 148 { 149 struct mv_ap806_sei_softc *sc; 150 struct mv_ap806_sei_irqsrc *sisrc; 151 152 sc = device_get_softc(dev); 153 sisrc = (struct mv_ap806_sei_irqsrc *)isrc; 154 mv_ap806_sei_isrc_mask(sc, sisrc, 1); 155 } 156 157 static int 158 mv_ap806_sei_map(device_t dev, struct intr_map_data *data, u_int *irqp) 159 { 160 struct intr_map_data_fdt *daf; 161 u_int irq; 162 163 if (data->type != INTR_MAP_DATA_FDT) 164 return (ENOTSUP); 165 166 daf = (struct intr_map_data_fdt *)data; 167 if (daf->ncells != 1) 168 return (EINVAL); 169 170 if (daf->cells[0] < MV_AP806_SEI_AP_FIRST || 171 daf->cells[0] >= MV_AP806_SEI_AP_FIRST + MV_AP806_SEI_AP_SIZE) 172 return (EINVAL); 173 174 irq = daf->cells[0]; 175 if (irqp != NULL) 176 *irqp = irq; 177 178 return(0); 179 } 180 181 static int 182 mv_ap806_sei_map_intr(device_t dev, struct intr_map_data *data, 183 struct intr_irqsrc **isrcp) 184 { 185 struct mv_ap806_sei_softc *sc; 186 u_int irq; 187 int rv; 188 189 sc = device_get_softc(dev); 190 rv = mv_ap806_sei_map(dev, data, &irq); 191 if (rv == 0) 192 *isrcp = &sc->isrcs[irq].isrc; 193 194 return (rv); 195 } 196 197 static int 198 mv_ap806_sei_setup_intr(device_t dev, struct intr_irqsrc *isrc, 199 struct resource *res, struct intr_map_data *data) 200 { 201 struct mv_ap806_sei_softc *sc; 202 struct mv_ap806_sei_irqsrc *sisrc; 203 u_int irq; 204 int rv; 205 206 sc = device_get_softc(dev); 207 sisrc = (struct mv_ap806_sei_irqsrc *)isrc; 208 if (data == NULL) 209 return (ENOTSUP); 210 rv = mv_ap806_sei_map(dev, data, &irq); 211 if (rv != 0) 212 return (rv); 213 if (irq != sisrc->irq) 214 return (EINVAL); 215 mv_ap806_sei_isrc_mask(sc, sisrc, 0); 216 return (0); 217 } 218 219 static int 220 mv_ap806_sei_teardown_intr(device_t dev, struct intr_irqsrc *isrc, 221 struct resource *res, struct intr_map_data *data) 222 { 223 struct mv_ap806_sei_softc *sc; 224 struct mv_ap806_sei_irqsrc *sisrc; 225 226 sc = device_get_softc(dev); 227 sisrc = (struct mv_ap806_sei_irqsrc *)isrc; 228 229 mv_ap806_sei_isrc_mask(sc, sisrc, 1); 230 return (0); 231 } 232 233 static void 234 mv_ap806_sei_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 235 { 236 struct mv_ap806_sei_softc *sc; 237 struct mv_ap806_sei_irqsrc *sisrc; 238 239 sc = device_get_softc(dev); 240 sisrc = (struct mv_ap806_sei_irqsrc *)isrc; 241 242 mv_ap806_sei_isrc_mask(sc, sisrc, 1); 243 mv_ap806_sei_isrc_eoi(sc, sisrc); 244 } 245 246 static void 247 mv_ap806_sei_post_ithread(device_t dev, struct intr_irqsrc *isrc) 248 { 249 struct mv_ap806_sei_softc *sc; 250 struct mv_ap806_sei_irqsrc *sisrc; 251 252 sc = device_get_softc(dev); 253 sisrc = (struct mv_ap806_sei_irqsrc *)isrc; 254 255 mv_ap806_sei_isrc_mask(sc, sisrc, 1); 256 } 257 258 static void 259 mv_ap806_sei_post_filter(device_t dev, struct intr_irqsrc *isrc) 260 { 261 struct mv_ap806_sei_softc *sc; 262 struct mv_ap806_sei_irqsrc *sisrc; 263 264 sc = device_get_softc(dev); 265 sisrc = (struct mv_ap806_sei_irqsrc *)isrc; 266 267 mv_ap806_sei_isrc_mask(sc, sisrc, 1); 268 mv_ap806_sei_isrc_eoi(sc, sisrc); 269 } 270 271 /* ---------------------------------------------------------------------------- 272 * 273 * B u s i n t e r f a c e 274 */ 275 static int 276 mv_ap806_sei_intr(void *arg) 277 { 278 struct mv_ap806_sei_softc *sc; 279 struct mv_ap806_sei_irqsrc *sirq; 280 struct trapframe *tf; 281 uint64_t cause; 282 u_int irq; 283 284 sc = (struct mv_ap806_sei_softc *)arg; 285 tf = curthread->td_intr_frame; 286 while (1) { 287 cause = RD4(sc, GICP_SECR1); 288 cause <<= 32; 289 cause |= RD4(sc, GICP_SECR0); 290 291 irq = ffsll(cause); 292 if (irq == 0) break; 293 irq--; 294 sirq = &sc->isrcs[irq]; 295 if (intr_isrc_dispatch(&sirq->isrc, tf) != 0) { 296 mv_ap806_sei_isrc_mask(sc, sirq, 0); 297 mv_ap806_sei_isrc_eoi(sc, sirq); 298 device_printf(sc->dev, 299 "Stray irq %u disabled\n", irq); 300 } 301 } 302 303 return (FILTER_HANDLED); 304 } 305 306 static int 307 mv_ap806_sei_probe(device_t dev) 308 { 309 310 if (!ofw_bus_status_okay(dev)) 311 return (ENXIO); 312 313 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 314 return (ENXIO); 315 316 device_set_desc(dev, "Marvell SEI"); 317 return (BUS_PROBE_DEFAULT); 318 } 319 320 static int 321 mv_ap806_sei_attach(device_t dev) 322 { 323 struct mv_ap806_sei_softc *sc; 324 phandle_t xref, node; 325 uint32_t irq; 326 const char *name; 327 int rv, rid; 328 329 sc = device_get_softc(dev); 330 sc->dev = dev; 331 node = ofw_bus_get_node(dev); 332 MV_AP806_SEI_LOCK_INIT(sc); 333 334 /* Allocate resources. */ 335 rid = 0; 336 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 337 RF_ACTIVE); 338 if (sc->mem_res == NULL) { 339 device_printf(dev, "Cannot allocate memory resources\n"); 340 rv = ENXIO; 341 goto fail; 342 } 343 344 rid = 0; 345 sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 346 if (sc->irq_res == NULL) { 347 device_printf(dev, "Cannot allocate IRQ resources\n"); 348 rv = ENXIO; 349 goto fail; 350 } 351 352 /* Mask all interrupts) */ 353 WR4(sc, GICP_SEMR0, 0xFFFFFFFF); 354 WR4(sc, GICP_SEMR1, 0xFFFFFFFF); 355 356 /* Create all interrupt sources */ 357 sc->isrcs = malloc(sizeof(*sc->isrcs) * MV_AP806_SEI_MAX_NIRQS, 358 M_DEVBUF, M_WAITOK | M_ZERO); 359 name = device_get_nameunit(sc->dev); 360 for (irq = 0; irq < MV_AP806_SEI_MAX_NIRQS; irq++) { 361 sc->isrcs[irq].irq = irq; 362 rv = intr_isrc_register(&sc->isrcs[irq].isrc, 363 sc->dev, 0, "%s,%u", name, irq); 364 if (rv != 0) 365 goto fail; /* XXX deregister ISRCs */ 366 } 367 xref = OF_xref_from_node(node); 368 if (intr_pic_register(dev, xref) == NULL) { 369 device_printf(dev, "Cannot register SEI\n"); 370 rv = ENXIO; 371 goto fail; 372 } 373 if (bus_setup_intr(dev, sc->irq_res,INTR_TYPE_MISC | INTR_MPSAFE, 374 mv_ap806_sei_intr, NULL, sc, &sc->irq_ih)) { 375 device_printf(dev, 376 "Unable to register interrupt handler\n"); 377 rv = ENXIO; 378 goto fail; 379 } 380 381 /* 382 * Bitmap of all IRQs. 383 * 1 - available, 0 - used. 384 */ 385 BIT_FILL(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap); 386 387 OF_device_register_xref(xref, dev); 388 return (0); 389 390 fail: 391 if (sc->irq_ih != NULL) 392 bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); 393 if (sc->irq_res != NULL) 394 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 395 if (sc->mem_res != NULL) 396 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); 397 MV_AP806_SEI_LOCK_DESTROY(sc); 398 return (ENXIO); 399 } 400 401 static int 402 mv_ap806_sei_detach(device_t dev) 403 { 404 405 return (EBUSY); 406 } 407 408 static int 409 mv_ap806_sei_alloc_msi(device_t dev, device_t child, int count, int maxcount, 410 device_t *pic, struct intr_irqsrc **srcs) 411 { 412 struct mv_ap806_sei_softc *sc; 413 int i, ret = 0, vector; 414 415 sc = device_get_softc(dev); 416 417 for (i = 0; i < count; i++) { 418 /* 419 * Find first available MSI vector represented by first set bit 420 * in the bitmap. BIT_FFS starts the count from 1, 421 * 0 means that nothing was found. 422 */ 423 vector = BIT_FFS_AT(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap, 0); 424 if (vector == 0) { 425 ret = ENOMEM; 426 i--; 427 goto fail; 428 } 429 430 vector--; 431 BIT_CLR(MV_AP806_SEI_CP_SIZE, vector, &sc->msi_bitmap); 432 vector += MV_AP806_SEI_CP_FIRST; 433 434 srcs[i] = &sc->isrcs[vector].isrc; 435 } 436 437 return (ret); 438 fail: 439 mv_ap806_sei_release_msi(dev, child, i + 1, srcs); 440 return (ret); 441 } 442 443 static int 444 mv_ap806_sei_release_msi(device_t dev, device_t child, int count, struct intr_irqsrc **srcs) 445 { 446 struct mv_ap806_sei_softc *sc; 447 int i; 448 449 sc = device_get_softc(dev); 450 451 for (i = 0; i < count; i++) { 452 BIT_SET(MV_AP806_SEI_CP_SIZE, 453 srcs[i]->isrc_irq - MV_AP806_SEI_CP_FIRST, 454 &sc->msi_bitmap); 455 } 456 457 return (0); 458 } 459 460 static int 461 mv_ap806_sei_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, 462 uint64_t *addr, uint32_t *data) 463 { 464 struct mv_ap806_sei_softc *sc; 465 466 sc = device_get_softc(dev); 467 468 *addr = rman_get_start(sc->mem_res) + MV_AP806_SEI_SETSPI_OFFSET; 469 *data = isrc->isrc_irq; 470 471 return (0); 472 } 473 474 static device_method_t mv_ap806_sei_methods[] = { 475 /* Device interface */ 476 DEVMETHOD(device_probe, mv_ap806_sei_probe), 477 DEVMETHOD(device_attach, mv_ap806_sei_attach), 478 DEVMETHOD(device_detach, mv_ap806_sei_detach), 479 480 /* Interrupt controller interface */ 481 DEVMETHOD(pic_disable_intr, mv_ap806_sei_disable_intr), 482 DEVMETHOD(pic_enable_intr, mv_ap806_sei_enable_intr), 483 DEVMETHOD(pic_map_intr, mv_ap806_sei_map_intr), 484 DEVMETHOD(pic_setup_intr, mv_ap806_sei_setup_intr), 485 DEVMETHOD(pic_teardown_intr, mv_ap806_sei_teardown_intr), 486 DEVMETHOD(pic_post_filter, mv_ap806_sei_post_filter), 487 DEVMETHOD(pic_post_ithread, mv_ap806_sei_post_ithread), 488 DEVMETHOD(pic_pre_ithread, mv_ap806_sei_pre_ithread), 489 490 /* MSI interface */ 491 DEVMETHOD(msi_alloc_msi, mv_ap806_sei_alloc_msi), 492 DEVMETHOD(msi_release_msi, mv_ap806_sei_release_msi), 493 DEVMETHOD(msi_map_msi, mv_ap806_sei_map_msi), 494 495 DEVMETHOD_END 496 }; 497 498 static driver_t mv_ap806_sei_driver = { 499 "mv_ap806_sei", 500 mv_ap806_sei_methods, 501 sizeof(struct mv_ap806_sei_softc), 502 }; 503 504 EARLY_DRIVER_MODULE(mv_ap806_sei, simplebus, mv_ap806_sei_driver, 0, 0, 505 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 506