acpica, pci_host_generic_acpi: redo pci_host_generic_acpi.c

This is a major update for pci_host_generic_acpi.c, the current
implementation has some gaps that are better fixed up in one go.
The changes are to:
 * Follow x86 method of not adding PCI resources to PCI host bridge in
   ACPI code. This has been moved to pci_host_generic_acpi.c, where we
   walk thru its resources of the host bridge and add them.
 * Fixup code in pci_host_generic_acpi.c to read all decoded ranges
   and update the 'ranges' property. This allows us to share most of
   the code with generic implementation (and the FDT one).
 * Parse and setup IO ranges and bus ranges when walking the resources
   above. Drop most of the changes related to this from acpica code.
 * Add the ECAM memory area as mem resource 0. Implement the logic to
   get the ECAM area from MCFG (using bus range which we now decode),
   or from _CBA (using _BBN/bus range). Drop aarch64 ifdefs from acpica
   code which did part of this.
 * Switch resource activation to similar code as FDT implementation,
   this can be moved into generic implementation in a later pass.
 * Drop the mechanism of using the 7th bit of bus number as the domain,
   this is not correct and will work only in very specific cases. Use
   _SEG as PCI domain and use the bus ranges of the host bridge to
   provide start bus number.

This commit should not make any functional change to dev/acpica/acpi.c
for other architectures, almost all the changes there are to revert
earlier additions in this file done for aarch64.

Reviewed by:	andrew
Differential Revision:	https://reviews.freebsd.org/D17791
This commit is contained in:
Jayachandran C. 2018-11-19 03:16:16 +00:00
parent d4d6ad3f05
commit 185c34f7c8
2 changed files with 252 additions and 146 deletions

View File

@ -179,9 +179,7 @@ static int acpi_child_location_str_method(device_t acdev, device_t child,
char *buf, size_t buflen);
static int acpi_child_pnpinfo_str_method(device_t acdev, device_t child,
char *buf, size_t buflen);
#if defined(__i386__) || defined(__amd64__)
static void acpi_enable_pcie(void);
#endif
static void acpi_hint_device_unit(device_t acdev, device_t child,
const char *name, int *unitp);
static void acpi_reset_interfaces(device_t dev);
@ -502,10 +500,8 @@ acpi_attach(device_t dev)
goto out;
}
#if defined(__i386__) || defined(__amd64__)
/* Handle MCFG table if present. */
acpi_enable_pcie();
#endif
/*
* Note that some systems (specifically, those with namespace evaluation
@ -1286,11 +1282,10 @@ acpi_set_resource(device_t dev, device_t child, int type, int rid,
struct acpi_softc *sc = device_get_softc(dev);
struct acpi_device *ad = device_get_ivars(child);
struct resource_list *rl = &ad->ad_rl;
#if defined(__i386__) || defined(__amd64__)
ACPI_DEVICE_INFO *devinfo;
#endif
rman_res_t end;
int allow;
/* Ignore IRQ resources for PCI link devices. */
if (type == SYS_RES_IRQ &&
ACPI_ID_PROBE(dev, child, pcilink_ids, NULL) <= 0)
@ -1305,11 +1300,15 @@ acpi_set_resource(device_t dev, device_t child, int type, int rid,
* x86 of a PCI bridge claiming the I/O ports used for PCI config
* access.
*/
#if defined(__i386__) || defined(__amd64__)
if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
if (ACPI_SUCCESS(AcpiGetObjectInfo(ad->ad_handle, &devinfo))) {
if ((devinfo->Flags & ACPI_PCI_ROOT_BRIDGE) != 0) {
if (!(type == SYS_RES_IOPORT && start == CONF1_ADDR_PORT)) {
#if defined(__i386__) || defined(__amd64__)
allow = (type == SYS_RES_IOPORT && start == CONF1_ADDR_PORT);
#else
allow = 0;
#endif
if (!allow) {
AcpiOsFree(devinfo);
return (0);
}
@ -1317,7 +1316,6 @@ acpi_set_resource(device_t dev, device_t child, int type, int rid,
AcpiOsFree(devinfo);
}
}
#endif
#ifdef INTRNG
/* map with default for now */
@ -1874,15 +1872,18 @@ acpi_isa_pnp_probe(device_t bus, device_t child, struct isa_pnp_id *ids)
return_VALUE (result);
}
#if defined(__i386__) || defined(__amd64__)
/*
* Look for a MCFG table. If it is present, use the settings for
* domain (segment) 0 to setup PCI config space access via the memory
* map.
*
* On non-x86 architectures (arm64 for now), this will be done from the
* PCI host bridge driver.
*/
static void
acpi_enable_pcie(void)
{
#if defined(__i386__) || defined(__amd64__)
ACPI_TABLE_HEADER *hdr;
ACPI_MCFG_ALLOCATION *alloc, *end;
ACPI_STATUS status;
@ -1901,31 +1902,8 @@ acpi_enable_pcie(void)
}
alloc++;
}
}
#elif defined(__aarch64__)
static void
acpi_enable_pcie(device_t child, int segment)
{
ACPI_TABLE_HEADER *hdr;
ACPI_MCFG_ALLOCATION *alloc, *end;
ACPI_STATUS status;
status = AcpiGetTable(ACPI_SIG_MCFG, 1, &hdr);
if (ACPI_FAILURE(status))
return;
end = (ACPI_MCFG_ALLOCATION *)((char *)hdr + hdr->Length);
alloc = (ACPI_MCFG_ALLOCATION *)((ACPI_TABLE_MCFG *)hdr + 1);
while (alloc < end) {
if (alloc->PciSegment == segment) {
bus_set_resource(child, SYS_RES_MEMORY, 0,
alloc->Address, 0x10000000);
return;
}
alloc++;
}
}
#endif
}
/*
* Scan all of the ACPI namespace and attach child devices.
@ -2016,9 +1994,6 @@ acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status)
{
ACPI_DEVICE_INFO *devinfo;
struct acpi_device *ad;
#ifdef __aarch64__
int segment;
#endif
struct acpi_prw_data prw;
ACPI_OBJECT_TYPE type;
ACPI_HANDLE h;
@ -2121,13 +2096,6 @@ acpi_probe_child(ACPI_HANDLE handle, UINT32 level, void *context, void **status)
ad->ad_cls_class = strtoul(devinfo->ClassCode.String,
NULL, 16);
}
#ifdef __aarch64__
if ((devinfo->Flags & ACPI_PCI_ROOT_BRIDGE) != 0) {
if (ACPI_SUCCESS(acpi_GetInteger(handle, "_SEG", &segment))) {
acpi_enable_pcie(child, segment);
}
}
#endif
AcpiOsFree(devinfo);
}
break;

View File

@ -63,8 +63,7 @@ __FBSDID("$FreeBSD$");
#include <machine/intr.h>
#include "pcib_if.h"
int pci_host_generic_acpi_attach(device_t);
#include "acpi_bus_if.h"
/* Assembling ECAM Configuration Address */
#define PCIE_BUS_SHIFT 20
@ -100,6 +99,9 @@ static int generic_pcie_acpi_probe(device_t dev);
static ACPI_STATUS pci_host_generic_acpi_parse_resource(ACPI_RESOURCE *, void *);
static int generic_pcie_acpi_read_ivar(device_t, device_t, int, uintptr_t *);
/*
* generic_pcie_acpi_probe - look for root bridge flag
*/
static int
generic_pcie_acpi_probe(device_t dev)
{
@ -119,81 +121,201 @@ generic_pcie_acpi_probe(device_t dev)
return (BUS_PROBE_GENERIC);
}
int
/*
* pci_host_generic_acpi_parse_resource - parse PCI memory, IO and bus spaces
* 'produced' by this bridge
*/
static ACPI_STATUS
pci_host_generic_acpi_parse_resource(ACPI_RESOURCE *res, void *arg)
{
device_t dev = (device_t)arg;
struct generic_pcie_acpi_softc *sc;
struct rman *rm;
rman_res_t min, max, off;
int r;
rm = NULL;
sc = device_get_softc(dev);
r = sc->base.nranges;
switch (res->Type) {
case ACPI_RESOURCE_TYPE_ADDRESS16:
min = res->Data.Address16.Address.Minimum;
max = res->Data.Address16.Address.Maximum;
break;
case ACPI_RESOURCE_TYPE_ADDRESS32:
min = res->Data.Address32.Address.Minimum;
max = res->Data.Address32.Address.Maximum;
off = res->Data.Address32.Address.TranslationOffset;
break;
case ACPI_RESOURCE_TYPE_ADDRESS64:
if (res->Data.Address.ResourceType != ACPI_MEMORY_RANGE)
break;
min = res->Data.Address64.Address.Minimum;
max = res->Data.Address64.Address.Maximum;
off = res->Data.Address64.Address.TranslationOffset;
break;
default:
return (AE_OK);
}
/* Save detected ranges */
if (res->Data.Address.ResourceType == ACPI_MEMORY_RANGE ||
res->Data.Address.ResourceType == ACPI_IO_RANGE) {
sc->base.ranges[r].pci_base = min;
sc->base.ranges[r].phys_base = min + off;
sc->base.ranges[r].size = max - min + 1;
if (res->Data.Address.ResourceType == ACPI_MEMORY_RANGE)
sc->base.ranges[r].flags |= FLAG_MEM;
else if (res->Data.Address.ResourceType == ACPI_IO_RANGE)
sc->base.ranges[r].flags |= FLAG_IO;
sc->base.nranges++;
} else if (res->Data.Address.ResourceType == ACPI_BUS_NUMBER_RANGE) {
sc->base.bus_start = min;
sc->base.bus_end = max;
}
return (AE_OK);
}
static int
pci_host_acpi_get_ecam_resource(device_t dev)
{
struct generic_pcie_acpi_softc *sc;
struct acpi_device *ad;
struct resource_list *rl;
ACPI_TABLE_HEADER *hdr;
ACPI_MCFG_ALLOCATION *mcfg_entry, *mcfg_end;
ACPI_HANDLE handle;
ACPI_STATUS status;
rman_res_t base, start, end;
int found, val;
sc = device_get_softc(dev);
handle = acpi_get_handle(dev);
/* Try MCFG first */
status = AcpiGetTable(ACPI_SIG_MCFG, 1, &hdr);
if (ACPI_SUCCESS(status)) {
found = FALSE;
mcfg_end = (ACPI_MCFG_ALLOCATION *)((char *)hdr + hdr->Length);
mcfg_entry = (ACPI_MCFG_ALLOCATION *)((ACPI_TABLE_MCFG *)hdr + 1);
while (mcfg_entry < mcfg_end && !found) {
if (mcfg_entry->PciSegment == sc->base.ecam &&
mcfg_entry->StartBusNumber <= sc->base.bus_start &&
mcfg_entry->EndBusNumber >= sc->base.bus_start)
found = TRUE;
else
mcfg_entry++;
}
if (found) {
if (mcfg_entry->EndBusNumber < sc->base.bus_end) {
device_printf(dev, "bus end mismatch! expected %d found %d.\n",
sc->base.bus_end, (int)mcfg_entry->EndBusNumber);
sc->base.bus_end = mcfg_entry->EndBusNumber;
}
base = mcfg_entry->Address;
} else {
device_printf(dev, "MCFG exists, but does not have bus %d-%d\n",
sc->base.bus_start, sc->base.bus_end);
return (ENXIO);
}
} else {
status = acpi_GetInteger(handle, "_CBA", &val);
if (ACPI_SUCCESS(status))
base = val;
else
return (ENXIO);
}
/* add as MEM rid 0 */
ad = device_get_ivars(dev);
rl = &ad->ad_rl;
start = base + (sc->base.bus_start << PCIE_BUS_SHIFT);
end = base + ((sc->base.bus_end + 1) << PCIE_BUS_SHIFT) - 1;
resource_list_add(rl, SYS_RES_MEMORY, 0, start, end, end - start + 1);
if (bootverbose)
device_printf(dev, "ECAM for bus %d-%d at mem %jx-%jx\n",
sc->base.bus_start, sc->base.bus_end, start, end);
return (0);
}
static int
pci_host_generic_acpi_attach(device_t dev)
{
struct generic_pcie_acpi_softc *sc;
ACPI_HANDLE handle;
uint64_t phys_base;
uint64_t pci_base;
uint64_t size;
ACPI_STATUS status;
int error, bus_start;
int error;
int tuple;
sc = device_get_softc(dev);
handle = acpi_get_handle(dev);
/* Get Start bus number for the PCI host bus is from _BBN method */
status = acpi_GetInteger(handle, "_BBN", &sc->base.bus_start);
if (ACPI_FAILURE(status)) {
device_printf(dev, "No _BBN, using start bus 0\n");
sc->base.bus_start = 0;
}
sc->base.bus_end = 255;
/* Get PCI Segment (domain) needed for MCFG lookup */
status = acpi_GetInteger(handle, "_SEG", &sc->base.ecam);
if (ACPI_FAILURE(status)) {
device_printf(dev, "No _SEG for PCI Bus, using segment 0\n");
sc->base.ecam = 0;
}
/* Bus decode ranges */
status = AcpiWalkResources(handle, "_CRS",
pci_host_generic_acpi_parse_resource, (void *)dev);
if (ACPI_FAILURE(status))
return (ENXIO);
/* Coherency attribute */
if (ACPI_FAILURE(acpi_GetInteger(handle, "_CCA", &sc->base.coherent)))
sc->base.coherent = 0;
if (bootverbose)
device_printf(dev, "Bus is%s cache-coherent\n",
sc->base.coherent ? "" : " not");
if (!ACPI_FAILURE(acpi_GetInteger(handle, "_BBN", &bus_start))) {
sc->base.ecam = bus_start >> 7;
sc->base.bus_start = bus_start & 0x7F;
} else {
sc->base.ecam = 0;
sc->base.bus_start = 0;
}
sc->base.bus_end = 0xFF;
/* add config space resource */
pci_host_acpi_get_ecam_resource(dev);
acpi_pcib_fetch_prt(dev, &sc->ap_prt);
error = pci_host_generic_core_attach(dev);
if (error != 0)
return (error);
status = AcpiWalkResources(handle, "_CRS",
pci_host_generic_acpi_parse_resource, (void *)dev);
if (ACPI_FAILURE(status))
return (ENXIO);
for (tuple = 0; tuple < MAX_RANGES_TUPLES; tuple++) {
phys_base = sc->base.ranges[tuple].phys_base;
pci_base = sc->base.ranges[tuple].pci_base;
size = sc->base.ranges[tuple].size;
if (phys_base == 0 || size == 0)
continue; /* empty range element */
if (sc->base.ranges[tuple].flags & FLAG_MEM) {
error = rman_manage_region(&sc->base.mem_rman,
phys_base, phys_base + size - 1);
} else if (sc->base.ranges[tuple].flags & FLAG_IO) {
error = rman_manage_region(&sc->base.io_rman,
pci_base + PCI_IO_WINDOW_OFFSET,
pci_base + PCI_IO_WINDOW_OFFSET + size - 1);
} else
continue;
if (error) {
device_printf(dev, "rman_manage_region() failed."
"error = %d\n", error);
rman_fini(&sc->base.mem_rman);
return (error);
}
}
device_add_child(dev, "pci", -1);
return (bus_generic_attach(dev));
}
static ACPI_STATUS
pci_host_generic_acpi_parse_resource(ACPI_RESOURCE *res, void *arg)
{
device_t dev = (device_t)arg;
struct generic_pcie_acpi_softc *sc;
rman_res_t min, max;
int error;
switch (res->Type) {
case ACPI_RESOURCE_TYPE_ADDRESS32:
min = (rman_res_t)res->Data.Address32.Address.Minimum;
max = (rman_res_t)res->Data.Address32.Address.Maximum;
break;
case ACPI_RESOURCE_TYPE_ADDRESS64:
min = (rman_res_t)res->Data.Address64.Address.Minimum;
max = (rman_res_t)res->Data.Address64.Address.Maximum;
break;
default:
return (AE_OK);
}
sc = device_get_softc(dev);
error = rman_manage_region(&sc->base.mem_rman, min, max);
if (error) {
device_printf(dev, "unable to allocate %lx-%lx range\n", min, max);
return (AE_NOT_FOUND);
}
device_printf(dev, "allocating %lx-%lx range\n", min, max);
return (AE_OK);
}
static int
generic_pcie_acpi_read_ivar(device_t dev, device_t child, int index,
uintptr_t *result)
@ -203,7 +325,7 @@ generic_pcie_acpi_read_ivar(device_t dev, device_t child, int index,
sc = device_get_softc(dev);
if (index == PCIB_IVAR_BUS) {
*result = sc->base.ecam * 0x80 + sc->base.bus_start;
*result = sc->base.bus_start;
return (0);
}
@ -223,50 +345,62 @@ generic_pcie_acpi_route_interrupt(device_t bus, device_t dev, int pin)
struct generic_pcie_acpi_softc *sc;
sc = device_get_softc(bus);
return (acpi_pcib_route_interrupt(bus, dev, pin, &sc->ap_prt));
}
static struct resource *
pci_host_generic_acpi_alloc_resource(device_t dev, device_t child, int type,
int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
{
struct resource *res = NULL;
#if defined(NEW_PCIB) && defined(PCI_RES_BUS)
struct generic_pcie_acpi_softc *sc;
if (type == PCI_RES_BUS) {
sc = device_get_softc(dev);
return (pci_domain_alloc_bus(sc->base.ecam, child, rid, start,
end, count, flags));
}
#endif
if (type == SYS_RES_MEMORY)
res = pci_host_generic_core_alloc_resource(dev, child, type,
rid, start, end, count, flags);
if (res == NULL)
res = bus_generic_alloc_resource(dev, child, type, rid, start, end,
count, flags);
return (res);
}
/*
* Follow logic of FDT activate
*/
static int
generic_pcie_acpi_activate_resource(device_t dev, device_t child, int type,
int rid, struct resource *r)
{
struct generic_pcie_acpi_softc *sc;
uint64_t phys_base;
uint64_t pci_base;
uint64_t size;
int found;
int res;
int i;
sc = device_get_softc(dev);
if ((res = rman_activate_resource(r)) != 0)
return (res);
res = BUS_ACTIVATE_RESOURCE(device_get_parent(dev), child, type, rid,r);
switch (type) {
case SYS_RES_IOPORT:
found = 0;
for (i = 0; i < MAX_RANGES_TUPLES; i++) {
pci_base = sc->base.ranges[i].pci_base;
phys_base = sc->base.ranges[i].phys_base;
size = sc->base.ranges[i].size;
if ((rid > pci_base) && (rid < (pci_base + size))) {
found = 1;
break;
}
}
if (found) {
rman_set_start(r, rman_get_start(r) + phys_base);
rman_set_end(r, rman_get_end(r) + phys_base);
res = BUS_ACTIVATE_RESOURCE(device_get_parent(dev),
child, type, rid, r);
} else {
device_printf(dev,
"Failed to activate IOPORT resource\n");
res = 0;
}
break;
case SYS_RES_MEMORY:
case SYS_RES_IRQ:
res = BUS_ACTIVATE_RESOURCE(device_get_parent(dev), child,
type, rid, r);
break;
default:
break;
}
return (res);
}
@ -279,8 +413,17 @@ generic_pcie_acpi_deactivate_resource(device_t dev, device_t child, int type,
if ((res = rman_deactivate_resource(r)) != 0)
return (res);
res = BUS_DEACTIVATE_RESOURCE(device_get_parent(dev), child, type,
rid, r);
switch (type) {
case SYS_RES_IOPORT:
case SYS_RES_MEMORY:
case SYS_RES_IRQ:
res = BUS_DEACTIVATE_RESOURCE(device_get_parent(dev), child,
type, rid, r);
break;
default:
break;
}
return (res);
}
@ -347,30 +490,25 @@ static int
generic_pcie_acpi_get_id(device_t pci, device_t child, enum pci_id_type type,
uintptr_t *id)
{
struct generic_pcie_acpi_softc *sc;
int err;
/* Use the PCI RID to find the MSI ID */
if (type == PCI_ID_MSI) {
sc = device_get_softc(pci);
type = PCI_ID_RID;
err = pcib_get_id(pci, child, type, id);
if (err != 0)
return (err);
*id |= sc->base.ecam << 16;
return (0);
}
return (pcib_get_id(pci, child, type, id));
/*
* Use the PCI RID to find the MSI ID for now, we support only 1:1
* mapping
*
* On aarch64, more complex mapping would come from IORT table
*/
if (type == PCI_ID_MSI)
return (pcib_get_id(pci, child, PCI_ID_RID, id));
else
return (pcib_get_id(pci, child, type, id));
}
static device_method_t generic_pcie_acpi_methods[] = {
DEVMETHOD(device_probe, generic_pcie_acpi_probe),
DEVMETHOD(device_attach, pci_host_generic_acpi_attach),
DEVMETHOD(bus_alloc_resource, pci_host_generic_acpi_alloc_resource),
DEVMETHOD(bus_activate_resource, generic_pcie_acpi_activate_resource),
DEVMETHOD(bus_deactivate_resource, generic_pcie_acpi_deactivate_resource),
DEVMETHOD(bus_read_ivar, generic_pcie_acpi_read_ivar),
DEVMETHOD(bus_activate_resource, generic_pcie_acpi_activate_resource),
DEVMETHOD(bus_deactivate_resource,generic_pcie_acpi_deactivate_resource),
/* pcib interface */
DEVMETHOD(pcib_route_interrupt, generic_pcie_acpi_route_interrupt),