bhnd(4): Implement backplane interrupt handling.

This adds bhnd(4) bus-level support for querying backplane interrupt vector
routing, and delegating machine/bridge-specific interrupt handling to the
concrete bhnd(4) driver implementation.

On bhndb(4) bridged PCI devices, we provide the PCI/MSI interrupt directly
to attached cores.

On MIPS devices, we report a backplane interrupt count of 0, effectively
disabling the bus-level interrupt assignment. This allows mips/broadcom
to temporarily continue using hard-coded MIPS IRQs until bhnd_mips PIC
support is implemented.

Reviewed by:	mizhka
Approved by:	adrian (mentor, implicit)
This commit is contained in:
landonf 2016-09-05 22:11:46 +00:00
parent bc0784fd1f
commit 47d8c9cde3
17 changed files with 584 additions and 137 deletions

View File

@ -41,8 +41,11 @@ __FBSDID("$FreeBSD$");
#include "bcmavar.h"
#include "bcma_dmp.h"
#include "bcma_eromreg.h"
#include "bcma_eromvar.h"
#include <dev/bhnd/bhnd_core.h>
/* RID used when allocating EROM table */
@ -434,6 +437,70 @@ bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
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 @@ bcma_add_children(device_t bus)
/* 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 @@ bcma_add_children(device_t bus)
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 @@ static device_method_t bcma_methods[] = {
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
};

View File

@ -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

View File

@ -74,6 +74,9 @@ struct bcma_sport;
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);

View File

@ -925,9 +925,14 @@ bhnd_generic_print_child(device_t dev, device_t child)
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_generic_probe_nomatch(device_t dev, device_t child)
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));

View File

@ -549,6 +549,45 @@ bhnd_read_board_info(device_t dev, struct bhnd_board_info *info)
return (BHND_BUS_READ_BOARD_INFO(device_get_parent(dev), dev, info));
}
/**
* 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.
*

View File

@ -1,5 +1,5 @@
#-
# Copyright (c) 2015 Landon Fuller <landon@landonf.org>
# Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@ -96,7 +96,26 @@ CODE {
{
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 @@ METHOD void free_devinfo {
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.
*

View File

@ -122,6 +122,13 @@ bhnd_nexus_deactivate_resource(device_t dev, device_t child,
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 @@ static device_method_t bhnd_nexus_methods[] = {
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
};

View File

@ -93,6 +93,13 @@ bhnd_bhndb_find_hostb_device(device_t dev)
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)
@ -126,6 +133,7 @@ static device_method_t bhnd_bhndb_methods[] = {
DEVMETHOD(bhnd_bus_is_hw_disabled, bhnd_bhndb_is_hw_disabled),
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),

View File

@ -993,7 +993,7 @@ bhndb_suspend_resource(device_t dev, device_t child, int type,
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;
@ -1024,7 +1024,7 @@ bhndb_resume_resource(device_t dev, device_t child, int type,
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);
@ -1040,7 +1040,6 @@ bhndb_resume_resource(device_t dev, device_t child, int type,
rman_get_rid(r), r, NULL));
}
/**
* Default bhndb(4) implementation of BUS_READ_IVAR().
*/
@ -1109,8 +1108,6 @@ bhndb_get_rman(struct bhndb_softc *sc, device_t child, int type)
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);
@ -1233,6 +1230,15 @@ bhndb_alloc_resource(device_t dev, device_t child, int type,
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. */
@ -1263,11 +1269,6 @@ bhndb_alloc_resource(device_t dev, device_t child, int type,
/* 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,
@ -1310,12 +1311,21 @@ static int
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);
@ -1352,15 +1362,18 @@ bhndb_adjust_resource(device_t dev, device_t child, int type,
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);
@ -1567,7 +1580,7 @@ bhndb_try_activate_resource(struct bhndb_softc *sc, device_t child, int type,
BHNDB_LOCK_ASSERT(sc, MA_NOTOWNED);
// TODO - IRQs
/* Only MMIO resources can be mapped via register windows */
if (type != SYS_RES_MEMORY)
return (ENXIO);
@ -1678,6 +1691,13 @@ bhndb_activate_resource(device_t dev, device_t child, int type, int rid,
{
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));
}
@ -1695,8 +1715,13 @@ bhndb_deactivate_resource(device_t dev, device_t child, int type,
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)))
@ -1752,6 +1777,15 @@ bhndb_activate_bhnd_resource(device_t dev, device_t child,
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);
@ -1815,7 +1849,7 @@ bhndb_deactivate_bhnd_resource(device_t dev, device_t child,
("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;
@ -2052,61 +2086,6 @@ bhndb_bus_barrier(device_t dev, device_t child, struct bhnd_resource *r,
BHNDB_IO_COMMON_TEARDOWN();
}
/**
* 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().
*/
@ -2138,11 +2117,11 @@ static device_method_t bhndb_methods[] = {
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),

View File

@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$");
#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 void bhndb_init_sromless_pci_config(
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 @@ bhndb_pci_probe(device_t dev)
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 @@ bhndb_pci_attach(device_t dev)
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, &reg) == 0)
sc->pci_devclass = BHND_DEVCLASS_PCIE;
else
@ -153,6 +198,9 @@ bhndb_pci_attach(device_t dev)
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 @@ bhndb_pci_detach(device_t dev)
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 @@ bhndb_pci_pwrctl_ungate_clock(device_t dev, device_t child,
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 @@ static device_method_t bhndb_pci_methods[] = {
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),

View File

@ -48,11 +48,19 @@ struct bhndb_pci_softc;
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 */
};

View File

@ -3395,14 +3395,14 @@ bhnd_pmu_radio_enable(struct bhnd_pmu_softc *sc, device_t d11core, bool enable)
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);

View File

@ -373,6 +373,60 @@ siba_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
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 @@ siba_add_children(device_t dev)
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 @@ siba_add_children(device_t dev)
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 @@ static device_method_t siba_methods[] = {
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
};

View File

@ -273,7 +273,6 @@ siba_bhndb_wars_hwup(struct siba_softc *sc)
return (0);
}
static device_method_t siba_bhndb_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, siba_bhndb_probe),

View File

@ -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 */

View File

@ -54,6 +54,9 @@ int siba_attach(device_t dev);
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);

View File

@ -47,16 +47,12 @@ __FBSDID("$FreeBSD$");
#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 @@ static int
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 @@ bwn_detach(device_t dev)
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);
}