diff options
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); |