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 */ |
1cde8a16 GKH |
16 | struct mmcfg_virt { |
17 | struct acpi_table_mcfg_config *cfg; | |
18 | char *virt; | |
19 | }; | |
20 | static struct mmcfg_virt *pci_mmcfg_virt; | |
1da177e4 | 21 | |
1cde8a16 | 22 | static char *get_virt(unsigned int seg, int bus) |
1da177e4 | 23 | { |
1cde8a16 GKH |
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)); | |
1da177e4 LT |
48 | } |
49 | ||
50 | static int pci_mmcfg_read(unsigned int seg, unsigned int bus, | |
51 | unsigned int devfn, int reg, int len, u32 *value) | |
52 | { | |
1cde8a16 | 53 | char *addr = pci_dev_base(seg, bus, devfn); |
1da177e4 LT |
54 | |
55 | if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095))) | |
56 | return -EINVAL; | |
57 | ||
58 | switch (len) { | |
59 | case 1: | |
60 | *value = readb(addr + reg); | |
61 | break; | |
62 | case 2: | |
63 | *value = readw(addr + reg); | |
64 | break; | |
65 | case 4: | |
66 | *value = readl(addr + reg); | |
67 | break; | |
68 | } | |
69 | ||
70 | return 0; | |
71 | } | |
72 | ||
73 | static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | |
74 | unsigned int devfn, int reg, int len, u32 value) | |
75 | { | |
1cde8a16 | 76 | char *addr = pci_dev_base(seg, bus, devfn); |
1da177e4 LT |
77 | |
78 | if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) | |
79 | return -EINVAL; | |
80 | ||
81 | switch (len) { | |
82 | case 1: | |
83 | writeb(value, addr + reg); | |
84 | break; | |
85 | case 2: | |
86 | writew(value, addr + reg); | |
87 | break; | |
88 | case 4: | |
89 | writel(value, addr + reg); | |
90 | break; | |
91 | } | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
96 | static struct pci_raw_ops pci_mmcfg = { | |
97 | .read = pci_mmcfg_read, | |
98 | .write = pci_mmcfg_write, | |
99 | }; | |
100 | ||
101 | static int __init pci_mmcfg_init(void) | |
102 | { | |
1cde8a16 GKH |
103 | int i; |
104 | ||
1da177e4 LT |
105 | if ((pci_probe & PCI_PROBE_MMCONF) == 0) |
106 | return 0; | |
54549391 GKH |
107 | |
108 | acpi_table_parse(ACPI_MCFG, acpi_parse_mcfg); | |
109 | if ((pci_mmcfg_config_num == 0) || | |
110 | (pci_mmcfg_config == NULL) || | |
111 | (pci_mmcfg_config[0].base_address == 0)) | |
1da177e4 LT |
112 | return 0; |
113 | ||
1da177e4 | 114 | /* RED-PEN i386 doesn't do _nocache right now */ |
1cde8a16 GKH |
115 | pci_mmcfg_virt = kmalloc(sizeof(*pci_mmcfg_virt) * pci_mmcfg_config_num, GFP_KERNEL); |
116 | if (pci_mmcfg_virt == NULL) { | |
117 | printk("PCI: Can not allocate memory for mmconfig structures\n"); | |
1da177e4 | 118 | return 0; |
1cde8a16 GKH |
119 | } |
120 | for (i = 0; i < pci_mmcfg_config_num; ++i) { | |
121 | pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i]; | |
122 | pci_mmcfg_virt[i].virt = ioremap_nocache(pci_mmcfg_config[i].base_address, MMCONFIG_APER_SIZE); | |
123 | if (!pci_mmcfg_virt[i].virt) { | |
124 | printk("PCI: Cannot map mmconfig aperture for segment %d\n", | |
125 | pci_mmcfg_config[i].pci_segment_group_number); | |
126 | return 0; | |
127 | } | |
128 | printk(KERN_INFO "PCI: Using MMCONFIG at %x\n", pci_mmcfg_config[i].base_address); | |
129 | } | |
1da177e4 | 130 | |
1da177e4 LT |
131 | raw_pci_ops = &pci_mmcfg; |
132 | pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
137 | arch_initcall(pci_mmcfg_init); |