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