summaryrefslogtreecommitdiff
path: root/arch/i386/pci
diff options
context:
space:
mode:
Diffstat (limited to 'arch/i386/pci')
-rw-r--r--arch/i386/pci/Makefile2
-rw-r--r--arch/i386/pci/common.c4
-rw-r--r--arch/i386/pci/direct.c25
-rw-r--r--arch/i386/pci/early.c52
-rw-r--r--arch/i386/pci/init.c9
-rw-r--r--arch/i386/pci/mmconfig.c41
-rw-r--r--arch/i386/pci/pci.h7
7 files changed, 124 insertions, 16 deletions
diff --git a/arch/i386/pci/Makefile b/arch/i386/pci/Makefile
index 62ad75c..1594d2f 100644
--- a/arch/i386/pci/Makefile
+++ b/arch/i386/pci/Makefile
@@ -11,4 +11,4 @@ pci-y += legacy.o irq.o
pci-$(CONFIG_X86_VISWS) := visws.o fixup.o
pci-$(CONFIG_X86_NUMAQ) := numa.o irq.o
-obj-y += $(pci-y) common.o
+obj-y += $(pci-y) common.o early.o
diff --git a/arch/i386/pci/common.c b/arch/i386/pci/common.c
index 0a362e3..68bce19 100644
--- a/arch/i386/pci/common.c
+++ b/arch/i386/pci/common.c
@@ -242,6 +242,10 @@ char * __devinit pcibios_setup(char *str)
acpi_noirq_set();
return NULL;
}
+ else if (!strcmp(str, "noearly")) {
+ pci_probe |= PCI_PROBE_NOEARLY;
+ return NULL;
+ }
#ifndef CONFIG_X86_VISWS
else if (!strcmp(str, "usepirqmask")) {
pci_probe |= PCI_USE_PIRQ_MASK;
diff --git a/arch/i386/pci/direct.c b/arch/i386/pci/direct.c
index 5d81fb5..5acf0b4 100644
--- a/arch/i386/pci/direct.c
+++ b/arch/i386/pci/direct.c
@@ -254,7 +254,16 @@ static int __init pci_check_type2(void)
return works;
}
-void __init pci_direct_init(void)
+void __init pci_direct_init(int type)
+{
+ printk(KERN_INFO "PCI: Using configuration type %d\n", type);
+ if (type == 1)
+ raw_pci_ops = &pci_direct_conf1;
+ else
+ raw_pci_ops = &pci_direct_conf2;
+}
+
+int __init pci_direct_probe(void)
{
struct resource *region, *region2;
@@ -264,19 +273,16 @@ void __init pci_direct_init(void)
if (!region)
goto type2;
- if (pci_check_type1()) {
- printk(KERN_INFO "PCI: Using configuration type 1\n");
- raw_pci_ops = &pci_direct_conf1;
- return;
- }
+ if (pci_check_type1())
+ return 1;
release_resource(region);
type2:
if ((pci_probe & PCI_PROBE_CONF2) == 0)
- return;
+ return 0;
region = request_region(0xCF8, 4, "PCI conf2");
if (!region)
- return;
+ return 0;
region2 = request_region(0xC000, 0x1000, "PCI conf2");
if (!region2)
goto fail2;
@@ -284,10 +290,11 @@ void __init pci_direct_init(void)
if (pci_check_type2()) {
printk(KERN_INFO "PCI: Using configuration type 2\n");
raw_pci_ops = &pci_direct_conf2;
- return;
+ return 2;
}
release_resource(region2);
fail2:
release_resource(region);
+ return 0;
}
diff --git a/arch/i386/pci/early.c b/arch/i386/pci/early.c
new file mode 100644
index 0000000..713d6c8
--- /dev/null
+++ b/arch/i386/pci/early.c
@@ -0,0 +1,52 @@
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <asm/pci-direct.h>
+#include <asm/io.h>
+#include "pci.h"
+
+/* Direct PCI access. This is used for PCI accesses in early boot before
+ the PCI subsystem works. */
+
+#define PDprintk(x...)
+
+u32 read_pci_config(u8 bus, u8 slot, u8 func, u8 offset)
+{
+ u32 v;
+ outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
+ v = inl(0xcfc);
+ if (v != 0xffffffff)
+ PDprintk("%x reading 4 from %x: %x\n", slot, offset, v);
+ return v;
+}
+
+u8 read_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset)
+{
+ u8 v;
+ outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
+ v = inb(0xcfc + (offset&3));
+ PDprintk("%x reading 1 from %x: %x\n", slot, offset, v);
+ return v;
+}
+
+u16 read_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset)
+{
+ u16 v;
+ outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
+ v = inw(0xcfc + (offset&2));
+ PDprintk("%x reading 2 from %x: %x\n", slot, offset, v);
+ return v;
+}
+
+void write_pci_config(u8 bus, u8 slot, u8 func, u8 offset,
+ u32 val)
+{
+ PDprintk("%x writing to %x: %x\n", slot, offset, val);
+ outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
+ outl(val, 0xcfc);
+}
+
+int early_pci_allowed(void)
+{
+ return (pci_probe & (PCI_PROBE_CONF1|PCI_PROBE_NOEARLY)) ==
+ PCI_PROBE_CONF1;
+}
diff --git a/arch/i386/pci/init.c b/arch/i386/pci/init.c
index 51087a9..d028e1b 100644
--- a/arch/i386/pci/init.c
+++ b/arch/i386/pci/init.c
@@ -6,8 +6,13 @@
in the right sequence from here. */
static __init int pci_access_init(void)
{
+ int type = 0;
+
+#ifdef CONFIG_PCI_DIRECT
+ type = pci_direct_probe();
+#endif
#ifdef CONFIG_PCI_MMCONFIG
- pci_mmcfg_init();
+ pci_mmcfg_init(type);
#endif
if (raw_pci_ops)
return 0;
@@ -21,7 +26,7 @@ static __init int pci_access_init(void)
* fails.
*/
#ifdef CONFIG_PCI_DIRECT
- pci_direct_init();
+ pci_direct_init(type);
#endif
return 0;
}
diff --git a/arch/i386/pci/mmconfig.c b/arch/i386/pci/mmconfig.c
index 972180f..05be8db 100644
--- a/arch/i386/pci/mmconfig.c
+++ b/arch/i386/pci/mmconfig.c
@@ -151,6 +151,38 @@ static struct pci_raw_ops pci_mmcfg = {
.write = pci_mmcfg_write,
};
+
+static __init void pci_mmcfg_insert_resources(void)
+{
+#define PCI_MMCFG_RESOURCE_NAME_LEN 19
+ int i;
+ struct resource *res;
+ char *names;
+ unsigned num_buses;
+
+ res = kcalloc(PCI_MMCFG_RESOURCE_NAME_LEN + sizeof(*res),
+ pci_mmcfg_config_num, GFP_KERNEL);
+
+ if (!res) {
+ printk(KERN_ERR "PCI: Unable to allocate MMCONFIG resources\n");
+ return;
+ }
+
+ names = (void *)&res[pci_mmcfg_config_num];
+ for (i = 0; i < pci_mmcfg_config_num; i++, res++) {
+ num_buses = pci_mmcfg_config[i].end_bus_number -
+ pci_mmcfg_config[i].start_bus_number + 1;
+ res->name = names;
+ snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN, "PCI MMCONFIG %u",
+ pci_mmcfg_config[i].pci_segment_group_number);
+ res->start = pci_mmcfg_config[i].base_address;
+ res->end = res->start + (num_buses << 20) - 1;
+ res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ insert_resource(&iomem_resource, res);
+ names += PCI_MMCFG_RESOURCE_NAME_LEN;
+ }
+}
+
/* K8 systems have some devices (typically in the builtin northbridge)
that are only accessible using type1
Normally this can be expressed in the MCFG by not listing them
@@ -187,7 +219,9 @@ static __init void unreachable_devices(void)
}
}
-void __init pci_mmcfg_init(void)
+
+
+void __init pci_mmcfg_init(int type)
{
if ((pci_probe & PCI_PROBE_MMCONF) == 0)
return;
@@ -198,7 +232,9 @@ void __init pci_mmcfg_init(void)
(pci_mmcfg_config[0].base_address == 0))
return;
- if (!e820_all_mapped(pci_mmcfg_config[0].base_address,
+ /* Only do this check when type 1 works. If it doesn't work
+ assume we run on a Mac and always use MCFG */
+ if (type == 1 && !e820_all_mapped(pci_mmcfg_config[0].base_address,
pci_mmcfg_config[0].base_address + MMCONFIG_APER_MIN,
E820_RESERVED)) {
printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %x is not E820-reserved\n",
@@ -212,4 +248,5 @@ void __init pci_mmcfg_init(void)
pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
unreachable_devices();
+ pci_mmcfg_insert_resources();
}
diff --git a/arch/i386/pci/pci.h b/arch/i386/pci/pci.h
index bf4e793..1814f74 100644
--- a/arch/i386/pci/pci.h
+++ b/arch/i386/pci/pci.h
@@ -17,6 +17,7 @@
#define PCI_PROBE_CONF2 0x0004
#define PCI_PROBE_MMCONF 0x0008
#define PCI_PROBE_MASK 0x000f
+#define PCI_PROBE_NOEARLY 0x0010
#define PCI_NO_SORT 0x0100
#define PCI_BIOS_SORT 0x0200
@@ -81,7 +82,9 @@ extern int pci_conf1_write(unsigned int seg, unsigned int bus,
extern int pci_conf1_read(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 *value);
-extern void pci_direct_init(void);
+extern int pci_direct_probe(void);
+extern void pci_direct_init(int type);
extern void pci_pcbios_init(void);
-extern void pci_mmcfg_init(void);
+extern void pci_mmcfg_init(int type);
extern void pcibios_sort(void);
+