Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
587 | giacomo | 1 | /* |
2 | * direct.c - Low-level direct PCI config space access |
||
3 | */ |
||
4 | |||
5 | #include <linuxcomp.h> |
||
6 | |||
7 | #include <linux/pci.h> |
||
8 | #include <linux/init.h> |
||
9 | #include "pci2.h" |
||
10 | |||
11 | /* |
||
12 | * Functions for accessing PCI configuration space with type 1 accesses |
||
13 | */ |
||
14 | |||
15 | #define PCI_CONF1_ADDRESS(bus, devfn, reg) \ |
||
16 | (0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3)) |
||
17 | |||
18 | static int pci_conf1_read (int seg, int bus, int devfn, int reg, int len, u32 *value) |
||
19 | { |
||
20 | unsigned long flags; |
||
21 | |||
22 | if (!value || (bus > 255) || (devfn > 255) || (reg > 255)) |
||
23 | return -EINVAL; |
||
24 | |||
25 | spin_lock_irqsave(&pci_config_lock, flags); |
||
26 | |||
27 | outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8); |
||
28 | |||
29 | switch (len) { |
||
30 | case 1: |
||
31 | *value = inb(0xCFC + (reg & 3)); |
||
32 | break; |
||
33 | case 2: |
||
34 | *value = inw(0xCFC + (reg & 2)); |
||
35 | break; |
||
36 | case 4: |
||
37 | *value = inl(0xCFC); |
||
38 | break; |
||
39 | } |
||
40 | |||
41 | spin_unlock_irqrestore(&pci_config_lock, flags); |
||
42 | |||
43 | return 0; |
||
44 | } |
||
45 | |||
46 | static int pci_conf1_write (int seg, int bus, int devfn, int reg, int len, u32 value) |
||
47 | { |
||
48 | unsigned long flags; |
||
49 | |||
50 | if ((bus > 255) || (devfn > 255) || (reg > 255)) |
||
51 | return -EINVAL; |
||
52 | |||
53 | spin_lock_irqsave(&pci_config_lock, flags); |
||
54 | |||
55 | outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8); |
||
56 | |||
57 | switch (len) { |
||
58 | case 1: |
||
59 | outb((u8)value, 0xCFC + (reg & 3)); |
||
60 | break; |
||
61 | case 2: |
||
62 | outw((u16)value, 0xCFC + (reg & 2)); |
||
63 | break; |
||
64 | case 4: |
||
65 | outl((u32)value, 0xCFC); |
||
66 | break; |
||
67 | } |
||
68 | |||
69 | spin_unlock_irqrestore(&pci_config_lock, flags); |
||
70 | |||
71 | return 0; |
||
72 | } |
||
73 | |||
74 | #undef PCI_CONF1_ADDRESS |
||
75 | |||
76 | struct pci_raw_ops pci_direct_conf1 = { |
||
77 | .read = pci_conf1_read, |
||
78 | .write = pci_conf1_write, |
||
79 | }; |
||
80 | |||
81 | |||
82 | /* |
||
83 | * Functions for accessing PCI configuration space with type 2 accesses |
||
84 | */ |
||
85 | |||
86 | #define PCI_CONF2_ADDRESS(dev, reg) (u16)(0xC000 | (dev << 8) | reg) |
||
87 | |||
88 | static int pci_conf2_read(int seg, int bus, int devfn, int reg, int len, u32 *value) |
||
89 | { |
||
90 | unsigned long flags; |
||
91 | int dev, fn; |
||
92 | |||
93 | if (!value || (bus > 255) || (devfn > 255) || (reg > 255)) |
||
94 | return -EINVAL; |
||
95 | |||
96 | dev = PCI_SLOT(devfn); |
||
97 | fn = PCI_FUNC(devfn); |
||
98 | |||
99 | if (dev & 0x10) |
||
100 | return PCIBIOS_DEVICE_NOT_FOUND; |
||
101 | |||
102 | spin_lock_irqsave(&pci_config_lock, flags); |
||
103 | |||
104 | outb((u8)(0xF0 | (fn << 1)), 0xCF8); |
||
105 | outb((u8)bus, 0xCFA); |
||
106 | |||
107 | switch (len) { |
||
108 | case 1: |
||
109 | *value = inb(PCI_CONF2_ADDRESS(dev, reg)); |
||
110 | break; |
||
111 | case 2: |
||
112 | *value = inw(PCI_CONF2_ADDRESS(dev, reg)); |
||
113 | break; |
||
114 | case 4: |
||
115 | *value = inl(PCI_CONF2_ADDRESS(dev, reg)); |
||
116 | break; |
||
117 | } |
||
118 | |||
119 | outb(0, 0xCF8); |
||
120 | |||
121 | spin_unlock_irqrestore(&pci_config_lock, flags); |
||
122 | |||
123 | return 0; |
||
124 | } |
||
125 | |||
126 | static int pci_conf2_write (int seg, int bus, int devfn, int reg, int len, u32 value) |
||
127 | { |
||
128 | unsigned long flags; |
||
129 | int dev, fn; |
||
130 | |||
131 | if ((bus > 255) || (devfn > 255) || (reg > 255)) |
||
132 | return -EINVAL; |
||
133 | |||
134 | dev = PCI_SLOT(devfn); |
||
135 | fn = PCI_FUNC(devfn); |
||
136 | |||
137 | if (dev & 0x10) |
||
138 | return PCIBIOS_DEVICE_NOT_FOUND; |
||
139 | |||
140 | spin_lock_irqsave(&pci_config_lock, flags); |
||
141 | |||
142 | outb((u8)(0xF0 | (fn << 1)), 0xCF8); |
||
143 | outb((u8)bus, 0xCFA); |
||
144 | |||
145 | switch (len) { |
||
146 | case 1: |
||
147 | outb((u8)value, PCI_CONF2_ADDRESS(dev, reg)); |
||
148 | break; |
||
149 | case 2: |
||
150 | outw((u16)value, PCI_CONF2_ADDRESS(dev, reg)); |
||
151 | break; |
||
152 | case 4: |
||
153 | outl((u32)value, PCI_CONF2_ADDRESS(dev, reg)); |
||
154 | break; |
||
155 | } |
||
156 | |||
157 | outb(0, 0xCF8); |
||
158 | |||
159 | spin_unlock_irqrestore(&pci_config_lock, flags); |
||
160 | |||
161 | return 0; |
||
162 | } |
||
163 | |||
164 | #undef PCI_CONF2_ADDRESS |
||
165 | |||
166 | static struct pci_raw_ops pci_direct_conf2 = { |
||
167 | .read = pci_conf2_read, |
||
168 | .write = pci_conf2_write, |
||
169 | }; |
||
170 | |||
171 | |||
172 | /* |
||
173 | * Before we decide to use direct hardware access mechanisms, we try to do some |
||
174 | * trivial checks to ensure it at least _seems_ to be working -- we just test |
||
175 | * whether bus 00 contains a host bridge (this is similar to checking |
||
176 | * techniques used in XFree86, but ours should be more reliable since we |
||
177 | * attempt to make use of direct access hints provided by the PCI BIOS). |
||
178 | * |
||
179 | * This should be close to trivial, but it isn't, because there are buggy |
||
180 | * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. |
||
181 | */ |
||
182 | static int __init pci_sanity_check(struct pci_raw_ops *o) |
||
183 | { |
||
184 | u32 x = 0; |
||
185 | int devfn; |
||
186 | |||
187 | if (pci_probe & PCI_NO_CHECKS) |
||
188 | return 1; |
||
189 | |||
190 | for (devfn = 0; devfn < 0x100; devfn++) { |
||
191 | if (o->read(0, 0, devfn, PCI_CLASS_DEVICE, 2, &x)) |
||
192 | continue; |
||
193 | if (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA) |
||
194 | return 1; |
||
195 | |||
196 | if (o->read(0, 0, devfn, PCI_VENDOR_ID, 2, &x)) |
||
197 | continue; |
||
198 | if (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ) |
||
199 | return 1; |
||
200 | } |
||
201 | |||
202 | DBG("PCI: Sanity check failed\n"); |
||
203 | return 0; |
||
204 | } |
||
205 | |||
206 | static int __init pci_check_type1(void) |
||
207 | { |
||
208 | unsigned long flags; |
||
209 | unsigned int tmp; |
||
210 | int works = 0; |
||
211 | |||
212 | local_irq_save(flags); |
||
213 | |||
214 | outb(0x01, 0xCFB); |
||
215 | tmp = inl(0xCF8); |
||
216 | outl(0x80000000, 0xCF8); |
||
217 | if (inl(0xCF8) == 0x80000000 && pci_sanity_check(&pci_direct_conf1)) { |
||
218 | works = 1; |
||
219 | } |
||
220 | outl(tmp, 0xCF8); |
||
221 | local_irq_restore(flags); |
||
222 | |||
223 | return works; |
||
224 | } |
||
225 | |||
226 | static int __init pci_check_type2(void) |
||
227 | { |
||
228 | unsigned long flags; |
||
229 | int works = 0; |
||
230 | |||
231 | local_irq_save(flags); |
||
232 | |||
233 | outb(0x00, 0xCFB); |
||
234 | outb(0x00, 0xCF8); |
||
235 | outb(0x00, 0xCFA); |
||
236 | if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00 && |
||
237 | pci_sanity_check(&pci_direct_conf2)) { |
||
238 | works = 1; |
||
239 | } |
||
240 | |||
241 | local_irq_restore(flags); |
||
242 | |||
243 | return works; |
||
244 | } |
||
245 | |||
246 | int __init pci_direct_init(void) |
||
247 | { |
||
248 | struct resource *region, *region2; |
||
249 | |||
250 | if ((pci_probe & PCI_PROBE_CONF1) == 0) |
||
251 | goto type2; |
||
252 | region = request_region(0xCF8, 8, "PCI conf1"); |
||
253 | if (!region) |
||
254 | goto type2; |
||
255 | |||
256 | if (pci_check_type1()) { |
||
257 | printk(KERN_INFO "PCI: Using configuration type 1\n"); |
||
258 | raw_pci_ops = &pci_direct_conf1; |
||
259 | return 0; |
||
260 | } |
||
261 | release_resource(region); |
||
262 | |||
263 | type2: |
||
264 | if ((!pci_probe & PCI_PROBE_CONF2) == 0) |
||
265 | goto out; |
||
266 | region = request_region(0xCF8, 4, "PCI conf2"); |
||
267 | if (!region) |
||
268 | goto out; |
||
269 | region2 = request_region(0xC000, 0x1000, "PCI conf2"); |
||
270 | if (!region2) |
||
271 | goto fail2; |
||
272 | |||
273 | if (pci_check_type2()) { |
||
274 | printk(KERN_INFO "PCI: Using configuration type 2\n"); |
||
275 | raw_pci_ops = &pci_direct_conf2; |
||
276 | return 0; |
||
277 | } |
||
278 | |||
279 | release_resource(region2); |
||
280 | fail2: |
||
281 | release_resource(region); |
||
282 | |||
283 | out: |
||
284 | return 0; |
||
285 | } |
||
286 | |||
287 | arch_initcall(pci_direct_init); |