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 ...@@ -1030,12 +1030,16 @@ config PCI_GOBIOS
PCI-based systems don't have any BIOS at all. Linux can also try to PCI-based systems don't have any BIOS at all. Linux can also try to
detect the PCI hardware directly without using the BIOS. detect the PCI hardware directly without using the BIOS.
With this option, you can specify how Linux should detect the PCI With this option, you can specify how Linux should detect the
devices. If you choose "BIOS", the BIOS will be used, if you choose PCI devices. If you choose "BIOS", the BIOS will be used,
"Direct", the BIOS won't be used, and if you choose "Any", the if you choose "Direct", the BIOS won't be used, and if you
kernel will try the direct access method and falls back to the BIOS choose "MMConfig", then PCI Express MMCONFIG will be used.
if that doesn't work. If unsure, go with the default, which is If you choose "Any", the kernel will try MMCONFIG, then the
"Any". 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 config PCI_GODIRECT
bool "Direct" bool "Direct"
...@@ -1055,6 +1059,12 @@ config PCI_DIRECT ...@@ -1055,6 +1059,12 @@ config PCI_DIRECT
depends on PCI && ((PCI_GODIRECT || PCI_GOANY) || X86_VISWS) depends on PCI && ((PCI_GODIRECT || PCI_GOANY) || X86_VISWS)
default y default y
config PCI_MMCONFIG
bool
depends on PCI && (PCI_GOMMCONFIG || PCI_GOANY)
select ACPI_BOOT
default y
config PCI_USE_VECTOR config PCI_USE_VECTOR
bool "Vector-based interrupt indexing" bool "Vector-based interrupt indexing"
depends on X86_LOCAL_APIC && X86_IO_APIC depends on X86_LOCAL_APIC && X86_IO_APIC
......
...@@ -104,6 +104,31 @@ char *__acpi_map_table(unsigned long phys, unsigned long size) ...@@ -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 #ifdef CONFIG_X86_LOCAL_APIC
static int __init static int __init
acpi_parse_madt ( acpi_parse_madt (
...@@ -598,6 +623,12 @@ acpi_boot_init (void) ...@@ -598,6 +623,12 @@ acpi_boot_init (void)
(void) acpi_table_parse(ACPI_HPET, acpi_parse_hpet); (void) acpi_table_parse(ACPI_HPET, acpi_parse_hpet);
#endif #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; return 0;
} }
obj-y := i386.o obj-y := i386.o
obj-$(CONFIG_PCI_BIOS) += pcbios.o obj-$(CONFIG_PCI_BIOS) += pcbios.o
obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o
obj-$(CONFIG_PCI_DIRECT) += direct.o obj-$(CONFIG_PCI_DIRECT) += direct.o
pci-y := fixup.o pci-y := fixup.o
......
...@@ -20,7 +20,8 @@ ...@@ -20,7 +20,8 @@
extern void pcibios_sort(void); extern void pcibios_sort(void);
#endif #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; int pcibios_last_bus = -1;
struct pci_bus *pci_root_bus = NULL; struct pci_bus *pci_root_bus = NULL;
...@@ -197,6 +198,12 @@ char * __devinit pcibios_setup(char *str) ...@@ -197,6 +198,12 @@ char * __devinit pcibios_setup(char *str)
pci_probe = PCI_PROBE_CONF2 | PCI_NO_CHECKS; pci_probe = PCI_PROBE_CONF2 | PCI_NO_CHECKS;
return NULL; return NULL;
} }
#endif
#ifdef CONFIG_PCI_MMCONFIG
else if (!strcmp(str, "nommconf")) {
pci_probe &= ~PCI_PROBE_MMCONF;
return NULL;
}
#endif #endif
else if (!strcmp(str, "noacpi")) { else if (!strcmp(str, "noacpi")) {
acpi_noirq_set(); 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 @@ ...@@ -15,6 +15,9 @@
#define PCI_PROBE_BIOS 0x0001 #define PCI_PROBE_BIOS 0x0001
#define PCI_PROBE_CONF1 0x0002 #define PCI_PROBE_CONF1 0x0002
#define PCI_PROBE_CONF2 0x0004 #define PCI_PROBE_CONF2 0x0004
#define PCI_PROBE_MMCONF 0x0008
#define PCI_PROBE_MASK 0x000f
#define PCI_NO_SORT 0x0100 #define PCI_NO_SORT 0x0100
#define PCI_BIOS_SORT 0x0200 #define PCI_BIOS_SORT 0x0200
#define PCI_NO_CHECKS 0x0400 #define PCI_NO_CHECKS 0x0400
......
...@@ -58,6 +58,7 @@ static char *acpi_table_signatures[ACPI_TABLE_COUNT] = { ...@@ -58,6 +58,7 @@ static char *acpi_table_signatures[ACPI_TABLE_COUNT] = {
[ACPI_SSDT] = "SSDT", [ACPI_SSDT] = "SSDT",
[ACPI_SPMI] = "SPMI", [ACPI_SPMI] = "SPMI",
[ACPI_HPET] = "HPET", [ACPI_HPET] = "HPET",
[ACPI_MCFG] = "MCFG",
}; };
static char *mps_inti_flags_polarity[] = { "dfl", "high", "res", "low" }; static char *mps_inti_flags_polarity[] = { "dfl", "high", "res", "low" };
......
...@@ -70,6 +70,9 @@ enum fixed_addresses { ...@@ -70,6 +70,9 @@ enum fixed_addresses {
#ifdef CONFIG_ACPI_BOOT #ifdef CONFIG_ACPI_BOOT
FIX_ACPI_BEGIN, FIX_ACPI_BEGIN,
FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1, FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1,
#endif
#ifdef CONFIG_PCI_MMCONFIG
FIX_PCIE_MCFG,
#endif #endif
__end_of_permanent_fixed_addresses, __end_of_permanent_fixed_addresses,
/* temporary boot-time mappings, used before ioremap() is functional */ /* temporary boot-time mappings, used before ioremap() is functional */
......
...@@ -317,6 +317,15 @@ struct acpi_table_ecdt { ...@@ -317,6 +317,15 @@ struct acpi_table_ecdt {
char ec_id[0]; char ec_id[0];
} __attribute__ ((packed)); } __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 */ /* Table Handlers */
enum acpi_table_id { enum acpi_table_id {
...@@ -338,6 +347,7 @@ enum acpi_table_id { ...@@ -338,6 +347,7 @@ enum acpi_table_id {
ACPI_SSDT, ACPI_SSDT,
ACPI_SPMI, ACPI_SPMI,
ACPI_HPET, ACPI_HPET,
ACPI_MCFG,
ACPI_TABLE_COUNT ACPI_TABLE_COUNT
}; };
...@@ -369,6 +379,8 @@ void acpi_numa_arch_fixup(void); ...@@ -369,6 +379,8 @@ void acpi_numa_arch_fixup(void);
extern int acpi_mp_config; extern int acpi_mp_config;
extern u32 pci_mmcfg_base_addr;
#else /*!CONFIG_ACPI_BOOT*/ #else /*!CONFIG_ACPI_BOOT*/
#define acpi_mp_config 0 #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