Skip to content

Commit

Permalink
x86/PCI/ACPI: Use common ACPI resource interfaces to simplify impleme…
Browse files Browse the repository at this point in the history
…ntation

Use common ACPI resource discovery interfaces to simplify PCI host bridge
resource enumeration.

Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  • Loading branch information
Jiang Liu authored and Rafael J. Wysocki committed Feb 5, 2015
1 parent 812dbd9 commit 593669c
Showing 1 changed file with 91 additions and 204 deletions.
295 changes: 91 additions & 204 deletions arch/x86/pci/acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
struct pci_root_info {
struct acpi_device *bridge;
char name[16];
unsigned int res_num;
struct resource *res;
resource_size_t *res_offset;
struct pci_sysdata sd;
#ifdef CONFIG_PCI_MMCONFIG
bool mcfg_added;
Expand Down Expand Up @@ -218,132 +215,41 @@ static void teardown_mcfg_map(struct pci_root_info *info)
}
#endif

static acpi_status resource_to_addr(struct acpi_resource *resource,
struct acpi_resource_address64 *addr)
{
acpi_status status;
struct acpi_resource_memory24 *memory24;
struct acpi_resource_memory32 *memory32;
struct acpi_resource_fixed_memory32 *fixed_memory32;

memset(addr, 0, sizeof(*addr));
switch (resource->type) {
case ACPI_RESOURCE_TYPE_MEMORY24:
memory24 = &resource->data.memory24;
addr->resource_type = ACPI_MEMORY_RANGE;
addr->address.minimum = memory24->minimum;
addr->address.address_length = memory24->address_length;
addr->address.maximum = addr->address.minimum + addr->address.address_length - 1;
return AE_OK;
case ACPI_RESOURCE_TYPE_MEMORY32:
memory32 = &resource->data.memory32;
addr->resource_type = ACPI_MEMORY_RANGE;
addr->address.minimum = memory32->minimum;
addr->address.address_length = memory32->address_length;
addr->address.maximum = addr->address.minimum + addr->address.address_length - 1;
return AE_OK;
case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
fixed_memory32 = &resource->data.fixed_memory32;
addr->resource_type = ACPI_MEMORY_RANGE;
addr->address.minimum = fixed_memory32->address;
addr->address.address_length = fixed_memory32->address_length;
addr->address.maximum = addr->address.minimum + addr->address.address_length - 1;
return AE_OK;
case ACPI_RESOURCE_TYPE_ADDRESS16:
case ACPI_RESOURCE_TYPE_ADDRESS32:
case ACPI_RESOURCE_TYPE_ADDRESS64:
status = acpi_resource_to_address64(resource, addr);
if (ACPI_SUCCESS(status) &&
(addr->resource_type == ACPI_MEMORY_RANGE ||
addr->resource_type == ACPI_IO_RANGE) &&
addr->address.address_length > 0) {
return AE_OK;
}
break;
}
return AE_ERROR;
}

static acpi_status count_resource(struct acpi_resource *acpi_res, void *data)
static void validate_resources(struct device *dev, struct list_head *crs_res,
unsigned long type)
{
struct pci_root_info *info = data;
struct acpi_resource_address64 addr;
acpi_status status;

status = resource_to_addr(acpi_res, &addr);
if (ACPI_SUCCESS(status))
info->res_num++;
return AE_OK;
}

static acpi_status setup_resource(struct acpi_resource *acpi_res, void *data)
{
struct pci_root_info *info = data;
struct resource *res;
struct acpi_resource_address64 addr;
acpi_status status;
unsigned long flags;
u64 start, orig_end, end, res_end;

status = resource_to_addr(acpi_res, &addr);
if (!ACPI_SUCCESS(status))
return AE_OK;

if (addr.resource_type == ACPI_MEMORY_RANGE) {
flags = IORESOURCE_MEM;
if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY)
flags |= IORESOURCE_PREFETCH;
res_end = (u64)iomem_resource.end;
} else if (addr.resource_type == ACPI_IO_RANGE) {
flags = IORESOURCE_IO;
res_end = (u64)ioport_resource.end;
} else
return AE_OK;

start = addr.address.minimum + addr.address.translation_offset;
orig_end = end = addr.address.maximum + addr.address.translation_offset;

/* Exclude non-addressable range or non-addressable portion of range */
end = min(end, res_end);
if (end <= start) {
dev_info(&info->bridge->dev,
"host bridge window [%#llx-%#llx] "
"(ignored, not CPU addressable)\n", start, orig_end);
return AE_OK;
} else if (orig_end != end) {
dev_info(&info->bridge->dev,
"host bridge window [%#llx-%#llx] "
"([%#llx-%#llx] ignored, not CPU addressable)\n",
start, orig_end, end + 1, orig_end);
}
LIST_HEAD(list);
struct resource *res1, *res2, *root = NULL;
struct resource_entry *tmp, *entry, *entry2;

res = &info->res[info->res_num];
res->name = info->name;
res->flags = flags;
res->start = start;
res->end = end;
info->res_offset[info->res_num] = addr.address.translation_offset;
info->res_num++;
BUG_ON((type & (IORESOURCE_MEM | IORESOURCE_IO)) == 0);
root = (type & IORESOURCE_MEM) ? &iomem_resource : &ioport_resource;

if (!pci_use_crs)
dev_printk(KERN_DEBUG, &info->bridge->dev,
"host bridge window %pR (ignored)\n", res);
list_splice_init(crs_res, &list);
resource_list_for_each_entry_safe(entry, tmp, &list) {
bool free = false;
resource_size_t end;

return AE_OK;
}

static void coalesce_windows(struct pci_root_info *info, unsigned long type)
{
int i, j;
struct resource *res1, *res2;

for (i = 0; i < info->res_num; i++) {
res1 = &info->res[i];
res1 = entry->res;
if (!(res1->flags & type))
continue;
goto next;

/* Exclude non-addressable range or non-addressable portion */
end = min(res1->end, root->end);
if (end <= res1->start) {
dev_info(dev, "host bridge window %pR (ignored, not CPU addressable)\n",
res1);
free = true;
goto next;
} else if (res1->end != end) {
dev_info(dev, "host bridge window %pR ([%#llx-%#llx] ignored, not CPU addressable)\n",
res1, (unsigned long long)end + 1,
(unsigned long long)res1->end);
res1->end = end;
}

for (j = i + 1; j < info->res_num; j++) {
res2 = &info->res[j];
resource_list_for_each_entry(entry2, crs_res) {
res2 = entry2->res;
if (!(res2->flags & type))
continue;

Expand All @@ -355,118 +261,92 @@ static void coalesce_windows(struct pci_root_info *info, unsigned long type)
if (resource_overlaps(res1, res2)) {
res2->start = min(res1->start, res2->start);
res2->end = max(res1->end, res2->end);
dev_info(&info->bridge->dev,
"host bridge window expanded to %pR; %pR ignored\n",
dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n",
res2, res1);
res1->flags = 0;
free = true;
goto next;
}
}

next:
resource_list_del(entry);
if (free)
resource_list_free_entry(entry);
else
resource_list_add_tail(entry, crs_res);
}
}

static void add_resources(struct pci_root_info *info,
struct list_head *resources)
struct list_head *resources,
struct list_head *crs_res)
{
int i;
struct resource *res, *root, *conflict;

coalesce_windows(info, IORESOURCE_MEM);
coalesce_windows(info, IORESOURCE_IO);
struct resource_entry *entry, *tmp;
struct resource *res, *conflict, *root = NULL;

for (i = 0; i < info->res_num; i++) {
res = &info->res[i];
validate_resources(&info->bridge->dev, crs_res, IORESOURCE_MEM);
validate_resources(&info->bridge->dev, crs_res, IORESOURCE_IO);

resource_list_for_each_entry_safe(entry, tmp, crs_res) {
res = entry->res;
if (res->flags & IORESOURCE_MEM)
root = &iomem_resource;
else if (res->flags & IORESOURCE_IO)
root = &ioport_resource;
else
continue;
BUG_ON(res);

conflict = insert_resource_conflict(root, res);
if (conflict)
if (conflict) {
dev_info(&info->bridge->dev,
"ignoring host bridge window %pR (conflicts with %s %pR)\n",
res, conflict->name, conflict);
else
pci_add_resource_offset(resources, res,
info->res_offset[i]);
resource_list_destroy_entry(entry);
}
}
}

static void free_pci_root_info_res(struct pci_root_info *info)
{
kfree(info->res);
info->res = NULL;
kfree(info->res_offset);
info->res_offset = NULL;
info->res_num = 0;
list_splice_tail(crs_res, resources);
}

static void __release_pci_root_info(struct pci_root_info *info)
static void release_pci_root_info(struct pci_host_bridge *bridge)
{
int i;
struct resource *res;
struct resource_entry *entry;
struct pci_root_info *info = bridge->release_data;

for (i = 0; i < info->res_num; i++) {
res = &info->res[i];

if (!res->parent)
continue;

if (!(res->flags & (IORESOURCE_MEM | IORESOURCE_IO)))
continue;

release_resource(res);
resource_list_for_each_entry(entry, &bridge->windows) {
res = entry->res;
if (res->parent &&
(res->flags & (IORESOURCE_MEM | IORESOURCE_IO)))
release_resource(res);
}

free_pci_root_info_res(info);

teardown_mcfg_map(info);

kfree(info);
}

static void release_pci_root_info(struct pci_host_bridge *bridge)
{
struct pci_root_info *info = bridge->release_data;

__release_pci_root_info(info);
}

static void probe_pci_root_info(struct pci_root_info *info,
struct acpi_device *device,
int busnum, int domain)
int busnum, int domain,
struct list_head *list)
{
size_t size;
int ret;
struct resource_entry *entry;

sprintf(info->name, "PCI Bus %04x:%02x", domain, busnum);
info->bridge = device;

info->res_num = 0;
acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_resource,
info);
if (!info->res_num)
return;

size = sizeof(*info->res) * info->res_num;
info->res = kzalloc_node(size, GFP_KERNEL, info->sd.node);
if (!info->res) {
info->res_num = 0;
return;
}

size = sizeof(*info->res_offset) * info->res_num;
info->res_num = 0;
info->res_offset = kzalloc_node(size, GFP_KERNEL, info->sd.node);
if (!info->res_offset) {
kfree(info->res);
info->res = NULL;
return;
}

acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource,
info);
ret = acpi_dev_get_resources(device, list,
acpi_dev_filter_resource_type_cb,
(void *)(IORESOURCE_IO | IORESOURCE_MEM));
if (ret < 0)
dev_warn(&device->dev,
"failed to parse _CRS method, error code %d\n", ret);
else if (ret == 0)
dev_dbg(&device->dev,
"no IO and memory resources present in _CRS\n");
else
resource_list_for_each_entry(entry, list)
entry->res->name = info->name;
}

struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
Expand All @@ -475,6 +355,8 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
struct pci_root_info *info;
int domain = root->segment;
int busnum = root->secondary.start;
struct resource_entry *res_entry;
LIST_HEAD(crs_res);
LIST_HEAD(resources);
struct pci_bus *bus;
struct pci_sysdata *sd;
Expand Down Expand Up @@ -522,18 +404,22 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
memcpy(bus->sysdata, sd, sizeof(*sd));
kfree(info);
} else {
probe_pci_root_info(info, device, busnum, domain);

/* insert busn res at first */
pci_add_resource(&resources, &root->secondary);

/*
* _CRS with no apertures is normal, so only fall back to
* defaults or native bridge info if we're ignoring _CRS.
*/
if (pci_use_crs)
add_resources(info, &resources);
else {
free_pci_root_info_res(info);
probe_pci_root_info(info, device, busnum, domain, &crs_res);
if (pci_use_crs) {
add_resources(info, &resources, &crs_res);
} else {
resource_list_for_each_entry(res_entry, &crs_res)
dev_printk(KERN_DEBUG, &device->dev,
"host bridge window %pR (ignored)\n",
res_entry->res);
resource_list_free(&crs_res);
x86_pci_root_bus_resources(busnum, &resources);
}

Expand All @@ -548,8 +434,9 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
to_pci_host_bridge(bus->bridge),
release_pci_root_info, info);
} else {
pci_free_resource_list(&resources);
__release_pci_root_info(info);
resource_list_free(&resources);
teardown_mcfg_map(info);
kfree(info);
}
}

Expand Down

0 comments on commit 593669c

Please sign in to comment.