diff options
author | Andi Kleen <ak@suse.de> | 2005-12-13 01:17:10 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-12-13 01:31:16 -0500 |
commit | 928cf8c62763349efc550a12f6518e52c3390906 (patch) | |
tree | 5fb257cbdaaebc3814e6df85100712127cdb8526 /arch | |
parent | bf5421c309bb89e5106452bc840983b1b4754d61 (diff) |
[PATCH] i386/x86-64 Fall back to type 1 access when no entry found
When there is no entry for a bus in MCFG fall back to type1. This is
especially important on K8 systems where always some devices can't be accessed
using mmconfig (in particular the builtin northbridge doesn't support it for
its own devices)
Cc: <gregkh@suse.de>
Cc: <jgarzik@pobox.com>
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/i386/pci/direct.c | 4 | ||||
-rw-r--r-- | arch/i386/pci/mmconfig.c | 24 | ||||
-rw-r--r-- | arch/i386/pci/pci.h | 7 | ||||
-rw-r--r-- | arch/x86_64/pci/mmconfig.c | 29 |
4 files changed, 45 insertions, 19 deletions
diff --git a/arch/i386/pci/direct.c b/arch/i386/pci/direct.c index 94331d6be7a3..e3ac502bf2fb 100644 --- a/arch/i386/pci/direct.c +++ b/arch/i386/pci/direct.c | |||
@@ -13,7 +13,7 @@ | |||
13 | #define PCI_CONF1_ADDRESS(bus, devfn, reg) \ | 13 | #define PCI_CONF1_ADDRESS(bus, devfn, reg) \ |
14 | (0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3)) | 14 | (0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3)) |
15 | 15 | ||
16 | static int pci_conf1_read(unsigned int seg, unsigned int bus, | 16 | int pci_conf1_read(unsigned int seg, unsigned int bus, |
17 | unsigned int devfn, int reg, int len, u32 *value) | 17 | unsigned int devfn, int reg, int len, u32 *value) |
18 | { | 18 | { |
19 | unsigned long flags; | 19 | unsigned long flags; |
@@ -42,7 +42,7 @@ static int pci_conf1_read(unsigned int seg, unsigned int bus, | |||
42 | return 0; | 42 | return 0; |
43 | } | 43 | } |
44 | 44 | ||
45 | static int pci_conf1_write(unsigned int seg, unsigned int bus, | 45 | int pci_conf1_write(unsigned int seg, unsigned int bus, |
46 | unsigned int devfn, int reg, int len, u32 value) | 46 | unsigned int devfn, int reg, int len, u32 value) |
47 | { | 47 | { |
48 | unsigned long flags; | 48 | unsigned long flags; |
diff --git a/arch/i386/pci/mmconfig.c b/arch/i386/pci/mmconfig.c index dfbf80cff834..cc787a7c030c 100644 --- a/arch/i386/pci/mmconfig.c +++ b/arch/i386/pci/mmconfig.c | |||
@@ -30,10 +30,8 @@ static u32 get_base_addr(unsigned int seg, int bus) | |||
30 | while (1) { | 30 | while (1) { |
31 | ++cfg_num; | 31 | ++cfg_num; |
32 | if (cfg_num >= pci_mmcfg_config_num) { | 32 | if (cfg_num >= pci_mmcfg_config_num) { |
33 | /* something bad is going on, no cfg table is found. */ | 33 | /* Not found - fallback to type 1 */ |
34 | /* so we fall back to the old way we used to do this */ | 34 | return 0; |
35 | /* and just rely on the first entry to be correct. */ | ||
36 | return pci_mmcfg_config[0].base_address; | ||
37 | } | 35 | } |
38 | cfg = &pci_mmcfg_config[cfg_num]; | 36 | cfg = &pci_mmcfg_config[cfg_num]; |
39 | if (cfg->pci_segment_group_number != seg) | 37 | if (cfg->pci_segment_group_number != seg) |
@@ -44,9 +42,9 @@ static u32 get_base_addr(unsigned int seg, int bus) | |||
44 | } | 42 | } |
45 | } | 43 | } |
46 | 44 | ||
47 | static inline void pci_exp_set_dev_base(unsigned int seg, int bus, int devfn) | 45 | static inline void pci_exp_set_dev_base(unsigned int base, int bus, int devfn) |
48 | { | 46 | { |
49 | u32 dev_base = get_base_addr(seg, bus) | (bus << 20) | (devfn << 12); | 47 | u32 dev_base = base | (bus << 20) | (devfn << 12); |
50 | if (dev_base != mmcfg_last_accessed_device) { | 48 | if (dev_base != mmcfg_last_accessed_device) { |
51 | mmcfg_last_accessed_device = dev_base; | 49 | mmcfg_last_accessed_device = dev_base; |
52 | set_fixmap_nocache(FIX_PCIE_MCFG, dev_base); | 50 | set_fixmap_nocache(FIX_PCIE_MCFG, dev_base); |
@@ -57,13 +55,18 @@ static int pci_mmcfg_read(unsigned int seg, unsigned int bus, | |||
57 | unsigned int devfn, int reg, int len, u32 *value) | 55 | unsigned int devfn, int reg, int len, u32 *value) |
58 | { | 56 | { |
59 | unsigned long flags; | 57 | unsigned long flags; |
58 | u32 base; | ||
60 | 59 | ||
61 | if (!value || (bus > 255) || (devfn > 255) || (reg > 4095)) | 60 | if (!value || (bus > 255) || (devfn > 255) || (reg > 4095)) |
62 | return -EINVAL; | 61 | return -EINVAL; |
63 | 62 | ||
63 | base = get_base_addr(seg, bus); | ||
64 | if (!base) | ||
65 | return pci_conf1_read(seg,bus,devfn,reg,len,value); | ||
66 | |||
64 | spin_lock_irqsave(&pci_config_lock, flags); | 67 | spin_lock_irqsave(&pci_config_lock, flags); |
65 | 68 | ||
66 | pci_exp_set_dev_base(seg, bus, devfn); | 69 | pci_exp_set_dev_base(base, bus, devfn); |
67 | 70 | ||
68 | switch (len) { | 71 | switch (len) { |
69 | case 1: | 72 | case 1: |
@@ -86,13 +89,18 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | |||
86 | unsigned int devfn, int reg, int len, u32 value) | 89 | unsigned int devfn, int reg, int len, u32 value) |
87 | { | 90 | { |
88 | unsigned long flags; | 91 | unsigned long flags; |
92 | u32 base; | ||
89 | 93 | ||
90 | if ((bus > 255) || (devfn > 255) || (reg > 4095)) | 94 | if ((bus > 255) || (devfn > 255) || (reg > 4095)) |
91 | return -EINVAL; | 95 | return -EINVAL; |
92 | 96 | ||
97 | base = get_base_addr(seg, bus); | ||
98 | if (!base) | ||
99 | return pci_conf1_write(seg,bus,devfn,reg,len,value); | ||
100 | |||
93 | spin_lock_irqsave(&pci_config_lock, flags); | 101 | spin_lock_irqsave(&pci_config_lock, flags); |
94 | 102 | ||
95 | pci_exp_set_dev_base(seg, bus, devfn); | 103 | pci_exp_set_dev_base(base, bus, devfn); |
96 | 104 | ||
97 | switch (len) { | 105 | switch (len) { |
98 | case 1: | 106 | case 1: |
diff --git a/arch/i386/pci/pci.h b/arch/i386/pci/pci.h index 127d53ad16be..f550781ec310 100644 --- a/arch/i386/pci/pci.h +++ b/arch/i386/pci/pci.h | |||
@@ -74,3 +74,10 @@ extern spinlock_t pci_config_lock; | |||
74 | 74 | ||
75 | extern int (*pcibios_enable_irq)(struct pci_dev *dev); | 75 | extern int (*pcibios_enable_irq)(struct pci_dev *dev); |
76 | extern void (*pcibios_disable_irq)(struct pci_dev *dev); | 76 | extern void (*pcibios_disable_irq)(struct pci_dev *dev); |
77 | |||
78 | extern int pci_conf1_write(unsigned int seg, unsigned int bus, | ||
79 | unsigned int devfn, int reg, int len, u32 value); | ||
80 | extern int pci_conf1_read(unsigned int seg, unsigned int bus, | ||
81 | unsigned int devfn, int reg, int len, u32 *value); | ||
82 | |||
83 | |||
diff --git a/arch/x86_64/pci/mmconfig.c b/arch/x86_64/pci/mmconfig.c index a0838c4a94e4..22ff1b07d4e0 100644 --- a/arch/x86_64/pci/mmconfig.c +++ b/arch/x86_64/pci/mmconfig.c | |||
@@ -19,7 +19,7 @@ struct mmcfg_virt { | |||
19 | }; | 19 | }; |
20 | static struct mmcfg_virt *pci_mmcfg_virt; | 20 | static struct mmcfg_virt *pci_mmcfg_virt; |
21 | 21 | ||
22 | static char *get_virt(unsigned int seg, int bus) | 22 | static char *get_virt(unsigned int seg, unsigned bus) |
23 | { | 23 | { |
24 | int cfg_num = -1; | 24 | int cfg_num = -1; |
25 | struct acpi_table_mcfg_config *cfg; | 25 | struct acpi_table_mcfg_config *cfg; |
@@ -27,10 +27,9 @@ static char *get_virt(unsigned int seg, int bus) | |||
27 | while (1) { | 27 | while (1) { |
28 | ++cfg_num; | 28 | ++cfg_num; |
29 | if (cfg_num >= pci_mmcfg_config_num) { | 29 | if (cfg_num >= pci_mmcfg_config_num) { |
30 | /* something bad is going on, no cfg table is found. */ | 30 | /* Not found - fall back to type 1. This happens |
31 | /* so we fall back to the old way we used to do this */ | 31 | e.g. on the internal devices of a K8 northbridge. */ |
32 | /* and just rely on the first entry to be correct. */ | 32 | return NULL; |
33 | return pci_mmcfg_virt[0].virt; | ||
34 | } | 33 | } |
35 | cfg = pci_mmcfg_virt[cfg_num].cfg; | 34 | cfg = pci_mmcfg_virt[cfg_num].cfg; |
36 | if (cfg->pci_segment_group_number != seg) | 35 | if (cfg->pci_segment_group_number != seg) |
@@ -43,18 +42,25 @@ static char *get_virt(unsigned int seg, int bus) | |||
43 | 42 | ||
44 | static inline char *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) | 43 | static inline char *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) |
45 | { | 44 | { |
46 | 45 | char *addr = get_virt(seg, bus); | |
47 | return get_virt(seg, bus) + ((bus << 20) | (devfn << 12)); | 46 | if (!addr) |
47 | return NULL; | ||
48 | return addr + ((bus << 20) | (devfn << 12)); | ||
48 | } | 49 | } |
49 | 50 | ||
50 | static int pci_mmcfg_read(unsigned int seg, unsigned int bus, | 51 | static int pci_mmcfg_read(unsigned int seg, unsigned int bus, |
51 | unsigned int devfn, int reg, int len, u32 *value) | 52 | unsigned int devfn, int reg, int len, u32 *value) |
52 | { | 53 | { |
53 | char *addr = pci_dev_base(seg, bus, devfn); | 54 | char *addr; |
54 | 55 | ||
56 | /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ | ||
55 | if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095))) | 57 | if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095))) |
56 | return -EINVAL; | 58 | return -EINVAL; |
57 | 59 | ||
60 | addr = pci_dev_base(seg, bus, devfn); | ||
61 | if (!addr) | ||
62 | return pci_conf1_read(seg,bus,devfn,reg,len,value); | ||
63 | |||
58 | switch (len) { | 64 | switch (len) { |
59 | case 1: | 65 | case 1: |
60 | *value = readb(addr + reg); | 66 | *value = readb(addr + reg); |
@@ -73,11 +79,16 @@ static int pci_mmcfg_read(unsigned int seg, unsigned int bus, | |||
73 | static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | 79 | static int pci_mmcfg_write(unsigned int seg, unsigned int bus, |
74 | unsigned int devfn, int reg, int len, u32 value) | 80 | unsigned int devfn, int reg, int len, u32 value) |
75 | { | 81 | { |
76 | char *addr = pci_dev_base(seg, bus, devfn); | 82 | char *addr; |
77 | 83 | ||
84 | /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ | ||
78 | if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) | 85 | if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) |
79 | return -EINVAL; | 86 | return -EINVAL; |
80 | 87 | ||
88 | addr = pci_dev_base(seg, bus, devfn); | ||
89 | if (!addr) | ||
90 | return pci_conf1_write(seg,bus,devfn,reg,len,value); | ||
91 | |||
81 | switch (len) { | 92 | switch (len) { |
82 | case 1: | 93 | case 1: |
83 | writeb(value, addr + reg); | 94 | writeb(value, addr + reg); |