]>
Commit | Line | Data |
---|---|---|
727ce158 | 1 | /* |
727ce158 MM |
2 | * The PCI Library -- Direct Configuration access via i386 Ports |
3 | * | |
73750606 | 4 | * Copyright (c) 1997--2006 Martin Mares <mj@ucw.cz> |
727ce158 MM |
5 | * |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
73750606 | 9 | #define _GNU_SOURCE |
727ce158 | 10 | |
727ce158 MM |
11 | #include "internal.h" |
12 | ||
73750606 MM |
13 | #include <unistd.h> |
14 | ||
489233b4 | 15 | #if defined(PCI_OS_LINUX) |
8fc75dbc | 16 | #include "i386-io-linux.h" |
489233b4 | 17 | #elif defined(PCI_OS_GNU) |
8fc75dbc | 18 | #include "i386-io-hurd.h" |
489233b4 | 19 | #elif defined(PCI_OS_SUNOS) |
8fc75dbc | 20 | #include "i386-io-sunos.h" |
489233b4 | 21 | #elif defined(PCI_OS_WINDOWS) |
a2413560 | 22 | #include "i386-io-windows.h" |
550d67d1 MM |
23 | #elif defined(PCI_OS_CYGWIN) |
24 | #include "i386-io-cygwin.h" | |
40e253d7 FR |
25 | #elif defined(PCI_OS_HAIKU) |
26 | #include "i386-io-haiku.h" | |
27 | #elif defined(PCI_OS_BEOS) | |
28 | #include "i386-io-beos.h" | |
5c5ce192 RM |
29 | #elif defined(PCI_OS_DJGPP) |
30 | #include "i386-io-djgpp.h" | |
8fc75dbc MM |
31 | #else |
32 | #error Do not know how to access I/O ports on this OS. | |
80459c65 MM |
33 | #endif |
34 | ||
9007a292 MM |
35 | static int conf12_io_enabled = -1; /* -1=haven't tried, 0=failed, 1=succeeded */ |
36 | ||
37 | static int | |
d305d704 | 38 | conf12_setup_io(struct pci_access *a) |
9007a292 MM |
39 | { |
40 | if (conf12_io_enabled < 0) | |
d305d704 | 41 | conf12_io_enabled = intel_setup_io(a); |
9007a292 MM |
42 | return conf12_io_enabled; |
43 | } | |
44 | ||
727ce158 MM |
45 | static void |
46 | conf12_init(struct pci_access *a) | |
47 | { | |
d305d704 | 48 | if (!conf12_setup_io(a)) |
9007a292 | 49 | a->error("No permission to access I/O ports (you probably have to be root)."); |
727ce158 MM |
50 | } |
51 | ||
52 | static void | |
a832f6f1 | 53 | conf12_cleanup(struct pci_access *a UNUSED) |
727ce158 | 54 | { |
9007a292 | 55 | if (conf12_io_enabled > 0) |
d305d704 | 56 | conf12_io_enabled = intel_cleanup_io(a); |
727ce158 MM |
57 | } |
58 | ||
59 | /* | |
60 | * Before we decide to use direct hardware access mechanisms, we try to do some | |
61 | * trivial checks to ensure it at least _seems_ to be working -- we just test | |
62 | * whether bus 00 contains a host bridge (this is similar to checking | |
63 | * techniques used in XFree86, but ours should be more reliable since we | |
64 | * attempt to make use of direct access hints provided by the PCI BIOS). | |
65 | * | |
66 | * This should be close to trivial, but it isn't, because there are buggy | |
67 | * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. | |
68 | */ | |
69 | ||
70 | static int | |
71 | intel_sanity_check(struct pci_access *a, struct pci_methods *m) | |
72 | { | |
73 | struct pci_dev d; | |
74 | ||
75 | a->debug("...sanity check"); | |
76 | d.bus = 0; | |
77 | d.func = 0; | |
de7ef8bc | 78 | for (d.dev = 0; d.dev < 32; d.dev++) |
727ce158 MM |
79 | { |
80 | u16 class, vendor; | |
81 | if (m->read(&d, PCI_CLASS_DEVICE, (byte *) &class, sizeof(class)) && | |
82 | (class == cpu_to_le16(PCI_CLASS_BRIDGE_HOST) || class == cpu_to_le16(PCI_CLASS_DISPLAY_VGA)) || | |
83 | m->read(&d, PCI_VENDOR_ID, (byte *) &vendor, sizeof(vendor)) && | |
84 | (vendor == cpu_to_le16(PCI_VENDOR_ID_INTEL) || vendor == cpu_to_le16(PCI_VENDOR_ID_COMPAQ))) | |
85 | { | |
86 | a->debug("...outside the Asylum at 0/%02x/0", d.dev); | |
87 | return 1; | |
88 | } | |
89 | } | |
90 | a->debug("...insane"); | |
91 | return 0; | |
92 | } | |
93 | ||
94 | /* | |
95 | * Configuration type 1 | |
96 | */ | |
97 | ||
98 | #define CONFIG_CMD(bus, device_fn, where) (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3)) | |
99 | ||
100 | static int | |
101 | conf1_detect(struct pci_access *a) | |
102 | { | |
103 | unsigned int tmp; | |
104 | int res = 0; | |
105 | ||
d305d704 | 106 | if (!conf12_setup_io(a)) |
727ce158 MM |
107 | { |
108 | a->debug("...no I/O permission"); | |
109 | return 0; | |
110 | } | |
5c5ce192 RM |
111 | |
112 | intel_io_lock(); | |
727ce158 MM |
113 | outb (0x01, 0xCFB); |
114 | tmp = inl (0xCF8); | |
115 | outl (0x80000000, 0xCF8); | |
116 | if (inl (0xCF8) == 0x80000000) | |
117 | res = 1; | |
118 | outl (tmp, 0xCF8); | |
5c5ce192 RM |
119 | intel_io_unlock(); |
120 | ||
727ce158 MM |
121 | if (res) |
122 | res = intel_sanity_check(a, &pm_intel_conf1); | |
123 | return res; | |
124 | } | |
125 | ||
126 | static int | |
127 | conf1_read(struct pci_dev *d, int pos, byte *buf, int len) | |
128 | { | |
129 | int addr = 0xcfc + (pos&3); | |
5c5ce192 | 130 | int res = 1; |
09817437 MM |
131 | |
132 | if (pos >= 256) | |
133 | return 0; | |
134 | ||
5c5ce192 | 135 | intel_io_lock(); |
727ce158 MM |
136 | outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8); |
137 | ||
138 | switch (len) | |
139 | { | |
140 | case 1: | |
141 | buf[0] = inb(addr); | |
142 | break; | |
143 | case 2: | |
144 | ((u16 *) buf)[0] = cpu_to_le16(inw(addr)); | |
145 | break; | |
146 | case 4: | |
147 | ((u32 *) buf)[0] = cpu_to_le32(inl(addr)); | |
148 | break; | |
149 | default: | |
5c5ce192 | 150 | res = pci_generic_block_read(d, pos, buf, len); |
727ce158 | 151 | } |
5c5ce192 RM |
152 | |
153 | intel_io_unlock(); | |
154 | return res; | |
727ce158 MM |
155 | } |
156 | ||
157 | static int | |
158 | conf1_write(struct pci_dev *d, int pos, byte *buf, int len) | |
159 | { | |
160 | int addr = 0xcfc + (pos&3); | |
5c5ce192 | 161 | int res = 1; |
09817437 MM |
162 | |
163 | if (pos >= 256) | |
164 | return 0; | |
165 | ||
5c5ce192 | 166 | intel_io_lock(); |
727ce158 MM |
167 | outl(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos&~3), 0xcf8); |
168 | ||
169 | switch (len) | |
170 | { | |
171 | case 1: | |
172 | outb(buf[0], addr); | |
173 | break; | |
174 | case 2: | |
175 | outw(le16_to_cpu(((u16 *) buf)[0]), addr); | |
176 | break; | |
177 | case 4: | |
178 | outl(le32_to_cpu(((u32 *) buf)[0]), addr); | |
179 | break; | |
180 | default: | |
5c5ce192 | 181 | res = pci_generic_block_write(d, pos, buf, len); |
727ce158 | 182 | } |
5c5ce192 RM |
183 | intel_io_unlock(); |
184 | return res; | |
727ce158 MM |
185 | } |
186 | ||
187 | /* | |
188 | * Configuration type 2. Obsolete and brain-damaged, but existing. | |
189 | */ | |
190 | ||
191 | static int | |
192 | conf2_detect(struct pci_access *a) | |
193 | { | |
5c5ce192 RM |
194 | int res = 0; |
195 | ||
d305d704 | 196 | if (!conf12_setup_io(a)) |
727ce158 MM |
197 | { |
198 | a->debug("...no I/O permission"); | |
199 | return 0; | |
200 | } | |
201 | ||
202 | /* This is ugly and tends to produce false positives. Beware. */ | |
203 | ||
5c5ce192 | 204 | intel_io_lock(); |
727ce158 MM |
205 | outb(0x00, 0xCFB); |
206 | outb(0x00, 0xCF8); | |
207 | outb(0x00, 0xCFA); | |
208 | if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00) | |
5c5ce192 RM |
209 | res = intel_sanity_check(a, &pm_intel_conf2); |
210 | intel_io_unlock(); | |
211 | return res; | |
727ce158 MM |
212 | } |
213 | ||
214 | static int | |
215 | conf2_read(struct pci_dev *d, int pos, byte *buf, int len) | |
216 | { | |
5c5ce192 | 217 | int res = 1; |
727ce158 MM |
218 | int addr = 0xc000 | (d->dev << 8) | pos; |
219 | ||
09817437 MM |
220 | if (pos >= 256) |
221 | return 0; | |
222 | ||
727ce158 MM |
223 | if (d->dev >= 16) |
224 | /* conf2 supports only 16 devices per bus */ | |
225 | return 0; | |
5c5ce192 RM |
226 | |
227 | intel_io_lock(); | |
727ce158 MM |
228 | outb((d->func << 1) | 0xf0, 0xcf8); |
229 | outb(d->bus, 0xcfa); | |
230 | switch (len) | |
231 | { | |
232 | case 1: | |
233 | buf[0] = inb(addr); | |
234 | break; | |
235 | case 2: | |
236 | ((u16 *) buf)[0] = cpu_to_le16(inw(addr)); | |
237 | break; | |
238 | case 4: | |
239 | ((u32 *) buf)[0] = cpu_to_le32(inl(addr)); | |
240 | break; | |
241 | default: | |
5c5ce192 | 242 | res = pci_generic_block_read(d, pos, buf, len); |
727ce158 MM |
243 | } |
244 | outb(0, 0xcf8); | |
5c5ce192 RM |
245 | intel_io_unlock(); |
246 | return res; | |
727ce158 MM |
247 | } |
248 | ||
249 | static int | |
250 | conf2_write(struct pci_dev *d, int pos, byte *buf, int len) | |
251 | { | |
5c5ce192 | 252 | int res = 1; |
727ce158 MM |
253 | int addr = 0xc000 | (d->dev << 8) | pos; |
254 | ||
09817437 MM |
255 | if (pos >= 256) |
256 | return 0; | |
257 | ||
727ce158 MM |
258 | if (d->dev >= 16) |
259 | d->access->error("conf2_write: only first 16 devices exist."); | |
5c5ce192 RM |
260 | |
261 | intel_io_lock(); | |
727ce158 MM |
262 | outb((d->func << 1) | 0xf0, 0xcf8); |
263 | outb(d->bus, 0xcfa); | |
264 | switch (len) | |
265 | { | |
266 | case 1: | |
267 | outb(buf[0], addr); | |
268 | break; | |
269 | case 2: | |
270 | outw(le16_to_cpu(* (u16 *) buf), addr); | |
271 | break; | |
272 | case 4: | |
273 | outl(le32_to_cpu(* (u32 *) buf), addr); | |
274 | break; | |
275 | default: | |
5c5ce192 | 276 | res = pci_generic_block_write(d, pos, buf, len); |
727ce158 | 277 | } |
5c5ce192 | 278 | |
727ce158 | 279 | outb(0, 0xcf8); |
5c5ce192 RM |
280 | intel_io_unlock(); |
281 | return res; | |
727ce158 MM |
282 | } |
283 | ||
284 | struct pci_methods pm_intel_conf1 = { | |
9ff67879 MM |
285 | "intel-conf1", |
286 | "Raw I/O port access using Intel conf1 interface", | |
727ce158 MM |
287 | NULL, /* config */ |
288 | conf1_detect, | |
289 | conf12_init, | |
290 | conf12_cleanup, | |
291 | pci_generic_scan, | |
292 | pci_generic_fill_info, | |
293 | conf1_read, | |
294 | conf1_write, | |
52c81519 | 295 | NULL, /* read_vpd */ |
727ce158 MM |
296 | NULL, /* init_dev */ |
297 | NULL /* cleanup_dev */ | |
298 | }; | |
299 | ||
300 | struct pci_methods pm_intel_conf2 = { | |
9ff67879 MM |
301 | "intel-conf2", |
302 | "Raw I/O port access using Intel conf2 interface", | |
727ce158 MM |
303 | NULL, /* config */ |
304 | conf2_detect, | |
305 | conf12_init, | |
306 | conf12_cleanup, | |
307 | pci_generic_scan, | |
308 | pci_generic_fill_info, | |
309 | conf2_read, | |
310 | conf2_write, | |
52c81519 | 311 | NULL, /* read_vpd */ |
727ce158 MM |
312 | NULL, /* init_dev */ |
313 | NULL /* cleanup_dev */ | |
314 | }; |