]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/pci/pci_cfgreg.c
add -n option to suppress clearing the build tree and add -DNO_CLEAN
[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/kernel.h>
37 #include <sys/mutex.h>
38 #include <dev/pci/pcivar.h>
39 #include <dev/pci/pcireg.h>
40 #include <vm/vm.h>
41 #include <vm/pmap.h>
42 #include <machine/pci_cfgreg.h>
43
44 enum {
45         CFGMECH_NONE = 0,
46         CFGMECH_1,
47         CFGMECH_PCIE,
48 };
49
50 static uint32_t pci_docfgregread(int bus, int slot, int func, int reg,
51                     int bytes);
52 static int      pciereg_cfgread(int bus, unsigned slot, unsigned func,
53                     unsigned reg, unsigned bytes);
54 static void     pciereg_cfgwrite(int bus, unsigned slot, unsigned func,
55                     unsigned reg, int data, unsigned bytes);
56 static int      pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
57 static void     pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
58
59 static int cfgmech;
60 static vm_offset_t pcie_base;
61 static int pcie_minbus, pcie_maxbus;
62 static uint32_t pcie_badslots;
63 static struct mtx pcicfg_mtx;
64 static int mcfg_enable = 1;
65 TUNABLE_INT("hw.pci.mcfg", &mcfg_enable);
66
67 /* 
68  * Initialise access to PCI configuration space 
69  */
70 int
71 pci_cfgregopen(void)
72 {
73         static int once = 0;
74         uint64_t pciebar;
75         uint16_t did, vid;
76
77         if (!once) {
78                 mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
79                 once = 1;
80         }
81
82         if (cfgmech != CFGMECH_NONE)
83                 return (1);
84         cfgmech = CFGMECH_1;
85
86         /*
87          * Grope around in the PCI config space to see if this is a
88          * chipset that is capable of doing memory-mapped config cycles.
89          * This also implies that it can do PCIe extended config cycles.
90          */
91
92         /* Check for supported chipsets */
93         vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
94         did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
95         switch (vid) {
96         case 0x8086:
97                 switch (did) {
98                 case 0x3590:
99                 case 0x3592:
100                         /* Intel 7520 or 7320 */
101                         pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
102                         pcie_cfgregopen(pciebar, 0, 255);
103                         break;
104                 case 0x2580:
105                 case 0x2584:
106                 case 0x2590:
107                         /* Intel 915, 925, or 915GM */
108                         pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
109                         pcie_cfgregopen(pciebar, 0, 255);
110                         break;
111                 }
112         }
113
114         return (1);
115 }
116
117 static uint32_t
118 pci_docfgregread(int bus, int slot, int func, int reg, int bytes)
119 {
120
121         if (cfgmech == CFGMECH_PCIE &&
122             (bus != 0 || !(1 << slot & pcie_badslots)))
123                 return (pciereg_cfgread(bus, slot, func, reg, bytes));
124         else
125                 return (pcireg_cfgread(bus, slot, func, reg, bytes));
126 }
127
128 /* 
129  * Read configuration space register
130  */
131 u_int32_t
132 pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
133 {
134         uint32_t line;
135
136         /*
137          * Some BIOS writers seem to want to ignore the spec and put
138          * 0 in the intline rather than 255 to indicate none.  Some use
139          * numbers in the range 128-254 to indicate something strange and
140          * apparently undocumented anywhere.  Assume these are completely bogus
141          * and map them to 255, which the rest of the PCI code recognizes as
142          * as an invalid IRQ.
143          */
144         if (reg == PCIR_INTLINE && bytes == 1) {
145                 line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1);
146                 if (line == 0 || line >= 128)
147                         line = PCI_INVALID_IRQ;
148                 return (line);
149         }
150         return (pci_docfgregread(bus, slot, func, reg, bytes));
151 }
152
153 /* 
154  * Write configuration space register 
155  */
156 void
157 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
158 {
159
160         if (cfgmech == CFGMECH_PCIE &&
161             (bus != 0 || !(1 << slot & pcie_badslots)))
162                 pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
163         else
164                 pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
165 }
166
167 /* 
168  * Configuration space access using direct register operations
169  */
170
171 /* enable configuration space accesses and return data port address */
172 static int
173 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
174 {
175         int dataport = 0;
176
177         if (bus <= PCI_BUSMAX && slot < 32 && func <= PCI_FUNCMAX &&
178             reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 &&
179             (reg & (bytes - 1)) == 0) {
180                 outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) 
181                     | (func << 8) | (reg & ~0x03));
182                 dataport = CONF1_DATA_PORT + (reg & 0x03);
183         }
184         return (dataport);
185 }
186
187 /* disable configuration space accesses */
188 static void
189 pci_cfgdisable(void)
190 {
191
192         /*
193          * Do nothing.  Writing a 0 to the address port can apparently
194          * confuse some bridges and cause spurious access failures.
195          */
196 }
197
198 static int
199 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
200 {
201         int data = -1;
202         int port;
203
204         mtx_lock_spin(&pcicfg_mtx);
205         port = pci_cfgenable(bus, slot, func, reg, bytes);
206         if (port != 0) {
207                 switch (bytes) {
208                 case 1:
209                         data = inb(port);
210                         break;
211                 case 2:
212                         data = inw(port);
213                         break;
214                 case 4:
215                         data = inl(port);
216                         break;
217                 }
218                 pci_cfgdisable();
219         }
220         mtx_unlock_spin(&pcicfg_mtx);
221         return (data);
222 }
223
224 static void
225 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
226 {
227         int port;
228
229         mtx_lock_spin(&pcicfg_mtx);
230         port = pci_cfgenable(bus, slot, func, reg, bytes);
231         if (port != 0) {
232                 switch (bytes) {
233                 case 1:
234                         outb(port, data);
235                         break;
236                 case 2:
237                         outw(port, data);
238                         break;
239                 case 4:
240                         outl(port, data);
241                         break;
242                 }
243                 pci_cfgdisable();
244         }
245         mtx_unlock_spin(&pcicfg_mtx);
246 }
247
248 int
249 pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
250 {
251         uint32_t val1, val2;
252         int slot;
253
254         if (!mcfg_enable)
255                 return (0);
256
257         if (minbus != 0)
258                 return (0);
259
260         if (bootverbose)
261                 printf("PCIe: Memory Mapped configuration base @ 0x%lx\n",
262                     base);
263
264         /* XXX: We should make sure this really fits into the direct map. */
265         pcie_base = (vm_offset_t)pmap_mapdev(base, (maxbus + 1) << 20);
266         pcie_minbus = minbus;
267         pcie_maxbus = maxbus;
268         cfgmech = CFGMECH_PCIE;
269
270         /*
271          * On some AMD systems, some of the devices on bus 0 are
272          * inaccessible using memory-mapped PCI config access.  Walk
273          * bus 0 looking for such devices.  For these devices, we will
274          * fall back to using type 1 config access instead.
275          */
276         if (pci_cfgregopen() != 0) {
277                 for (slot = 0; slot < 32; slot++) {
278                         val1 = pcireg_cfgread(0, slot, 0, 0, 4);
279                         if (val1 == 0xffffffff)
280                                 continue;
281
282                         val2 = pciereg_cfgread(0, slot, 0, 0, 4);
283                         if (val2 != val1)
284                                 pcie_badslots |= (1 << slot);
285                 }
286         }
287
288         return (1);
289 }
290
291 #define PCIE_VADDR(base, reg, bus, slot, func)  \
292         ((base)                         +       \
293         ((((bus) & 0xff) << 20)         |       \
294         (((slot) & 0x1f) << 15)         |       \
295         (((func) & 0x7) << 12)          |       \
296         ((reg) & 0xfff)))
297
298 static int
299 pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
300     unsigned bytes)
301 {
302         volatile vm_offset_t va;
303         int data = -1;
304
305         if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
306             func > PCI_FUNCMAX || reg >= 0x1000)
307                 return (-1);
308
309         va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
310
311         switch (bytes) {
312         case 4:
313                 data = *(volatile uint32_t *)(va);
314                 break;
315         case 2:
316                 data = *(volatile uint16_t *)(va);
317                 break;
318         case 1:
319                 data = *(volatile uint8_t *)(va);
320                 break;
321         }
322
323         return (data);
324 }
325
326 static void
327 pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data,
328     unsigned bytes)
329 {
330         volatile vm_offset_t va;
331
332         if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 ||
333             func > PCI_FUNCMAX || reg >= 0x1000)
334                 return;
335
336         va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
337
338         switch (bytes) {
339         case 4:
340                 *(volatile uint32_t *)(va) = data;
341                 break;
342         case 2:
343                 *(volatile uint16_t *)(va) = data;
344                 break;
345         case 1:
346                 *(volatile uint8_t *)(va) = data;
347                 break;
348         }
349 }