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