]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/pci/pci_cfgreg.c
This commit was generated by cvs2svn to compensate for changes in r169693,
[FreeBSD/FreeBSD.git] / sys / amd64 / pci / pci_cfgreg.c
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                 /*
143                  * Do nothing for the config mechanism 1 case.
144                  * Writing a 0 to the address port can apparently
145                  * confuse some bridges and cause spurious
146                  * access failures.
147                  */
148                 break;
149         case 2:
150                 outb(CONF2_ENABLE_PORT, 0);
151                 break;
152         }
153 }
154
155 static int
156 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
157 {
158         int data = -1;
159         int port;
160
161         mtx_lock_spin(&pcicfg_mtx);
162         port = pci_cfgenable(bus, slot, func, reg, bytes);
163         if (port != 0) {
164                 switch (bytes) {
165                 case 1:
166                         data = inb(port);
167                         break;
168                 case 2:
169                         data = inw(port);
170                         break;
171                 case 4:
172                         data = inl(port);
173                         break;
174                 }
175                 pci_cfgdisable();
176         }
177         mtx_unlock_spin(&pcicfg_mtx);
178         return (data);
179 }
180
181 static void
182 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
183 {
184         int port;
185
186         mtx_lock_spin(&pcicfg_mtx);
187         port = pci_cfgenable(bus, slot, func, reg, bytes);
188         if (port != 0) {
189                 switch (bytes) {
190                 case 1:
191                         outb(port, data);
192                         break;
193                 case 2:
194                         outw(port, data);
195                         break;
196                 case 4:
197                         outl(port, data);
198                         break;
199                 }
200                 pci_cfgdisable();
201         }
202         mtx_unlock_spin(&pcicfg_mtx);
203 }
204
205 /* check whether the configuration mechanism has been correctly identified */
206 static int
207 pci_cfgcheck(int maxdev)
208 {
209         uint32_t id, class;
210         uint8_t header;
211         uint8_t device;
212         int port;
213
214         if (bootverbose) 
215                 printf("pci_cfgcheck:\tdevice ");
216
217         for (device = 0; device < maxdev; device++) {
218                 if (bootverbose) 
219                         printf("%d ", device);
220
221                 port = pci_cfgenable(0, device, 0, 0, 4);
222                 id = inl(port);
223                 if (id == 0 || id == 0xffffffff)
224                         continue;
225
226                 port = pci_cfgenable(0, device, 0, 8, 4);
227                 class = inl(port) >> 8;
228                 if (bootverbose)
229                         printf("[class=%06x] ", class);
230                 if (class == 0 || (class & 0xf870ff) != 0)
231                         continue;
232
233                 port = pci_cfgenable(0, device, 0, 14, 1);
234                 header = inb(port);
235                 if (bootverbose)
236                         printf("[hdr=%02x] ", header);
237                 if ((header & 0x7e) != 0)
238                         continue;
239
240                 if (bootverbose)
241                         printf("is there (id=%08x)\n", id);
242
243                 pci_cfgdisable();
244                 return (1);
245         }
246         if (bootverbose) 
247                 printf("-- nothing found\n");
248
249         pci_cfgdisable();
250         return (0);
251 }
252
253 static int
254 pcireg_cfgopen(void)
255 {
256         uint32_t mode1res, oldval1;
257         uint8_t mode2res, oldval2;
258
259         oldval1 = inl(CONF1_ADDR_PORT);
260
261         if (bootverbose) {
262                 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
263                     oldval1);
264         }
265
266         if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
267
268                 cfgmech = 1;
269                 devmax = 32;
270
271                 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
272                 DELAY(1);
273                 mode1res = inl(CONF1_ADDR_PORT);
274                 outl(CONF1_ADDR_PORT, oldval1);
275
276                 if (bootverbose)
277                         printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", 
278                             mode1res, CONF1_ENABLE_CHK);
279
280                 if (mode1res) {
281                         if (pci_cfgcheck(32)) 
282                                 return (cfgmech);
283                 }
284
285                 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
286                 mode1res = inl(CONF1_ADDR_PORT);
287                 outl(CONF1_ADDR_PORT, oldval1);
288
289                 if (bootverbose)
290                         printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", 
291                             mode1res, CONF1_ENABLE_CHK1);
292
293                 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
294                         if (pci_cfgcheck(32)) 
295                                 return (cfgmech);
296                 }
297         }
298
299         oldval2 = inb(CONF2_ENABLE_PORT);
300
301         if (bootverbose) {
302                 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
303                     oldval2);
304         }
305
306         if ((oldval2 & 0xf0) == 0) {
307
308                 cfgmech = 2;
309                 devmax = 16;
310
311                 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
312                 mode2res = inb(CONF2_ENABLE_PORT);
313                 outb(CONF2_ENABLE_PORT, oldval2);
314
315                 if (bootverbose)
316                         printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 
317                             mode2res, CONF2_ENABLE_CHK);
318
319                 if (mode2res == CONF2_ENABLE_RES) {
320                         if (bootverbose)
321                                 printf("pci_open(2a):\tnow trying mechanism 2\n");
322
323                         if (pci_cfgcheck(16)) 
324                                 return (cfgmech);
325                 }
326         }
327
328         cfgmech = 0;
329         devmax = 0;
330         return (cfgmech);
331 }