Use a more proper fix for enabling HT MSI mapping windows on Host-PCI

bridges.  Rather than blindly enabling the windows on all of them, only
enable the window when an MSI interrupt is enabled for a device behind
the bridge, similar to what already happens for HT PCI-PCI bridges.

To implement this, each x86 Host-PCI bridge driver has to be able to
locate it's actual backing device on bus 0.  For ACPI, use the _ADR
method to find the slot and function of the device.  For the non-ACPI
case, the legacy(4) driver already scans bus 0 looking for Host-PCI
bridge devices.  Now it saves the slot and function of each bridge that
it finds as ivars that the Host-PCI bridge driver can then use in its
pcib_map_msi() method.

This fixes machines where non-MSI interrupts were broken by the previous
round of HT MSI changes.

Tested by:	bapt
MFC after:	1 week
This commit is contained in:
John Baldwin 2012-03-29 19:03:22 +00:00
parent 46092aeec0
commit 0d95597ca9
8 changed files with 104 additions and 35 deletions

View file

@ -53,7 +53,9 @@ __FBSDID("$FreeBSD$");
static MALLOC_DEFINE(M_LEGACYDEV, "legacydrv", "legacy system device");
struct legacy_device {
int lg_pcibus;
int lg_pcibus;
int lg_pcislot;
int lg_pcifunc;
};
#define DEVTOAT(dev) ((struct legacy_device *)device_get_ivars(dev))
@ -161,6 +163,8 @@ legacy_add_child(device_t bus, u_int order, const char *name, int unit)
if (atdev == NULL)
return(NULL);
atdev->lg_pcibus = -1;
atdev->lg_pcislot = -1;
atdev->lg_pcifunc = -1;
child = device_add_child_ordered(bus, order, name, unit);
if (child == NULL)
@ -184,6 +188,12 @@ legacy_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
case LEGACY_IVAR_PCIBUS:
*result = atdev->lg_pcibus;
break;
case LEGACY_IVAR_PCISLOT:
*result = atdev->lg_pcislot;
break;
case LEGACY_IVAR_PCIFUNC:
*result = atdev->lg_pcifunc;
break;
default:
return ENOENT;
}
@ -202,6 +212,12 @@ legacy_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
case LEGACY_IVAR_PCIBUS:
atdev->lg_pcibus = value;
break;
case LEGACY_IVAR_PCISLOT:
atdev->lg_pcislot = value;
break;
case LEGACY_IVAR_PCIFUNC:
atdev->lg_pcifunc = value;
break;
default:
return ENOENT;
}

View file

@ -31,7 +31,9 @@
enum legacy_device_ivars {
LEGACY_IVAR_PCIDOMAIN,
LEGACY_IVAR_PCIBUS
LEGACY_IVAR_PCIBUS,
LEGACY_IVAR_PCISLOT,
LEGACY_IVAR_PCIFUNC
};
#define LEGACY_ACCESSOR(var, ivar, type) \
@ -39,6 +41,8 @@ enum legacy_device_ivars {
LEGACY_ACCESSOR(pcidomain, PCIDOMAIN, uint32_t)
LEGACY_ACCESSOR(pcibus, PCIBUS, uint32_t)
LEGACY_ACCESSOR(pcislot, PCISLOT, int)
LEGACY_ACCESSOR(pcifunc, PCIFUNC, int)
#undef LEGACY_ACCESSOR
@ -53,5 +57,7 @@ int legacy_pcib_write_ivar(device_t dev, device_t child, int which,
uintptr_t value);
struct resource *legacy_pcib_alloc_resource(device_t dev, device_t child,
int type, int *rid, u_long start, u_long end, u_long count, u_int flags);
int legacy_pcib_map_msi(device_t pcib, device_t dev, int irq,
uint64_t *addr, uint32_t *data);
#endif /* !_MACHINE_LEGACYVAR_H_ */

View file

@ -59,8 +59,9 @@ struct acpi_hpcib_softc {
ACPI_HANDLE ap_handle;
int ap_flags;
int ap_segment; /* analagous to Alpha 'hose' */
int ap_segment; /* PCI domain */
int ap_bus; /* bios-assigned bus number */
int ap_addr; /* device/func of PCI-Host bridge */
ACPI_BUFFER ap_prt; /* interrupt routing table */
#ifdef NEW_PCIB
@ -276,7 +277,7 @@ acpi_pcib_acpi_attach(device_t dev)
struct acpi_hpcib_softc *sc;
ACPI_STATUS status;
static int bus0_seen = 0;
u_int addr, slot, func, busok;
u_int slot, func, busok;
uint8_t busno;
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
@ -286,7 +287,7 @@ acpi_pcib_acpi_attach(device_t dev)
sc->ap_handle = acpi_get_handle(dev);
/*
* Get our segment number by evaluating _SEG
* Get our segment number by evaluating _SEG.
* It's OK for this to not exist.
*/
status = acpi_GetInteger(sc->ap_handle, "_SEG", &sc->ap_segment);
@ -300,6 +301,18 @@ acpi_pcib_acpi_attach(device_t dev)
sc->ap_segment = 0;
}
/*
* Get the address (device and function) of the associated
* PCI-Host bridge device from _ADR. Assume we don't have one if
* it doesn't exist.
*/
status = acpi_GetInteger(sc->ap_handle, "_ADR", &sc->ap_addr);
if (ACPI_FAILURE(status)) {
device_printf(dev, "could not evaluate _ADR - %s\n",
AcpiFormatException(status));
sc->ap_addr = -1;
}
#ifdef NEW_PCIB
/*
* Determine which address ranges this bridge decodes and setup
@ -354,18 +367,10 @@ acpi_pcib_acpi_attach(device_t dev)
busok = 1;
if (sc->ap_segment == 0 && sc->ap_bus == 0 && bus0_seen) {
busok = 0;
status = acpi_GetInteger(sc->ap_handle, "_ADR", &addr);
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND) {
device_printf(dev, "could not evaluate _ADR - %s\n",
AcpiFormatException(status));
return_VALUE (ENXIO);
} else
device_printf(dev, "couldn't find _ADR\n");
} else {
if (sc->ap_addr != -1) {
/* XXX: We assume bus 0. */
slot = ACPI_ADR_PCI_SLOT(addr);
func = ACPI_ADR_PCI_FUNC(addr);
slot = ACPI_ADR_PCI_SLOT(sc->ap_addr);
func = ACPI_ADR_PCI_FUNC(sc->ap_addr);
if (bootverbose)
device_printf(dev, "reading config registers from 0:%d:%d\n",
slot, func);
@ -488,10 +493,24 @@ static int
acpi_pcib_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr,
uint32_t *data)
{
device_t bus;
struct acpi_hpcib_softc *sc;
device_t bus, hostb;
int error;
bus = device_get_parent(pcib);
return (PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data));
error = PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data);
if (error)
return (error);
sc = device_get_softc(dev);
if (sc->ap_addr == -1)
return (0);
/* XXX: Assumes all bridges are on bus 0. */
hostb = pci_find_dbsf(sc->ap_segment, 0, ACPI_ADR_PCI_SLOT(sc->ap_addr),
ACPI_ADR_PCI_FUNC(sc->ap_addr));
if (hostb != NULL)
pci_ht_map_msi(hostb, *addr);
return (0);
}
struct resource *

View file

@ -751,7 +751,7 @@ pci_read_cap(device_t pcib, pcicfgregs *cfg)
}
}
#if defined(__i386__) || defined(__amd64__) || defined(__powerpc__)
#if defined(__powerpc__)
/*
* Enable the MSI mapping window for all HyperTransport
* slaves. PCI-PCI bridges have their windows enabled via

View file

@ -58,7 +58,9 @@ __FBSDID("$FreeBSD$");
static MALLOC_DEFINE(M_LEGACYDEV, "legacydrv", "legacy system device");
struct legacy_device {
int lg_pcibus;
int lg_pcibus;
int lg_pcislot;
int lg_pcifunc;
};
#define DEVTOAT(dev) ((struct legacy_device *)device_get_ivars(dev))
@ -182,6 +184,8 @@ legacy_add_child(device_t bus, u_int order, const char *name, int unit)
if (atdev == NULL)
return(NULL);
atdev->lg_pcibus = -1;
atdev->lg_pcislot = -1;
atdev->lg_pcifunc = -1;
child = device_add_child_ordered(bus, order, name, unit);
if (child == NULL)
@ -205,6 +209,12 @@ legacy_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
case LEGACY_IVAR_PCIBUS:
*result = atdev->lg_pcibus;
break;
case LEGACY_IVAR_PCISLOT:
*result = atdev->lg_pcislot;
break;
case LEGACY_IVAR_PCIFUNC:
*result = atdev->lg_pcifunc;
break;
default:
return ENOENT;
}
@ -223,6 +233,12 @@ legacy_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
case LEGACY_IVAR_PCIBUS:
atdev->lg_pcibus = value;
break;
case LEGACY_IVAR_PCISLOT:
atdev->lg_pcislot = value;
break;
case LEGACY_IVAR_PCIFUNC:
atdev->lg_pcifunc = value;
break;
default:
return ENOENT;
}

View file

@ -31,7 +31,9 @@
enum legacy_device_ivars {
LEGACY_IVAR_PCIDOMAIN,
LEGACY_IVAR_PCIBUS
LEGACY_IVAR_PCIBUS,
LEGACY_IVAR_PCISLOT,
LEGACY_IVAR_PCIFUNC
};
#define LEGACY_ACCESSOR(var, ivar, type) \
@ -39,6 +41,8 @@ enum legacy_device_ivars {
LEGACY_ACCESSOR(pcidomain, PCIDOMAIN, uint32_t)
LEGACY_ACCESSOR(pcibus, PCIBUS, uint32_t)
LEGACY_ACCESSOR(pcislot, PCISLOT, int)
LEGACY_ACCESSOR(pcifunc, PCIFUNC, int)
#undef LEGACY_ACCESSOR
@ -53,5 +57,7 @@ int legacy_pcib_write_ivar(device_t dev, device_t child, int which,
uintptr_t value);
struct resource *legacy_pcib_alloc_resource(device_t dev, device_t child,
int type, int *rid, u_long start, u_long end, u_long count, u_int flags);
int legacy_pcib_map_msi(device_t pcib, device_t dev, int irq,
uint64_t *addr, uint32_t *data);
#endif /* !_MACHINE_LEGACYVAR_H_ */

View file

@ -112,14 +112,28 @@ legacy_pcib_alloc_msix(device_t pcib, device_t dev, int *irq)
return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, irq));
}
static int
int
legacy_pcib_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr,
uint32_t *data)
{
device_t bus;
device_t bus, hostb;
int error, func, slot;
bus = device_get_parent(pcib);
return (PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data));
error = PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data);
if (error)
return (error);
slot = legacy_get_pcislot(pcib);
func = legacy_get_pcifunc(pcib);
if (slot == -1 || func == -1)
return (0);
hostb = pci_find_bsf(0, slot, func);
KASSERT(hostb != NULL, ("%s: missing hostb for 0:%d:%d", __func__,
slot, func));
pci_ht_map_msi(hostb, *addr);
return (0);
}
static const char *
@ -453,6 +467,8 @@ legacy_pcib_identify(driver_t *driver, device_t parent)
"pcib", busnum);
device_set_desc(child, s);
legacy_set_pcibus(child, busnum);
legacy_set_pcislot(child, slot);
legacy_set_pcifunc(child, func);
found = 1;
if (id == 0x12258086)

View file

@ -97,16 +97,6 @@ mptable_hostb_alloc_msix(device_t pcib, device_t dev, int *irq)
return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, irq));
}
static int
mptable_hostb_map_msi(device_t pcib, device_t dev, int irq, uint64_t *addr,
uint32_t *data)
{
device_t bus;
bus = device_get_parent(pcib);
return (PCIB_MAP_MSI(device_get_parent(bus), dev, irq, addr, data));
}
#ifdef NEW_PCIB
static int
mptable_is_isa_range(u_long start, u_long end)
@ -214,7 +204,7 @@ static device_method_t mptable_hostb_methods[] = {
DEVMETHOD(pcib_release_msi, pcib_release_msi),
DEVMETHOD(pcib_alloc_msix, mptable_hostb_alloc_msix),
DEVMETHOD(pcib_release_msix, pcib_release_msix),
DEVMETHOD(pcib_map_msi, mptable_hostb_map_msi),
DEVMETHOD(pcib_map_msi, legacy_pcib_map_msi),
DEVMETHOD_END
};