aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/pci/mmconfig.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/i386/pci/mmconfig.c')
-rw-r--r--arch/i386/pci/mmconfig.c45
1 files changed, 42 insertions, 3 deletions
diff --git a/arch/i386/pci/mmconfig.c b/arch/i386/pci/mmconfig.c
index cc787a7c030c..08a084901212 100644
--- a/arch/i386/pci/mmconfig.c
+++ b/arch/i386/pci/mmconfig.c
@@ -19,14 +19,20 @@
19/* The base address of the last MMCONFIG device accessed */ 19/* The base address of the last MMCONFIG device accessed */
20static u32 mmcfg_last_accessed_device; 20static u32 mmcfg_last_accessed_device;
21 21
22static DECLARE_BITMAP(fallback_slots, 32);
23
22/* 24/*
23 * Functions for accessing PCI configuration space with MMCONFIG accesses 25 * Functions for accessing PCI configuration space with MMCONFIG accesses
24 */ 26 */
25static u32 get_base_addr(unsigned int seg, int bus) 27static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
26{ 28{
27 int cfg_num = -1; 29 int cfg_num = -1;
28 struct acpi_table_mcfg_config *cfg; 30 struct acpi_table_mcfg_config *cfg;
29 31
32 if (seg == 0 && bus == 0 &&
33 test_bit(PCI_SLOT(devfn), fallback_slots))
34 return 0;
35
30 while (1) { 36 while (1) {
31 ++cfg_num; 37 ++cfg_num;
32 if (cfg_num >= pci_mmcfg_config_num) { 38 if (cfg_num >= pci_mmcfg_config_num) {
@@ -60,7 +66,7 @@ static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
60 if (!value || (bus > 255) || (devfn > 255) || (reg > 4095)) 66 if (!value || (bus > 255) || (devfn > 255) || (reg > 4095))
61 return -EINVAL; 67 return -EINVAL;
62 68
63 base = get_base_addr(seg, bus); 69 base = get_base_addr(seg, bus, devfn);
64 if (!base) 70 if (!base)
65 return pci_conf1_read(seg,bus,devfn,reg,len,value); 71 return pci_conf1_read(seg,bus,devfn,reg,len,value);
66 72
@@ -94,7 +100,7 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
94 if ((bus > 255) || (devfn > 255) || (reg > 4095)) 100 if ((bus > 255) || (devfn > 255) || (reg > 4095))
95 return -EINVAL; 101 return -EINVAL;
96 102
97 base = get_base_addr(seg, bus); 103 base = get_base_addr(seg, bus, devfn);
98 if (!base) 104 if (!base)
99 return pci_conf1_write(seg,bus,devfn,reg,len,value); 105 return pci_conf1_write(seg,bus,devfn,reg,len,value);
100 106
@@ -124,6 +130,37 @@ static struct pci_raw_ops pci_mmcfg = {
124 .write = pci_mmcfg_write, 130 .write = pci_mmcfg_write,
125}; 131};
126 132
133/* K8 systems have some devices (typically in the builtin northbridge)
134 that are only accessible using type1
135 Normally this can be expressed in the MCFG by not listing them
136 and assigning suitable _SEGs, but this isn't implemented in some BIOS.
137 Instead try to discover all devices on bus 0 that are unreachable using MM
138 and fallback for them.
139 We only do this for bus 0/seg 0 */
140static __init void unreachable_devices(void)
141{
142 int i;
143 unsigned long flags;
144
145 for (i = 0; i < 32; i++) {
146 u32 val1;
147 u32 addr;
148
149 pci_conf1_read(0, 0, PCI_DEVFN(i, 0), 0, 4, &val1);
150 if (val1 == 0xffffffff)
151 continue;
152
153 /* Locking probably not needed, but safer */
154 spin_lock_irqsave(&pci_config_lock, flags);
155 addr = get_base_addr(0, 0, PCI_DEVFN(i, 0));
156 if (addr != 0)
157 pci_exp_set_dev_base(addr, 0, PCI_DEVFN(i, 0));
158 if (addr == 0 || readl((u32 *)addr) != val1)
159 set_bit(i, fallback_slots);
160 spin_unlock_irqrestore(&pci_config_lock, flags);
161 }
162}
163
127static int __init pci_mmcfg_init(void) 164static int __init pci_mmcfg_init(void)
128{ 165{
129 if ((pci_probe & PCI_PROBE_MMCONF) == 0) 166 if ((pci_probe & PCI_PROBE_MMCONF) == 0)
@@ -139,6 +176,8 @@ static int __init pci_mmcfg_init(void)
139 raw_pci_ops = &pci_mmcfg; 176 raw_pci_ops = &pci_mmcfg;
140 pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; 177 pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
141 178
179 unreachable_devices();
180
142 out: 181 out:
143 return 0; 182 return 0;
144} 183}