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