1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-17 10:26:15 +00:00

Open Firmware interrupt specifiers can consist of arbitrary-length byte

strings and include arbitrary information (IRQ line/domain/sense). When the
ofw_bus_map_intr() API was introduced, it assumed that, as on most systems,
these were either 1 cell, containing an interrupt line, or 2, containing
a line number plus a sense code. It turns out a non-negligible number of
ARM systems use 3 (or even 4!) cells for interrupts, so make this more
general.
This commit is contained in:
Nathan Whitehorn 2014-02-01 17:17:35 +00:00
parent f613b2d3b1
commit bbc6da03ef
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=261351
11 changed files with 54 additions and 88 deletions

View File

@ -98,7 +98,7 @@ static int nexus_teardown_intr(device_t, device_t, struct resource *, void *);
#ifdef FDT #ifdef FDT
static int nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, static int nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent,
int irq); int icells, pcell_t *intr);
#endif #endif
static device_method_t nexus_methods[] = { static device_method_t nexus_methods[] = {
@ -339,15 +339,16 @@ nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
#ifdef FDT #ifdef FDT
static int static int
nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int irq) nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells,
pcell_t *intr)
{ {
pcell_t intr[2];
fdt_pic_decode_t intr_decode; fdt_pic_decode_t intr_decode;
phandle_t intr_offset; phandle_t intr_offset;
int i, rv, interrupt, trig, pol; int i, rv, interrupt, trig, pol;
intr_offset = OF_xref_phandle(iparent); intr_offset = OF_xref_phandle(iparent);
intr[0] = cpu_to_fdt32(irq); for (i = 0; i < icells; i++)
intr[i] = cpu_to_fdt32(intr[i]);
for (i = 0; fdt_pic_table[i] != NULL; i++) { for (i = 0; fdt_pic_table[i] != NULL; i++) {
intr_decode = fdt_pic_table[i]; intr_decode = fdt_pic_table[i];
@ -355,13 +356,13 @@ nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int irq)
if (rv == 0) { if (rv == 0) {
/* This was recognized as our PIC and decoded. */ /* This was recognized as our PIC and decoded. */
interrupt = FDT_MAP_IRQ(intr_parent, interrupt); interrupt = FDT_MAP_IRQ(intr_parent, intr[0]);
return (interrupt); return (interrupt);
} }
} }
/* Not in table, so guess */ /* Not in table, so guess */
interrupt = FDT_MAP_IRQ(intr_parent, fdt32_to_cpu(*intr)); interrupt = FDT_MAP_IRQ(intr_parent, intr[0]);
return (interrupt); return (interrupt);
} }

View File

@ -1050,7 +1050,8 @@ mv_pcib_route_interrupt(device_t bus, device_t dev, int pin)
{ {
struct mv_pcib_softc *sc; struct mv_pcib_softc *sc;
struct ofw_pci_register reg; struct ofw_pci_register reg;
uint32_t pintr, mintr; uint32_t pintr, mintr[4];
int icells;
phandle_t iparent; phandle_t iparent;
sc = device_get_softc(bus); sc = device_get_softc(bus);
@ -1062,10 +1063,11 @@ mv_pcib_route_interrupt(device_t bus, device_t dev, int pin)
(pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) | (pci_get_slot(dev) << OFW_PCI_PHYS_HI_DEVICESHIFT) |
(pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT); (pci_get_function(dev) << OFW_PCI_PHYS_HI_FUNCTIONSHIFT);
if (ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo, &reg, icells = ofw_bus_lookup_imap(ofw_bus_get_node(dev), &sc->sc_pci_iinfo,
sizeof(reg), &pintr, sizeof(pintr), &mintr, sizeof(mintr), &reg, sizeof(reg), &pintr, sizeof(pintr), mintr, sizeof(mintr),
&iparent)) &iparent);
return (ofw_bus_map_intr(dev, iparent, mintr)); if (icells > 0)
return (ofw_bus_map_intr(dev, iparent, icells, mintr));
/* Maybe it's a real interrupt, not an intpin */ /* Maybe it's a real interrupt, not an intpin */
if (pin > 4) if (pin > 4)

View File

@ -501,11 +501,9 @@ fdt_intr_to_rl(device_t dev, phandle_t node, struct resource_list *rl,
icells = 1; icells = 1;
} }
for (i = 0, k = 0; i < nintr; i += icells, k++) { for (i = 0, k = 0; i < nintr; i += icells, k++) {
intr[i] = ofw_bus_map_intr(dev, iparent, intr[i]); intr[i] = ofw_bus_map_intr(dev, iparent, icells, intr);
resource_list_add(rl, SYS_RES_IRQ, k, intr[i], intr[i], resource_list_add(rl, SYS_RES_IRQ, k, intr[i], intr[i],
1); 1);
if (icells > 1)
ofw_bus_config_intr(dev, intr[i], intr[i+1]);
} }
free(intr, M_OFWPROP); free(intr, M_OFWPROP);
} }

View File

@ -71,15 +71,9 @@ ofw_bus_get_type(device_t dev)
} }
static __inline int static __inline int
ofw_bus_map_intr(device_t dev, phandle_t iparent, int irq) ofw_bus_map_intr(device_t dev, phandle_t iparent, int icells, pcell_t *intr)
{ {
return (OFW_BUS_MAP_INTR(dev, dev, iparent, irq)); return (OFW_BUS_MAP_INTR(dev, dev, iparent, icells, intr));
}
static __inline int
ofw_bus_config_intr(device_t dev, int irq, int sense)
{
return (OFW_BUS_CONFIG_INTR(dev, dev, irq, sense));
} }
#endif /* !_DEV_OFW_OFW_BUS_H_ */ #endif /* !_DEV_OFW_OFW_BUS_H_ */

View File

@ -57,7 +57,6 @@ CODE {
static ofw_bus_get_node_t ofw_bus_default_get_node; static ofw_bus_get_node_t ofw_bus_default_get_node;
static ofw_bus_get_type_t ofw_bus_default_get_type; static ofw_bus_get_type_t ofw_bus_default_get_type;
static ofw_bus_map_intr_t ofw_bus_default_map_intr; static ofw_bus_map_intr_t ofw_bus_default_map_intr;
static ofw_bus_config_intr_t ofw_bus_default_config_intr;
static const struct ofw_bus_devinfo * static const struct ofw_bus_devinfo *
ofw_bus_default_get_devinfo(device_t bus, device_t dev) ofw_bus_default_get_devinfo(device_t bus, device_t dev)
@ -103,27 +102,15 @@ CODE {
int int
ofw_bus_default_map_intr(device_t bus, device_t dev, phandle_t iparent, ofw_bus_default_map_intr(device_t bus, device_t dev, phandle_t iparent,
int irq) int icells, pcell_t *interrupt)
{ {
/* Propagate up the bus hierarchy until someone handles it. */ /* Propagate up the bus hierarchy until someone handles it. */
if (device_get_parent(bus) != NULL) if (device_get_parent(bus) != NULL)
return OFW_BUS_MAP_INTR(device_get_parent(bus), dev, return OFW_BUS_MAP_INTR(device_get_parent(bus), dev,
iparent, irq); iparent, icells, interrupt);
/* If that fails, then assume a one-domain system */ /* If that fails, then assume a one-domain system */
return (irq); return (interrupt[0]);
}
int
ofw_bus_default_config_intr(device_t bus, device_t dev, int irq,
int sense)
{
/* Propagate up the bus hierarchy until someone handles it. */
if (device_get_parent(bus) != NULL)
return OFW_BUS_CONFIG_INTR(device_get_parent(bus), dev,
irq, sense);
return (ENXIO);
} }
}; };
@ -172,20 +159,14 @@ METHOD const char * get_type {
} DEFAULT ofw_bus_default_get_type; } DEFAULT ofw_bus_default_get_type;
# Map an (interrupt parent, IRQ) pair to a unique system-wide interrupt number. # Map an (interrupt parent, IRQ) pair to a unique system-wide interrupt number.
# If the interrupt encoding includes a sense field, the interrupt sense will
# also be configured.
METHOD int map_intr { METHOD int map_intr {
device_t bus; device_t bus;
device_t dev; device_t dev;
phandle_t iparent; phandle_t iparent;
int irq; int icells;
pcell_t *interrupt;
} DEFAULT ofw_bus_default_map_intr; } DEFAULT ofw_bus_default_map_intr;
# Configure an interrupt using the device-tree encoded sense key (the second
# value in the interrupts property if interrupt-cells is 2). IRQ should be
# encoded as from ofw_bus_map_intr().
METHOD int config_intr {
device_t bus;
device_t dev;
int irq;
int sense;
} DEFAULT ofw_bus_default_config_intr;

View File

@ -467,11 +467,10 @@ nexus_setup_dinfo(device_t dev, phandle_t node)
OF_searchencprop(OF_xref_phandle(iparent), "#interrupt-cells", OF_searchencprop(OF_xref_phandle(iparent), "#interrupt-cells",
&icells, sizeof(icells)); &icells, sizeof(icells));
for (i = 0; i < nintr; i+= icells) { for (i = 0; i < nintr; i+= icells) {
intr[i] = ofw_bus_map_intr(dev, iparent, intr[i]); intr[i] = ofw_bus_map_intr(dev, iparent, icells,
&intr[i]);
resource_list_add(&ndi->ndi_rl, SYS_RES_IRQ, i, intr[i], resource_list_add(&ndi->ndi_rl, SYS_RES_IRQ, i, intr[i],
intr[i], 1); intr[i], 1);
if (icells > 1)
ofw_bus_config_intr(dev, intr[i], intr[i+1]);
} }
free(intr, M_OFWPROP); free(intr, M_OFWPROP);
} }

View File

@ -273,9 +273,7 @@ ofw_pci_route_interrupt(device_t bus, device_t dev, int pin)
&sc->sc_pci_iinfo, &reg, sizeof(reg), &pintr, sizeof(pintr), &sc->sc_pci_iinfo, &reg, sizeof(reg), &pintr, sizeof(pintr),
mintr, sizeof(mintr), &iparent); mintr, sizeof(mintr), &iparent);
if (intrcells) { if (intrcells) {
pintr = ofw_bus_map_intr(dev, iparent, mintr[0]); pintr = ofw_bus_map_intr(dev, iparent, intrcells, mintr);
if (intrcells == 2)
ofw_bus_config_intr(dev, pintr, mintr[1]);
return (pintr); return (pintr);
} }

View File

@ -158,10 +158,8 @@ ofw_pcib_pci_route_interrupt(device_t bridge, device_t dev, int intpin)
* it again on higher levels - that causes problems * it again on higher levels - that causes problems
* in some cases, and never seems to be required. * in some cases, and never seems to be required.
*/ */
mintr[0] = ofw_bus_map_intr(dev, iparent, mintr[0]); mintr[0] = ofw_bus_map_intr(dev, iparent, intrcells,
if (intrcells == 2) mintr);
ofw_bus_config_intr(dev, mintr[0], mintr[1]);
return (mintr[0]); return (mintr[0]);
} }
} else if (intpin >= 1 && intpin <= 4) { } else if (intpin >= 1 && intpin <= 4) {

View File

@ -216,13 +216,9 @@ ofw_pcibus_enum_devtree(device_t dev, u_int domain, u_int busno)
"#interrupt-cells", &icells, "#interrupt-cells", &icells,
sizeof(icells)); sizeof(icells));
intr[0] = ofw_bus_map_intr(dev, iparent, intr[0] = ofw_bus_map_intr(dev, iparent,
intr[0]); icells, intr);
} }
if (iparent != 0 && icells > 1)
ofw_bus_config_intr(dev, intr[0],
intr[1]);
resource_list_add(&dinfo->opd_dinfo.resources, resource_list_add(&dinfo->opd_dinfo.resources,
SYS_RES_IRQ, 0, intr[0], intr[0], 1); SYS_RES_IRQ, 0, intr[0], intr[0], 1);
} }
@ -309,18 +305,18 @@ ofw_pcibus_child_pnpinfo_str_method(device_t cbdev, device_t child, char *buf,
static int static int
ofw_pcibus_assign_interrupt(device_t dev, device_t child) ofw_pcibus_assign_interrupt(device_t dev, device_t child)
{ {
ofw_pci_intr_t intr; ofw_pci_intr_t intr[2];
phandle_t node, iparent; phandle_t node, iparent;
int isz; int isz, icells;
node = ofw_bus_get_node(child); node = ofw_bus_get_node(child);
if (node == -1) { if (node == -1) {
/* Non-firmware enumerated child, use standard routing */ /* Non-firmware enumerated child, use standard routing */
intr = pci_get_intpin(child); intr[0] = pci_get_intpin(child);
return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child,
intr)); intr[0]));
} }
/* /*
@ -331,24 +327,28 @@ ofw_pcibus_assign_interrupt(device_t dev, device_t child)
iparent = -1; iparent = -1;
if (OF_getprop(node, "interrupt-parent", &iparent, sizeof(iparent)) < 0) if (OF_getprop(node, "interrupt-parent", &iparent, sizeof(iparent)) < 0)
iparent = -1; iparent = -1;
icells = 1;
if (iparent != -1)
OF_getprop(OF_xref_phandle(iparent), "#interrupt-cells",
&icells, sizeof(icells));
/* /*
* Any AAPL,interrupts property gets priority and is * Any AAPL,interrupts property gets priority and is
* fully specified (i.e. does not need routing) * fully specified (i.e. does not need routing)
*/ */
isz = OF_getprop(node, "AAPL,interrupts", &intr, sizeof(intr)); isz = OF_getprop(node, "AAPL,interrupts", intr, sizeof(intr));
if (isz == sizeof(intr)) if (isz == sizeof(intr[0])*icells)
return ((iparent == -1) ? intr : ofw_bus_map_intr(dev, iparent, return ((iparent == -1) ? intr[0] : ofw_bus_map_intr(dev,
intr)); iparent, icells, intr));
isz = OF_getprop(node, "interrupts", &intr, sizeof(intr)); isz = OF_getprop(node, "interrupts", intr, sizeof(intr));
if (isz == sizeof(intr)) { if (isz == sizeof(intr[0])*icells) {
if (iparent != -1) if (iparent != -1)
intr = ofw_bus_map_intr(dev, iparent, intr); intr[0] = ofw_bus_map_intr(dev, iparent, icells, intr);
} else { } else {
/* No property: our best guess is the intpin. */ /* No property: our best guess is the intpin. */
intr = pci_get_intpin(child); intr[0] = pci_get_intpin(child);
} }
/* /*
@ -361,7 +361,7 @@ ofw_pcibus_assign_interrupt(device_t dev, device_t child)
* will always use the route_interrupt method, and treat exceptions * will always use the route_interrupt method, and treat exceptions
* on the level they become apparent. * on the level they become apparent.
*/ */
return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr)); return (PCIB_ROUTE_INTERRUPT(device_get_parent(dev), child, intr[0]));
} }
static const struct ofw_bus_devinfo * static const struct ofw_bus_devinfo *

View File

@ -75,7 +75,6 @@ static bus_bind_intr_t nexus_bind_intr;
#endif #endif
static bus_config_intr_t nexus_config_intr; static bus_config_intr_t nexus_config_intr;
static ofw_bus_map_intr_t nexus_ofw_map_intr; static ofw_bus_map_intr_t nexus_ofw_map_intr;
static ofw_bus_config_intr_t nexus_ofw_config_intr;
static device_method_t nexus_methods[] = { static device_method_t nexus_methods[] = {
/* Bus interface */ /* Bus interface */
@ -90,7 +89,6 @@ static device_method_t nexus_methods[] = {
/* ofw_bus interface */ /* ofw_bus interface */
DEVMETHOD(ofw_bus_map_intr, nexus_ofw_map_intr), DEVMETHOD(ofw_bus_map_intr, nexus_ofw_map_intr),
DEVMETHOD(ofw_bus_config_intr, nexus_ofw_config_intr),
DEVMETHOD_END DEVMETHOD_END
}; };
@ -157,18 +155,15 @@ nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
} }
static int static int
nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int irq) nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells,
pcell_t *irq)
{ {
return (MAP_IRQ(iparent, irq)); u_int intr = MAP_IRQ(iparent, irq[0]);
if (icells > 1)
powerpc_fw_config_intr(irq[0], irq[1]);
return (intr);
} }
static int
nexus_ofw_config_intr(device_t dev, device_t child, int irq, int sense)
{
return (powerpc_fw_config_intr(irq, sense));
}
static int static int
nexus_activate_resource(device_t bus __unused, device_t child __unused, nexus_activate_resource(device_t bus __unused, device_t child __unused,
int type, int rid __unused, struct resource *r) int type, int rid __unused, struct resource *r)

View File

@ -157,7 +157,7 @@ vdevice_attach(device_t dev)
u_int irq = intr[i]; u_int irq = intr[i];
if (iparent != -1) if (iparent != -1)
irq = ofw_bus_map_intr(dev, iparent, irq = ofw_bus_map_intr(dev, iparent,
intr[i]); icells, &intr[i]);
resource_list_add(&dinfo->mdi_resources, resource_list_add(&dinfo->mdi_resources,
SYS_RES_IRQ, i, irq, irq, i); SYS_RES_IRQ, i, irq, irq, i);