Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * mmconfig.c - Low-level direct PCI config space access via MMCONFIG | |
3 | * | |
4 | * This is an 64bit optimized version that always keeps the full mmconfig | |
5 | * space mapped. This allows lockless config space operation. | |
6 | */ | |
7 | ||
8 | #include <linux/pci.h> | |
9 | #include <linux/init.h> | |
54549391 | 10 | #include <linux/acpi.h> |
1da177e4 LT |
11 | #include "pci.h" |
12 | ||
13 | #define MMCONFIG_APER_SIZE (256*1024*1024) | |
14 | ||
1da177e4 | 15 | /* Static virtual mapping of the MMCONFIG aperture */ |
54549391 | 16 | static char *pci_mmcfg_virt; |
1da177e4 LT |
17 | |
18 | static inline char *pci_dev_base(unsigned int bus, unsigned int devfn) | |
19 | { | |
20 | return pci_mmcfg_virt + ((bus << 20) | (devfn << 12)); | |
21 | } | |
22 | ||
23 | static int pci_mmcfg_read(unsigned int seg, unsigned int bus, | |
24 | unsigned int devfn, int reg, int len, u32 *value) | |
25 | { | |
26 | char *addr = pci_dev_base(bus, devfn); | |
27 | ||
28 | if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095))) | |
29 | return -EINVAL; | |
30 | ||
31 | switch (len) { | |
32 | case 1: | |
33 | *value = readb(addr + reg); | |
34 | break; | |
35 | case 2: | |
36 | *value = readw(addr + reg); | |
37 | break; | |
38 | case 4: | |
39 | *value = readl(addr + reg); | |
40 | break; | |
41 | } | |
42 | ||
43 | return 0; | |
44 | } | |
45 | ||
46 | static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | |
47 | unsigned int devfn, int reg, int len, u32 value) | |
48 | { | |
49 | char *addr = pci_dev_base(bus,devfn); | |
50 | ||
51 | if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) | |
52 | return -EINVAL; | |
53 | ||
54 | switch (len) { | |
55 | case 1: | |
56 | writeb(value, addr + reg); | |
57 | break; | |
58 | case 2: | |
59 | writew(value, addr + reg); | |
60 | break; | |
61 | case 4: | |
62 | writel(value, addr + reg); | |
63 | break; | |
64 | } | |
65 | ||
66 | return 0; | |
67 | } | |
68 | ||
69 | static struct pci_raw_ops pci_mmcfg = { | |
70 | .read = pci_mmcfg_read, | |
71 | .write = pci_mmcfg_write, | |
72 | }; | |
73 | ||
74 | static int __init pci_mmcfg_init(void) | |
75 | { | |
76 | if ((pci_probe & PCI_PROBE_MMCONF) == 0) | |
77 | return 0; | |
54549391 GKH |
78 | |
79 | acpi_table_parse(ACPI_MCFG, acpi_parse_mcfg); | |
80 | if ((pci_mmcfg_config_num == 0) || | |
81 | (pci_mmcfg_config == NULL) || | |
82 | (pci_mmcfg_config[0].base_address == 0)) | |
1da177e4 LT |
83 | return 0; |
84 | ||
85 | /* Kludge for now. Don't use mmconfig on AMD systems because | |
86 | those have some busses where mmconfig doesn't work, | |
87 | and we don't parse ACPI MCFG well enough to handle that. | |
88 | Remove when proper handling is added. */ | |
89 | if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) | |
90 | return 0; | |
91 | ||
92 | /* RED-PEN i386 doesn't do _nocache right now */ | |
54549391 | 93 | pci_mmcfg_virt = ioremap_nocache(pci_mmcfg_config[0].base_address, MMCONFIG_APER_SIZE); |
1da177e4 LT |
94 | if (!pci_mmcfg_virt) { |
95 | printk("PCI: Cannot map mmconfig aperture\n"); | |
96 | return 0; | |
97 | } | |
98 | ||
54549391 | 99 | printk(KERN_INFO "PCI: Using MMCONFIG at %x\n", pci_mmcfg_config[0].base_address); |
1da177e4 LT |
100 | raw_pci_ops = &pci_mmcfg; |
101 | pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; | |
102 | ||
103 | return 0; | |
104 | } | |
105 | ||
106 | arch_initcall(pci_mmcfg_init); |