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