Index: sys/dev/bhnd/bcma/bcma.c =================================================================== --- sys/dev/bhnd/bcma/bcma.c +++ sys/dev/bhnd/bcma/bcma.c @@ -41,8 +41,11 @@ #include "bcmavar.h" +#include "bcma_dmp.h" + #include "bcma_eromreg.h" #include "bcma_eromvar.h" + #include /* RID used when allocating EROM table */ @@ -434,6 +437,70 @@ return (ENOENT); } +/** + * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT(). + * + * This implementation consults @p child's agent register block, + * returning the number of interrupt output lines routed to @p child. + */ +int +bcma_get_intr_count(device_t dev, device_t child) +{ + struct bcma_devinfo *dinfo; + uint32_t dmpcfg, oobw; + + dinfo = device_get_ivars(child); + + /* Agent block must be mapped */ + if (dinfo->res_agent == NULL) + return (0); + + /* Agent must support OOB */ + dmpcfg = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_CONFIG); + if (!BCMA_DMP_GET_FLAG(dmpcfg, BCMA_DMP_CFG_OOB)) + return (0); + + /* Return OOB width as interrupt count */ + oobw = bhnd_bus_read_4(dinfo->res_agent, + BCMA_DMP_OOB_OUTWIDTH(BCMA_OOB_BANK_INTR)); + if (oobw > BCMA_OOB_NUM_SEL) { + device_printf(dev, "ignoring invalid OOBOUTWIDTH for core %u: " + "%#x\n", BCMA_DINFO_COREIDX(dinfo), oobw); + return (0); + } + + return (oobw); +} + +/** + * Default bcma(4) bus driver implementation of BHND_BUS_GET_CORE_IVEC(). + * + * This implementation consults @p child's agent register block, + * returning the interrupt output line routed to @p child, at OOB selector + * @p intr. + */ +int +bcma_get_core_ivec(device_t dev, device_t child, u_int intr, uint32_t *ivec) +{ + struct bcma_devinfo *dinfo; + uint32_t oobsel; + + dinfo = device_get_ivars(child); + + /* Interrupt ID must be valid. */ + if (intr >= bcma_get_intr_count(dev, child)) + return (ENXIO); + + /* Fetch OOBSEL busline value */ + KASSERT(dinfo->res_agent != NULL, ("missing agent registers")); + oobsel = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_OOBSELOUT( + BCMA_OOB_BANK_INTR, intr)); + *ivec = (oobsel >> BCMA_DMP_OOBSEL_SHIFT(intr)) & + BCMA_DMP_OOBSEL_BUSLINE_MASK; + + return (0); +} + static struct bhnd_devinfo * bcma_alloc_bhnd_dinfo(device_t dev) { @@ -475,6 +542,8 @@ /* Add all cores. */ bcma_erom = (struct bcma_erom *)erom; while ((error = bcma_erom_next_corecfg(bcma_erom, &corecfg)) == 0) { + int nintr; + /* Add the child device */ child = BUS_ADD_CHILD(bus, 0, NULL, -1); if (child == NULL) { @@ -494,6 +563,17 @@ if ((error = bcma_dinfo_alloc_agent(bus, child, dinfo))) goto cleanup; + /* Assign interrupts */ + nintr = bhnd_get_intr_count(child); + for (int rid = 0; rid < nintr; rid++) { + error = BHND_BUS_ASSIGN_INTR(bus, child, rid); + if (error) { + device_printf(bus, "failed to assign interrupt " + "%d to core %u: %d\n", rid, + BCMA_DINFO_COREIDX(dinfo), error); + } + } + /* If pins are floating or the hardware is otherwise * unpopulated, the device shouldn't be used. */ if (bhnd_is_hw_disabled(child)) @@ -544,6 +624,8 @@ DEVMETHOD(bhnd_bus_get_port_rid, bcma_get_port_rid), DEVMETHOD(bhnd_bus_decode_port_rid, bcma_decode_port_rid), DEVMETHOD(bhnd_bus_get_region_addr, bcma_get_region_addr), + DEVMETHOD(bhnd_bus_get_intr_count, bcma_get_intr_count), + DEVMETHOD(bhnd_bus_get_core_ivec, bcma_get_core_ivec), DEVMETHOD_END }; Index: sys/dev/bhnd/bcma/bcma_dmp.h =================================================================== --- sys/dev/bhnd/bcma/bcma_dmp.h +++ sys/dev/bhnd/bcma/bcma_dmp.h @@ -37,8 +37,18 @@ * in the proprietary "NIC-301 Interconnect Device Management (PL368)" * errata publication, available to licensees as part of ARM's * CoreLink Controllers and Peripherals Engineering Errata. + * + * As such, the exact interpretation of these register definitions is + * unconfirmed, and may be incorrect. */ +#define BCMA_DMP_GET_FLAG(_value, _flag) \ + (((_value) & _flag) != 0) +#define BCMA_DMP_GET_BITS(_value, _field) \ + ((_value & _field ## _MASK) >> _field ## _SHIFT) +#define BHND_DMP_SET_BITS(_value, _field) \ + (((_value) << _field ## _SHIFT) & _field ## _MASK) + /* Out-of-band Router registers */ #define BCMA_OOB_BUSCONFIG 0x020 #define BCMA_OOB_STATUSA 0x100 @@ -71,23 +81,36 @@ #define BCMA_OOB_ITOPOOBC 0xf38 #define BCMA_OOB_ITOPOOBD 0xf3c -/* DMP wrapper registers */ -#define BCMA_DMP_OOBSELINA30 0x000 -#define BCMA_DMP_OOBSELINA74 0x004 -#define BCMA_DMP_OOBSELINB30 0x020 -#define BCMA_DMP_OOBSELINB74 0x024 -#define BCMA_DMP_OOBSELINC30 0x040 -#define BCMA_DMP_OOBSELINC74 0x044 -#define BCMA_DMP_OOBSELIND30 0x060 -#define BCMA_DMP_OOBSELIND74 0x064 -#define BCMA_DMP_OOBSELOUTA30 0x100 -#define BCMA_DMP_OOBSELOUTA74 0x104 -#define BCMA_DMP_OOBSELOUTB30 0x120 -#define BCMA_DMP_OOBSELOUTB74 0x124 -#define BCMA_DMP_OOBSELOUTC30 0x140 -#define BCMA_DMP_OOBSELOUTC74 0x144 -#define BCMA_DMP_OOBSELOUTD30 0x160 -#define BCMA_DMP_OOBSELOUTD74 0x164 +/* Common definitions */ +#define BCMA_OOB_NUM_BANKS 4 /**< number of OOB banks (A, B, C, D) */ +#define BCMA_OOB_NUM_SEL 8 /**< number of OOB selectors per bank */ +#define BCMA_OOB_NUM_BUSLINES 32 /**< number of bus lines managed by OOB core */ + +#define BCMA_OOB_BANKA 0 /**< bank A index */ +#define BCMA_OOB_BANKB 1 /**< bank B index */ +#define BCMA_OOB_BANKC 2 /**< bank C index */ +#define BCMA_OOB_BANKD 3 /**< bank D index */ + +/** OOB bank used for interrupt lines */ +#define BCMA_OOB_BANK_INTR BCMA_OOB_BANKA + +/* DMP agent registers */ +#define BCMA_DMP_OOBSELINA30 0x000 /**< A0-A3 input selectors */ +#define BCMA_DMP_OOBSELINA74 0x004 /**< A4-A7 input selectors */ +#define BCMA_DMP_OOBSELINB30 0x020 /**< B0-B3 input selectors */ +#define BCMA_DMP_OOBSELINB74 0x024 /**< B4-B7 input selectors */ +#define BCMA_DMP_OOBSELINC30 0x040 /**< C0-C3 input selectors */ +#define BCMA_DMP_OOBSELINC74 0x044 /**< C4-C7 input selectors */ +#define BCMA_DMP_OOBSELIND30 0x060 /**< D0-D3 input selectors */ +#define BCMA_DMP_OOBSELIND74 0x064 /**< D4-D7 input selectors */ +#define BCMA_DMP_OOBSELOUTA30 0x100 /**< A0-A3 output selectors */ +#define BCMA_DMP_OOBSELOUTA74 0x104 /**< A4-A7 output selectors */ +#define BCMA_DMP_OOBSELOUTB30 0x120 /**< B0-B3 output selectors */ +#define BCMA_DMP_OOBSELOUTB74 0x124 /**< B4-B7 output selectors */ +#define BCMA_DMP_OOBSELOUTC30 0x140 /**< C0-C3 output selectors */ +#define BCMA_DMP_OOBSELOUTC74 0x144 /**< C4-C7 output selectors */ +#define BCMA_DMP_OOBSELOUTD30 0x160 /**< D0-D3 output selectors */ +#define BCMA_DMP_OOBSELOUTD74 0x164 /**< D4-D7 output selectors */ #define BCMA_DMP_OOBSYNCA 0x200 #define BCMA_DMP_OOBSELOUTAEN 0x204 #define BCMA_DMP_OOBSYNCB 0x220 @@ -109,18 +132,20 @@ #define BCMA_DMP_OOBDINWIDTH 0x364 #define BCMA_DMP_OOBDOUTWIDTH 0x368 -/* The exact interpretation of these bits is unverified; these - * are our best guesses as to their use */ -#define BCMA_DMP_OOBSEL_MASK 0xFF /**< OOBSEL config mask */ -#define BCMA_DMP_OOBSEL_0_MASK BCMA_DMP_OOBSEL_MASK -#define BCMA_DMP_OOBSEL_1_MASK BCMA_DMP_OOBSEL_MASK -#define BCMA_DMP_OOBSEL_2_MASK BCMA_DMP_OOBSEL_MASK -#define BCMA_DMP_OOBSEL_3_MASK BCMA_DMP_OOBSEL_MASK -#define BCMA_DMP_OOBSEL_0_SHIFT 0 /**< first OOBSEL config */ -#define BCMA_DMP_OOBSEL_1_SHIFT 8 /**< second OOBSEL config */ -#define BCMA_DMP_OOBSEL_2_SHIFT 16 /**< third OOBSEL config */ -#define BCMA_DMP_OOBSEL_3_SHIFT 24 /**< fouth OOBSEL config */ -#define BCMA_DMP_OOBSEL_EN (1 << 7) /**< enable bit */ +#define BCMA_DMP_OOBSEL(_base, _bank, _sel) \ + (_base + (_bank * 8) + (_sel >= 4 ? 4 : 0)) + +#define BCMA_DMP_OOBSELIN(_bank, _sel) \ + BCMA_DMP_OOBSEL(BCMA_DMP_OOBSELINA30, _bank, _sel) + +#define BCMA_DMP_OOBSELOUT(_bank, _sel) \ + BCMA_DMP_OOBSEL(BCMA_DMP_OOBSELOUTA30, _bank, _sel) + +#define BCMA_DMP_OOBSYNC(_bank) (BCMA_DMP_OOBSYNCA + (_bank * 8)) +#define BCMA_DMP_OOBSELOUT_EN(_bank) (BCMA_DMP_OOBSELOUTAEN + (_bank * 8)) +#define BCMA_DMP_OOB_EXTWIDTH(_bank) (BCMA_DMP_OOBAEXTWIDTH + (_bank * 12)) +#define BCMA_DMP_OOB_INWIDTH(_bank) (BCMA_DMP_OOBAINWIDTH + (_bank * 12)) +#define BCMA_DMP_OOB_OUTWIDTH(_bank) (BCMA_DMP_OOBAOUTWIDTH + (_bank * 12)) // This was inherited from Broadcom's aidmp.h header // Is it required for any of our use-cases? @@ -192,6 +217,34 @@ #define BCMA_DMP_COMPONENTID2 0xff8 #define BCMA_DMP_COMPONENTID3 0xffc + +/* OOBSEL(IN|OUT) */ +#define BCMA_DMP_OOBSEL_MASK 0xFF /**< OOB selector mask */ +#define BCMA_DMP_OOBSEL_EN (1<<7) /**< OOB selector enable bit */ +#define BCMA_DMP_OOBSEL_SHIFT(_sel) ((_sel % BCMA_OOB_NUM_SEL) * 8) +#define BCMA_DMP_OOBSEL_BUSLINE_MASK 0x7F /**< OOB selector bus line mask */ +#define BCMA_DMP_OOBSEL_BUSLINE_SHIFT 0 + +#define BCMA_DMP_OOBSEL_0_MASK BCMA_DMP_OOBSEL_MASK +#define BCMA_DMP_OOBSEL_1_MASK BCMA_DMP_OOBSEL_MASK +#define BCMA_DMP_OOBSEL_2_MASK BCMA_DMP_OOBSEL_MASK +#define BCMA_DMP_OOBSEL_3_MASK BCMA_DMP_OOBSEL_MASK + +#define BCMA_DMP_OOBSEL_4_MASK BCMA_DMP_OOBSEL_MASK +#define BCMA_DMP_OOBSEL_5_MASK BCMA_DMP_OOBSEL_MASK +#define BCMA_DMP_OOBSEL_6_MASK BCMA_DMP_OOBSEL_MASK +#define BCMA_DMP_OOBSEL_7_MASK BCMA_DMP_OOBSEL_MASK + +#define BCMA_DMP_OOBSEL_0_SHIFT BCMA_DMP_OOBSEL_SHIFT(0) +#define BCMA_DMP_OOBSEL_1_SHIFT BCMA_DMP_OOBSEL_SHIFT(1) +#define BCMA_DMP_OOBSEL_2_SHIFT BCMA_DMP_OOBSEL_SHIFT(2) +#define BCMA_DMP_OOBSEL_3_SHIFT BCMA_DMP_OOBSEL_SHIFT(3) + +#define BCMA_DMP_OOBSEL_4_SHIFT BCMA_DMP_OOBSEL_0_SHIFT +#define BCMA_DMP_OOBSEL_5_SHIFT BCMA_DMP_OOBSEL_1_SHIFT +#define BCMA_DMP_OOBSEL_6_SHIFT BCMA_DMP_OOBSEL_2_SHIFT +#define BCMA_DMP_OOBSEL_7_SHIFT BCMA_DMP_OOBSEL_3_SHIFT + /* resetctrl */ #define BMCA_DMP_RC_RESET 1 Index: sys/dev/bhnd/bcma/bcmavar.h =================================================================== --- sys/dev/bhnd/bcma/bcmavar.h +++ sys/dev/bhnd/bcma/bcmavar.h @@ -74,6 +74,9 @@ int bcma_probe(device_t dev); int bcma_attach(device_t dev); int bcma_detach(device_t dev); +int bcma_get_intr_count(device_t dev, device_t child); +int bcma_get_core_ivec(device_t dev, device_t child, + u_int intr, uint32_t *ivec); int bcma_add_children(device_t bus); Index: sys/dev/bhnd/bhnd.h =================================================================== --- sys/dev/bhnd/bhnd.h +++ sys/dev/bhnd/bhnd.h @@ -550,6 +550,45 @@ } /** + * Return the number of interrupts to be assigned to @p child via + * BHND_BUS_ASSIGN_INTR(). + * + * @param dev A bhnd bus child device. + */ +static inline int +bhnd_get_intr_count(device_t dev) +{ + return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), dev)); +} + +/** + * Return the backplane interrupt vector corresponding to @p dev's given + * @p intr number. + * + * @param dev A bhnd bus child device. + * @param intr The interrupt number being queried. This is equivalent to the + * bus resource ID for the interrupt. + * @param[out] ivec On success, the assigned hardware interrupt vector be + * written to this pointer. + * + * On bcma(4) devices, this returns the OOB bus line assigned to the + * interrupt. + * + * On siba(4) devices, this returns the target OCP slave flag number assigned + * to the interrupt. + * + * @retval 0 success + * @retval ENXIO If @p intr exceeds the number of interrupts available + * to @p child. + */ +static inline int +bhnd_get_core_ivec(device_t dev, u_int intr, uint32_t *ivec) +{ + return (BHND_BUS_GET_CORE_IVEC(device_get_parent(dev), dev, intr, + ivec)); +} + +/** * Allocate and enable per-core PMU request handling for @p child. * * The region containing the core's PMU register block (if any) must be Index: sys/dev/bhnd/bhnd.c =================================================================== --- sys/dev/bhnd/bhnd.c +++ sys/dev/bhnd/bhnd.c @@ -925,9 +925,14 @@ retval += bus_print_child_header(dev, child); rl = BUS_GET_RESOURCE_LIST(dev, child); + + if (rl != NULL) { retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + + retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, + "%#jd"); } retval += printf(" at core %u", bhnd_get_core_index(child)); @@ -974,8 +979,10 @@ bhnd_get_device_name(child)); rl = BUS_GET_RESOURCE_LIST(dev, child); - if (rl != NULL) + if (rl != NULL) { resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#jx"); + resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%#jd"); + } printf(" at core %u (no driver attached)\n", bhnd_get_core_index(child)); Index: sys/dev/bhnd/bhnd_bus_if.m =================================================================== --- sys/dev/bhnd/bhnd_bus_if.m +++ sys/dev/bhnd/bhnd_bus_if.m @@ -1,5 +1,5 @@ #- -# Copyright (c) 2015 Landon Fuller +# Copyright (c) 2015-2016 Landon Fuller # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -96,7 +96,26 @@ { panic("bhnd_bus_read_boardinfo unimplemented"); } - + + static int + bhnd_bus_null_get_intr_count(device_t dev, device_t child) + { + panic("bhnd_bus_get_intr_count unimplemented"); + } + + static int + bhnd_bus_null_assign_intr(device_t dev, device_t child, int rid) + { + panic("bhnd_bus_assign_intr unimplemented"); + } + + static int + bhnd_bus_null_get_core_ivec(device_t dev, device_t child, u_int intr, + uint32_t *ivec) + { + panic("bhnd_bus_get_core_ivec unimplemented"); + } + static void bhnd_bus_null_child_added(device_t dev, device_t child) { @@ -349,6 +368,78 @@ struct bhnd_devinfo *dinfo; }; + +/** + * Return the number of interrupts to be assigned to @p child via + * BHND_BUS_ASSIGN_INTR(). + * + * @param dev The bhnd bus parent of @p child. + * @param child The bhnd device for which a count should be returned. + * + * @retval 0 If no interrupts should be assigned. + * @retval non-zero The count of interrupt resource IDs to be + * assigned, starting at rid 0. + */ +METHOD int get_intr_count { + device_t dev; + device_t child; +} DEFAULT bhnd_bus_null_get_intr_count; + +/** + * Assign an interrupt to @p child via bus_set_resource(). + * + * The default bus implementation of this method should assign backplane + * interrupt values to @p child. + * + * Bridge-attached bus implementations may instead override standard + * interconnect IRQ assignment, providing IRQs inherited from the parent bus. + * + * TODO: Once we can depend on INTRNG, investigate replacing this with a + * bridge-level interrupt controller. + * + * @param dev The bhnd bus parent of @p child. + * @param child The bhnd device to which an interrupt should be assigned. + * @param rid The interrupt resource ID to be assigned. + * + * @retval 0 If an interrupt was assigned. + * @retval non-zero If assigning an interrupt otherwise fails, a regular + * unix error code will be returned. + */ +METHOD int assign_intr { + device_t dev; + device_t child; + int rid; +} DEFAULT bhnd_bus_null_assign_intr; + +/** + * Return the backplane interrupt vector corresponding to @p child's given + * @p intr number. + * + * @param dev The bhnd bus parent of @p child. + * @param child The bhnd device for which the assigned interrupt vector should + * be queried. + * @param intr The interrupt number being queried. This is equivalent to the + * bus resource ID for the interrupt. + * @param[out] ivec On success, the assigned hardware interrupt vector be + * written to this pointer. + * + * On bcma(4) devices, this returns the OOB bus line assigned to the + * interrupt. + * + * On siba(4) devices, this returns the target OCP slave flag number assigned + * to the interrupt. + * + * @retval 0 success + * @retval ENXIO If @p intr exceeds the number of interrupts available + * to @p child. + */ +METHOD int get_core_ivec { + device_t dev; + device_t child; + u_int intr; + uint32_t *ivec; +} DEFAULT bhnd_bus_null_get_core_ivec; + /** * Notify a bhnd bus that a child was added. * Index: sys/dev/bhnd/bhnd_nexus.c =================================================================== --- sys/dev/bhnd/bhnd_nexus.c +++ sys/dev/bhnd/bhnd_nexus.c @@ -122,6 +122,13 @@ return (0); } +static int +bhnd_nexus_get_intr_count(device_t dev, device_t child) +{ + // TODO: arch-specific interrupt handling. + return (0); +} + static device_method_t bhnd_nexus_methods[] = { /* bhnd interface */ DEVMETHOD(bhnd_bus_activate_resource, bhnd_nexus_activate_resource), @@ -129,6 +136,8 @@ DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_nexus_is_hw_disabled), DEVMETHOD(bhnd_bus_get_attach_type, bhnd_nexus_get_attach_type), + DEVMETHOD(bhnd_bus_get_intr_count, bhnd_nexus_get_intr_count), + DEVMETHOD_END }; Index: sys/dev/bhnd/bhndb/bhnd_bhndb.c =================================================================== --- sys/dev/bhnd/bhndb/bhnd_bhndb.c +++ sys/dev/bhnd/bhndb/bhnd_bhndb.c @@ -82,6 +82,13 @@ return (bhnd_match_child(dev, &md)); } +static int +bhnd_bhndb_assign_intr(device_t dev, device_t child, int rid) +{ + /* Delegate to parent bridge */ + return (BHND_BUS_ASSIGN_INTR(device_get_parent(dev), child, rid)); +} + static bhnd_clksrc bhnd_bhndb_pwrctl_get_clksrc(device_t dev, device_t child, bhnd_clock clock) @@ -114,6 +121,7 @@ DEVMETHOD(bhnd_bus_get_attach_type, bhnd_bhndb_get_attach_type), DEVMETHOD(bhnd_bus_find_hostb_device, bhnd_bhndb_find_hostb_device), DEVMETHOD(bhnd_bus_read_board_info, bhnd_bhndb_read_board_info), + DEVMETHOD(bhnd_bus_assign_intr, bhnd_bhndb_assign_intr), DEVMETHOD(bhnd_bus_pwrctl_get_clksrc, bhnd_bhndb_pwrctl_get_clksrc), DEVMETHOD(bhnd_bus_pwrctl_gate_clock, bhnd_bhndb_pwrctl_gate_clock), Index: sys/dev/bhnd/bhndb/bhndb.c =================================================================== --- sys/dev/bhnd/bhndb/bhndb.c +++ sys/dev/bhnd/bhndb/bhndb.c @@ -986,7 +986,7 @@ sc = device_get_softc(dev); - // TODO: IRQs? + /* Non-MMIO resources (e.g. IRQs) are handled solely by our parent */ if (type != SYS_RES_MEMORY) return; @@ -1017,7 +1017,7 @@ sc = device_get_softc(dev); - // TODO: IRQs? + /* Non-MMIO resources (e.g. IRQs) are handled solely by our parent */ if (type != SYS_RES_MEMORY) return (0); @@ -1033,7 +1033,6 @@ rman_get_rid(r), r, NULL)); } - /** * Default bhndb(4) implementation of BUS_READ_IVAR(). */ @@ -1102,8 +1101,6 @@ case SYS_RES_MEMORY: return (&sc->bus_res->br_mem_rman); case SYS_RES_IRQ: - // TODO - // return &sc->irq_rman; return (NULL); default: return (NULL); @@ -1229,6 +1226,15 @@ isdefault = RMAN_IS_DEFAULT_RANGE(start, end); rle = NULL; + /* Fetch the resource manager */ + rm = bhndb_get_rman(sc, child, type); + if (rm == NULL) { + /* Delegate to our parent device's bus; the requested + * resource type isn't handled locally. */ + return (BUS_ALLOC_RESOURCE(device_get_parent(sc->parent_dev), + child, type, rid, start, end, count, flags)); + } + /* Populate defaults */ if (!passthrough && isdefault) { /* Fetch the resource list entry. */ @@ -1259,11 +1265,6 @@ /* Validate resource addresses */ if (start > end || count > ((end - start) + 1)) return (NULL); - - /* Fetch the resource manager */ - rm = bhndb_get_rman(sc, child, type); - if (rm == NULL) - return (NULL); /* Make our reservation */ rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE, @@ -1306,12 +1307,21 @@ bhndb_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { + struct bhndb_softc *sc; struct resource_list_entry *rle; bool passthrough; int error; - + + sc = device_get_softc(dev); passthrough = (device_get_parent(child) != dev); + /* Delegate to our parent device's bus if the requested resource type + * isn't handled locally. */ + if (bhndb_get_rman(sc, child, type) == NULL) { + return (BUS_RELEASE_RESOURCE(device_get_parent(sc->parent_dev), + child, type, rid, r)); + } + /* Deactivate resources */ if (rman_get_flags(r) & RF_ACTIVE) { error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r); @@ -1348,15 +1358,18 @@ sc = device_get_softc(dev); error = 0; + /* Delegate to our parent device's bus if the requested resource type + * isn't handled locally. */ + rm = bhndb_get_rman(sc, child, type); + if (rm == NULL) { + return (BUS_ADJUST_RESOURCE(device_get_parent(sc->parent_dev), + child, type, r, start, end)); + } + /* Verify basic constraints */ if (end <= start) return (EINVAL); - /* Fetch resource manager */ - rm = bhndb_get_rman(sc, child, type); - if (rm == NULL) - return (ENXIO); - if (!rman_is_region_manager(r, rm)) return (ENXIO); @@ -1563,7 +1576,7 @@ BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED); - // TODO - IRQs + /* Only MMIO resources can be mapped via register windows */ if (type != SYS_RES_MEMORY) return (ENXIO); @@ -1674,6 +1687,13 @@ { struct bhndb_softc *sc = device_get_softc(dev); + /* Delegate directly to our parent device's bus if the requested + * resource type isn't handled locally. */ + if (bhndb_get_rman(sc, child, type) == NULL) { + return (BUS_ACTIVATE_RESOURCE(device_get_parent(sc->parent_dev), + child, type, rid, r)); + } + return (bhndb_try_activate_resource(sc, child, type, rid, r, NULL)); } @@ -1691,8 +1711,13 @@ sc = device_get_softc(dev); - if ((rm = bhndb_get_rman(sc, child, type)) == NULL) - return (EINVAL); + /* Delegate directly to our parent device's bus if the requested + * resource type isn't handled locally. */ + rm = bhndb_get_rman(sc, child, type); + if (rm == NULL) { + return (BUS_DEACTIVATE_RESOURCE( + device_get_parent(sc->parent_dev), child, type, rid, r)); + } /* Mark inactive */ if ((error = rman_deactivate_resource(r))) @@ -1748,6 +1773,15 @@ sc = device_get_softc(dev); + /* Delegate directly to BUS_ACTIVATE_RESOURCE() if the requested + * resource type isn't handled locally. */ + if (bhndb_get_rman(sc, child, type) == NULL) { + error = BUS_ACTIVATE_RESOURCE(dev, child, type, rid, r->res); + if (error == 0) + r->direct = true; + return (error); + } + r_start = rman_get_start(r->res); r_size = rman_get_size(r->res); @@ -1811,7 +1845,7 @@ ("RF_ACTIVE not set on direct resource")); /* Perform deactivation */ - error = bus_deactivate_resource(child, type, rid, r->res); + error = BUS_DEACTIVATE_RESOURCE(dev, child, type, rid, r->res); if (!error) r->direct = false; @@ -2049,61 +2083,6 @@ } /** - * Default bhndb(4) implementation of BUS_SETUP_INTR(). - */ -static int -bhndb_setup_intr(device_t dev, device_t child, struct resource *r, - int flags, driver_filter_t filter, driver_intr_t handler, void *arg, - void **cookiep) -{ - // TODO - return (EOPNOTSUPP); -} - -/** - * Default bhndb(4) implementation of BUS_TEARDOWN_INTR(). - */ -static int -bhndb_teardown_intr(device_t dev, device_t child, struct resource *r, - void *cookie) -{ - // TODO - return (EOPNOTSUPP); -} - -/** - * Default bhndb(4) implementation of BUS_CONFIG_INTR(). - */ -static int -bhndb_config_intr(device_t dev, int irq, enum intr_trigger trig, - enum intr_polarity pol) -{ - // TODO - return (EOPNOTSUPP); -} - -/** - * Default bhndb(4) implementation of BUS_BIND_INTR(). - */ -static int -bhndb_bind_intr(device_t dev, device_t child, struct resource *r, int cpu) -{ - // TODO - return (EOPNOTSUPP); -} - -/** - * Default bhndb(4) implementation of BUS_DESCRIBE_INTR(). - */ -static int -bhndb_describe_intr(device_t dev, device_t child, struct resource *irq, void *cookie, - const char *descr) -{ - // TODO - return (EOPNOTSUPP); -} - -/** * Default bhndb(4) implementation of BUS_GET_DMA_TAG(). */ static bus_dma_tag_t @@ -2134,11 +2113,11 @@ DEVMETHOD(bus_activate_resource, bhndb_activate_resource), DEVMETHOD(bus_deactivate_resource, bhndb_deactivate_resource), - DEVMETHOD(bus_setup_intr, bhndb_setup_intr), - DEVMETHOD(bus_teardown_intr, bhndb_teardown_intr), - DEVMETHOD(bus_config_intr, bhndb_config_intr), - DEVMETHOD(bus_bind_intr, bhndb_bind_intr), - DEVMETHOD(bus_describe_intr, bhndb_describe_intr), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_config_intr, bus_generic_config_intr), + DEVMETHOD(bus_bind_intr, bus_generic_bind_intr), + DEVMETHOD(bus_describe_intr, bus_generic_describe_intr), DEVMETHOD(bus_get_dma_tag, bhndb_get_dma_tag), Index: sys/dev/bhnd/bhndb/bhndb_pci.c =================================================================== --- sys/dev/bhnd/bhndb/bhndb_pci.c +++ sys/dev/bhnd/bhndb/bhndb_pci.c @@ -62,6 +62,7 @@ #include "bhndb_pcivar.h" #include "bhndb_private.h" +static int bhndb_pci_init_msi(struct bhndb_pci_softc *sc); static int bhndb_pci_add_children(struct bhndb_pci_softc *sc); static int bhndb_enable_pci_clocks(struct bhndb_pci_softc *sc); @@ -78,6 +79,8 @@ static bus_addr_t bhndb_pci_sprom_addr(struct bhndb_pci_softc *sc); static bus_size_t bhndb_pci_sprom_size(struct bhndb_pci_softc *sc); +#define BHNDB_PCI_MSI_COUNT 1 + /** * Default bhndb_pci implementation of device_probe(). * @@ -103,6 +106,33 @@ return (BUS_PROBE_DEFAULT); } +/* Configure MSI interrupts */ +static int +bhndb_pci_init_msi(struct bhndb_pci_softc *sc) +{ + int error; + + /* Is MSI available? */ + if (pci_msi_count(sc->parent) < BHNDB_PCI_MSI_COUNT) + return (ENXIO); + + /* Allocate expected message count */ + sc->intr.msi_count = BHNDB_PCI_MSI_COUNT; + if ((error = pci_alloc_msi(sc->parent, &sc->intr.msi_count))) { + device_printf(sc->dev, "failed to allocate MSI interrupts: " + "%d\n", error); + return (error); + } + + if (sc->intr.msi_count < BHNDB_PCI_MSI_COUNT) + return (ENXIO); + + /* MSI uses resource IDs starting at 1 */ + sc->intr.intr_rid = 1; + + return (0); +} + static int bhndb_pci_attach(device_t dev) { @@ -114,6 +144,21 @@ sc->parent = device_get_parent(dev); sc->set_regwin = bhndb_pci_compat_setregwin; + /* Enable PCI bus mastering */ + pci_enable_busmaster(sc->parent); + + /* Set up interrupt handling */ + if (bhndb_pci_init_msi(sc) == 0) { + device_printf(dev, "Using MSI interrupts on %s\n", + device_get_nameunit(sc->parent)); + } else { + device_printf(dev, "Using INTx interrupts on %s\n", + device_get_nameunit(sc->parent)); + sc->intr.intr_rid = 0; + } + + /* Determine our bridge device class */ + sc->pci_devclass = BHND_DEVCLASS_PCI; if (pci_find_cap(sc->parent, PCIY_EXPRESS, ®) == 0) sc->pci_devclass = BHND_DEVCLASS_PCIE; else @@ -153,6 +198,9 @@ cleanup: device_delete_children(dev); bhndb_disable_pci_clocks(sc); + if (sc->intr.msi_count > 0) + pci_release_msi(dev); + pci_disable_busmaster(sc->parent); return (error); @@ -178,6 +226,10 @@ if ((error = bhndb_disable_pci_clocks(sc))) return (error); + /* Release MSI interrupts */ + if (sc->intr.msi_count > 0) + pci_release_msi(dev); + /* Disable PCI bus mastering */ pci_disable_busmaster(sc->parent); @@ -679,6 +731,29 @@ return (bhndb_enable_pci_clocks(sc)); } +static int +bhndb_pci_assign_intr(device_t dev, device_t child, int rid) +{ + struct bhndb_pci_softc *sc; + rman_res_t start, count; + int error; + + sc = device_get_softc(dev); + + /* Is the rid valid? */ + if (rid >= bhnd_get_intr_count(child)) + return (EINVAL); + + /* Fetch our common PCI interrupt's start/count. */ + error = bus_get_resource(sc->parent, SYS_RES_IRQ, sc->intr.intr_rid, + &start, &count); + if (error) + return (error); + + /* Add to child's resource list */ + return (bus_set_resource(child, SYS_RES_IRQ, rid, start, count)); +} + static device_method_t bhndb_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bhndb_pci_probe), @@ -688,6 +763,8 @@ DEVMETHOD(device_detach, bhndb_pci_detach), /* BHND interface */ + DEVMETHOD(bhnd_bus_assign_intr, bhndb_pci_assign_intr), + DEVMETHOD(bhnd_bus_pwrctl_get_clksrc, bhndb_pci_pwrctl_get_clksrc), DEVMETHOD(bhnd_bus_pwrctl_gate_clock, bhndb_pci_pwrctl_gate_clock), DEVMETHOD(bhnd_bus_pwrctl_ungate_clock, bhndb_pci_pwrctl_ungate_clock), Index: sys/dev/bhnd/bhndb/bhndb_pcivar.h =================================================================== --- sys/dev/bhnd/bhndb/bhndb_pcivar.h +++ sys/dev/bhnd/bhndb/bhndb_pcivar.h @@ -48,11 +48,19 @@ typedef int (*bhndb_pci_set_regwin_t)(struct bhndb_pci_softc *sc, const struct bhndb_regwin *rw, bhnd_addr_t addr); +/* bhndb_pci interrupt state */ +struct bhndb_pci_intr { + int msi_count; /**< MSI count, or 0 */ + int intr_rid; /**< interrupt resource ID.*/ +}; + struct bhndb_pci_softc { struct bhndb_softc bhndb; /**< parent softc */ device_t dev; /**< bridge device */ device_t parent; /**< parent PCI device */ bhnd_devclass_t pci_devclass; /**< PCI core's devclass */ + struct bhndb_pci_intr intr; /**< PCI interrupt config */ + bhndb_pci_set_regwin_t set_regwin; /**< regwin handler */ }; Index: sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c =================================================================== --- sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c +++ sys/dev/bhnd/cores/pmu/bhnd_pmu_subr.c @@ -3395,14 +3395,14 @@ if (enable) { oobsel |= BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, - BCMA_DMP_OOBSEL_1); + BCMA_DMP_OOBSEL_5); oobsel |= BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, - BCMA_DMP_OOBSEL_2); + BCMA_DMP_OOBSEL_6); } else { oobsel &= ~BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, - BCMA_DMP_OOBSEL_1); + BCMA_DMP_OOBSEL_5); oobsel &= ~BHND_PMU_SET_BITS(BCMA_DMP_OOBSEL_EN, - BCMA_DMP_OOBSEL_2); + BCMA_DMP_OOBSEL_6); } bhnd_write_config(d11core, BCMA_DMP_OOBSELOUTB74, oobsel, 4); Index: sys/dev/bhnd/siba/siba.c =================================================================== --- sys/dev/bhnd/siba/siba.c +++ sys/dev/bhnd/siba/siba.c @@ -373,6 +373,60 @@ return (0); } +/** + * Default siba(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT(). + * + * This implementation consults @p child's configuration block mapping, + * returning SIBA_CORE_NUM_INTR if a valid CFG0 block is mapped. + */ +int +siba_get_intr_count(device_t dev, device_t child) +{ + struct siba_devinfo *dinfo; + + /* delegate non-bus-attached devices to our parent */ + if (device_get_parent(child) != dev) + return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), child)); + + dinfo = device_get_ivars(child); + + /* We can get/set interrupt sbflags on any core with a valid cfg0 + * block; whether the core actually makes use of it is another matter + * entirely */ + if (dinfo->cfg[0] == NULL) + return (0); + + return (SIBA_CORE_NUM_INTR); +} + +/** + * Default siba(4) bus driver implementation of BHND_BUS_GET_CORE_IVEC(). + * + * This implementation consults @p child's CFG0 register block, + * returning the interrupt flag assigned to @p child. + */ +int +siba_get_core_ivec(device_t dev, device_t child, u_int intr, uint32_t *ivec) +{ + struct siba_devinfo *dinfo; + uint32_t tpsflag; + + /* delegate non-bus-attached devices to our parent */ + if (device_get_parent(child) != dev) + return (BHND_BUS_GET_CORE_IVEC(device_get_parent(dev), child, + intr, ivec)); + + /* Must be a valid interrupt ID */ + if (intr >= siba_get_intr_count(dev, child)) + return (ENXIO); + + /* Fetch sbflag number */ + dinfo = device_get_ivars(child); + tpsflag = bhnd_bus_read_4(dinfo->cfg[0], SIBA_CFG0_TPSFLAG); + *ivec = SIBA_REG_GET(tpsflag, TPS_NUM0); + + return (0); +} /** * Register all address space mappings for @p di. @@ -538,6 +592,7 @@ device_t child; uint32_t idhigh, idlow; rman_res_t r_count, r_end, r_start; + int nintr; /* Map the core's register block */ rid = 0; @@ -594,6 +649,16 @@ if ((error = siba_map_cfg_resources(dev, dinfo))) goto cleanup; + /* Assign interrupts */ + nintr = bhnd_get_intr_count(child); + for (int rid = 0; rid < nintr; rid++) { + error = BHND_BUS_ASSIGN_INTR(dev, child, rid); + if (error) { + device_printf(dev, "failed to assign interrupt " + "%d to core %u: %d\n", rid, i, error); + } + } + /* If pins are floating or the hardware is otherwise * unpopulated, the device shouldn't be used. */ if (bhnd_is_hw_disabled(child)) @@ -639,6 +704,8 @@ DEVMETHOD(bhnd_bus_get_port_rid, siba_get_port_rid), DEVMETHOD(bhnd_bus_decode_port_rid, siba_decode_port_rid), DEVMETHOD(bhnd_bus_get_region_addr, siba_get_region_addr), + DEVMETHOD(bhnd_bus_get_intr_count, siba_get_intr_count), + DEVMETHOD(bhnd_bus_get_core_ivec, siba_get_core_ivec), DEVMETHOD_END }; Index: sys/dev/bhnd/siba/siba_bhndb.c =================================================================== --- sys/dev/bhnd/siba/siba_bhndb.c +++ sys/dev/bhnd/siba/siba_bhndb.c @@ -271,7 +271,6 @@ return (0); } - static device_method_t siba_bhndb_methods[] = { /* Device interface */ DEVMETHOD(device_probe, siba_bhndb_probe), Index: sys/dev/bhnd/siba/sibareg.h =================================================================== --- sys/dev/bhnd/siba/sibareg.h +++ sys/dev/bhnd/siba/sibareg.h @@ -48,6 +48,7 @@ #define SIBA_ENUM_ADDR BHND_DEFAULT_CHIPC_ADDR /**< enumeration space */ #define SIBA_ENUM_SIZE 0x00100000 /**< size of the enumeration space */ #define SIBA_CORE_SIZE BHND_DEFAULT_CORE_SIZE /**< per-core register block size */ +#define SIBA_CORE_NUM_INTR 1 /**< number of per-core interrupt lines */ #define SIBA_MAX_CORES \ (SIBA_ENUM_SIZE/SIBA_CORE_SIZE) /**< Maximum number of cores */ @@ -119,6 +120,7 @@ /* sbtpsflag */ #define SIBA_TPS_NUM0_MASK 0x3f /* interrupt sbFlag # generated by this core */ +#define SIBA_TPS_NUM0_SHIFT 0 #define SIBA_TPS_F0EN0 0x40 /* interrupt is always sent on the backplane */ /* sbtmerrlog */ Index: sys/dev/bhnd/siba/sibavar.h =================================================================== --- sys/dev/bhnd/siba/sibavar.h +++ sys/dev/bhnd/siba/sibavar.h @@ -54,6 +54,9 @@ int siba_detach(device_t dev); int siba_resume(device_t dev); int siba_suspend(device_t dev); +int siba_get_intr_count(device_t dev, device_t child); +int siba_get_core_ivec(device_t dev, device_t child, + u_int intr, uint32_t *ivec); uint16_t siba_get_bhnd_mfgid(uint16_t ocp_vendor); Index: sys/dev/bwn/bwn_mac.c =================================================================== --- sys/dev/bwn/bwn_mac.c +++ sys/dev/bwn/bwn_mac.c @@ -47,16 +47,12 @@ #include "bhnd_nvram_map.h" -static const struct resource_spec bwn_rspec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE }, - { -1, -1, 0 } -}; - -#define RSPEC_LEN (sizeof(bwn_rspec)/sizeof(bwn_rspec[0])) - struct bwn_softc { - struct resource_spec rspec[RSPEC_LEN]; - struct bhnd_resource *res[RSPEC_LEN-1]; + int mem_rid; + struct bhnd_resource *mem_res; + + int intr_rid; + struct resource *intr_res; }; static const struct bwn_device { @@ -89,28 +85,50 @@ bwn_attach(device_t dev) { struct bwn_softc *sc; - struct bhnd_resource *r; int error; sc = device_get_softc(dev); - memcpy(sc->rspec, bwn_rspec, sizeof(bwn_rspec)); - if ((error = bhnd_alloc_resources(dev, sc->rspec, sc->res))) - return (error); + /* Allocate device resources */ + sc->mem_rid = 0; + sc->mem_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->mem_rid, RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "failed to allocate device registers\n"); + error = ENXIO; + goto cleanup; + } - // XXX TODO - r = sc->res[0]; - device_printf(dev, "got rid=%d res=%p\n", sc->rspec[0].rid, r); + sc->intr_rid = 0; + sc->intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->intr_rid, + RF_ACTIVE|RF_SHAREABLE); + if (sc->intr_res == NULL) { + device_printf(dev, "failed to allocate device interrupt\n"); + error = ENXIO; + goto cleanup; + } + // TODO uint8_t macaddr[6]; error = bhnd_nvram_getvar_array(dev, BHND_NVAR_MACADDR, macaddr, sizeof(macaddr), BHND_NVRAM_TYPE_UINT8); if (error) - return (error); + device_printf(dev, "error fetching macaddr: %d\n", error); + else + device_printf(dev, "got macaddr %6D\n", macaddr, ":"); - device_printf(dev, "got macaddr %6D\n", macaddr, ":"); - return (0); + +cleanup: + if (sc->mem_res != NULL) + bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, + sc->mem_res); + + if (sc->intr_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, + sc->intr_res); + + return (error); } static int @@ -119,7 +137,9 @@ struct bwn_softc *sc; sc = device_get_softc(dev); - bhnd_release_resources(dev, sc->rspec, sc->res); + + bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res); + bus_release_resource(dev, SYS_RES_IRQ, sc->intr_rid, sc->intr_res); return (0); }