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