Commit e5028b52 authored by Bjorn Helgaas's avatar Bjorn Helgaas

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
parents 0f6662a4 39703851
...@@ -100,6 +100,7 @@ struct pci_raw_ops { ...@@ -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 { ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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 @@ ...@@ -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,23 +49,24 @@ static __init void free_all_mmcfg(void) ...@@ -45,23 +49,24 @@ 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 start,
int end, u64 addr) int end, u64 addr)
{ {
struct pci_mmcfg_region *new; struct pci_mmcfg_region *new;
...@@ -79,8 +84,6 @@ static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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,18 +371,7 @@ static int __init pci_mmcfg_check_hostbridge(void) ...@@ -353,18 +371,7 @@ 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,
{
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) void *data)
{ {
struct resource *mcfg_res = data; struct resource *mcfg_res = data;
...@@ -401,7 +408,7 @@ static acpi_status __init check_mcfg_resource(struct acpi_resource *res, ...@@ -401,7 +408,7 @@ 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, ...@@ -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) ...@@ -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,11 +457,15 @@ static int __init is_mmconf_reserved(check_reserved_t is_reserved, ...@@ -448,11 +457,15 @@ 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"); if (dev)
valid = 1; dev_info(dev, "MMCONFIG at %pR reserved in %s\n",
&cfg->res, method);
else
pr_info(PREFIX "MMCONFIG at %pR reserved in %s\n",
&cfg->res, method);
if (old_size != size) { if (old_size != size) {
/* update end_bus */ /* update end_bus */
...@@ -463,54 +476,72 @@ static int __init is_mmconf_reserved(check_reserved_t is_reserved, ...@@ -463,54 +476,72 @@ static int __init is_mmconf_reserved(check_reserved_t is_reserved,
snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN, snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN,
"PCI MMCONFIG %04x [bus %02x-%02x]", "PCI MMCONFIG %04x [bus %02x-%02x]",
cfg->segment, cfg->start_bus, cfg->end_bus); cfg->segment, cfg->start_bus, cfg->end_bus);
printk(KERN_INFO PREFIX
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] " "MMCONFIG for %04x [bus%02x-%02x] "
"at %pR (base %#lx) (size reduced!)\n", "at %pR (base %#lx) (size reduced!)\n",
cfg->segment, cfg->start_bus, cfg->end_bus, cfg->segment, cfg->start_bus, cfg->end_bus,
&cfg->res, (unsigned long) cfg->address); &cfg->res, (unsigned long) cfg->address);
} }
}
return valid; return 1;
} }
static void __init pci_mmcfg_reject_broken(int early) static int __ref pci_mmcfg_check_reserved(struct device *dev,
struct pci_mmcfg_region *cfg, int early)
{ {
struct pci_mmcfg_region *cfg;
list_for_each_entry(cfg, &pci_mmcfg_list, list) {
int valid = 0;
if (!early && !acpi_disabled) { if (!early && !acpi_disabled) {
valid = is_mmconf_reserved(is_acpi_reserved, cfg, 0); if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, 0))
return 1;
if (valid) if (dev)
continue; dev_info(dev, FW_INFO
"MMCONFIG at %pR not reserved in "
"ACPI motherboard resources\n",
&cfg->res);
else else
printk(KERN_ERR FW_BUG PREFIX pr_info(FW_INFO PREFIX
"MMCONFIG at %pR not reserved in " "MMCONFIG at %pR not reserved in "
"ACPI motherboard resources\n", "ACPI motherboard resources\n",
&cfg->res); &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 /* Don't try to do this check unless configuration
type 1 is available. how about type 2 ?*/ type 1 is available. how about type 2 ?*/
if (raw_pci_ops) if (raw_pci_ops)
valid = is_mmconf_reserved(e820_all_mapped, cfg, 1); return is_mmconf_reserved(e820_all_mapped, cfg, dev, 1);
if (!valid) return 0;
goto reject; }
}
return; static void __init pci_mmcfg_reject_broken(int early)
{
struct pci_mmcfg_region *cfg;
reject: list_for_each_entry(cfg, &pci_mmcfg_list, list) {
printk(KERN_INFO PREFIX "not using MMCONFIG\n"); if (pci_mmcfg_check_reserved(NULL, cfg, early) == 0) {
pr_info(PREFIX "not using MMCONFIG\n");
free_all_mmcfg(); free_all_mmcfg();
return;
}
}
} }
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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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)
{ {
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); __pci_mmcfg_init(1);
}
} }
void __init pci_mmcfg_late_init(void) void __init pci_mmcfg_late_init(void)
{ {
/* 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); __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) ...@@ -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) ...@@ -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 @@ ...@@ -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; ...@@ -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; ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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 @@ ...@@ -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; ...@@ -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; ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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);
}
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)); iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
cfg->virt = NULL; cfg->virt = NULL;
} }
}
} }
...@@ -505,6 +505,8 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) ...@@ -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) ...@@ -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 @@ ...@@ -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 { ...@@ -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); ...@@ -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, ...@@ -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, ...@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment