pci_cfgreg.c revision 139731
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: head/sys/amd64/pci/pci_cfgreg.c 139731 2005-01-05 20:17:21Z imp $");
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
41static int cfgmech;
42static int devmax;
43
44static int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
45static void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
46static int	pcireg_cfgopen(void);
47
48static struct mtx pcicfg_mtx;
49
50/*
51 * Initialise access to PCI configuration space
52 */
53int
54pci_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 */
70u_int32_t
71pci_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 */
95void
96pci_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 */
107static int
108pci_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 */
137static void
138pci_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
151static int
152pcireg_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
177static void
178pcireg_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 */
202static int
203pci_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
249static int
250pcireg_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}
328
329