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 {
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 pci_mmcfg;
extern const struct pci_raw_ops pci_direct_conf1;
extern bool port_cf9_safe;
......@@ -135,6 +136,12 @@ struct pci_mmcfg_region {
extern int __init pci_mmcfg_arch_init(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 list_head pci_mmcfg_list;
......
......@@ -13,6 +13,12 @@ struct pci_root_info {
unsigned int res_num;
struct resource *res;
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;
......@@ -119,6 +125,81 @@ void __init pci_acpi_crs_quirks(void)
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
resource_to_addr(struct acpi_resource *resource,
struct acpi_resource_address64 *addr)
......@@ -233,13 +314,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
}
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;
}
......@@ -331,8 +405,11 @@ static void __release_pci_root_info(struct pci_root_info *info)
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;
......@@ -372,7 +449,7 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root)
int domain = root->segment;
int busnum = root->secondary.start;
LIST_HEAD(resources);
struct pci_bus *bus;
struct pci_bus *bus = NULL;
struct pci_sysdata *sd;
int node;
#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);
}
bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, sd,
&resources);
if (!setup_mcfg_map(info, domain, (u8)root->secondary.start,
(u8)root->secondary.end, root->mcfg_addr))
bus = pci_create_root_bus(NULL, busnum, &pci_root_ops,
sd, &resources);
if (bus) {
pci_scan_child_bus(bus);
pci_set_host_bridge_release(
......
This diff is collapsed.
......@@ -11,6 +11,7 @@
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/rcupdate.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
#include <acpi/acpi.h>
......@@ -60,9 +61,12 @@ err: *value = -1;
return -EINVAL;
}
rcu_read_lock();
base = get_base_addr(seg, bus, devfn);
if (!base)
if (!base) {
rcu_read_unlock();
goto err;
}
raw_spin_lock_irqsave(&pci_config_lock, flags);
......@@ -80,6 +84,7 @@ err: *value = -1;
break;
}
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
rcu_read_unlock();
return 0;
}
......@@ -93,9 +98,12 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
if ((bus > 255) || (devfn > 255) || (reg > 4095))
return -EINVAL;
rcu_read_lock();
base = get_base_addr(seg, bus, devfn);
if (!base)
if (!base) {
rcu_read_unlock();
return -EINVAL;
}
raw_spin_lock_irqsave(&pci_config_lock, flags);
......@@ -113,11 +121,12 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
break;
}
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
rcu_read_unlock();
return 0;
}
static const struct pci_raw_ops pci_mmcfg = {
const struct pci_raw_ops pci_mmcfg = {
.read = pci_mmcfg_read,
.write = pci_mmcfg_write,
};
......@@ -132,3 +141,18 @@ int __init pci_mmcfg_arch_init(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/acpi.h>
#include <linux/bitmap.h>
#include <linux/rcupdate.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
......@@ -34,9 +35,12 @@ err: *value = -1;
return -EINVAL;
}
rcu_read_lock();
addr = pci_dev_base(seg, bus, devfn);
if (!addr)
if (!addr) {
rcu_read_unlock();
goto err;
}
switch (len) {
case 1:
......@@ -49,6 +53,7 @@ err: *value = -1;
*value = mmio_config_readl(addr + reg);
break;
}
rcu_read_unlock();
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)))
return -EINVAL;
rcu_read_lock();
addr = pci_dev_base(seg, bus, devfn);
if (!addr)
if (!addr) {
rcu_read_unlock();
return -EINVAL;
}
switch (len) {
case 1:
......@@ -77,16 +85,17 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
mmio_config_writel(addr + reg, value);
break;
}
rcu_read_unlock();
return 0;
}
static const struct pci_raw_ops pci_mmcfg = {
const struct pci_raw_ops pci_mmcfg = {
.read = pci_mmcfg_read,
.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;
u64 start, size;
......@@ -105,16 +114,14 @@ int __init pci_mmcfg_arch_init(void)
{
struct pci_mmcfg_region *cfg;
list_for_each_entry(cfg, &pci_mmcfg_list, list) {
cfg->virt = mcfg_ioremap(cfg);
if (!cfg->virt) {
printk(KERN_ERR PREFIX "can't map MMCONFIG at %pR\n",
&cfg->res);
list_for_each_entry(cfg, &pci_mmcfg_list, list)
if (pci_mmcfg_arch_map(cfg)) {
pci_mmcfg_arch_free();
return 0;
}
}
raw_pci_ext_ops = &pci_mmcfg;
return 1;
}
......@@ -122,10 +129,25 @@ void __init pci_mmcfg_arch_free(void)
{
struct pci_mmcfg_region *cfg;
list_for_each_entry(cfg, &pci_mmcfg_list, list) {
if (cfg->virt) {
iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
cfg->virt = NULL;
}
list_for_each_entry(cfg, &pci_mmcfg_list, list)
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));
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);
device->driver_data = root;
root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle);
/*
* All supported architectures that use ACPI have support for
* 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);
}
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
* (lowest D-state number) supported in the S-state "x".
......
......@@ -62,6 +62,7 @@
#define METHOD_NAME__AEI "_AEI"
#define METHOD_NAME__PRW "_PRW"
#define METHOD_NAME__SRS "_SRS"
#define METHOD_NAME__CBA "_CBA"
/* 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_control_set; /* _OSC state of control bits */
phys_addr_t mcfg_addr;
};
/* 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,
struct pci_dev *pci_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)
{
......
......@@ -66,7 +66,7 @@ extern int sfi_acpi_table_parse(char *signature, char *oem_id,
char *oem_table_id,
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 *))
{
if (!acpi_table_parse(signature, handler))
......@@ -83,7 +83,7 @@ static inline int sfi_acpi_table_parse(char *signature, char *oem_id,
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 *))
{
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