1 /*- 2 * Copyright (c) 2012 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Matt Thomas of 3am Software Foundry. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #define _ARM32_BUS_DMA_PRIVATE 31 #define PCIE_PRIVATE 32 33 #include "locators.h" 34 35 #include <sys/cdefs.h> 36 37 __KERNEL_RCSID(1, "$NetBSD: bcm53xx_pax.c,v 1.13 2014/03/30 01:12:18 matt Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/bus.h> 41 #include <sys/device.h> 42 #include <sys/extent.h> 43 #include <sys/intr.h> 44 #include <sys/kmem.h> 45 #include <sys/systm.h> 46 47 #include <dev/pci/pcireg.h> 48 #include <dev/pci/pcivar.h> 49 #include <dev/pci/pciconf.h> 50 51 #include <arm/locore.h> 52 53 #include <arm/broadcom/bcm53xx_reg.h> 54 #include <arm/broadcom/bcm53xx_var.h> 55 56 #ifndef __HAVE_PCI_CONF_HOOK 57 #error __HAVE_PCI_CONF_HOOK must be defined 58 #endif 59 60 static const struct { 61 paddr_t owin_base; 62 psize_t owin_size; 63 } bcmpax_owins[] = { 64 [0] = { BCM53XX_PCIE0_OWIN_PBASE, BCM53XX_PCIE0_OWIN_SIZE }, 65 [1] = { BCM53XX_PCIE1_OWIN_PBASE, BCM53XX_PCIE1_OWIN_SIZE }, 66 [2] = { BCM53XX_PCIE2_OWIN_PBASE, BCM53XX_PCIE2_OWIN_SIZE }, 67 }; 68 69 static int bcmpax_ccb_match(device_t, cfdata_t, void *); 70 static void bcmpax_ccb_attach(device_t, device_t, void *); 71 72 struct bcmpax_intrhand { 73 TAILQ_ENTRY(bcmpax_intrhand) ih_link; 74 int (*ih_func)(void *); 75 void *ih_arg; 76 int ih_ipl; 77 }; 78 79 TAILQ_HEAD(bcmpax_ihqh, bcmpax_intrhand); 80 81 struct bcmpax_softc { 82 device_t sc_dev; 83 bus_space_tag_t sc_bst; 84 bus_space_handle_t sc_bsh; 85 bus_dma_tag_t sc_dmat; 86 kmutex_t *sc_lock; 87 kmutex_t *sc_cfg_lock; 88 bool sc_linkup; 89 int sc_pba_flags; 90 uint32_t sc_intrgen; 91 struct arm32_pci_chipset sc_pc; 92 struct bcmpax_ihqh sc_intrs; 93 void *sc_ih[6]; 94 int sc_port; 95 }; 96 97 static inline uint32_t 98 bcmpax_read_4(struct bcmpax_softc *sc, bus_size_t o) 99 { 100 return bus_space_read_4(sc->sc_bst, sc->sc_bsh, o); 101 } 102 103 static inline void 104 bcmpax_write_4(struct bcmpax_softc *sc, bus_size_t o, uint32_t v) 105 { 106 bus_space_write_4(sc->sc_bst, sc->sc_bsh, o, v); 107 } 108 109 static void bcmpax_attach_hook(device_t, device_t, struct pcibus_attach_args *); 110 static int bcmpax_bus_maxdevs(void *, int); 111 static pcitag_t bcmpax_make_tag(void *, int, int, int); 112 static void bcmpax_decompose_tag(void *, pcitag_t, int *, int *, int *); 113 static pcireg_t bcmpax_conf_read(void *, pcitag_t, int); 114 static void bcmpax_conf_write(void *, pcitag_t, int, pcireg_t); 115 116 static int bcmpax_intr_map(const struct pci_attach_args *, pci_intr_handle_t *); 117 static const char *bcmpax_intr_string(void *, pci_intr_handle_t, char *, size_t); 118 static const struct evcnt *bcmpax_intr_evcnt(void *, pci_intr_handle_t); 119 static void *bcmpax_intr_establish(void *, pci_intr_handle_t, int, 120 int (*)(void *), void *); 121 static void bcmpax_intr_disestablish(void *, void *); 122 123 static int bcmpax_conf_hook(void *, int, int, int, pcireg_t); 124 static void bcmpax_conf_interrupt(void *, int, int, int, int, int *); 125 126 static int bcmpax_intr(void *); 127 128 CFATTACH_DECL_NEW(bcmpax_ccb, sizeof(struct bcmpax_softc), 129 bcmpax_ccb_match, bcmpax_ccb_attach, NULL, NULL); 130 131 static int 132 bcmpax_ccb_match(device_t parent, cfdata_t cf, void *aux) 133 { 134 struct bcmccb_attach_args * const ccbaa = aux; 135 const struct bcm_locators * const loc = &ccbaa->ccbaa_loc; 136 137 if (strcmp(cf->cf_name, loc->loc_name)) 138 return 0; 139 140 #ifdef DIAGNOSTIC 141 const int port = cf->cf_loc[BCMCCBCF_PORT]; 142 #endif 143 KASSERT(port == BCMCCBCF_PORT_DEFAULT || port == loc->loc_port); 144 145 return 1; 146 } 147 148 static int 149 bcmpax_iwin_init(struct bcmpax_softc *sc) 150 { 151 #if 0 152 uint32_t megs = (physical_end + 0xfffff - physical_start) >> 20; 153 uint32_t iwin_megs = min(256, megs); 154 #if 1 155 bus_addr_t iwin1_start = physical_start; 156 #else 157 bus_addr_t iwin1_start = 0; 158 #endif 159 #if 1 160 bcmpax_write_4(sc, PCIE_IARR_1_LOWER, iwin1_start | min(megs, 128)); 161 bcmpax_write_4(sc, PCIE_FUNC0_IMAP1, iwin1_start | 1); 162 #else 163 bcmpax_write_4(sc, PCIE_FUNC0_IMAP1, iwin1_start | min(megs, 128)); 164 bcmpax_write_4(sc, PCIE_IARR_1_LOWER, iwin1_start | 1); 165 #endif 166 bcmpax_conf_write(sc, 0, PCI_MAPREG_START+4, iwin1_start); 167 if (iwin_megs > 128) { 168 bus_addr_t iwin2_start = iwin1_start + 128*1024*1024; 169 #if 1 170 bcmpax_write_4(sc, PCIE_IARR_2_LOWER, iwin2_start | min(megs - 128, 128)); 171 bcmpax_write_4(sc, PCIE_FUNC0_IMAP2, iwin2_start | 1); 172 #else 173 bcmpax_write_4(sc, PCIE_FUNC0_IMAP2, iwin2_start | min(megs - 128, 128)); 174 bcmpax_write_4(sc, PCIE_IARR_2_LOWER, iwin2_start | 1); 175 #endif 176 bcmpax_conf_write(sc, 0, PCI_MAPREG_START+8, iwin2_start); 177 } 178 179 if (megs <= iwin_megs) { 180 /* 181 * We could can DMA to all of memory so we don't need to subregion! 182 */ 183 return 0; 184 } 185 186 return bus_dmatag_subregion(sc->sc_dmat, physical_start, 187 physical_start + (iwin_megs << 20) - 1, &sc->sc_dmat, 0); 188 #else 189 bcmpax_write_4(sc, PCIE_IARR_1_LOWER, 0); 190 bcmpax_write_4(sc, PCIE_FUNC0_IMAP1, 0); 191 bcmpax_write_4(sc, PCIE_IARR_2_LOWER, 0); 192 bcmpax_write_4(sc, PCIE_FUNC0_IMAP2, 0); 193 return 0; 194 #endif 195 } 196 197 static void 198 bcmpax_ccb_attach(device_t parent, device_t self, void *aux) 199 { 200 struct bcmpax_softc * const sc = device_private(self); 201 struct bcmccb_attach_args * const ccbaa = aux; 202 const struct bcm_locators * const loc = &ccbaa->ccbaa_loc; 203 cfdata_t cf = device_cfdata(self); 204 205 sc->sc_dev = self; 206 sc->sc_dmat = &bcm53xx_coherent_dma_tag; 207 #ifdef _ARM32_NEED_BUS_DMA_BOUNCE 208 if (cf->cf_flags & 2) { 209 sc->sc_dmat = &bcm53xx_bounce_dma_tag; 210 } 211 #endif 212 213 sc->sc_bst = ccbaa->ccbaa_ccb_bst; 214 bus_space_subregion(sc->sc_bst, ccbaa->ccbaa_ccb_bsh, 215 loc->loc_offset, loc->loc_size, &sc->sc_bsh); 216 217 /* 218 * Kick the hardware into RC mode. 219 */ 220 bcmpax_write_4(sc, PCIE_CLK_CONTROL, 3); 221 delay(250); 222 bcmpax_write_4(sc, PCIE_CLK_CONTROL, 1); 223 224 uint32_t v = bcmpax_read_4(sc, PCIE_STRAP_STATUS); 225 const bool enabled = (v & STRAP_PCIE_IF_ENABLE) != 0; 226 const bool is_v2_p = (v & STRAP_PCIE_USER_FOR_CE_GEN1) == 0; 227 const bool is_x2_p = (v & STRAP_PCIE_USER_FOR_CE_1LANE) == 0; 228 const bool is_rc_p = (v & STRAP_PCIE_USER_RC_MODE) != 0; 229 230 aprint_naive("\n"); 231 aprint_normal(": PCI Express V%u %u-lane %s Controller%s\n", 232 is_v2_p ? 2 : 1, 233 is_x2_p ? 2 : 1, 234 is_rc_p ? "RC" : "EP", 235 enabled ? "" : "(disabled)"); 236 if (!enabled || !is_rc_p) 237 return; 238 239 sc->sc_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_VM); 240 sc->sc_cfg_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_VM); 241 242 TAILQ_INIT(&sc->sc_intrs); 243 244 sc->sc_pc.pc_conf_v = sc; 245 sc->sc_pc.pc_attach_hook = bcmpax_attach_hook; 246 sc->sc_pc.pc_bus_maxdevs = bcmpax_bus_maxdevs; 247 sc->sc_pc.pc_make_tag = bcmpax_make_tag; 248 sc->sc_pc.pc_decompose_tag = bcmpax_decompose_tag; 249 sc->sc_pc.pc_conf_read = bcmpax_conf_read; 250 sc->sc_pc.pc_conf_write = bcmpax_conf_write; 251 252 sc->sc_pc.pc_intr_v = sc; 253 sc->sc_pc.pc_intr_map = bcmpax_intr_map; 254 sc->sc_pc.pc_intr_string = bcmpax_intr_string; 255 sc->sc_pc.pc_intr_evcnt = bcmpax_intr_evcnt; 256 sc->sc_pc.pc_intr_establish = bcmpax_intr_establish; 257 sc->sc_pc.pc_intr_disestablish = bcmpax_intr_disestablish; 258 259 sc->sc_pc.pc_conf_hook = bcmpax_conf_hook; 260 sc->sc_pc.pc_conf_interrupt = bcmpax_conf_interrupt; 261 262 sc->sc_pba_flags |= PCI_FLAGS_MRL_OKAY; 263 sc->sc_pba_flags |= PCI_FLAGS_MRM_OKAY; 264 sc->sc_pba_flags |= PCI_FLAGS_MWI_OKAY; 265 // sc->sc_pba_flags |= PCI_FLAGS_MSI_OKAY; 266 // sc->sc_pba_flags |= PCI_FLAGS_MSIX_OKAY; 267 268 for (size_t i = 0; i < loc->loc_nintrs; i++) { 269 sc->sc_ih[i] = intr_establish(loc->loc_intrs[0] + i, IPL_VM, 270 IST_LEVEL, bcmpax_intr, sc); 271 if (sc->sc_ih[i] == NULL) { 272 aprint_error_dev(self, 273 "failed to establish interrupt #%zu (%zu)\n", i, 274 loc->loc_intrs[0] + i); 275 while (i-- > 0) { 276 intr_disestablish(sc->sc_ih[i]); 277 } 278 return; 279 } 280 } 281 aprint_normal_dev(self, "interrupting on irqs %d-%d\n", 282 loc->loc_intrs[0], loc->loc_intrs[0] + loc->loc_nintrs - 1); 283 284 /* 285 * Enable INTA-INTD 286 */ 287 bcmpax_write_4(sc, PCIE_SYS_RC_INTX_EN, 0x0f); 288 289 int offset; 290 const bool ok = pci_get_capability(&sc->sc_pc, 0, PCI_CAP_PCIEXPRESS, 291 &offset, NULL); 292 KASSERT(ok); 293 294 /* 295 * This will force the device to negotiate to a max of gen1. 296 */ 297 if (cf->cf_flags & 1) { 298 bcmpax_conf_write(sc, 0, offset + PCIE_LCSR2, 1); 299 } 300 301 /* 302 * Now we wait (.25 sec) for the link to come up. 303 */ 304 offset += PCIE_LCSR; 305 for (size_t timo = 0;; timo++) { 306 const pcireg_t lcsr = bcmpax_conf_read(sc, 0, offset); 307 sc->sc_linkup = __SHIFTOUT(lcsr, PCIE_LCSR_NLW) != 0 308 && (1 || (lcsr & PCIE_LCSR_DLACTIVE) != 0); 309 if (sc->sc_linkup || timo == 250) { 310 aprint_debug_dev(self, 311 "lcsr=%#x nlw=%jd linkup=%d, timo=%zu\n", 312 lcsr, __SHIFTOUT(lcsr, PCIE_LCSR_NLW), 313 sc->sc_linkup, timo); 314 break; 315 } 316 DELAY(1000); 317 } 318 319 if (sc->sc_linkup) { 320 /* 321 * Enable the inbound (device->memory) map. 322 */ 323 int error = bcmpax_iwin_init(sc); 324 if (error) { 325 aprint_error_dev(sc->sc_dev, 326 "failed to subregion dma tag: %d\n", error); 327 return; 328 } 329 330 aprint_normal_dev(self, "iwin[1]=%#x/%#x iwin[2]=%#x/%#x\n", 331 bcmpax_read_4(sc, PCIE_FUNC0_IMAP1), 332 bcmpax_read_4(sc, PCIE_IARR_1_LOWER), 333 bcmpax_read_4(sc, PCIE_FUNC0_IMAP2), 334 bcmpax_read_4(sc, PCIE_IARR_2_LOWER)); 335 336 paddr_t base = bcmpax_owins[loc->loc_port].owin_base; 337 psize_t size = bcmpax_owins[loc->loc_port].owin_size; 338 KASSERT((size & ~PCIE_OARR_ADDR) == 0); 339 if (size > 0) { 340 bcmpax_write_4(sc, PCIE_OARR_0, base); 341 bcmpax_write_4(sc, PCIE_OMAP_0_LOWER, base | 1); 342 } 343 if (size > __LOWEST_SET_BIT(PCIE_OARR_ADDR)) { 344 paddr_t base1 = base + __LOWEST_SET_BIT(PCIE_OARR_ADDR); 345 bcmpax_write_4(sc, PCIE_OARR_1, base1); 346 bcmpax_write_4(sc, PCIE_OMAP_1_LOWER, base1 | 1); 347 } 348 349 struct extent *memext = extent_create("pcimem", base, 350 base + size, NULL, 0, EX_NOWAIT); 351 352 error = pci_configure_bus(&sc->sc_pc, 353 NULL, memext, NULL, 0, arm_pcache.dcache_line_size); 354 355 extent_destroy(memext); 356 357 if (error) { 358 aprint_normal_dev(self, "configuration failed\n"); 359 return; 360 } 361 } 362 363 struct pcibus_attach_args pba; 364 memset(&pba, 0, sizeof(pba)); 365 366 pba.pba_flags = sc->sc_pba_flags; 367 pba.pba_flags |= PCI_FLAGS_MEM_OKAY; 368 pba.pba_memt = sc->sc_bst; 369 pba.pba_dmat = sc->sc_dmat; 370 pba.pba_pc = &sc->sc_pc; 371 pba.pba_bus = 0; 372 373 config_found_ia(self, "pcibus", &pba, pcibusprint); 374 } 375 376 static void 377 bcmpax_attach_hook(device_t parent, device_t self, 378 struct pcibus_attach_args *pba) 379 { 380 } 381 382 static int 383 bcmpax_bus_maxdevs(void *v, int bus) 384 { 385 struct bcmpax_softc * const sc = v; 386 387 if (__predict_true(sc->sc_linkup)) 388 return bus > 1 ? 32 : 1; 389 390 return bus ? 0 : 1; 391 } 392 393 static void 394 bcmpax_decompose_tag(void *v, pcitag_t tag, int *busp, int *devp, int *funcp) 395 { 396 if (busp) 397 *busp = __SHIFTOUT(tag, CFG_ADDR_BUS); 398 if (devp) 399 *devp = __SHIFTOUT(tag, CFG_ADDR_DEV); 400 if (funcp) 401 *funcp = __SHIFTOUT(tag, CFG_ADDR_FUNC); 402 } 403 404 static pcitag_t 405 bcmpax_make_tag(void *v, int bus, int dev, int func) 406 { 407 return __SHIFTIN(bus, CFG_ADDR_BUS) 408 | __SHIFTIN(dev, CFG_ADDR_DEV) 409 | __SHIFTIN(func, CFG_ADDR_FUNC) 410 | (bus == 0 ? CFG_ADDR_TYPE0 : CFG_ADDR_TYPE1); 411 } 412 413 static inline bus_size_t 414 bcmpax_conf_addr_write(struct bcmpax_softc *sc, pcitag_t tag) 415 { 416 if ((tag & (CFG_ADDR_BUS|CFG_ADDR_DEV)) == 0) { 417 uint32_t reg = __SHIFTOUT(tag, CFG_ADDR_REG); 418 uint32_t func = __SHIFTOUT(tag, CFG_ADDR_FUNC); 419 bcmpax_write_4(sc, PCIE_CFG_IND_ADDR, 420 __SHIFTIN(func, CFG_IND_ADDR_FUNC) 421 | __SHIFTIN(reg, CFG_IND_ADDR_REG)); 422 __asm __volatile("dsb"); 423 return PCIE_CFG_IND_DATA; 424 } 425 if (sc->sc_linkup) { 426 bcmpax_write_4(sc, PCIE_CFG_ADDR, tag); 427 __asm __volatile("dsb"); 428 return PCIE_CFG_DATA; 429 } 430 return 0; 431 } 432 433 static pcireg_t 434 bcmpax_conf_read(void *v, pcitag_t tag, int reg) 435 { 436 struct bcmpax_softc * const sc = v; 437 438 /* 439 * Even in RC mode, the PCI Express Root Complex return itself 440 * as BCM Ethernet Controller!. We could change ppb.c to match it 441 * but we'll just lie and say we are a PPB bridge. 442 */ 443 if ((tag & (CFG_ADDR_BUS|CFG_ADDR_DEV|CFG_ADDR_FUNC)) == 0 444 && reg == PCI_CLASS_REG) { 445 return PCI_CLASS_CODE(PCI_CLASS_BRIDGE, 446 PCI_SUBCLASS_BRIDGE_PCI, 0); 447 } 448 449 //printf("%s: tag %#lx reg %#x:", __func__, tag, reg); 450 451 mutex_enter(sc->sc_cfg_lock); 452 bus_size_t data_reg = bcmpax_conf_addr_write(sc, tag | reg); 453 454 //printf(" [from %#lx]:\n", data_reg); 455 456 pcireg_t rv; 457 if (data_reg) 458 rv = bcmpax_read_4(sc, data_reg); 459 else 460 rv = 0xffffffff; 461 462 mutex_exit(sc->sc_cfg_lock); 463 464 //printf(" %#x\n", rv); 465 466 return rv; 467 } 468 469 static void 470 bcmpax_conf_write(void *v, pcitag_t tag, int reg, pcireg_t val) 471 { 472 struct bcmpax_softc * const sc = v; 473 474 mutex_enter(sc->sc_cfg_lock); 475 bus_size_t data_reg = bcmpax_conf_addr_write(sc, tag | reg); 476 477 //printf("%s: tag %#lx reg %#x:", __func__, tag, reg); 478 479 if (data_reg) { 480 //printf(" [to %#lx]:\n", data_reg); 481 bcmpax_write_4(sc, data_reg, val); 482 //printf(" %#x\n", val); 483 } 484 485 mutex_exit(sc->sc_cfg_lock); 486 } 487 488 static void 489 bcmpax_conf_interrupt(void *v, int bus, int dev, int ipin, int swiz, int *ilinep) 490 { 491 *ilinep = 5; /* (ipin + swiz) & 3; */ 492 } 493 494 static int 495 bcmpax_conf_hook(void *v, int bus, int dev, int func, pcireg_t id) 496 { 497 if (func > 0) 498 return 0; 499 500 return PCI_CONF_ENABLE_MEM | PCI_CONF_MAP_MEM | PCI_CONF_ENABLE_BM; 501 } 502 503 static int 504 bcmpax_intr(void *v) 505 { 506 struct bcmpax_softc * const sc = v; 507 508 while (bcmpax_read_4(sc, PCIE_SYS_RC_INTX_CSR)) { 509 struct bcmpax_intrhand *ih; 510 mutex_enter(sc->sc_lock); 511 const uint32_t lastgen = sc->sc_intrgen; 512 TAILQ_FOREACH(ih, &sc->sc_intrs, ih_link) { 513 int (* const func)(void *) = ih->ih_func; 514 void * const arg = ih->ih_arg; 515 mutex_exit(sc->sc_lock); 516 int rv = (*func)(arg); 517 if (rv) { 518 return rv; 519 } 520 mutex_enter(sc->sc_lock); 521 /* 522 * Check to see if the interrupt list changed. 523 * If so, restart from the beginning. 524 */ 525 if (lastgen != sc->sc_intrgen) 526 break; 527 } 528 mutex_exit(sc->sc_lock); 529 } 530 531 return 0; 532 } 533 534 static int 535 bcmpax_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *pihp) 536 { 537 if (pa->pa_intrpin == 0) 538 return EINVAL; 539 540 *pihp = pa->pa_intrpin; 541 return 0; 542 } 543 544 static const char * 545 bcmpax_intr_string(void *v, pci_intr_handle_t pih, char *buf, size_t len) 546 { 547 struct bcmpax_softc * const sc = v; 548 549 if (pih) { 550 snprintf(buf, len, "%s int%c", 551 device_xname(sc->sc_dev), 552 (char) ('a' + pih - PCI_INTERRUPT_PIN_A)); 553 return buf; 554 } 555 556 return NULL; 557 } 558 559 static const struct evcnt * 560 bcmpax_intr_evcnt(void *v, pci_intr_handle_t pih) 561 { 562 return NULL; 563 } 564 565 static void * 566 bcmpax_intr_establish(void *v, pci_intr_handle_t pih, int ipl, 567 int (*func)(void *), void *arg) 568 { 569 struct bcmpax_softc * const sc = v; 570 571 KASSERT(!cpu_intr_p()); 572 KASSERT(!cpu_softintr_p()); 573 KASSERT(ipl == IPL_VM); 574 KASSERT(func != NULL); 575 KASSERT(arg != NULL); 576 577 if (pih == 0) 578 return NULL; 579 580 struct bcmpax_intrhand * const ih = kmem_alloc(sizeof(*ih), KM_SLEEP); 581 582 ih->ih_func = func; 583 ih->ih_arg = arg; 584 585 mutex_enter(sc->sc_lock); 586 TAILQ_INSERT_TAIL(&sc->sc_intrs, ih, ih_link); 587 mutex_exit(sc->sc_lock); 588 589 return ih; 590 } 591 592 static void 593 bcmpax_intr_disestablish(void *v, void *vih) 594 { 595 struct bcmpax_softc * const sc = v; 596 struct bcmpax_intrhand * const ih = vih; 597 598 mutex_enter(sc->sc_lock); 599 TAILQ_REMOVE(&sc->sc_intrs, ih, ih_link); 600 sc->sc_intrgen++; 601 mutex_exit(sc->sc_lock); 602 603 kmem_free(ih, sizeof(*ih)); 604 } 605