Commit cec1ea6b authored by Len Brown's avatar Len Brown

[ACPI] Support for PCI MMCONFIG for PCI Express (Matt Wilcox)

parent 594473bf
......@@ -1030,12 +1030,16 @@ config PCI_GOBIOS
PCI-based systems don't have any BIOS at all. Linux can also try to
detect the PCI hardware directly without using the BIOS.
With this option, you can specify how Linux should detect the PCI
devices. If you choose "BIOS", the BIOS will be used, if you choose
"Direct", the BIOS won't be used, and if you choose "Any", the
kernel will try the direct access method and falls back to the BIOS
if that doesn't work. If unsure, go with the default, which is
"Any".
With this option, you can specify how Linux should detect the
PCI devices. If you choose "BIOS", the BIOS will be used,
if you choose "Direct", the BIOS won't be used, and if you
choose "MMConfig", then PCI Express MMCONFIG will be used.
If you choose "Any", the kernel will try MMCONFIG, then the
direct access method and falls back to the BIOS if that doesn't
work. If unsure, go with the default, which is "Any".
config PCI_GOMMCONFIG
bool "MMConfig"
config PCI_GODIRECT
bool "Direct"
......@@ -1055,6 +1059,12 @@ config PCI_DIRECT
depends on PCI && ((PCI_GODIRECT || PCI_GOANY) || X86_VISWS)
default y
config PCI_MMCONFIG
bool
depends on PCI && (PCI_GOMMCONFIG || PCI_GOANY)
select ACPI_BOOT
default y
config PCI_USE_VECTOR
bool "Vector-based interrupt indexing"
depends on X86_LOCAL_APIC && X86_IO_APIC
......
......@@ -104,6 +104,31 @@ char *__acpi_map_table(unsigned long phys, unsigned long size)
}
#ifdef CONFIG_PCI_MMCONFIG
static int __init acpi_parse_mcfg(unsigned long phys_addr, unsigned long size)
{
struct acpi_table_mcfg *mcfg;
if (!phys_addr || !size)
return -EINVAL;
mcfg = (struct acpi_table_mcfg *) __acpi_map_table(phys_addr, size);
if (!mcfg) {
printk(KERN_WARNING PREFIX "Unable to map MCFG\n");
return -ENODEV;
}
if (mcfg->base_reserved) {
printk(KERN_ERR PREFIX "MMCONFIG not in low 4GB of memory\n");
return -ENODEV;
}
pci_mmcfg_base_addr = mcfg->base_address;
return 0;
}
#endif /* CONFIG_PCI_MMCONFIG */
#ifdef CONFIG_X86_LOCAL_APIC
static int __init
acpi_parse_madt (
......@@ -598,6 +623,12 @@ acpi_boot_init (void)
(void) acpi_table_parse(ACPI_HPET, acpi_parse_hpet);
#endif
#ifdef CONFIG_PCI_MMCONFIG
error = acpi_table_parse(ACPI_MCFG, acpi_parse_mcfg);
if (error)
printk(KERN_ERR PREFIX "Error %d parsing MCFG\n", error);
#endif
return 0;
}
obj-y := i386.o
obj-$(CONFIG_PCI_BIOS) += pcbios.o
obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o
obj-$(CONFIG_PCI_DIRECT) += direct.o
pci-y := fixup.o
......
......@@ -20,7 +20,8 @@
extern void pcibios_sort(void);
#endif
unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2;
unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2 |
PCI_PROBE_MMCONF;
int pcibios_last_bus = -1;
struct pci_bus *pci_root_bus = NULL;
......@@ -197,6 +198,12 @@ char * __devinit pcibios_setup(char *str)
pci_probe = PCI_PROBE_CONF2 | PCI_NO_CHECKS;
return NULL;
}
#endif
#ifdef CONFIG_PCI_MMCONFIG
else if (!strcmp(str, "nommconf")) {
pci_probe &= ~PCI_PROBE_MMCONF;
return NULL;
}
#endif
else if (!strcmp(str, "noacpi")) {
acpi_noirq_set();
......
/*
* mmconfig.c - Low-level direct PCI config space access via MMCONFIG
*/
#include <linux/pci.h>
#include <linux/init.h>
#include "pci.h"
/* The physical address of the MMCONFIG aperture. Set from ACPI tables. */
u32 pci_mmcfg_base_addr;
#define mmcfg_virt_addr (fix_to_virt(FIX_PCIE_MCFG))
/* The base address of the last MMCONFIG device accessed */
static u32 mmcfg_last_accessed_device;
/*
* Functions for accessing PCI configuration space with MMCONFIG accesses
*/
static inline void pci_exp_set_dev_base(int bus, int devfn)
{
u32 dev_base = pci_mmcfg_base_addr | (bus << 20) | (devfn << 12);
if (dev_base != mmcfg_last_accessed_device) {
mmcfg_last_accessed_device = dev_base;
set_fixmap(FIX_PCIE_MCFG, dev_base);
}
}
static int pci_mmcfg_read(int seg, int bus, int devfn, int reg, int len, u32 *value)
{
unsigned long flags;
if (!value || (bus > 255) || (devfn > 255) || (reg > 4095))
return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags);
pci_exp_set_dev_base(bus, devfn);
switch (len) {
case 1:
*value = readb(mmcfg_virt_addr + reg);
break;
case 2:
*value = readw(mmcfg_virt_addr + reg);
break;
case 4:
*value = readl(mmcfg_virt_addr + reg);
break;
}
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
static int pci_mmcfg_write(int seg, int bus, int devfn, int reg, int len, u32 value)
{
unsigned long flags;
if ((bus > 255) || (devfn > 255) || (reg > 4095))
return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags);
pci_exp_set_dev_base(bus, devfn);
switch (len) {
case 1:
writeb(value, mmcfg_virt_addr + reg);
break;
case 2:
writew(value, mmcfg_virt_addr + reg);
break;
case 4:
writel(value, mmcfg_virt_addr + reg);
break;
}
/* Dummy read to flush PCI write */
readl(mmcfg_virt_addr);
spin_unlock_irqrestore(&pci_config_lock, flags);
return 0;
}
static struct pci_raw_ops pci_mmcfg = {
.read = pci_mmcfg_read,
.write = pci_mmcfg_write,
};
static int __init pci_mmcfg_init(void)
{
if ((pci_probe & PCI_PROBE_MMCONF) == 0)
goto out;
if (!pci_mmcfg_base_addr)
goto out;
printk(KERN_INFO "PCI: Using MMCONFIG\n");
raw_pci_ops = &pci_mmcfg;
pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
out:
return 0;
}
arch_initcall(pci_mmcfg_init);
......@@ -15,6 +15,9 @@
#define PCI_PROBE_BIOS 0x0001
#define PCI_PROBE_CONF1 0x0002
#define PCI_PROBE_CONF2 0x0004
#define PCI_PROBE_MMCONF 0x0008
#define PCI_PROBE_MASK 0x000f
#define PCI_NO_SORT 0x0100
#define PCI_BIOS_SORT 0x0200
#define PCI_NO_CHECKS 0x0400
......
......@@ -58,6 +58,7 @@ static char *acpi_table_signatures[ACPI_TABLE_COUNT] = {
[ACPI_SSDT] = "SSDT",
[ACPI_SPMI] = "SPMI",
[ACPI_HPET] = "HPET",
[ACPI_MCFG] = "MCFG",
};
static char *mps_inti_flags_polarity[] = { "dfl", "high", "res", "low" };
......
......@@ -70,6 +70,9 @@ enum fixed_addresses {
#ifdef CONFIG_ACPI_BOOT
FIX_ACPI_BEGIN,
FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1,
#endif
#ifdef CONFIG_PCI_MMCONFIG
FIX_PCIE_MCFG,
#endif
__end_of_permanent_fixed_addresses,
/* temporary boot-time mappings, used before ioremap() is functional */
......
......@@ -317,6 +317,15 @@ struct acpi_table_ecdt {
char ec_id[0];
} __attribute__ ((packed));
/* PCI MMCONFIG */
struct acpi_table_mcfg {
struct acpi_table_header header;
u8 reserved[8];
u32 base_address;
u32 base_reserved;
} __attribute__ ((packed));
/* Table Handlers */
enum acpi_table_id {
......@@ -338,6 +347,7 @@ enum acpi_table_id {
ACPI_SSDT,
ACPI_SPMI,
ACPI_HPET,
ACPI_MCFG,
ACPI_TABLE_COUNT
};
......@@ -369,6 +379,8 @@ void acpi_numa_arch_fixup(void);
extern int acpi_mp_config;
extern u32 pci_mmcfg_base_addr;
#else /*!CONFIG_ACPI_BOOT*/
#define acpi_mp_config 0
......
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