1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2018 Rubicon Communications, LLC (Netgate) 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 #include <sys/cdefs.h> 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/bus.h> 32 33 #include <sys/kernel.h> 34 #include <sys/module.h> 35 #include <sys/rman.h> 36 #include <sys/lock.h> 37 #include <sys/mutex.h> 38 39 #include <machine/bus.h> 40 #include <machine/resource.h> 41 #include <machine/intr.h> 42 43 #include <dev/fdt/simplebus.h> 44 45 #include <dev/ofw/ofw_bus.h> 46 #include <dev/ofw/ofw_bus_subr.h> 47 48 #include <dt-bindings/interrupt-controller/irq.h> 49 50 #include "pic_if.h" 51 #include "msi_if.h" 52 53 #define ICU_TYPE_NSR 1 54 #define ICU_TYPE_SEI 2 55 56 #define ICU_GRP_NSR 0x0 57 #define ICU_GRP_SR 0x1 58 #define ICU_GRP_SEI 0x4 59 #define ICU_GRP_REI 0x5 60 61 #define ICU_SETSPI_NSR_AL 0x10 62 #define ICU_SETSPI_NSR_AH 0x14 63 #define ICU_CLRSPI_NSR_AL 0x18 64 #define ICU_CLRSPI_NSR_AH 0x1c 65 #define ICU_SETSPI_SEI_AL 0x50 66 #define ICU_SETSPI_SEI_AH 0x54 67 #define ICU_INT_CFG(x) (0x100 + (x) * 4) 68 #define ICU_INT_ENABLE (1 << 24) 69 #define ICU_INT_EDGE (1 << 28) 70 #define ICU_INT_GROUP_SHIFT 29 71 #define ICU_INT_MASK 0x3ff 72 73 #define ICU_INT_SATA0 109 74 #define ICU_INT_SATA1 107 75 76 #define MV_CP110_ICU_MAX_NIRQS 207 77 78 #define MV_CP110_ICU_CLRSPI_OFFSET 0x8 79 80 struct mv_cp110_icu_softc { 81 device_t dev; 82 device_t parent; 83 struct resource *res; 84 struct intr_map_data_fdt *parent_map_data; 85 bool initialized; 86 int type; 87 }; 88 89 static struct resource_spec mv_cp110_icu_res_spec[] = { 90 { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, 91 { -1, 0 } 92 }; 93 94 static struct ofw_compat_data compat_data[] = { 95 {"marvell,cp110-icu-nsr", ICU_TYPE_NSR}, 96 {"marvell,cp110-icu-sei", ICU_TYPE_SEI}, 97 {NULL, 0} 98 }; 99 100 #define RD4(sc, reg) bus_read_4((sc)->res, (reg)) 101 #define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) 102 103 static int 104 mv_cp110_icu_probe(device_t dev) 105 { 106 107 if (!ofw_bus_status_okay(dev)) 108 return (ENXIO); 109 110 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 111 return (ENXIO); 112 113 device_set_desc(dev, "Marvell Interrupt Consolidation Unit"); 114 return (BUS_PROBE_DEFAULT); 115 } 116 117 static int 118 mv_cp110_icu_attach(device_t dev) 119 { 120 struct mv_cp110_icu_softc *sc; 121 phandle_t node, msi_parent; 122 uint32_t reg, icu_grp; 123 int i; 124 125 sc = device_get_softc(dev); 126 sc->dev = dev; 127 node = ofw_bus_get_node(dev); 128 sc->type = (int)ofw_bus_search_compatible(dev, compat_data)->ocd_data; 129 sc->initialized = false; 130 131 if (OF_getencprop(node, "msi-parent", &msi_parent, 132 sizeof(phandle_t)) <= 0) { 133 device_printf(dev, "cannot find msi-parent property\n"); 134 return (ENXIO); 135 } 136 137 if ((sc->parent = OF_device_from_xref(msi_parent)) == NULL) { 138 device_printf(dev, "cannot find msi-parent device\n"); 139 return (ENXIO); 140 } 141 if (bus_alloc_resources(dev, mv_cp110_icu_res_spec, &sc->res) != 0) { 142 device_printf(dev, "cannot allocate resources for device\n"); 143 return (ENXIO); 144 } 145 146 if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) { 147 device_printf(dev, "Cannot register ICU\n"); 148 goto fail; 149 } 150 151 /* Allocate GICP/SEI compatible mapping entry (2 cells) */ 152 sc->parent_map_data = (struct intr_map_data_fdt *)intr_alloc_map_data( 153 INTR_MAP_DATA_FDT, sizeof(struct intr_map_data_fdt) + 154 + 3 * sizeof(phandle_t), M_WAITOK | M_ZERO); 155 156 /* Clear any previous mapping done by firmware. */ 157 for (i = 0; i < MV_CP110_ICU_MAX_NIRQS; i++) { 158 reg = RD4(sc, ICU_INT_CFG(i)); 159 icu_grp = reg >> ICU_INT_GROUP_SHIFT; 160 161 if (icu_grp == ICU_GRP_NSR || icu_grp == ICU_GRP_SEI) 162 WR4(sc, ICU_INT_CFG(i), 0); 163 } 164 165 return (0); 166 167 fail: 168 bus_release_resources(dev, mv_cp110_icu_res_spec, &sc->res); 169 return (ENXIO); 170 } 171 172 static struct intr_map_data * 173 mv_cp110_icu_convert_map_data(struct mv_cp110_icu_softc *sc, struct intr_map_data *data) 174 { 175 struct intr_map_data_fdt *daf; 176 uint32_t reg, irq_no, irq_type; 177 178 daf = (struct intr_map_data_fdt *)data; 179 if (daf->ncells != 2) 180 return (NULL); 181 182 irq_no = daf->cells[0]; 183 if (irq_no >= MV_CP110_ICU_MAX_NIRQS) 184 return (NULL); 185 186 irq_type = daf->cells[1]; 187 if (irq_type != IRQ_TYPE_LEVEL_HIGH && 188 irq_type != IRQ_TYPE_EDGE_RISING) 189 return (NULL); 190 191 /* ICU -> GICP/SEI mapping is set in mv_cp110_icu_map_intr. */ 192 reg = RD4(sc, ICU_INT_CFG(irq_no)); 193 194 /* Construct GICP compatible mapping. */ 195 sc->parent_map_data->ncells = 2; 196 sc->parent_map_data->cells[0] = reg & ICU_INT_MASK; 197 sc->parent_map_data->cells[1] = irq_type; 198 199 return ((struct intr_map_data *)sc->parent_map_data); 200 } 201 202 static int 203 mv_cp110_icu_detach(device_t dev) 204 { 205 206 return (EBUSY); 207 } 208 209 static int 210 mv_cp110_icu_activate_intr(device_t dev, struct intr_irqsrc *isrc, 211 struct resource *res, struct intr_map_data *data) 212 { 213 struct mv_cp110_icu_softc *sc; 214 215 sc = device_get_softc(dev); 216 data = mv_cp110_icu_convert_map_data(sc, data); 217 if (data == NULL) 218 return (EINVAL); 219 return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data)); 220 } 221 222 static void 223 mv_cp110_icu_enable_intr(device_t dev, struct intr_irqsrc *isrc) 224 { 225 struct mv_cp110_icu_softc *sc; 226 sc = device_get_softc(dev); 227 228 PIC_ENABLE_INTR(sc->parent, isrc); 229 } 230 231 static void 232 mv_cp110_icu_disable_intr(device_t dev, struct intr_irqsrc *isrc) 233 { 234 struct mv_cp110_icu_softc *sc; 235 236 sc = device_get_softc(dev); 237 238 PIC_DISABLE_INTR(sc->parent, isrc); 239 } 240 241 static void 242 mv_cp110_icu_init(struct mv_cp110_icu_softc *sc, uint64_t addr) 243 { 244 245 if (sc->initialized) 246 return; 247 248 switch (sc->type) { 249 case ICU_TYPE_NSR: 250 WR4(sc, ICU_SETSPI_NSR_AL, addr & UINT32_MAX); 251 WR4(sc, ICU_SETSPI_NSR_AH, (addr >> 32) & UINT32_MAX); 252 addr += MV_CP110_ICU_CLRSPI_OFFSET; 253 WR4(sc, ICU_CLRSPI_NSR_AL, addr & UINT32_MAX); 254 WR4(sc, ICU_CLRSPI_NSR_AH, (addr >> 32) & UINT32_MAX); 255 break; 256 case ICU_TYPE_SEI: 257 WR4(sc, ICU_SETSPI_SEI_AL, addr & UINT32_MAX); 258 WR4(sc, ICU_SETSPI_SEI_AH, (addr >> 32) & UINT32_MAX); 259 break; 260 default: 261 panic("Unkown ICU type."); 262 } 263 264 sc->initialized = true; 265 } 266 267 static int 268 mv_cp110_icu_map_intr(device_t dev, struct intr_map_data *data, 269 struct intr_irqsrc **isrcp) 270 { 271 struct mv_cp110_icu_softc *sc; 272 struct intr_map_data_fdt *daf; 273 uint32_t vector, irq_no, irq_type; 274 uint64_t addr; 275 int ret; 276 277 sc = device_get_softc(dev); 278 279 if (data->type != INTR_MAP_DATA_FDT) 280 return (ENOTSUP); 281 282 /* Parse original */ 283 daf = (struct intr_map_data_fdt *)data; 284 if (daf->ncells != 2) 285 return (EINVAL); 286 287 irq_no = daf->cells[0]; 288 if (irq_no >= MV_CP110_ICU_MAX_NIRQS) 289 return (EINVAL); 290 291 irq_type = daf->cells[1]; 292 if (irq_type != IRQ_TYPE_LEVEL_HIGH && 293 irq_type != IRQ_TYPE_EDGE_RISING) 294 return (EINVAL); 295 296 /* 297 * Allocate MSI vector. 298 * We don't use intr_alloc_msi wrapper, since it registers a new irq 299 * in the kernel. In our case irq was already added by the ofw code. 300 */ 301 ret = MSI_ALLOC_MSI(sc->parent, dev, 1, 1, NULL, isrcp); 302 if (ret != 0) 303 return (ret); 304 305 ret = MSI_MAP_MSI(sc->parent, dev, *isrcp, &addr, &vector); 306 if (ret != 0) 307 goto fail; 308 309 mv_cp110_icu_init(sc, addr); 310 vector |= ICU_INT_ENABLE; 311 312 if (sc->type == ICU_TYPE_NSR) 313 vector |= ICU_GRP_NSR << ICU_INT_GROUP_SHIFT; 314 else 315 vector |= ICU_GRP_SEI << ICU_INT_GROUP_SHIFT; 316 317 if (irq_type & IRQ_TYPE_EDGE_BOTH) 318 vector |= ICU_INT_EDGE; 319 320 WR4(sc, ICU_INT_CFG(irq_no), vector); 321 322 /* 323 * SATA controller has two ports, each gets its own interrupt. 324 * The problem is that only one irq is described in dts. 325 * Also ahci_generic driver supports only one irq per controller. 326 * As a workaround map both interrupts when one of them is allocated. 327 * This allows us to use both SATA ports. 328 */ 329 if (irq_no == ICU_INT_SATA0) 330 WR4(sc, ICU_INT_CFG(ICU_INT_SATA1), vector); 331 if (irq_no == ICU_INT_SATA1) 332 WR4(sc, ICU_INT_CFG(ICU_INT_SATA0), vector); 333 334 (*isrcp)->isrc_dev = sc->dev; 335 return (ret); 336 337 fail: 338 if (*isrcp != NULL) 339 MSI_RELEASE_MSI(sc->parent, dev, 1, isrcp); 340 341 return (ret); 342 } 343 344 static int 345 mv_cp110_icu_deactivate_intr(device_t dev, struct intr_irqsrc *isrc, 346 struct resource *res, struct intr_map_data *data) 347 { 348 struct mv_cp110_icu_softc *sc; 349 struct intr_map_data_fdt *daf; 350 int irq_no, ret; 351 352 if (data->type != INTR_MAP_DATA_FDT) 353 return (ENOTSUP); 354 355 sc = device_get_softc(dev); 356 daf = (struct intr_map_data_fdt *)data; 357 if (daf->ncells != 2) 358 return (EINVAL); 359 360 irq_no = daf->cells[0]; 361 data = mv_cp110_icu_convert_map_data(sc, data); 362 if (data == NULL) 363 return (EINVAL); 364 365 /* Clear the mapping. */ 366 WR4(sc, ICU_INT_CFG(irq_no), 0); 367 368 ret = PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data); 369 if (ret != 0) 370 return (ret); 371 372 return (MSI_RELEASE_MSI(sc->parent, dev, 1, &isrc)); 373 } 374 375 static int 376 mv_cp110_icu_setup_intr(device_t dev, struct intr_irqsrc *isrc, 377 struct resource *res, struct intr_map_data *data) 378 { 379 struct mv_cp110_icu_softc *sc; 380 381 sc = device_get_softc(dev); 382 data = mv_cp110_icu_convert_map_data(sc, data); 383 if (data == NULL) 384 return (EINVAL); 385 386 return (PIC_SETUP_INTR(sc->parent, isrc, res, data)); 387 } 388 389 static int 390 mv_cp110_icu_teardown_intr(device_t dev, struct intr_irqsrc *isrc, 391 struct resource *res, struct intr_map_data *data) 392 { 393 struct mv_cp110_icu_softc *sc; 394 395 sc = device_get_softc(dev); 396 data = mv_cp110_icu_convert_map_data(sc, data); 397 if (data == NULL) 398 return (EINVAL); 399 400 return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data)); 401 } 402 403 static void 404 mv_cp110_icu_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 405 { 406 struct mv_cp110_icu_softc *sc; 407 408 sc = device_get_softc(dev); 409 410 PIC_PRE_ITHREAD(sc->parent, isrc); 411 } 412 413 static void 414 mv_cp110_icu_post_ithread(device_t dev, struct intr_irqsrc *isrc) 415 { 416 struct mv_cp110_icu_softc *sc; 417 418 sc = device_get_softc(dev); 419 420 PIC_POST_ITHREAD(sc->parent, isrc); 421 } 422 423 static void 424 mv_cp110_icu_post_filter(device_t dev, struct intr_irqsrc *isrc) 425 { 426 struct mv_cp110_icu_softc *sc; 427 428 sc = device_get_softc(dev); 429 430 PIC_POST_FILTER(sc->parent, isrc); 431 } 432 433 static device_method_t mv_cp110_icu_methods[] = { 434 /* Device interface */ 435 DEVMETHOD(device_probe, mv_cp110_icu_probe), 436 DEVMETHOD(device_attach, mv_cp110_icu_attach), 437 DEVMETHOD(device_detach, mv_cp110_icu_detach), 438 439 /* Interrupt controller interface */ 440 DEVMETHOD(pic_activate_intr, mv_cp110_icu_activate_intr), 441 DEVMETHOD(pic_disable_intr, mv_cp110_icu_disable_intr), 442 DEVMETHOD(pic_enable_intr, mv_cp110_icu_enable_intr), 443 DEVMETHOD(pic_map_intr, mv_cp110_icu_map_intr), 444 DEVMETHOD(pic_deactivate_intr, mv_cp110_icu_deactivate_intr), 445 DEVMETHOD(pic_setup_intr, mv_cp110_icu_setup_intr), 446 DEVMETHOD(pic_teardown_intr, mv_cp110_icu_teardown_intr), 447 DEVMETHOD(pic_post_filter, mv_cp110_icu_post_filter), 448 DEVMETHOD(pic_post_ithread, mv_cp110_icu_post_ithread), 449 DEVMETHOD(pic_pre_ithread, mv_cp110_icu_pre_ithread), 450 451 DEVMETHOD_END 452 }; 453 454 static driver_t mv_cp110_icu_driver = { 455 "mv_cp110_icu", 456 mv_cp110_icu_methods, 457 sizeof(struct mv_cp110_icu_softc), 458 }; 459 460 EARLY_DRIVER_MODULE(mv_cp110_icu, mv_cp110_icu_bus, mv_cp110_icu_driver, 0, 0, 461 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); 462