1 /*-
2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3 * Copyright (c) 2000, Michael Smith <msmith@freebsd.org>
4 * Copyright (c) 2000, BSDi
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
12 * disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37 #include <dev/pci/pcivar.h>
38 #include <dev/pci/pcireg.h>
39 #include <machine/pci_cfgreg.h>
40
41 static int cfgmech;
42 static int devmax;
43
44 static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
45 static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
46 static int pcireg_cfgopen(void);
47
48 static struct mtx pcicfg_mtx;
49
50 /*
51 * Initialise access to PCI configuration space
52 */
53 int
54 pci_cfgregopen(void)
55 {
56 static int opened = 0;
57
58 if (opened)
59 return (1);
60 if (pcireg_cfgopen() == 0)
61 return (0);
62 mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
63 opened = 1;
64 return (1);
65 }
66
67 /*
68 * Read configuration space register
69 */
70 u_int32_t
71 pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
72 {
73 uint32_t line;
74
75 /*
76 * Some BIOS writers seem to want to ignore the spec and put
77 * 0 in the intline rather than 255 to indicate none. Some use
78 * numbers in the range 128-254 to indicate something strange and
79 * apparently undocumented anywhere. Assume these are completely bogus
80 * and map them to 255, which the rest of the PCI code recognizes as
81 * as an invalid IRQ.
82 */
83 if (reg == PCIR_INTLINE && bytes == 1) {
84 line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
85 if (line == 0 || line >= 128)
86 line = PCI_INVALID_IRQ;
87 return (line);
88 }
89 return (pcireg_cfgread(bus, slot, func, reg, bytes));
90 }
91
92 /*
93 * Write configuration space register
94 */
95 void
96 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
97 {
98
99 pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
100 }
101
102 /*
103 * Configuration space access using direct register operations
104 */
105
106 /* enable configuration space accesses and return data port address */
107 static int
108 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
109 {
110 int dataport = 0;
111
112 if (bus <= PCI_BUSMAX
113 && slot < devmax
114 && func <= PCI_FUNCMAX
115 && reg <= PCI_REGMAX
116 && bytes != 3
117 && (unsigned) bytes <= 4
118 && (reg & (bytes - 1)) == 0) {
119 switch (cfgmech) {
120 case 1:
121 outl(CONF1_ADDR_PORT, (1 << 31)
122 | (bus << 16) | (slot << 11)
123 | (func << 8) | (reg & ~0x03));
124 dataport = CONF1_DATA_PORT + (reg & 0x03);
125 break;
126 case 2:
127 outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
128 outb(CONF2_FORWARD_PORT, bus);
129 dataport = 0xc000 | (slot << 8) | reg;
130 break;
131 }
132 }
133 return (dataport);
134 }
135
136 /* disable configuration space accesses */
137 static void
138 pci_cfgdisable(void)
139 {
140 switch (cfgmech) {
141 case 1:
142 outl(CONF1_ADDR_PORT, 0);
143 break;
144 case 2:
145 outb(CONF2_ENABLE_PORT, 0);
146 outb(CONF2_FORWARD_PORT, 0);
147 break;
148 }
149 }
150
151 static int
152 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
153 {
154 int data = -1;
155 int port;
156
157 mtx_lock_spin(&pcicfg_mtx);
158 port = pci_cfgenable(bus, slot, func, reg, bytes);
159 if (port != 0) {
160 switch (bytes) {
161 case 1:
162 data = inb(port);
163 break;
164 case 2:
165 data = inw(port);
166 break;
167 case 4:
168 data = inl(port);
169 break;
170 }
171 pci_cfgdisable();
172 }
173 mtx_unlock_spin(&pcicfg_mtx);
174 return (data);
175 }
176
177 static void
178 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
179 {
180 int port;
181
182 mtx_lock_spin(&pcicfg_mtx);
183 port = pci_cfgenable(bus, slot, func, reg, bytes);
184 if (port != 0) {
185 switch (bytes) {
186 case 1:
187 outb(port, data);
188 break;
189 case 2:
190 outw(port, data);
191 break;
192 case 4:
193 outl(port, data);
194 break;
195 }
196 pci_cfgdisable();
197 }
198 mtx_unlock_spin(&pcicfg_mtx);
199 }
200
201 /* check whether the configuration mechanism has been correctly identified */
202 static int
203 pci_cfgcheck(int maxdev)
204 {
205 uint32_t id, class;
206 uint8_t header;
207 uint8_t device;
208 int port;
209
210 if (bootverbose)
211 printf("pci_cfgcheck:\tdevice ");
212
213 for (device = 0; device < maxdev; device++) {
214 if (bootverbose)
215 printf("%d ", device);
216
217 port = pci_cfgenable(0, device, 0, 0, 4);
218 id = inl(port);
219 if (id == 0 || id == 0xffffffff)
220 continue;
221
222 port = pci_cfgenable(0, device, 0, 8, 4);
223 class = inl(port) >> 8;
224 if (bootverbose)
225 printf("[class=%06x] ", class);
226 if (class == 0 || (class & 0xf870ff) != 0)
227 continue;
228
229 port = pci_cfgenable(0, device, 0, 14, 1);
230 header = inb(port);
231 if (bootverbose)
232 printf("[hdr=%02x] ", header);
233 if ((header & 0x7e) != 0)
234 continue;
235
236 if (bootverbose)
237 printf("is there (id=%08x)\n", id);
238
239 pci_cfgdisable();
240 return (1);
241 }
242 if (bootverbose)
243 printf("-- nothing found\n");
244
245 pci_cfgdisable();
246 return (0);
247 }
248
249 static int
250 pcireg_cfgopen(void)
251 {
252 uint32_t mode1res, oldval1;
253 uint8_t mode2res, oldval2;
254
255 oldval1 = inl(CONF1_ADDR_PORT);
256
257 if (bootverbose) {
258 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
259 oldval1);
260 }
261
262 if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
263
264 cfgmech = 1;
265 devmax = 32;
266
267 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
268 DELAY(1);
269 mode1res = inl(CONF1_ADDR_PORT);
270 outl(CONF1_ADDR_PORT, oldval1);
271
272 if (bootverbose)
273 printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n",
274 mode1res, CONF1_ENABLE_CHK);
275
276 if (mode1res) {
277 if (pci_cfgcheck(32))
278 return (cfgmech);
279 }
280
281 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
282 mode1res = inl(CONF1_ADDR_PORT);
283 outl(CONF1_ADDR_PORT, oldval1);
284
285 if (bootverbose)
286 printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n",
287 mode1res, CONF1_ENABLE_CHK1);
288
289 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
290 if (pci_cfgcheck(32))
291 return (cfgmech);
292 }
293 }
294
295 oldval2 = inb(CONF2_ENABLE_PORT);
296
297 if (bootverbose) {
298 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
299 oldval2);
300 }
301
302 if ((oldval2 & 0xf0) == 0) {
303
304 cfgmech = 2;
305 devmax = 16;
306
307 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
308 mode2res = inb(CONF2_ENABLE_PORT);
309 outb(CONF2_ENABLE_PORT, oldval2);
310
311 if (bootverbose)
312 printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
313 mode2res, CONF2_ENABLE_CHK);
314
315 if (mode2res == CONF2_ENABLE_RES) {
316 if (bootverbose)
317 printf("pci_open(2a):\tnow trying mechanism 2\n");
318
319 if (pci_cfgcheck(16))
320 return (cfgmech);
321 }
322 }
323
324 cfgmech = 0;
325 devmax = 0;
326 return (cfgmech);
327 }
Cache object: aeea1eb66341458822d7e76a4551c579
|