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