diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2005-06-23 20:35:56 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-06-28 00:52:48 -0400 |
commit | 1cde8a16815bd85c8137d1ea556398983c597c11 (patch) | |
tree | c43ab735f7fd96d0576dfb7749c8ded74f9b63b7 | |
parent | d57e26ceb7dbf44cd08128cb6146116d4281b58b (diff) |
[PATCH] PCI: use the MCFG table to properly access pci devices (x86-64)
Now that we have access to the whole MCFG table, let's properly use it
for all pci device accesses (as that's what it is there for, some boxes
don't put all the busses into one entry.)
If, for some reason, the table is incorrect, we fallback to the "old
style" of mmconfig accesses, namely, we just assume the first entry in
the table is the one for us, and blindly use it.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | arch/x86_64/pci/mmconfig.c | 58 |
1 files changed, 48 insertions, 10 deletions
diff --git a/arch/x86_64/pci/mmconfig.c b/arch/x86_64/pci/mmconfig.c index 09cfcc1234b9..657e88aa0902 100644 --- a/arch/x86_64/pci/mmconfig.c +++ b/arch/x86_64/pci/mmconfig.c | |||
@@ -13,17 +13,44 @@ | |||
13 | #define MMCONFIG_APER_SIZE (256*1024*1024) | 13 | #define MMCONFIG_APER_SIZE (256*1024*1024) |
14 | 14 | ||
15 | /* Static virtual mapping of the MMCONFIG aperture */ | 15 | /* Static virtual mapping of the MMCONFIG aperture */ |
16 | static char *pci_mmcfg_virt; | 16 | struct mmcfg_virt { |
17 | struct acpi_table_mcfg_config *cfg; | ||
18 | char *virt; | ||
19 | }; | ||
20 | static struct mmcfg_virt *pci_mmcfg_virt; | ||
17 | 21 | ||
18 | static inline char *pci_dev_base(unsigned int bus, unsigned int devfn) | 22 | static char *get_virt(unsigned int seg, int bus) |
19 | { | 23 | { |
20 | return pci_mmcfg_virt + ((bus << 20) | (devfn << 12)); | 24 | int cfg_num = -1; |
25 | struct acpi_table_mcfg_config *cfg; | ||
26 | |||
27 | while (1) { | ||
28 | ++cfg_num; | ||
29 | if (cfg_num >= pci_mmcfg_config_num) { | ||
30 | /* something bad is going on, no cfg table is found. */ | ||
31 | /* so we fall back to the old way we used to do this */ | ||
32 | /* and just rely on the first entry to be correct. */ | ||
33 | return pci_mmcfg_virt[0].virt; | ||
34 | } | ||
35 | cfg = pci_mmcfg_virt[cfg_num].cfg; | ||
36 | if (cfg->pci_segment_group_number != seg) | ||
37 | continue; | ||
38 | if ((cfg->start_bus_number <= bus) && | ||
39 | (cfg->end_bus_number >= bus)) | ||
40 | return pci_mmcfg_virt[cfg_num].virt; | ||
41 | } | ||
42 | } | ||
43 | |||
44 | static inline char *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) | ||
45 | { | ||
46 | |||
47 | return get_virt(seg, bus) + ((bus << 20) | (devfn << 12)); | ||
21 | } | 48 | } |
22 | 49 | ||
23 | static int pci_mmcfg_read(unsigned int seg, unsigned int bus, | 50 | static int pci_mmcfg_read(unsigned int seg, unsigned int bus, |
24 | unsigned int devfn, int reg, int len, u32 *value) | 51 | unsigned int devfn, int reg, int len, u32 *value) |
25 | { | 52 | { |
26 | char *addr = pci_dev_base(bus, devfn); | 53 | char *addr = pci_dev_base(seg, bus, devfn); |
27 | 54 | ||
28 | if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095))) | 55 | if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095))) |
29 | return -EINVAL; | 56 | return -EINVAL; |
@@ -46,7 +73,7 @@ static int pci_mmcfg_read(unsigned int seg, unsigned int bus, | |||
46 | static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | 73 | static int pci_mmcfg_write(unsigned int seg, unsigned int bus, |
47 | unsigned int devfn, int reg, int len, u32 value) | 74 | unsigned int devfn, int reg, int len, u32 value) |
48 | { | 75 | { |
49 | char *addr = pci_dev_base(bus,devfn); | 76 | char *addr = pci_dev_base(seg, bus, devfn); |
50 | 77 | ||
51 | if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) | 78 | if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) |
52 | return -EINVAL; | 79 | return -EINVAL; |
@@ -73,6 +100,8 @@ static struct pci_raw_ops pci_mmcfg = { | |||
73 | 100 | ||
74 | static int __init pci_mmcfg_init(void) | 101 | static int __init pci_mmcfg_init(void) |
75 | { | 102 | { |
103 | int i; | ||
104 | |||
76 | if ((pci_probe & PCI_PROBE_MMCONF) == 0) | 105 | if ((pci_probe & PCI_PROBE_MMCONF) == 0) |
77 | return 0; | 106 | return 0; |
78 | 107 | ||
@@ -90,13 +119,22 @@ static int __init pci_mmcfg_init(void) | |||
90 | return 0; | 119 | return 0; |
91 | 120 | ||
92 | /* RED-PEN i386 doesn't do _nocache right now */ | 121 | /* RED-PEN i386 doesn't do _nocache right now */ |
93 | pci_mmcfg_virt = ioremap_nocache(pci_mmcfg_config[0].base_address, MMCONFIG_APER_SIZE); | 122 | pci_mmcfg_virt = kmalloc(sizeof(*pci_mmcfg_virt) * pci_mmcfg_config_num, GFP_KERNEL); |
94 | if (!pci_mmcfg_virt) { | 123 | if (pci_mmcfg_virt == NULL) { |
95 | printk("PCI: Cannot map mmconfig aperture\n"); | 124 | printk("PCI: Can not allocate memory for mmconfig structures\n"); |
96 | return 0; | 125 | return 0; |
97 | } | 126 | } |
127 | for (i = 0; i < pci_mmcfg_config_num; ++i) { | ||
128 | pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i]; | ||
129 | pci_mmcfg_virt[i].virt = ioremap_nocache(pci_mmcfg_config[i].base_address, MMCONFIG_APER_SIZE); | ||
130 | if (!pci_mmcfg_virt[i].virt) { | ||
131 | printk("PCI: Cannot map mmconfig aperture for segment %d\n", | ||
132 | pci_mmcfg_config[i].pci_segment_group_number); | ||
133 | return 0; | ||
134 | } | ||
135 | printk(KERN_INFO "PCI: Using MMCONFIG at %x\n", pci_mmcfg_config[i].base_address); | ||
136 | } | ||
98 | 137 | ||
99 | printk(KERN_INFO "PCI: Using MMCONFIG at %x\n", pci_mmcfg_config[0].base_address); | ||
100 | raw_pci_ops = &pci_mmcfg; | 138 | raw_pci_ops = &pci_mmcfg; |
101 | pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; | 139 | pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; |
102 | 140 | ||