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 78BITSET_DEFINE(sei_msi_bitmap, MV_AP806_SEI_CP_SIZE); 79 80struct mv_ap806_sei_irqsrc { 81 struct intr_irqsrc isrc; 82 u_int irq; 83}; 84 85struct 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 97static 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 105static msi_alloc_msi_t mv_ap806_sei_alloc_msi; 106static msi_release_msi_t mv_ap806_sei_release_msi; 107static msi_map_msi_t mv_ap806_sei_map_msi; 108 109static inline void 110mv_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 127static inline void 128mv_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 135static void 136mv_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 146static void 147mv_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 157static int 158mv_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 181static int 182mv_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 197static int 198mv_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 219static int 220mv_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 233static void 234mv_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 246static void 247mv_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 258static void 259mv_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 */ 275static int 276mv_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 306static int 307mv_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 320static int 321mv_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 390fail: 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 401static int 402mv_ap806_sei_detach(device_t dev) 403{ 404 405 return (EBUSY); 406} 407 408static int 409mv_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); 438fail: 439 mv_ap806_sei_release_msi(dev, child, i + 1, srcs); 440 return (ret); 441} 442 443static int 444mv_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 460static int 461mv_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 474static 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 498static driver_t mv_ap806_sei_driver = { 499 "mv_ap806_sei", 500 mv_ap806_sei_methods, 501 sizeof(struct mv_ap806_sei_softc), 502}; 503 504EARLY_DRIVER_MODULE(mv_ap806_sei, simplebus, mv_ap806_sei_driver, 0, 0, 505 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 506