ciu.c revision 265999
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/10/sys/mips/cavium/ciu.c 265999 2014-05-14 01:35:43Z ian $ 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: stable/10/sys/mips/cavium/ciu.c 265999 2014-05-14 01:35:43Z ian $"); 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 u_long, u_long, u_long, u_int); 79static int ciu_setup_intr(device_t, device_t, struct resource *, 80 int, driver_filter_t *, driver_intr_t *, 81 void *, void **); 82static int ciu_teardown_intr(device_t, device_t, 83 struct resource *, void *); 84static int ciu_bind_intr(device_t, device_t, struct resource *, 85 int); 86static int ciu_describe_intr(device_t, device_t, 87 struct resource *, void *, 88 const char *); 89static void ciu_hinted_child(device_t, const char *, int); 90 91static void ciu_en0_intr_mask(void *); 92static void ciu_en0_intr_unmask(void *); 93#ifdef SMP 94static int ciu_en0_intr_bind(void *, u_char); 95#endif 96 97static void ciu_en1_intr_mask(void *); 98static void ciu_en1_intr_unmask(void *); 99#ifdef SMP 100static int ciu_en1_intr_bind(void *, u_char); 101#endif 102 103static int ciu_intr(void *); 104 105static int 106ciu_probe(device_t dev) 107{ 108 if (device_get_unit(dev) != 0) 109 return (ENXIO); 110 111 device_set_desc(dev, "Cavium Octeon Central Interrupt Unit"); 112 return (BUS_PROBE_NOWILDCARD); 113} 114 115static int 116ciu_attach(device_t dev) 117{ 118 char name[MAXCOMLEN + 1]; 119 struct ciu_softc *sc; 120 unsigned i; 121 int error; 122 int rid; 123 124 sc = device_get_softc(dev); 125 126 rid = 0; 127 sc->ciu_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, CIU_IRQ_HARD, 128 CIU_IRQ_HARD, 1, RF_ACTIVE); 129 if (sc->ciu_irq == NULL) { 130 device_printf(dev, "could not allocate irq%d\n", CIU_IRQ_HARD); 131 return (ENXIO); 132 } 133 134 error = bus_setup_intr(dev, sc->ciu_irq, INTR_TYPE_MISC, ciu_intr, 135 NULL, sc, NULL); 136 if (error != 0) { 137 device_printf(dev, "bus_setup_intr failed: %d\n", error); 138 return (error); 139 } 140 141 sc->irq_rman.rm_type = RMAN_ARRAY; 142 sc->irq_rman.rm_descr = "CIU IRQ"; 143 144 error = rman_init(&sc->irq_rman); 145 if (error != 0) 146 return (error); 147 148 /* 149 * We have two contiguous IRQ regions, use a single rman. 150 */ 151 error = rman_manage_region(&sc->irq_rman, CIU_IRQ_EN0_BEGIN, 152 CIU_IRQ_EN1_END); 153 if (error != 0) 154 return (error); 155 156 for (i = 0; i < CIU_IRQ_EN0_COUNT; i++) { 157 snprintf(name, sizeof name, "int%d:", i + CIU_IRQ_EN0_BEGIN); 158 ciu_en0_intrcnt[i] = mips_intrcnt_create(name); 159 } 160 161 for (i = 0; i < CIU_IRQ_EN1_COUNT; i++) { 162 snprintf(name, sizeof name, "int%d:", i + CIU_IRQ_EN1_BEGIN); 163 ciu_en1_intrcnt[i] = mips_intrcnt_create(name); 164 } 165 166 bus_generic_probe(dev); 167 bus_generic_attach(dev); 168 169 return (0); 170} 171 172static struct resource * 173ciu_alloc_resource(device_t bus, device_t child, int type, int *rid, 174 u_long start, u_long end, u_long count, u_int flags) 175{ 176 struct resource *res; 177 struct ciu_softc *sc; 178 179 sc = device_get_softc(bus); 180 181 switch (type) { 182 case SYS_RES_IRQ: 183 break; 184 default: 185 return (bus_alloc_resource(device_get_parent(bus), type, rid, 186 start, end, count, flags)); 187 } 188 189 /* 190 * One interrupt at a time for now. 191 */ 192 if (start != end) 193 return (NULL); 194 195 res = rman_reserve_resource(&sc->irq_rman, start, end, count, flags, 196 child); 197 if (res != NULL) 198 return (res); 199 200 return (NULL); 201} 202 203static int 204ciu_setup_intr(device_t bus, device_t child, struct resource *res, int flags, 205 driver_filter_t *filter, driver_intr_t *intr, void *arg, 206 void **cookiep) 207{ 208 struct intr_event *event, **eventp; 209 void (*mask_func)(void *); 210 void (*unmask_func)(void *); 211 int (*bind_func)(void *, u_char); 212 mips_intrcnt_t intrcnt; 213 int error; 214 int irq; 215 216 irq = rman_get_start(res); 217 if (irq <= CIU_IRQ_EN0_END) { 218 eventp = &ciu_en0_intr_events[irq - CIU_IRQ_EN0_BEGIN]; 219 intrcnt = ciu_en0_intrcnt[irq - CIU_IRQ_EN0_BEGIN]; 220 mask_func = ciu_en0_intr_mask; 221 unmask_func = ciu_en0_intr_unmask; 222#ifdef SMP 223 bind_func = ciu_en0_intr_bind; 224#endif 225 } else { 226 eventp = &ciu_en1_intr_events[irq - CIU_IRQ_EN1_BEGIN]; 227 intrcnt = ciu_en1_intrcnt[irq - CIU_IRQ_EN1_BEGIN]; 228 mask_func = ciu_en1_intr_mask; 229 unmask_func = ciu_en1_intr_unmask; 230#ifdef SMP 231 bind_func = ciu_en1_intr_bind; 232#endif 233 } 234#if !defined(SMP) 235 bind_func = NULL; 236#endif 237 238 if ((event = *eventp) == NULL) { 239 error = intr_event_create(eventp, (void *)(uintptr_t)irq, 0, 240 irq, mask_func, unmask_func, NULL, bind_func, "int%d", irq); 241 if (error != 0) 242 return (error); 243 244 event = *eventp; 245 246 unmask_func((void *)(uintptr_t)irq); 247 } 248 249 intr_event_add_handler(event, device_get_nameunit(child), 250 filter, intr, arg, intr_priority(flags), flags, cookiep); 251 252 mips_intrcnt_setname(intrcnt, event->ie_fullname); 253 254 return (0); 255} 256 257static int 258ciu_teardown_intr(device_t bus, device_t child, struct resource *res, 259 void *cookie) 260{ 261 int error; 262 263 error = intr_event_remove_handler(cookie); 264 if (error != 0) 265 return (error); 266 267 return (0); 268} 269 270#ifdef SMP 271static int 272ciu_bind_intr(device_t bus, device_t child, struct resource *res, int cpu) 273{ 274 struct intr_event *event; 275 int irq; 276 277 irq = rman_get_start(res); 278 if (irq <= CIU_IRQ_EN0_END) 279 event = ciu_en0_intr_events[irq - CIU_IRQ_EN0_BEGIN]; 280 else 281 event = ciu_en1_intr_events[irq - CIU_IRQ_EN1_BEGIN]; 282 283 return (intr_event_bind(event, cpu)); 284} 285#endif 286 287static int 288ciu_describe_intr(device_t bus, device_t child, struct resource *res, 289 void *cookie, const char *descr) 290{ 291 struct intr_event *event; 292 mips_intrcnt_t intrcnt; 293 int error; 294 int irq; 295 296 irq = rman_get_start(res); 297 if (irq <= CIU_IRQ_EN0_END) { 298 event = ciu_en0_intr_events[irq - CIU_IRQ_EN0_BEGIN]; 299 intrcnt = ciu_en0_intrcnt[irq - CIU_IRQ_EN0_BEGIN]; 300 } else { 301 event = ciu_en1_intr_events[irq - CIU_IRQ_EN1_BEGIN]; 302 intrcnt = ciu_en1_intrcnt[irq - CIU_IRQ_EN1_BEGIN]; 303 } 304 305 error = intr_event_describe_handler(event, cookie, descr); 306 if (error != 0) 307 return (error); 308 309 mips_intrcnt_setname(intrcnt, event->ie_fullname); 310 311 return (0); 312} 313 314static void 315ciu_hinted_child(device_t bus, const char *dname, int dunit) 316{ 317 BUS_ADD_CHILD(bus, 0, dname, dunit); 318} 319 320static void 321ciu_en0_intr_mask(void *arg) 322{ 323 uint64_t mask; 324 int irq; 325 326 irq = (uintptr_t)arg; 327 mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2)); 328 mask &= ~(1ull << (irq - CIU_IRQ_EN0_BEGIN)); 329 cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2), mask); 330} 331 332static void 333ciu_en0_intr_unmask(void *arg) 334{ 335 uint64_t mask; 336 int irq; 337 338 irq = (uintptr_t)arg; 339 mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2)); 340 mask |= 1ull << (irq - CIU_IRQ_EN0_BEGIN); 341 cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2), mask); 342} 343 344#ifdef SMP 345static int 346ciu_en0_intr_bind(void *arg, u_char target) 347{ 348 uint64_t mask; 349 int core; 350 int irq; 351 352 irq = (uintptr_t)arg; 353 CPU_FOREACH(core) { 354 mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(core*2)); 355 if (core == target) 356 mask |= 1ull << (irq - CIU_IRQ_EN0_BEGIN); 357 else 358 mask &= ~(1ull << (irq - CIU_IRQ_EN0_BEGIN)); 359 cvmx_write_csr(CVMX_CIU_INTX_EN0(core*2), mask); 360 } 361 362 return (0); 363} 364#endif 365 366static void 367ciu_en1_intr_mask(void *arg) 368{ 369 uint64_t mask; 370 int irq; 371 372 irq = (uintptr_t)arg; 373 mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2)); 374 mask &= ~(1ull << (irq - CIU_IRQ_EN1_BEGIN)); 375 cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2), mask); 376} 377 378static void 379ciu_en1_intr_unmask(void *arg) 380{ 381 uint64_t mask; 382 int irq; 383 384 irq = (uintptr_t)arg; 385 mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2)); 386 mask |= 1ull << (irq - CIU_IRQ_EN1_BEGIN); 387 cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2), mask); 388} 389 390#ifdef SMP 391static int 392ciu_en1_intr_bind(void *arg, u_char target) 393{ 394 uint64_t mask; 395 int core; 396 int irq; 397 398 irq = (uintptr_t)arg; 399 CPU_FOREACH(core) { 400 mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(core*2)); 401 if (core == target) 402 mask |= 1ull << (irq - CIU_IRQ_EN1_BEGIN); 403 else 404 mask &= ~(1ull << (irq - CIU_IRQ_EN1_BEGIN)); 405 cvmx_write_csr(CVMX_CIU_INTX_EN1(core*2), mask); 406 } 407 408 return (0); 409} 410#endif 411 412static int 413ciu_intr(void *arg) 414{ 415 struct ciu_softc *sc; 416 uint64_t en0_sum, en1_sum; 417 uint64_t en0_mask, en1_mask; 418 int irq_index; 419 int error; 420 421 sc = arg; 422 (void)sc; 423 424 en0_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(cvmx_get_core_num()*2)); 425 en1_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1); 426 427 en0_mask = cvmx_read_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num()*2)); 428 en1_mask = cvmx_read_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num()*2)); 429 430 en0_sum &= en0_mask; 431 en1_sum &= en1_mask; 432 433 if (en0_sum == 0 && en1_sum == 0) 434 return (FILTER_STRAY); 435 436 irq_index = 0; 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 irq_index = 0; 449 for (irq_index = 0; en1_sum != 0; irq_index++, en1_sum >>= 1) { 450 if ((en1_sum & 1) == 0) 451 continue; 452 453 mips_intrcnt_inc(ciu_en1_intrcnt[irq_index]); 454 455 error = intr_event_handle(ciu_en1_intr_events[irq_index], NULL); 456 if (error != 0) 457 printf("%s: stray en1 irq%d\n", __func__, irq_index); 458 } 459 460 return (FILTER_HANDLED); 461} 462 463static device_method_t ciu_methods[] = { 464 DEVMETHOD(device_probe, ciu_probe), 465 DEVMETHOD(device_attach, ciu_attach), 466 467 DEVMETHOD(bus_alloc_resource, ciu_alloc_resource), 468 DEVMETHOD(bus_activate_resource,bus_generic_activate_resource), 469 DEVMETHOD(bus_setup_intr, ciu_setup_intr), 470 DEVMETHOD(bus_teardown_intr, ciu_teardown_intr), 471#ifdef SMP 472 DEVMETHOD(bus_bind_intr, ciu_bind_intr), 473#endif 474 DEVMETHOD(bus_describe_intr, ciu_describe_intr), 475 476 DEVMETHOD(bus_add_child, bus_generic_add_child), 477 DEVMETHOD(bus_hinted_child, ciu_hinted_child), 478 479 { 0, 0 } 480}; 481 482static driver_t ciu_driver = { 483 "ciu", 484 ciu_methods, 485 sizeof(struct ciu_softc), 486}; 487static devclass_t ciu_devclass; 488DRIVER_MODULE(ciu, nexus, ciu_driver, ciu_devclass, 0, 0); 489