apb.c revision 276684
1155324Simp/*- 2155324Simp * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3155324Simp * All rights reserved. 4155324Simp * 5155324Simp * Redistribution and use in source and binary forms, with or without 6155324Simp * modification, are permitted provided that the following conditions 7155324Simp * are met: 8155324Simp * 1. Redistributions of source code must retain the above copyright 9155324Simp * notice unmodified, this list of conditions, and the following 10155324Simp * disclaimer. 11155324Simp * 2. Redistributions in binary form must reproduce the above copyright 12155324Simp * notice, this list of conditions and the following disclaimer in the 13155324Simp * documentation and/or other materials provided with the distribution. 14155324Simp * 15155324Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16155324Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17155324Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18155324Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19155324Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20155324Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21155324Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22155324Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23155324Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24155324Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25155324Simp * SUCH DAMAGE. 26155324Simp */ 27155324Simp 28155324Simp#include <sys/cdefs.h> 29155324Simp__FBSDID("$FreeBSD: head/sys/mips/atheros/apb.c 276684 2015-01-05 02:00:41Z adrian $"); 30155324Simp 31155324Simp#include <sys/param.h> 32155324Simp#include <sys/systm.h> 33155324Simp#include <sys/bus.h> 34155324Simp#include <sys/interrupt.h> 35155324Simp#include <sys/kernel.h> 36155324Simp#include <sys/module.h> 37155324Simp#include <sys/rman.h> 38155324Simp#include <sys/malloc.h> 39155324Simp#include <sys/pcpu.h> 40155324Simp#include <sys/proc.h> 41155324Simp#include <sys/pmc.h> 42155324Simp#include <sys/pmckern.h> 43155324Simp 44155324Simp#include <machine/bus.h> 45155324Simp#include <machine/intr_machdep.h> 46155324Simp 47155324Simp#include <mips/atheros/apbvar.h> 48155324Simp#include <mips/atheros/ar71xxreg.h> 49155324Simp#include <mips/atheros/ar71xx_setup.h> 50155324Simp 51155324Simp#define APB_INTR_PMC 5 52155324Simp 53155324Simp#undef APB_DEBUG 54155324Simp#ifdef APB_DEBUG 55155324Simp#define dprintf printf 56155324Simp#else 57155324Simp#define dprintf(x, arg...) 58155324Simp#endif /* APB_DEBUG */ 59155324Simp 60155324Simp#define DEVTOAPB(dev) ((struct apb_ivar *) device_get_ivars(dev)) 61155324Simp 62155324Simpstatic int apb_activate_resource(device_t, device_t, int, int, 63155324Simp struct resource *); 64155324Simpstatic device_t apb_add_child(device_t, u_int, const char *, int); 65155324Simpstatic struct resource * 66155324Simp apb_alloc_resource(device_t, device_t, int, int *, u_long, 67155324Simp u_long, u_long, u_int); 68155324Simpstatic int apb_attach(device_t); 69155324Simpstatic int apb_deactivate_resource(device_t, device_t, int, int, 70155324Simp struct resource *); 71155324Simpstatic struct resource_list * 72155324Simp apb_get_resource_list(device_t, device_t); 73155324Simpstatic void apb_hinted_child(device_t, const char *, int); 74155324Simpstatic int apb_filter(void *); 75155324Simpstatic int apb_probe(device_t); 76155324Simpstatic int apb_release_resource(device_t, device_t, int, int, 77155324Simp struct resource *); 78155324Simpstatic int apb_setup_intr(device_t, device_t, struct resource *, int, 79155324Simp driver_filter_t *, driver_intr_t *, void *, void **); 80155324Simpstatic int apb_teardown_intr(device_t, device_t, struct resource *, 81155324Simp void *); 82155324Simp 83155324Simpstatic void 84155324Simpapb_mask_irq(void *source) 85155324Simp{ 86155324Simp unsigned int irq = (unsigned int)source; 87155324Simp uint32_t reg; 88155324Simp 89155324Simp reg = ATH_READ_REG(AR71XX_MISC_INTR_MASK); 90155324Simp ATH_WRITE_REG(AR71XX_MISC_INTR_MASK, reg & ~(1 << irq)); 91155324Simp 92155324Simp} 93155324Simp 94155324Simpstatic void 95155324Simpapb_unmask_irq(void *source) 96155324Simp{ 97155324Simp uint32_t reg; 98155324Simp unsigned int irq = (unsigned int)source; 99155324Simp 100155324Simp reg = ATH_READ_REG(AR71XX_MISC_INTR_MASK); 101155324Simp ATH_WRITE_REG(AR71XX_MISC_INTR_MASK, reg | (1 << irq)); 102155324Simp} 103155324Simp 104155324Simpstatic int 105155324Simpapb_probe(device_t dev) 106155324Simp{ 107155324Simp 108155324Simp return (BUS_PROBE_NOWILDCARD); 109155324Simp} 110155324Simp 111155324Simpstatic int 112155324Simpapb_attach(device_t dev) 113155324Simp{ 114155324Simp struct apb_softc *sc = device_get_softc(dev); 115155324Simp int rid = 0; 116155324Simp 117155324Simp device_set_desc(dev, "APB Bus bridge"); 118155324Simp 119155324Simp sc->apb_mem_rman.rm_type = RMAN_ARRAY; 120155324Simp sc->apb_mem_rman.rm_descr = "APB memory window"; 121155324Simp 122155324Simp if (rman_init(&sc->apb_mem_rman) != 0 || 123155324Simp rman_manage_region(&sc->apb_mem_rman, 124155324Simp AR71XX_APB_BASE, 125155324Simp AR71XX_APB_BASE + AR71XX_APB_SIZE - 1) != 0) 126155324Simp panic("apb_attach: failed to set up memory rman"); 127155324Simp 128155324Simp sc->apb_irq_rman.rm_type = RMAN_ARRAY; 129155324Simp sc->apb_irq_rman.rm_descr = "APB IRQ"; 130155324Simp 131155324Simp if (rman_init(&sc->apb_irq_rman) != 0 || 132155324Simp rman_manage_region(&sc->apb_irq_rman, 133155324Simp APB_IRQ_BASE, APB_IRQ_END) != 0) 134155324Simp panic("apb_attach: failed to set up IRQ rman"); 135155324Simp 136155324Simp if ((sc->sc_misc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 137155324Simp RF_SHAREABLE | RF_ACTIVE)) == NULL) { 138155324Simp device_printf(dev, "unable to allocate IRQ resource\n"); 139155324Simp return (ENXIO); 140155324Simp } 141155324Simp 142155324Simp if ((bus_setup_intr(dev, sc->sc_misc_irq, INTR_TYPE_MISC, 143155324Simp apb_filter, NULL, sc, &sc->sc_misc_ih))) { 144155324Simp device_printf(dev, 145155324Simp "WARNING: unable to register interrupt handler\n"); 146155324Simp return (ENXIO); 147155324Simp } 148155324Simp 149155324Simp bus_generic_probe(dev); 150155324Simp bus_enumerate_hinted_children(dev); 151155324Simp bus_generic_attach(dev); 152155324Simp 153155324Simp /* 154155324Simp * Unmask performance counter IRQ 155155324Simp */ 156155324Simp apb_unmask_irq((void*)APB_INTR_PMC); 157155324Simp sc->sc_intr_counter[APB_INTR_PMC] = mips_intrcnt_create("apb irq5: pmc"); 158155324Simp 159155324Simp return (0); 160155324Simp} 161155324Simp 162155324Simpstatic struct resource * 163155324Simpapb_alloc_resource(device_t bus, device_t child, int type, int *rid, 164155324Simp u_long start, u_long end, u_long count, u_int flags) 165155324Simp{ 166155324Simp struct apb_softc *sc = device_get_softc(bus); 167155324Simp struct apb_ivar *ivar = device_get_ivars(child); 168155324Simp struct resource *rv; 169155324Simp struct resource_list_entry *rle; 170155324Simp struct rman *rm; 171155324Simp int isdefault, needactivate, passthrough; 172155324Simp 173155324Simp isdefault = (start == 0UL && end == ~0UL); 174155324Simp needactivate = flags & RF_ACTIVE; 175155324Simp /* 176155324Simp * Pass memory requests to nexus device 177155324Simp */ 178155324Simp passthrough = (device_get_parent(child) != bus); 179155324Simp rle = NULL; 180155324Simp 181155324Simp dprintf("%s: entry (%p, %p, %d, %d, %p, %p, %ld, %d)\n", 182155324Simp __func__, bus, child, type, *rid, (void *)(intptr_t)start, 183155324Simp (void *)(intptr_t)end, count, flags); 184155324Simp 185155324Simp if (passthrough) 186155324Simp return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, 187155324Simp rid, start, end, count, flags)); 188155324Simp 189155324Simp /* 190155324Simp * If this is an allocation of the "default" range for a given RID, 191155324Simp * and we know what the resources for this device are (ie. they aren't 192155324Simp * maintained by a child bus), then work out the start/end values. 193155324Simp */ 194155324Simp 195155324Simp if (isdefault) { 196155324Simp rle = resource_list_find(&ivar->resources, type, *rid); 197155324Simp if (rle == NULL) { 198155324Simp return (NULL); 199155324Simp } 200155324Simp 201155324Simp if (rle->res != NULL) { 202155324Simp panic("%s: resource entry is busy", __func__); 203155324Simp } 204155324Simp start = rle->start; 205155324Simp end = rle->end; 206155324Simp count = rle->count; 207155324Simp 208155324Simp dprintf("%s: default resource (%p, %p, %ld)\n", 209155324Simp __func__, (void *)(intptr_t)start, 210155324Simp (void *)(intptr_t)end, count); 211155324Simp } 212155324Simp 213155324Simp switch (type) { 214155324Simp case SYS_RES_IRQ: 215155324Simp rm = &sc->apb_irq_rman; 216155324Simp break; 217155324Simp case SYS_RES_MEMORY: 218155324Simp rm = &sc->apb_mem_rman; 219155324Simp break; 220155324Simp default: 221155324Simp printf("%s: unknown resource type %d\n", __func__, type); 222155324Simp return (0); 223155324Simp } 224155324Simp 225155324Simp rv = rman_reserve_resource(rm, start, end, count, flags, child); 226155324Simp if (rv == 0) { 227155324Simp printf("%s: could not reserve resource\n", __func__); 228155324Simp return (0); 229155324Simp } 230155324Simp 231155324Simp rman_set_rid(rv, *rid); 232155324Simp 233155324Simp if (needactivate) { 234155324Simp if (bus_activate_resource(child, type, *rid, rv)) { 235155324Simp printf("%s: could not activate resource\n", __func__); 236155324Simp rman_release_resource(rv); 237155324Simp return (0); 238155324Simp } 239155324Simp } 240155324Simp 241155324Simp return (rv); 242155324Simp} 243155324Simp 244155324Simpstatic int 245155324Simpapb_activate_resource(device_t bus, device_t child, int type, int rid, 246155324Simp struct resource *r) 247155324Simp{ 248155324Simp 249155324Simp /* XXX: should we mask/unmask IRQ here? */ 250155324Simp return (BUS_ACTIVATE_RESOURCE(device_get_parent(bus), child, 251155324Simp type, rid, r)); 252155324Simp} 253155324Simp 254155324Simpstatic int 255155324Simpapb_deactivate_resource(device_t bus, device_t child, int type, int rid, 256155324Simp struct resource *r) 257155324Simp{ 258155324Simp 259155324Simp /* XXX: should we mask/unmask IRQ here? */ 260155324Simp return (BUS_DEACTIVATE_RESOURCE(device_get_parent(bus), child, 261155324Simp type, rid, r)); 262155324Simp} 263155324Simp 264155324Simpstatic int 265155324Simpapb_release_resource(device_t dev, device_t child, int type, 266155324Simp int rid, struct resource *r) 267155324Simp{ 268155324Simp struct resource_list *rl; 269155324Simp struct resource_list_entry *rle; 270155324Simp 271155324Simp rl = apb_get_resource_list(dev, child); 272155324Simp if (rl == NULL) 273155324Simp return (EINVAL); 274155324Simp rle = resource_list_find(rl, type, rid); 275155324Simp if (rle == NULL) 276155324Simp return (EINVAL); 277155324Simp rman_release_resource(r); 278155324Simp rle->res = NULL; 279155324Simp 280155324Simp return (0); 281155324Simp} 282155324Simp 283155324Simpstatic int 284155324Simpapb_setup_intr(device_t bus, device_t child, struct resource *ires, 285155324Simp int flags, driver_filter_t *filt, driver_intr_t *handler, 286155324Simp void *arg, void **cookiep) 287155324Simp{ 288155324Simp struct apb_softc *sc = device_get_softc(bus); 289155324Simp struct intr_event *event; 290155324Simp int irq, error; 291155324Simp 292155324Simp irq = rman_get_start(ires); 293155324Simp 294155324Simp if (irq > APB_IRQ_END) 295155324Simp panic("%s: bad irq %d", __func__, irq); 296155324Simp 297155324Simp event = sc->sc_eventstab[irq]; 298155324Simp if (event == NULL) { 299155324Simp error = intr_event_create(&event, (void *)irq, 0, irq, 300155324Simp apb_mask_irq, apb_unmask_irq, 301155324Simp NULL, NULL, 302155324Simp "apb intr%d:", irq); 303155324Simp 304155324Simp if (error == 0) { 305155324Simp sc->sc_eventstab[irq] = event; 306155324Simp sc->sc_intr_counter[irq] = 307155324Simp mips_intrcnt_create(event->ie_name); 308155324Simp } 309155324Simp else 310155324Simp return (error); 311155324Simp } 312155324Simp 313155324Simp intr_event_add_handler(event, device_get_nameunit(child), filt, 314155324Simp handler, arg, intr_priority(flags), flags, cookiep); 315155324Simp mips_intrcnt_setname(sc->sc_intr_counter[irq], event->ie_fullname); 316155324Simp 317155324Simp apb_unmask_irq((void*)irq); 318155324Simp 319155324Simp return (0); 320155324Simp} 321155324Simp 322155324Simpstatic int 323155324Simpapb_teardown_intr(device_t dev, device_t child, struct resource *ires, 324155324Simp void *cookie) 325155324Simp{ 326155324Simp struct apb_softc *sc = device_get_softc(dev); 327155324Simp int irq, result; 328155324Simp 329155324Simp irq = rman_get_start(ires); 330155324Simp if (irq > APB_IRQ_END) 331155324Simp panic("%s: bad irq %d", __func__, irq); 332155324Simp 333155324Simp if (sc->sc_eventstab[irq] == NULL) 334155324Simp panic("Trying to teardown unoccupied IRQ"); 335155324Simp 336155324Simp apb_mask_irq((void*)irq); 337155324Simp 338155324Simp result = intr_event_remove_handler(cookie); 339155324Simp if (!result) 340155324Simp sc->sc_eventstab[irq] = NULL; 341155324Simp 342155324Simp return (result); 343155324Simp} 344155324Simp 345155324Simpstatic int 346155324Simpapb_filter(void *arg) 347155324Simp{ 348155324Simp struct apb_softc *sc = arg; 349155324Simp struct intr_event *event; 350155324Simp uint32_t reg, irq; 351155324Simp struct thread *td; 352155324Simp struct trapframe *tf; 353155324Simp 354155324Simp reg = ATH_READ_REG(AR71XX_MISC_INTR_STATUS); 355155324Simp for (irq = 0; irq < APB_NIRQS; irq++) { 356155324Simp if (reg & (1 << irq)) { 357155324Simp 358155324Simp switch (ar71xx_soc) { 359155324Simp case AR71XX_SOC_AR7240: 360155324Simp case AR71XX_SOC_AR7241: 361155324Simp case AR71XX_SOC_AR7242: 362155324Simp case AR71XX_SOC_AR9330: 363155324Simp case AR71XX_SOC_AR9331: 364155324Simp case AR71XX_SOC_AR9341: 365155324Simp case AR71XX_SOC_AR9342: 366155324Simp case AR71XX_SOC_AR9344: 367155324Simp case AR71XX_SOC_QCA9556: 368155324Simp case AR71XX_SOC_QCA9558: 369155324Simp /* ACK/clear the given interrupt */ 370155324Simp ATH_WRITE_REG(AR71XX_MISC_INTR_STATUS, 371155324Simp (1 << irq)); 372155324Simp break; 373155324Simp default: 374155324Simp /* fallthrough */ 375155324Simp break; 376155324Simp } 377155324Simp 378155324Simp event = sc->sc_eventstab[irq]; 379155324Simp if (!event || TAILQ_EMPTY(&event->ie_handlers)) { 380155324Simp if (irq == APB_INTR_PMC) { 381155324Simp td = PCPU_GET(curthread); 382155324Simp tf = td->td_intr_frame; 383155324Simp 384155324Simp if (pmc_intr) 385155324Simp (*pmc_intr)(PCPU_GET(cpuid), tf); 386155324Simp 387155324Simp mips_intrcnt_inc(sc->sc_intr_counter[irq]); 388155324Simp 389155324Simp continue; 390155324Simp } 391155324Simp /* Ignore timer interrupts */ 392155324Simp if (irq != 0 && irq != 8 && irq != 9 && irq != 10) 393155324Simp printf("Stray APB IRQ %d\n", irq); 394155324Simp continue; 395155324Simp } 396155324Simp 397155324Simp intr_event_handle(event, PCPU_GET(curthread)->td_intr_frame); 398155324Simp mips_intrcnt_inc(sc->sc_intr_counter[irq]); 399155324Simp } 400155324Simp } 401155324Simp 402155324Simp return (FILTER_HANDLED); 403155324Simp} 404155324Simp 405155324Simpstatic void 406155324Simpapb_hinted_child(device_t bus, const char *dname, int dunit) 407155324Simp{ 408155324Simp device_t child; 409155324Simp long maddr; 410155324Simp int msize; 411155324Simp int irq; 412155324Simp int result; 413155324Simp int mem_hints_count; 414155324Simp 415155324Simp child = BUS_ADD_CHILD(bus, 0, dname, dunit); 416155324Simp 417155324Simp /* 418155324Simp * Set hard-wired resources for hinted child using 419155324Simp * specific RIDs. 420155324Simp */ 421155324Simp mem_hints_count = 0; 422155324Simp if (resource_long_value(dname, dunit, "maddr", &maddr) == 0) 423155324Simp mem_hints_count++; 424155324Simp if (resource_int_value(dname, dunit, "msize", &msize) == 0) 425155324Simp mem_hints_count++; 426155324Simp 427155324Simp /* check if all info for mem resource has been provided */ 428155324Simp if ((mem_hints_count > 0) && (mem_hints_count < 2)) { 429155324Simp printf("Either maddr or msize hint is missing for %s%d\n", 430155324Simp dname, dunit); 431155324Simp } else if (mem_hints_count) { 432155324Simp result = bus_set_resource(child, SYS_RES_MEMORY, 0, 433155324Simp maddr, msize); 434155324Simp if (result != 0) 435155324Simp device_printf(bus, 436155324Simp "warning: bus_set_resource() failed\n"); 437155324Simp } 438155324Simp 439155324Simp if (resource_int_value(dname, dunit, "irq", &irq) == 0) { 440155324Simp result = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); 441155324Simp if (result != 0) 442155324Simp device_printf(bus, 443155324Simp "warning: bus_set_resource() failed\n"); 444155324Simp } 445155324Simp} 446155324Simp 447155324Simpstatic device_t 448155324Simpapb_add_child(device_t bus, u_int order, const char *name, int unit) 449155324Simp{ 450155324Simp device_t child; 451155324Simp struct apb_ivar *ivar; 452155324Simp 453155324Simp ivar = malloc(sizeof(struct apb_ivar), M_DEVBUF, M_WAITOK | M_ZERO); 454155324Simp if (ivar == NULL) { 455155324Simp printf("Failed to allocate ivar\n"); 456155324Simp return (0); 457155324Simp } 458155324Simp resource_list_init(&ivar->resources); 459155324Simp 460155324Simp child = device_add_child_ordered(bus, order, name, unit); 461155324Simp if (child == NULL) { 462155324Simp printf("Can't add child %s%d ordered\n", name, unit); 463155324Simp return (0); 464155324Simp } 465155324Simp 466155324Simp device_set_ivars(child, ivar); 467155324Simp 468155324Simp return (child); 469155324Simp} 470155324Simp 471155324Simp/* 472155324Simp * Helper routine for bus_generic_rl_get_resource/bus_generic_rl_set_resource 473155324Simp * Provides pointer to resource_list for these routines 474155324Simp */ 475155324Simpstatic struct resource_list * 476155324Simpapb_get_resource_list(device_t dev, device_t child) 477155324Simp{ 478155324Simp struct apb_ivar *ivar; 479155324Simp 480155324Simp ivar = device_get_ivars(child); 481155324Simp return (&(ivar->resources)); 482155324Simp} 483155324Simp 484155324Simpstatic int 485155324Simpapb_print_all_resources(device_t dev) 486155324Simp{ 487155324Simp struct apb_ivar *ndev = DEVTOAPB(dev); 488155324Simp struct resource_list *rl = &ndev->resources; 489155324Simp int retval = 0; 490155324Simp 491155324Simp if (STAILQ_FIRST(rl)) 492155324Simp retval += printf(" at"); 493155324Simp 494155324Simp retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx"); 495155324Simp retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 496155324Simp 497155324Simp return (retval); 498155324Simp} 499155324Simp 500155324Simpstatic int 501155324Simpapb_print_child(device_t bus, device_t child) 502155324Simp{ 503155324Simp int retval = 0; 504155324Simp 505155324Simp retval += bus_print_child_header(bus, child); 506155324Simp retval += apb_print_all_resources(child); 507155324Simp if (device_get_flags(child)) 508155324Simp retval += printf(" flags %#x", device_get_flags(child)); 509155324Simp retval += printf(" on %s\n", device_get_nameunit(bus)); 510155324Simp 511155324Simp return (retval); 512155324Simp} 513155324Simp 514155324Simp 515155324Simpstatic device_method_t apb_methods[] = { 516155324Simp DEVMETHOD(bus_activate_resource, apb_activate_resource), 517155324Simp DEVMETHOD(bus_add_child, apb_add_child), 518155324Simp DEVMETHOD(bus_alloc_resource, apb_alloc_resource), 519155324Simp DEVMETHOD(bus_deactivate_resource, apb_deactivate_resource), 520155324Simp DEVMETHOD(bus_get_resource_list, apb_get_resource_list), 521155324Simp DEVMETHOD(bus_hinted_child, apb_hinted_child), 522155324Simp DEVMETHOD(bus_release_resource, apb_release_resource), 523155324Simp DEVMETHOD(bus_setup_intr, apb_setup_intr), 524155324Simp DEVMETHOD(bus_teardown_intr, apb_teardown_intr), 525155324Simp DEVMETHOD(device_attach, apb_attach), 526155324Simp DEVMETHOD(device_probe, apb_probe), 527155324Simp DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 528155324Simp DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 529155324Simp DEVMETHOD(bus_print_child, apb_print_child), 530155324Simp 531155324Simp DEVMETHOD_END 532155324Simp}; 533155324Simp 534155324Simpstatic driver_t apb_driver = { 535155324Simp "apb", 536155324Simp apb_methods, 537155324Simp sizeof(struct apb_softc), 538155324Simp}; 539155324Simpstatic devclass_t apb_devclass; 540155324Simp 541155324SimpDRIVER_MODULE(apb, nexus, apb_driver, apb_devclass, 0, 0); 542155324Simp