Merge branch 'topic/jiang-mmconfig-v10' into next
* topic/jiang-mmconfig-v10: ACPI: mark acpi_sfi_table_parse() as __init x86/PCI: use pr_level() to replace printk(KERN_LEVEL) x86/PCI: refine __pci_mmcfg_init() for better code readability x86/PCI: get rid of redundant log messages x86/PCI: simplify pci_mmcfg_late_insert_resources() x86/PCI: update MMCONFIG information when hot-plugging PCI host bridges PCI/ACPI: provide MMCONFIG address for PCI host bridges x86/PCI: add pci_mmconfig_insert()/delete() for PCI root bridge hotplug x86/PCI: prepare pci_mmcfg_check_reserved() to be called at runtime x86/PCI: introduce pci_mmcfg_arch_map()/pci_mmcfg_arch_unmap() x86/PCI: use RCU list to protect mmconfig list x86/PCI: split out pci_mmconfig_alloc() for code reuse x86/PCI: split out pci_mmcfg_check_reserved() for code reuse
This commit is contained in:
commit
e5028b52e4
|
@ -100,6 +100,7 @@ struct pci_raw_ops {
|
||||||
extern const struct pci_raw_ops *raw_pci_ops;
|
extern const struct pci_raw_ops *raw_pci_ops;
|
||||||
extern const struct pci_raw_ops *raw_pci_ext_ops;
|
extern const struct pci_raw_ops *raw_pci_ext_ops;
|
||||||
|
|
||||||
|
extern const struct pci_raw_ops pci_mmcfg;
|
||||||
extern const struct pci_raw_ops pci_direct_conf1;
|
extern const struct pci_raw_ops pci_direct_conf1;
|
||||||
extern bool port_cf9_safe;
|
extern bool port_cf9_safe;
|
||||||
|
|
||||||
|
@ -135,6 +136,12 @@ struct pci_mmcfg_region {
|
||||||
|
|
||||||
extern int __init pci_mmcfg_arch_init(void);
|
extern int __init pci_mmcfg_arch_init(void);
|
||||||
extern void __init pci_mmcfg_arch_free(void);
|
extern void __init pci_mmcfg_arch_free(void);
|
||||||
|
extern int __devinit pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg);
|
||||||
|
extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg);
|
||||||
|
extern int __devinit pci_mmconfig_insert(struct device *dev,
|
||||||
|
u16 seg, u8 start,
|
||||||
|
u8 end, phys_addr_t addr);
|
||||||
|
extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
|
||||||
extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
|
extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
|
||||||
|
|
||||||
extern struct list_head pci_mmcfg_list;
|
extern struct list_head pci_mmcfg_list;
|
||||||
|
|
|
@ -13,6 +13,12 @@ struct pci_root_info {
|
||||||
unsigned int res_num;
|
unsigned int res_num;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct pci_sysdata sd;
|
struct pci_sysdata sd;
|
||||||
|
#ifdef CONFIG_PCI_MMCONFIG
|
||||||
|
bool mcfg_added;
|
||||||
|
u16 segment;
|
||||||
|
u8 start_bus;
|
||||||
|
u8 end_bus;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool pci_use_crs = true;
|
static bool pci_use_crs = true;
|
||||||
|
@ -119,6 +125,81 @@ void __init pci_acpi_crs_quirks(void)
|
||||||
pci_use_crs ? "nocrs" : "use_crs");
|
pci_use_crs ? "nocrs" : "use_crs");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PCI_MMCONFIG
|
||||||
|
static int __devinit check_segment(u16 seg, struct device *dev, char *estr)
|
||||||
|
{
|
||||||
|
if (seg) {
|
||||||
|
dev_err(dev,
|
||||||
|
"%s can't access PCI configuration "
|
||||||
|
"space under this host bridge.\n",
|
||||||
|
estr);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Failure in adding MMCFG information is not fatal,
|
||||||
|
* just can't access extended configuration space of
|
||||||
|
* devices under this host bridge.
|
||||||
|
*/
|
||||||
|
dev_warn(dev,
|
||||||
|
"%s can't access extended PCI configuration "
|
||||||
|
"space under this bridge.\n",
|
||||||
|
estr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit setup_mcfg_map(struct pci_root_info *info,
|
||||||
|
u16 seg, u8 start, u8 end,
|
||||||
|
phys_addr_t addr)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
struct device *dev = &info->bridge->dev;
|
||||||
|
|
||||||
|
info->start_bus = start;
|
||||||
|
info->end_bus = end;
|
||||||
|
info->mcfg_added = false;
|
||||||
|
|
||||||
|
/* return success if MMCFG is not in use */
|
||||||
|
if (raw_pci_ext_ops && raw_pci_ext_ops != &pci_mmcfg)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!(pci_probe & PCI_PROBE_MMCONF))
|
||||||
|
return check_segment(seg, dev, "MMCONFIG is disabled,");
|
||||||
|
|
||||||
|
result = pci_mmconfig_insert(dev, seg, start, end, addr);
|
||||||
|
if (result == 0) {
|
||||||
|
/* enable MMCFG if it hasn't been enabled yet */
|
||||||
|
if (raw_pci_ext_ops == NULL)
|
||||||
|
raw_pci_ext_ops = &pci_mmcfg;
|
||||||
|
info->mcfg_added = true;
|
||||||
|
} else if (result != -EEXIST)
|
||||||
|
return check_segment(seg, dev,
|
||||||
|
"fail to add MMCONFIG information,");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void teardown_mcfg_map(struct pci_root_info *info)
|
||||||
|
{
|
||||||
|
if (info->mcfg_added) {
|
||||||
|
pci_mmconfig_delete(info->segment, info->start_bus,
|
||||||
|
info->end_bus);
|
||||||
|
info->mcfg_added = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static int __devinit setup_mcfg_map(struct pci_root_info *info,
|
||||||
|
u16 seg, u8 start, u8 end,
|
||||||
|
phys_addr_t addr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static void teardown_mcfg_map(struct pci_root_info *info)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static acpi_status
|
static acpi_status
|
||||||
resource_to_addr(struct acpi_resource *resource,
|
resource_to_addr(struct acpi_resource *resource,
|
||||||
struct acpi_resource_address64 *addr)
|
struct acpi_resource_address64 *addr)
|
||||||
|
@ -233,13 +314,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
info->res_num++;
|
info->res_num++;
|
||||||
if (addr.translation_offset)
|
|
||||||
dev_info(&info->bridge->dev, "host bridge window %pR "
|
|
||||||
"(PCI address [%#llx-%#llx])\n",
|
|
||||||
res, res->start - addr.translation_offset,
|
|
||||||
res->end - addr.translation_offset);
|
|
||||||
else
|
|
||||||
dev_info(&info->bridge->dev, "host bridge window %pR\n", res);
|
|
||||||
|
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
}
|
}
|
||||||
|
@ -331,8 +405,11 @@ static void __release_pci_root_info(struct pci_root_info *info)
|
||||||
|
|
||||||
free_pci_root_info_res(info);
|
free_pci_root_info_res(info);
|
||||||
|
|
||||||
|
teardown_mcfg_map(info);
|
||||||
|
|
||||||
kfree(info);
|
kfree(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void release_pci_root_info(struct pci_host_bridge *bridge)
|
static void release_pci_root_info(struct pci_host_bridge *bridge)
|
||||||
{
|
{
|
||||||
struct pci_root_info *info = bridge->release_data;
|
struct pci_root_info *info = bridge->release_data;
|
||||||
|
@ -372,7 +449,7 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root)
|
||||||
int domain = root->segment;
|
int domain = root->segment;
|
||||||
int busnum = root->secondary.start;
|
int busnum = root->secondary.start;
|
||||||
LIST_HEAD(resources);
|
LIST_HEAD(resources);
|
||||||
struct pci_bus *bus;
|
struct pci_bus *bus = NULL;
|
||||||
struct pci_sysdata *sd;
|
struct pci_sysdata *sd;
|
||||||
int node;
|
int node;
|
||||||
#ifdef CONFIG_ACPI_NUMA
|
#ifdef CONFIG_ACPI_NUMA
|
||||||
|
@ -438,8 +515,11 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root)
|
||||||
x86_pci_root_bus_resources(busnum, &resources);
|
x86_pci_root_bus_resources(busnum, &resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, sd,
|
if (!setup_mcfg_map(info, domain, (u8)root->secondary.start,
|
||||||
&resources);
|
(u8)root->secondary.end, root->mcfg_addr))
|
||||||
|
bus = pci_create_root_bus(NULL, busnum, &pci_root_ops,
|
||||||
|
sd, &resources);
|
||||||
|
|
||||||
if (bus) {
|
if (bus) {
|
||||||
pci_scan_child_bus(bus);
|
pci_scan_child_bus(bus);
|
||||||
pci_set_host_bridge_release(
|
pci_set_host_bridge_release(
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include <linux/bitmap.h>
|
#include <linux/bitmap.h>
|
||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/rculist.h>
|
||||||
#include <asm/e820.h>
|
#include <asm/e820.h>
|
||||||
#include <asm/pci_x86.h>
|
#include <asm/pci_x86.h>
|
||||||
#include <asm/acpi.h>
|
#include <asm/acpi.h>
|
||||||
|
@ -24,7 +26,9 @@
|
||||||
#define PREFIX "PCI: "
|
#define PREFIX "PCI: "
|
||||||
|
|
||||||
/* Indicate if the mmcfg resources have been placed into the resource table. */
|
/* Indicate if the mmcfg resources have been placed into the resource table. */
|
||||||
static int __initdata pci_mmcfg_resources_inserted;
|
static bool pci_mmcfg_running_state;
|
||||||
|
static bool pci_mmcfg_arch_init_failed;
|
||||||
|
static DEFINE_MUTEX(pci_mmcfg_lock);
|
||||||
|
|
||||||
LIST_HEAD(pci_mmcfg_list);
|
LIST_HEAD(pci_mmcfg_list);
|
||||||
|
|
||||||
|
@ -45,24 +49,25 @@ static __init void free_all_mmcfg(void)
|
||||||
pci_mmconfig_remove(cfg);
|
pci_mmconfig_remove(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __init void list_add_sorted(struct pci_mmcfg_region *new)
|
static __devinit void list_add_sorted(struct pci_mmcfg_region *new)
|
||||||
{
|
{
|
||||||
struct pci_mmcfg_region *cfg;
|
struct pci_mmcfg_region *cfg;
|
||||||
|
|
||||||
/* keep list sorted by segment and starting bus number */
|
/* keep list sorted by segment and starting bus number */
|
||||||
list_for_each_entry(cfg, &pci_mmcfg_list, list) {
|
list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
|
||||||
if (cfg->segment > new->segment ||
|
if (cfg->segment > new->segment ||
|
||||||
(cfg->segment == new->segment &&
|
(cfg->segment == new->segment &&
|
||||||
cfg->start_bus >= new->start_bus)) {
|
cfg->start_bus >= new->start_bus)) {
|
||||||
list_add_tail(&new->list, &cfg->list);
|
list_add_tail_rcu(&new->list, &cfg->list);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list_add_tail(&new->list, &pci_mmcfg_list);
|
list_add_tail_rcu(&new->list, &pci_mmcfg_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
|
static __devinit struct pci_mmcfg_region *pci_mmconfig_alloc(int segment,
|
||||||
int end, u64 addr)
|
int start,
|
||||||
|
int end, u64 addr)
|
||||||
{
|
{
|
||||||
struct pci_mmcfg_region *new;
|
struct pci_mmcfg_region *new;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
@ -79,8 +84,6 @@ static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
|
||||||
new->start_bus = start;
|
new->start_bus = start;
|
||||||
new->end_bus = end;
|
new->end_bus = end;
|
||||||
|
|
||||||
list_add_sorted(new);
|
|
||||||
|
|
||||||
res = &new->res;
|
res = &new->res;
|
||||||
res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
|
res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
|
||||||
res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
|
res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
|
||||||
|
@ -89,9 +92,25 @@ static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
|
||||||
"PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
|
"PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
|
||||||
res->name = new->name;
|
res->name = new->name;
|
||||||
|
|
||||||
printk(KERN_INFO PREFIX "MMCONFIG for domain %04x [bus %02x-%02x] at "
|
return new;
|
||||||
"%pR (base %#lx)\n", segment, start, end, &new->res,
|
}
|
||||||
(unsigned long) addr);
|
|
||||||
|
static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
|
||||||
|
int end, u64 addr)
|
||||||
|
{
|
||||||
|
struct pci_mmcfg_region *new;
|
||||||
|
|
||||||
|
new = pci_mmconfig_alloc(segment, start, end, addr);
|
||||||
|
if (new) {
|
||||||
|
mutex_lock(&pci_mmcfg_lock);
|
||||||
|
list_add_sorted(new);
|
||||||
|
mutex_unlock(&pci_mmcfg_lock);
|
||||||
|
|
||||||
|
pr_info(PREFIX
|
||||||
|
"MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
|
||||||
|
"(base %#lx)\n",
|
||||||
|
segment, start, end, &new->res, (unsigned long)addr);
|
||||||
|
}
|
||||||
|
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +119,7 @@ struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
|
||||||
{
|
{
|
||||||
struct pci_mmcfg_region *cfg;
|
struct pci_mmcfg_region *cfg;
|
||||||
|
|
||||||
list_for_each_entry(cfg, &pci_mmcfg_list, list)
|
list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
|
||||||
if (cfg->segment == segment &&
|
if (cfg->segment == segment &&
|
||||||
cfg->start_bus <= bus && bus <= cfg->end_bus)
|
cfg->start_bus <= bus && bus <= cfg->end_bus)
|
||||||
return cfg;
|
return cfg;
|
||||||
|
@ -343,8 +362,7 @@ static int __init pci_mmcfg_check_hostbridge(void)
|
||||||
name = pci_mmcfg_probes[i].probe();
|
name = pci_mmcfg_probes[i].probe();
|
||||||
|
|
||||||
if (name)
|
if (name)
|
||||||
printk(KERN_INFO PREFIX "%s with MMCONFIG support\n",
|
pr_info(PREFIX "%s with MMCONFIG support\n", name);
|
||||||
name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* some end_bus_number is crazy, fix it */
|
/* some end_bus_number is crazy, fix it */
|
||||||
|
@ -353,19 +371,8 @@ static int __init pci_mmcfg_check_hostbridge(void)
|
||||||
return !list_empty(&pci_mmcfg_list);
|
return !list_empty(&pci_mmcfg_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init pci_mmcfg_insert_resources(void)
|
static acpi_status __devinit check_mcfg_resource(struct acpi_resource *res,
|
||||||
{
|
void *data)
|
||||||
struct pci_mmcfg_region *cfg;
|
|
||||||
|
|
||||||
list_for_each_entry(cfg, &pci_mmcfg_list, list)
|
|
||||||
insert_resource(&iomem_resource, &cfg->res);
|
|
||||||
|
|
||||||
/* Mark that the resources have been inserted. */
|
|
||||||
pci_mmcfg_resources_inserted = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static acpi_status __init check_mcfg_resource(struct acpi_resource *res,
|
|
||||||
void *data)
|
|
||||||
{
|
{
|
||||||
struct resource *mcfg_res = data;
|
struct resource *mcfg_res = data;
|
||||||
struct acpi_resource_address64 address;
|
struct acpi_resource_address64 address;
|
||||||
|
@ -401,8 +408,8 @@ static acpi_status __init check_mcfg_resource(struct acpi_resource *res,
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl,
|
static acpi_status __devinit find_mboard_resource(acpi_handle handle, u32 lvl,
|
||||||
void *context, void **rv)
|
void *context, void **rv)
|
||||||
{
|
{
|
||||||
struct resource *mcfg_res = context;
|
struct resource *mcfg_res = context;
|
||||||
|
|
||||||
|
@ -415,7 +422,7 @@ static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl,
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init is_acpi_reserved(u64 start, u64 end, unsigned not_used)
|
static int __devinit is_acpi_reserved(u64 start, u64 end, unsigned not_used)
|
||||||
{
|
{
|
||||||
struct resource mcfg_res;
|
struct resource mcfg_res;
|
||||||
|
|
||||||
|
@ -434,13 +441,15 @@ static int __init is_acpi_reserved(u64 start, u64 end, unsigned not_used)
|
||||||
|
|
||||||
typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type);
|
typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type);
|
||||||
|
|
||||||
static int __init is_mmconf_reserved(check_reserved_t is_reserved,
|
static int __ref is_mmconf_reserved(check_reserved_t is_reserved,
|
||||||
struct pci_mmcfg_region *cfg, int with_e820)
|
struct pci_mmcfg_region *cfg,
|
||||||
|
struct device *dev, int with_e820)
|
||||||
{
|
{
|
||||||
u64 addr = cfg->res.start;
|
u64 addr = cfg->res.start;
|
||||||
u64 size = resource_size(&cfg->res);
|
u64 size = resource_size(&cfg->res);
|
||||||
u64 old_size = size;
|
u64 old_size = size;
|
||||||
int valid = 0, num_buses;
|
int num_buses;
|
||||||
|
char *method = with_e820 ? "E820" : "ACPI motherboard resources";
|
||||||
|
|
||||||
while (!is_reserved(addr, addr + size, E820_RESERVED)) {
|
while (!is_reserved(addr, addr + size, E820_RESERVED)) {
|
||||||
size >>= 1;
|
size >>= 1;
|
||||||
|
@ -448,30 +457,76 @@ static int __init is_mmconf_reserved(check_reserved_t is_reserved,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size >= (16UL<<20) || size == old_size) {
|
if (size < (16UL<<20) && size != old_size)
|
||||||
printk(KERN_INFO PREFIX "MMCONFIG at %pR reserved in %s\n",
|
return 0;
|
||||||
&cfg->res,
|
|
||||||
with_e820 ? "E820" : "ACPI motherboard resources");
|
|
||||||
valid = 1;
|
|
||||||
|
|
||||||
if (old_size != size) {
|
if (dev)
|
||||||
/* update end_bus */
|
dev_info(dev, "MMCONFIG at %pR reserved in %s\n",
|
||||||
cfg->end_bus = cfg->start_bus + ((size>>20) - 1);
|
&cfg->res, method);
|
||||||
num_buses = cfg->end_bus - cfg->start_bus + 1;
|
else
|
||||||
cfg->res.end = cfg->res.start +
|
pr_info(PREFIX "MMCONFIG at %pR reserved in %s\n",
|
||||||
PCI_MMCFG_BUS_OFFSET(num_buses) - 1;
|
&cfg->res, method);
|
||||||
snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN,
|
|
||||||
"PCI MMCONFIG %04x [bus %02x-%02x]",
|
if (old_size != size) {
|
||||||
cfg->segment, cfg->start_bus, cfg->end_bus);
|
/* update end_bus */
|
||||||
printk(KERN_INFO PREFIX
|
cfg->end_bus = cfg->start_bus + ((size>>20) - 1);
|
||||||
"MMCONFIG for %04x [bus%02x-%02x] "
|
num_buses = cfg->end_bus - cfg->start_bus + 1;
|
||||||
"at %pR (base %#lx) (size reduced!)\n",
|
cfg->res.end = cfg->res.start +
|
||||||
cfg->segment, cfg->start_bus, cfg->end_bus,
|
PCI_MMCFG_BUS_OFFSET(num_buses) - 1;
|
||||||
&cfg->res, (unsigned long) cfg->address);
|
snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN,
|
||||||
}
|
"PCI MMCONFIG %04x [bus %02x-%02x]",
|
||||||
|
cfg->segment, cfg->start_bus, cfg->end_bus);
|
||||||
|
|
||||||
|
if (dev)
|
||||||
|
dev_info(dev,
|
||||||
|
"MMCONFIG "
|
||||||
|
"at %pR (base %#lx) (size reduced!)\n",
|
||||||
|
&cfg->res, (unsigned long) cfg->address);
|
||||||
|
else
|
||||||
|
pr_info(PREFIX
|
||||||
|
"MMCONFIG for %04x [bus%02x-%02x] "
|
||||||
|
"at %pR (base %#lx) (size reduced!)\n",
|
||||||
|
cfg->segment, cfg->start_bus, cfg->end_bus,
|
||||||
|
&cfg->res, (unsigned long) cfg->address);
|
||||||
}
|
}
|
||||||
|
|
||||||
return valid;
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __ref pci_mmcfg_check_reserved(struct device *dev,
|
||||||
|
struct pci_mmcfg_region *cfg, int early)
|
||||||
|
{
|
||||||
|
if (!early && !acpi_disabled) {
|
||||||
|
if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, 0))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (dev)
|
||||||
|
dev_info(dev, FW_INFO
|
||||||
|
"MMCONFIG at %pR not reserved in "
|
||||||
|
"ACPI motherboard resources\n",
|
||||||
|
&cfg->res);
|
||||||
|
else
|
||||||
|
pr_info(FW_INFO PREFIX
|
||||||
|
"MMCONFIG at %pR not reserved in "
|
||||||
|
"ACPI motherboard resources\n",
|
||||||
|
&cfg->res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* e820_all_mapped() is marked as __init.
|
||||||
|
* All entries from ACPI MCFG table have been checked at boot time.
|
||||||
|
* For MCFG information constructed from hotpluggable host bridge's
|
||||||
|
* _CBA method, just assume it's reserved.
|
||||||
|
*/
|
||||||
|
if (pci_mmcfg_running_state)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Don't try to do this check unless configuration
|
||||||
|
type 1 is available. how about type 2 ?*/
|
||||||
|
if (raw_pci_ops)
|
||||||
|
return is_mmconf_reserved(e820_all_mapped, cfg, dev, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init pci_mmcfg_reject_broken(int early)
|
static void __init pci_mmcfg_reject_broken(int early)
|
||||||
|
@ -479,38 +534,14 @@ static void __init pci_mmcfg_reject_broken(int early)
|
||||||
struct pci_mmcfg_region *cfg;
|
struct pci_mmcfg_region *cfg;
|
||||||
|
|
||||||
list_for_each_entry(cfg, &pci_mmcfg_list, list) {
|
list_for_each_entry(cfg, &pci_mmcfg_list, list) {
|
||||||
int valid = 0;
|
if (pci_mmcfg_check_reserved(NULL, cfg, early) == 0) {
|
||||||
|
pr_info(PREFIX "not using MMCONFIG\n");
|
||||||
if (!early && !acpi_disabled) {
|
free_all_mmcfg();
|
||||||
valid = is_mmconf_reserved(is_acpi_reserved, cfg, 0);
|
return;
|
||||||
|
|
||||||
if (valid)
|
|
||||||
continue;
|
|
||||||
else
|
|
||||||
printk(KERN_ERR FW_BUG PREFIX
|
|
||||||
"MMCONFIG at %pR not reserved in "
|
|
||||||
"ACPI motherboard resources\n",
|
|
||||||
&cfg->res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't try to do this check unless configuration
|
|
||||||
type 1 is available. how about type 2 ?*/
|
|
||||||
if (raw_pci_ops)
|
|
||||||
valid = is_mmconf_reserved(e820_all_mapped, cfg, 1);
|
|
||||||
|
|
||||||
if (!valid)
|
|
||||||
goto reject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
reject:
|
|
||||||
printk(KERN_INFO PREFIX "not using MMCONFIG\n");
|
|
||||||
free_all_mmcfg();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __initdata known_bridge;
|
|
||||||
|
|
||||||
static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
|
static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
|
||||||
struct acpi_mcfg_allocation *cfg)
|
struct acpi_mcfg_allocation *cfg)
|
||||||
{
|
{
|
||||||
|
@ -529,7 +560,7 @@ static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
printk(KERN_ERR PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
|
pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
|
||||||
"is above 4GB, ignored\n", cfg->pci_segment,
|
"is above 4GB, ignored\n", cfg->pci_segment,
|
||||||
cfg->start_bus_number, cfg->end_bus_number, cfg->address);
|
cfg->start_bus_number, cfg->end_bus_number, cfg->address);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -556,7 +587,7 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header)
|
||||||
i -= sizeof(struct acpi_mcfg_allocation);
|
i -= sizeof(struct acpi_mcfg_allocation);
|
||||||
};
|
};
|
||||||
if (entries == 0) {
|
if (entries == 0) {
|
||||||
printk(KERN_ERR PREFIX "MMCONFIG has no entries\n");
|
pr_err(PREFIX "MMCONFIG has no entries\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,8 +601,7 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header)
|
||||||
|
|
||||||
if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
|
if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
|
||||||
cfg->end_bus_number, cfg->address) == NULL) {
|
cfg->end_bus_number, cfg->address) == NULL) {
|
||||||
printk(KERN_WARNING PREFIX
|
pr_warn(PREFIX "no memory for MCFG entries\n");
|
||||||
"no memory for MCFG entries\n");
|
|
||||||
free_all_mmcfg();
|
free_all_mmcfg();
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -582,28 +612,7 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header)
|
||||||
|
|
||||||
static void __init __pci_mmcfg_init(int early)
|
static void __init __pci_mmcfg_init(int early)
|
||||||
{
|
{
|
||||||
/* MMCONFIG disabled */
|
|
||||||
if ((pci_probe & PCI_PROBE_MMCONF) == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* MMCONFIG already enabled */
|
|
||||||
if (!early && !(pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* for late to exit */
|
|
||||||
if (known_bridge)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (early) {
|
|
||||||
if (pci_mmcfg_check_hostbridge())
|
|
||||||
known_bridge = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!known_bridge)
|
|
||||||
acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
|
|
||||||
|
|
||||||
pci_mmcfg_reject_broken(early);
|
pci_mmcfg_reject_broken(early);
|
||||||
|
|
||||||
if (list_empty(&pci_mmcfg_list))
|
if (list_empty(&pci_mmcfg_list))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -620,33 +629,48 @@ static void __init __pci_mmcfg_init(int early)
|
||||||
if (pci_mmcfg_arch_init())
|
if (pci_mmcfg_arch_init())
|
||||||
pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
|
pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
|
||||||
else {
|
else {
|
||||||
/*
|
free_all_mmcfg();
|
||||||
* Signal not to attempt to insert mmcfg resources because
|
pci_mmcfg_arch_init_failed = true;
|
||||||
* the architecture mmcfg setup could not initialize.
|
|
||||||
*/
|
|
||||||
pci_mmcfg_resources_inserted = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __initdata known_bridge;
|
||||||
|
|
||||||
void __init pci_mmcfg_early_init(void)
|
void __init pci_mmcfg_early_init(void)
|
||||||
{
|
{
|
||||||
__pci_mmcfg_init(1);
|
if (pci_probe & PCI_PROBE_MMCONF) {
|
||||||
|
if (pci_mmcfg_check_hostbridge())
|
||||||
|
known_bridge = 1;
|
||||||
|
else
|
||||||
|
acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
|
||||||
|
__pci_mmcfg_init(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void __init pci_mmcfg_late_init(void)
|
void __init pci_mmcfg_late_init(void)
|
||||||
{
|
{
|
||||||
__pci_mmcfg_init(0);
|
/* MMCONFIG disabled */
|
||||||
|
if ((pci_probe & PCI_PROBE_MMCONF) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (known_bridge)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* MMCONFIG hasn't been enabled yet, try again */
|
||||||
|
if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
|
||||||
|
acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
|
||||||
|
__pci_mmcfg_init(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init pci_mmcfg_late_insert_resources(void)
|
static int __init pci_mmcfg_late_insert_resources(void)
|
||||||
{
|
{
|
||||||
/*
|
struct pci_mmcfg_region *cfg;
|
||||||
* If resources are already inserted or we are not using MMCONFIG,
|
|
||||||
* don't insert the resources.
|
pci_mmcfg_running_state = true;
|
||||||
*/
|
|
||||||
if ((pci_mmcfg_resources_inserted == 1) ||
|
/* If we are not using MMCONFIG, don't insert the resources. */
|
||||||
(pci_probe & PCI_PROBE_MMCONF) == 0 ||
|
if ((pci_probe & PCI_PROBE_MMCONF) == 0)
|
||||||
list_empty(&pci_mmcfg_list))
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -654,7 +678,9 @@ static int __init pci_mmcfg_late_insert_resources(void)
|
||||||
* marked so it won't cause request errors when __request_region is
|
* marked so it won't cause request errors when __request_region is
|
||||||
* called.
|
* called.
|
||||||
*/
|
*/
|
||||||
pci_mmcfg_insert_resources();
|
list_for_each_entry(cfg, &pci_mmcfg_list, list)
|
||||||
|
if (!cfg->res.parent)
|
||||||
|
insert_resource(&iomem_resource, &cfg->res);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -665,3 +691,101 @@ static int __init pci_mmcfg_late_insert_resources(void)
|
||||||
* with other system resources.
|
* with other system resources.
|
||||||
*/
|
*/
|
||||||
late_initcall(pci_mmcfg_late_insert_resources);
|
late_initcall(pci_mmcfg_late_insert_resources);
|
||||||
|
|
||||||
|
/* Add MMCFG information for host bridges */
|
||||||
|
int __devinit pci_mmconfig_insert(struct device *dev,
|
||||||
|
u16 seg, u8 start, u8 end,
|
||||||
|
phys_addr_t addr)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct resource *tmp = NULL;
|
||||||
|
struct pci_mmcfg_region *cfg;
|
||||||
|
|
||||||
|
if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (start > end)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mutex_lock(&pci_mmcfg_lock);
|
||||||
|
cfg = pci_mmconfig_lookup(seg, start);
|
||||||
|
if (cfg) {
|
||||||
|
if (cfg->end_bus < end)
|
||||||
|
dev_info(dev, FW_INFO
|
||||||
|
"MMCONFIG for "
|
||||||
|
"domain %04x [bus %02x-%02x] "
|
||||||
|
"only partially covers this bridge\n",
|
||||||
|
cfg->segment, cfg->start_bus, cfg->end_bus);
|
||||||
|
mutex_unlock(&pci_mmcfg_lock);
|
||||||
|
return -EEXIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!addr) {
|
||||||
|
mutex_unlock(&pci_mmcfg_lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = -EBUSY;
|
||||||
|
cfg = pci_mmconfig_alloc(seg, start, end, addr);
|
||||||
|
if (cfg == NULL) {
|
||||||
|
dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
|
||||||
|
rc = -ENOMEM;
|
||||||
|
} else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
|
||||||
|
dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n",
|
||||||
|
&cfg->res);
|
||||||
|
} else {
|
||||||
|
/* Insert resource if it's not in boot stage */
|
||||||
|
if (pci_mmcfg_running_state)
|
||||||
|
tmp = insert_resource_conflict(&iomem_resource,
|
||||||
|
&cfg->res);
|
||||||
|
|
||||||
|
if (tmp) {
|
||||||
|
dev_warn(dev,
|
||||||
|
"MMCONFIG %pR conflicts with "
|
||||||
|
"%s %pR\n",
|
||||||
|
&cfg->res, tmp->name, tmp);
|
||||||
|
} else if (pci_mmcfg_arch_map(cfg)) {
|
||||||
|
dev_warn(dev, "fail to map MMCONFIG %pR.\n",
|
||||||
|
&cfg->res);
|
||||||
|
} else {
|
||||||
|
list_add_sorted(cfg);
|
||||||
|
dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
|
||||||
|
&cfg->res, (unsigned long)addr);
|
||||||
|
cfg = NULL;
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg) {
|
||||||
|
if (cfg->res.parent)
|
||||||
|
release_resource(&cfg->res);
|
||||||
|
kfree(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&pci_mmcfg_lock);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete MMCFG information for host bridges */
|
||||||
|
int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
|
||||||
|
{
|
||||||
|
struct pci_mmcfg_region *cfg;
|
||||||
|
|
||||||
|
mutex_lock(&pci_mmcfg_lock);
|
||||||
|
list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
|
||||||
|
if (cfg->segment == seg && cfg->start_bus == start &&
|
||||||
|
cfg->end_bus == end) {
|
||||||
|
list_del_rcu(&cfg->list);
|
||||||
|
synchronize_rcu();
|
||||||
|
pci_mmcfg_arch_unmap(cfg);
|
||||||
|
if (cfg->res.parent)
|
||||||
|
release_resource(&cfg->res);
|
||||||
|
mutex_unlock(&pci_mmcfg_lock);
|
||||||
|
kfree(cfg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
mutex_unlock(&pci_mmcfg_lock);
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/rcupdate.h>
|
||||||
#include <asm/e820.h>
|
#include <asm/e820.h>
|
||||||
#include <asm/pci_x86.h>
|
#include <asm/pci_x86.h>
|
||||||
#include <acpi/acpi.h>
|
#include <acpi/acpi.h>
|
||||||
|
@ -60,9 +61,12 @@ err: *value = -1;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
base = get_base_addr(seg, bus, devfn);
|
base = get_base_addr(seg, bus, devfn);
|
||||||
if (!base)
|
if (!base) {
|
||||||
|
rcu_read_unlock();
|
||||||
goto err;
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&pci_config_lock, flags);
|
raw_spin_lock_irqsave(&pci_config_lock, flags);
|
||||||
|
|
||||||
|
@ -80,6 +84,7 @@ err: *value = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
|
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -93,9 +98,12 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
|
||||||
if ((bus > 255) || (devfn > 255) || (reg > 4095))
|
if ((bus > 255) || (devfn > 255) || (reg > 4095))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
base = get_base_addr(seg, bus, devfn);
|
base = get_base_addr(seg, bus, devfn);
|
||||||
if (!base)
|
if (!base) {
|
||||||
|
rcu_read_unlock();
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&pci_config_lock, flags);
|
raw_spin_lock_irqsave(&pci_config_lock, flags);
|
||||||
|
|
||||||
|
@ -113,11 +121,12 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
|
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct pci_raw_ops pci_mmcfg = {
|
const struct pci_raw_ops pci_mmcfg = {
|
||||||
.read = pci_mmcfg_read,
|
.read = pci_mmcfg_read,
|
||||||
.write = pci_mmcfg_write,
|
.write = pci_mmcfg_write,
|
||||||
};
|
};
|
||||||
|
@ -132,3 +141,18 @@ int __init pci_mmcfg_arch_init(void)
|
||||||
void __init pci_mmcfg_arch_free(void)
|
void __init pci_mmcfg_arch_free(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int __devinit pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
/* Invalidate the cached mmcfg map entry. */
|
||||||
|
raw_spin_lock_irqsave(&pci_config_lock, flags);
|
||||||
|
mmcfg_last_accessed_device = 0;
|
||||||
|
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/bitmap.h>
|
#include <linux/bitmap.h>
|
||||||
|
#include <linux/rcupdate.h>
|
||||||
#include <asm/e820.h>
|
#include <asm/e820.h>
|
||||||
#include <asm/pci_x86.h>
|
#include <asm/pci_x86.h>
|
||||||
|
|
||||||
|
@ -34,9 +35,12 @@ err: *value = -1;
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
addr = pci_dev_base(seg, bus, devfn);
|
addr = pci_dev_base(seg, bus, devfn);
|
||||||
if (!addr)
|
if (!addr) {
|
||||||
|
rcu_read_unlock();
|
||||||
goto err;
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
switch (len) {
|
switch (len) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -49,6 +53,7 @@ err: *value = -1;
|
||||||
*value = mmio_config_readl(addr + reg);
|
*value = mmio_config_readl(addr + reg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -62,9 +67,12 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
|
||||||
if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
|
if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
addr = pci_dev_base(seg, bus, devfn);
|
addr = pci_dev_base(seg, bus, devfn);
|
||||||
if (!addr)
|
if (!addr) {
|
||||||
|
rcu_read_unlock();
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
switch (len) {
|
switch (len) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -77,16 +85,17 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
|
||||||
mmio_config_writel(addr + reg, value);
|
mmio_config_writel(addr + reg, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct pci_raw_ops pci_mmcfg = {
|
const struct pci_raw_ops pci_mmcfg = {
|
||||||
.read = pci_mmcfg_read,
|
.read = pci_mmcfg_read,
|
||||||
.write = pci_mmcfg_write,
|
.write = pci_mmcfg_write,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void __iomem * __init mcfg_ioremap(struct pci_mmcfg_region *cfg)
|
static void __iomem * __devinit mcfg_ioremap(struct pci_mmcfg_region *cfg)
|
||||||
{
|
{
|
||||||
void __iomem *addr;
|
void __iomem *addr;
|
||||||
u64 start, size;
|
u64 start, size;
|
||||||
|
@ -105,16 +114,14 @@ int __init pci_mmcfg_arch_init(void)
|
||||||
{
|
{
|
||||||
struct pci_mmcfg_region *cfg;
|
struct pci_mmcfg_region *cfg;
|
||||||
|
|
||||||
list_for_each_entry(cfg, &pci_mmcfg_list, list) {
|
list_for_each_entry(cfg, &pci_mmcfg_list, list)
|
||||||
cfg->virt = mcfg_ioremap(cfg);
|
if (pci_mmcfg_arch_map(cfg)) {
|
||||||
if (!cfg->virt) {
|
|
||||||
printk(KERN_ERR PREFIX "can't map MMCONFIG at %pR\n",
|
|
||||||
&cfg->res);
|
|
||||||
pci_mmcfg_arch_free();
|
pci_mmcfg_arch_free();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
raw_pci_ext_ops = &pci_mmcfg;
|
raw_pci_ext_ops = &pci_mmcfg;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,10 +129,25 @@ void __init pci_mmcfg_arch_free(void)
|
||||||
{
|
{
|
||||||
struct pci_mmcfg_region *cfg;
|
struct pci_mmcfg_region *cfg;
|
||||||
|
|
||||||
list_for_each_entry(cfg, &pci_mmcfg_list, list) {
|
list_for_each_entry(cfg, &pci_mmcfg_list, list)
|
||||||
if (cfg->virt) {
|
pci_mmcfg_arch_unmap(cfg);
|
||||||
iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
|
}
|
||||||
cfg->virt = NULL;
|
|
||||||
}
|
int __devinit pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
|
||||||
|
{
|
||||||
|
cfg->virt = mcfg_ioremap(cfg);
|
||||||
|
if (!cfg->virt) {
|
||||||
|
pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
|
||||||
|
{
|
||||||
|
if (cfg && cfg->virt) {
|
||||||
|
iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
|
||||||
|
cfg->virt = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -505,6 +505,8 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
||||||
strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS);
|
strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS);
|
||||||
device->driver_data = root;
|
device->driver_data = root;
|
||||||
|
|
||||||
|
root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All supported architectures that use ACPI have support for
|
* All supported architectures that use ACPI have support for
|
||||||
* PCI domains, so we indicate this in _OSC support capabilities.
|
* PCI domains, so we indicate this in _OSC support capabilities.
|
||||||
|
|
|
@ -162,6 +162,20 @@ acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev)
|
||||||
return remove_pm_notifier(dev, pci_acpi_wake_dev);
|
return remove_pm_notifier(dev, pci_acpi_wake_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
|
||||||
|
{
|
||||||
|
acpi_status status = AE_NOT_EXIST;
|
||||||
|
unsigned long long mcfg_addr;
|
||||||
|
|
||||||
|
if (handle)
|
||||||
|
status = acpi_evaluate_integer(handle, METHOD_NAME__CBA,
|
||||||
|
NULL, &mcfg_addr);
|
||||||
|
if (ACPI_FAILURE(status))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (phys_addr_t)mcfg_addr;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* _SxD returns the D-state with the highest power
|
* _SxD returns the D-state with the highest power
|
||||||
* (lowest D-state number) supported in the S-state "x".
|
* (lowest D-state number) supported in the S-state "x".
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
#define METHOD_NAME__AEI "_AEI"
|
#define METHOD_NAME__AEI "_AEI"
|
||||||
#define METHOD_NAME__PRW "_PRW"
|
#define METHOD_NAME__PRW "_PRW"
|
||||||
#define METHOD_NAME__SRS "_SRS"
|
#define METHOD_NAME__SRS "_SRS"
|
||||||
|
#define METHOD_NAME__CBA "_CBA"
|
||||||
|
|
||||||
/* Method names - these methods must appear at the namespace root */
|
/* Method names - these methods must appear at the namespace root */
|
||||||
|
|
||||||
|
|
|
@ -401,6 +401,7 @@ struct acpi_pci_root {
|
||||||
|
|
||||||
u32 osc_support_set; /* _OSC state of support bits */
|
u32 osc_support_set; /* _OSC state of support bits */
|
||||||
u32 osc_control_set; /* _OSC state of control bits */
|
u32 osc_control_set; /* _OSC state of control bits */
|
||||||
|
phys_addr_t mcfg_addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* helper */
|
/* helper */
|
||||||
|
|
|
@ -17,6 +17,7 @@ extern acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev);
|
||||||
extern acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev,
|
extern acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev,
|
||||||
struct pci_dev *pci_dev);
|
struct pci_dev *pci_dev);
|
||||||
extern acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev);
|
extern acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev);
|
||||||
|
extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle);
|
||||||
|
|
||||||
static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev)
|
static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
|
|
|
@ -66,7 +66,7 @@ extern int sfi_acpi_table_parse(char *signature, char *oem_id,
|
||||||
char *oem_table_id,
|
char *oem_table_id,
|
||||||
int (*handler)(struct acpi_table_header *));
|
int (*handler)(struct acpi_table_header *));
|
||||||
|
|
||||||
static inline int acpi_sfi_table_parse(char *signature,
|
static inline int __init acpi_sfi_table_parse(char *signature,
|
||||||
int (*handler)(struct acpi_table_header *))
|
int (*handler)(struct acpi_table_header *))
|
||||||
{
|
{
|
||||||
if (!acpi_table_parse(signature, handler))
|
if (!acpi_table_parse(signature, handler))
|
||||||
|
@ -83,7 +83,7 @@ static inline int sfi_acpi_table_parse(char *signature, char *oem_id,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int acpi_sfi_table_parse(char *signature,
|
static inline int __init acpi_sfi_table_parse(char *signature,
|
||||||
int (*handler)(struct acpi_table_header *))
|
int (*handler)(struct acpi_table_header *))
|
||||||
{
|
{
|
||||||
return acpi_table_parse(signature, handler);
|
return acpi_table_parse(signature, handler);
|
||||||
|
|
Loading…
Reference in New Issue