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