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