ciu.c revision 331722
1/*- 2 * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: stable/11/sys/mips/cavium/ciu.c 331722 2018-03-29 02:50:57Z eadler $ 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: stable/11/sys/mips/cavium/ciu.c 331722 2018-03-29 02:50:57Z eadler $"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/bus.h> 35#include <sys/interrupt.h> 36#include <sys/kernel.h> 37#include <sys/module.h> 38#include <sys/rman.h> 39#include <sys/malloc.h> 40#include <sys/smp.h> 41 42#include <machine/bus.h> 43#include <machine/intr_machdep.h> 44 45#include <contrib/octeon-sdk/cvmx.h> 46#include <mips/cavium/octeon_irq.h> 47 48/* 49 * This bus sits between devices/buses and nexus and handles CIU interrupts 50 * and passes everything else through. It should really be a nexus subclass 51 * or something, but for now this will be sufficient. 52 */ 53 54#define CIU_IRQ_HARD (0) 55 56#define CIU_IRQ_EN0_BEGIN OCTEON_IRQ_WORKQ0 57#define CIU_IRQ_EN0_END OCTEON_IRQ_BOOTDMA 58#define CIU_IRQ_EN0_COUNT ((CIU_IRQ_EN0_END - CIU_IRQ_EN0_BEGIN) + 1) 59 60#define CIU_IRQ_EN1_BEGIN OCTEON_IRQ_WDOG0 61#define CIU_IRQ_EN1_END OCTEON_IRQ_DFM 62#define CIU_IRQ_EN1_COUNT ((CIU_IRQ_EN1_END - CIU_IRQ_EN1_BEGIN) + 1) 63 64struct ciu_softc { 65 struct rman irq_rman; 66 struct resource *ciu_irq; 67}; 68 69static mips_intrcnt_t ciu_en0_intrcnt[CIU_IRQ_EN0_COUNT]; 70static mips_intrcnt_t ciu_en1_intrcnt[CIU_IRQ_EN1_COUNT]; 71 72static struct intr_event *ciu_en0_intr_events[CIU_IRQ_EN0_COUNT]; 73static struct intr_event *ciu_en1_intr_events[CIU_IRQ_EN1_COUNT]; 74 75static int ciu_probe(device_t); 76static int ciu_attach(device_t); 77static struct resource *ciu_alloc_resource(device_t, device_t, int, int *, 78 rman_res_t, rman_res_t, rman_res_t, 79 u_int); 80static int ciu_setup_intr(device_t, device_t, struct resource *, 81 int, driver_filter_t *, driver_intr_t *, 82 void *, void **); 83static int ciu_teardown_intr(device_t, device_t, 84 struct resource *, void *); 85static int ciu_bind_intr(device_t, device_t, struct resource *, 86 int); 87static int ciu_describe_intr(device_t, device_t, 88 struct resource *, void *, 89 const char *); 90static void ciu_hinted_child(device_t, const char *, int); 91 92static void ciu_en0_intr_mask(void *); 93static void ciu_en0_intr_unmask(void *); 94#ifdef SMP 95static int ciu_en0_intr_bind(void *, int); 96#endif 97 98static void ciu_en1_intr_mask(void *); 99static void ciu_en1_intr_unmask(void *); 100#ifdef SMP 101static int ciu_en1_intr_bind(void *, int); 102#endif 103 104static int ciu_intr(void *); 105 106static int 107ciu_probe(device_t dev) 108{ 109 if (device_get_unit(dev) != 0) 110 return (ENXIO); 111 112 device_set_desc(dev, "Cavium Octeon Central Interrupt Unit"); 113 return (BUS_PROBE_NOWILDCARD); 114} 115 116static int 117ciu_attach(device_t dev) 118{ 119 char name[MAXCOMLEN + 1]; 120 struct ciu_softc *sc; 121 unsigned i; 122 int error; 123 int rid; 124 125 sc = device_get_softc(dev); 126 127 rid = 0; 128 sc->ciu_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, CIU_IRQ_HARD, 129 CIU_IRQ_HARD, 1, RF_ACTIVE); 130 if (sc->ciu_irq == NULL) { 131 device_printf(dev, "could not allocate irq%d\n", CIU_IRQ_HARD); 132 return (ENXIO); 133 } 134 135 error = bus_setup_intr(dev, sc->ciu_irq, INTR_TYPE_MISC, ciu_intr, 136 NULL, sc, NULL); 137 if (error != 0) { 138 device_printf(dev, "bus_setup_intr failed: %d\n", error); 139 return (error); 140 } 141 142 sc->irq_rman.rm_type = RMAN_ARRAY; 143 sc->irq_rman.rm_descr = "CIU IRQ"; 144 145 error = rman_init(&sc->irq_rman); 146 if (error != 0) 147 return (error); 148 149 /* 150 * We have two contiguous IRQ regions, use a single rman. 151 */ 152 error = rman_manage_region(&sc->irq_rman, CIU_IRQ_EN0_BEGIN, 153 CIU_IRQ_EN1_END); 154 if (error != 0) 155 return (error); 156 157 for (i = 0; i < CIU_IRQ_EN0_COUNT; i++) { 158 snprintf(name, sizeof name, "int%d:", i + CIU_IRQ_EN0_BEGIN); 159 ciu_en0_intrcnt[i] = mips_intrcnt_create(name); 160 } 161 162 for (i = 0; i < CIU_IRQ_EN1_COUNT; i++) { 163 snprintf(name, sizeof name, "int%d:", i + CIU_IRQ_EN1_BEGIN); 164 ciu_en1_intrcnt[i] = mips_intrcnt_create(name); 165 } 166 167 bus_generic_probe(dev); 168 bus_generic_attach(dev); 169 170 return (0); 171} 172 173static struct resource * 174ciu_alloc_resource(device_t bus, device_t child, int type, int *rid, 175 rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 176{ 177 struct resource *res; 178 struct ciu_softc *sc; 179 180 sc = device_get_softc(bus); 181 182 switch (type) { 183 case SYS_RES_IRQ: 184 break; 185 default: 186 return (bus_alloc_resource(device_get_parent(bus), type, rid, 187 start, end, count, flags)); 188 } 189 190 /* 191 * One interrupt at a time for now. 192 */ 193 if (start != end) 194 return (NULL); 195 196 res = rman_reserve_resource(&sc->irq_rman, start, end, count, flags, 197 child); 198 if (res != NULL) 199 return (res); 200 201 return (NULL); 202} 203 204static int 205ciu_setup_intr(device_t bus, device_t child, struct resource *res, int flags, 206 driver_filter_t *filter, driver_intr_t *intr, void *arg, 207 void **cookiep) 208{ 209 struct intr_event *event, **eventp; 210 void (*mask_func)(void *); 211 void (*unmask_func)(void *); 212 int (*bind_func)(void *, int); 213 mips_intrcnt_t intrcnt; 214 int error; 215 int irq; 216 217 irq = rman_get_start(res); 218 if (irq <= CIU_IRQ_EN0_END) { 219 eventp = &ciu_en0_intr_events[irq - CIU_IRQ_EN0_BEGIN]; 220 intrcnt = ciu_en0_intrcnt[irq - CIU_IRQ_EN0_BEGIN]; 221 mask_func = ciu_en0_intr_mask; 222 unmask_func = ciu_en0_intr_unmask; 223#ifdef SMP 224 bind_func = ciu_en0_intr_bind; 225#endif 226 } else { 227 eventp = &ciu_en1_intr_events[irq - CIU_IRQ_EN1_BEGIN]; 228 intrcnt = ciu_en1_intrcnt[irq - CIU_IRQ_EN1_BEGIN]; 229 mask_func = ciu_en1_intr_mask; 230 unmask_func = ciu_en1_intr_unmask; 231#ifdef SMP 232 bind_func = ciu_en1_intr_bind; 233#endif 234 } 235#if !defined(SMP) 236 bind_func = NULL; 237#endif 238 239 if ((event = *eventp) == NULL) { 240 error = intr_event_create(eventp, (void *)(uintptr_t)irq, 0, 241 irq, mask_func, unmask_func, NULL, bind_func, "int%d", irq); 242 if (error != 0) 243 return (error); 244 245 event = *eventp; 246 247 unmask_func((void *)(uintptr_t)irq); 248 } 249 250 intr_event_add_handler(event, device_get_nameunit(child), 251 filter, intr, arg, intr_priority(flags), flags, cookiep); 252 253 mips_intrcnt_setname(intrcnt, event->ie_fullname); 254 255 return (0); 256} 257 258static int 259ciu_teardown_intr(device_t bus, device_t child, struct resource *res, 260 void *cookie) 261{ 262 int error; 263 264 error = intr_event_remove_handler(cookie); 265 if (error != 0) 266 return (error); 267 268 return (0); 269} 270 271#ifdef SMP 272static int 273ciu_bind_intr(device_t bus, device_t child, struct resource *res, int cpu) 274{ 275 struct intr_event *event; 276 int irq; 277 278 irq = rman_get_start(res); 279 if (irq <= CIU_IRQ_EN0_END) 280 event = ciu_en0_intr_events[irq - CIU_IRQ_EN0_BEGIN]; 281 else 282 event = ciu_en1_intr_events[irq - CIU_IRQ_EN1_BEGIN]; 283 284 return (intr_event_bind(event, cpu)); 285} 286#endif 287 288static int 289ciu_describe_intr(device_t bus, device_t child, struct resource *res, 290 void *cookie, const char *descr) 291{ 292 struct intr_event *event; 293 mips_intrcnt_t intrcnt; 294 int error; 295 int irq; 296 297 irq = rman_get_start(res); 298 if (irq <= CIU_IRQ_EN0_END) { 299 event = ciu_en0_intr_events[irq - CIU_IRQ_EN0_BEGIN]; 300 intrcnt = ciu_en0_intrcnt[irq - CIU_IRQ_EN0_BEGIN]; 301 } else { 302 event = ciu_en1_intr_events[irq - CIU_IRQ_EN1_BEGIN]; 303 intrcnt = ciu_en1_intrcnt[irq - CIU_IRQ_EN1_BEGIN]; 304 } 305 306 error = intr_event_describe_handler(event, cookie, descr); 307 if (error != 0) 308 return (error); 309 310 mips_intrcnt_setname(intrcnt, event->ie_fullname); 311 312 return (0); 313} 314 315static void 316ciu_hinted_child(device_t bus, const char *dname, int dunit) 317{ 318 BUS_ADD_CHILD(bus, 0, dname, dunit); 319} 320 321static void 322ciu_en0_intr_mask(void *arg) 323{ 324 uint64_t mask; 325 int irq; 326 327 irq = (uintptr_t)arg; 328 mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2)); 329 mask &= ~(1ull << (irq - CIU_IRQ_EN0_BEGIN)); 330 cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2), mask); 331} 332 333static void 334ciu_en0_intr_unmask(void *arg) 335{ 336 uint64_t mask; 337 int irq; 338 339 irq = (uintptr_t)arg; 340 mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2)); 341 mask |= 1ull << (irq - CIU_IRQ_EN0_BEGIN); 342 cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2), mask); 343} 344 345#ifdef SMP 346static int 347ciu_en0_intr_bind(void *arg, int target) 348{ 349 uint64_t mask; 350 int core; 351 int irq; 352 353 irq = (uintptr_t)arg; 354 CPU_FOREACH(core) { 355 mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2)); 356 if (core == target) 357 mask |= 1ull << (irq - CIU_IRQ_EN0_BEGIN); 358 else 359 mask &= ~(1ull << (irq - CIU_IRQ_EN0_BEGIN)); 360 cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), mask); 361 } 362 363 return (0); 364} 365#endif 366 367static void 368ciu_en1_intr_mask(void *arg) 369{ 370 uint64_t mask; 371 int irq; 372 373 irq = (uintptr_t)arg; 374 mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2)); 375 mask &= ~(1ull << (irq - CIU_IRQ_EN1_BEGIN)); 376 cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2), mask); 377} 378 379static void 380ciu_en1_intr_unmask(void *arg) 381{ 382 uint64_t mask; 383 int irq; 384 385 irq = (uintptr_t)arg; 386 mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2)); 387 mask |= 1ull << (irq - CIU_IRQ_EN1_BEGIN); 388 cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2), mask); 389} 390 391#ifdef SMP 392static int 393ciu_en1_intr_bind(void *arg, int target) 394{ 395 uint64_t mask; 396 int core; 397 int irq; 398 399 irq = (uintptr_t)arg; 400 CPU_FOREACH(core) { 401 mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(core*2)); 402 if (core == target) 403 mask |= 1ull << (irq - CIU_IRQ_EN1_BEGIN); 404 else 405 mask &= ~(1ull << (irq - CIU_IRQ_EN1_BEGIN)); 406 cvmx_write_csr(CVMX_CIU_INTX_EN1(core*2), mask); 407 } 408 409 return (0); 410} 411#endif 412 413static int 414ciu_intr(void *arg) 415{ 416 struct ciu_softc *sc; 417 uint64_t en0_sum, en1_sum; 418 uint64_t en0_mask, en1_mask; 419 int irq_index; 420 int error; 421 422 sc = arg; 423 (void)sc; 424 425 en0_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(cvmx_get_core_num()*2)); 426 en1_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1); 427 428 en0_mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2)); 429 en1_mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2)); 430 431 en0_sum &= en0_mask; 432 en1_sum &= en1_mask; 433 434 if (en0_sum == 0 && en1_sum == 0) 435 return (FILTER_STRAY); 436 437 for (irq_index = 0; en0_sum != 0; irq_index++, en0_sum >>= 1) { 438 if ((en0_sum & 1) == 0) 439 continue; 440 441 mips_intrcnt_inc(ciu_en0_intrcnt[irq_index]); 442 443 error = intr_event_handle(ciu_en0_intr_events[irq_index], NULL); 444 if (error != 0) 445 printf("%s: stray en0 irq%d\n", __func__, irq_index); 446 } 447 448 for (irq_index = 0; en1_sum != 0; irq_index++, en1_sum >>= 1) { 449 if ((en1_sum & 1) == 0) 450 continue; 451 452 mips_intrcnt_inc(ciu_en1_intrcnt[irq_index]); 453 454 error = intr_event_handle(ciu_en1_intr_events[irq_index], NULL); 455 if (error != 0) 456 printf("%s: stray en1 irq%d\n", __func__, irq_index); 457 } 458 459 return (FILTER_HANDLED); 460} 461 462static device_method_t ciu_methods[] = { 463 DEVMETHOD(device_probe, ciu_probe), 464 DEVMETHOD(device_attach, ciu_attach), 465 466 DEVMETHOD(bus_alloc_resource, ciu_alloc_resource), 467 DEVMETHOD(bus_activate_resource,bus_generic_activate_resource), 468 DEVMETHOD(bus_setup_intr, ciu_setup_intr), 469 DEVMETHOD(bus_teardown_intr, ciu_teardown_intr), 470#ifdef SMP 471 DEVMETHOD(bus_bind_intr, ciu_bind_intr), 472#endif 473 DEVMETHOD(bus_describe_intr, ciu_describe_intr), 474 475 DEVMETHOD(bus_add_child, bus_generic_add_child), 476 DEVMETHOD(bus_hinted_child, ciu_hinted_child), 477 478 { 0, 0 } 479}; 480 481static driver_t ciu_driver = { 482 "ciu", 483 ciu_methods, 484 sizeof(struct ciu_softc), 485}; 486static devclass_t ciu_devclass; 487DRIVER_MODULE(ciu, nexus, ciu_driver, ciu_devclass, 0, 0); 488