pci_cfgreg.c revision 123180
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 123180 2003-12-06 23:19:47Z peter $");
31
32#include <sys/param.h>		/* XXX trim includes */
33#include <sys/systm.h>
34#include <sys/bus.h>
35#include <sys/kernel.h>
36#include <sys/module.h>
37#include <sys/malloc.h>
38#include <sys/lock.h>
39#include <sys/mutex.h>
40#include <vm/vm.h>
41#include <vm/pmap.h>
42#include <machine/md_var.h>
43#include <dev/pci/pcivar.h>
44#include <dev/pci/pcireg.h>
45#include <isa/isavar.h>
46#include <machine/pci_cfgreg.h>
47
48#include "pcib_if.h"
49
50static int cfgmech;
51static int devmax;
52
53static int	pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
54static void	pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes);
55static int	pcireg_cfgopen(void);
56
57static struct mtx pcicfg_mtx;
58
59/*
60 * Initialise access to PCI configuration space
61 */
62int
63pci_cfgregopen(void)
64{
65	static int		opened = 0;
66
67	if (opened)
68		return (1);
69	if (pcireg_cfgopen() == 0)
70		return (0);
71	mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN);
72	opened = 1;
73	return (1);
74}
75
76/*
77 * Read configuration space register
78 */
79u_int32_t
80pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
81{
82	uint32_t line;
83
84	/*
85	 * Some BIOS writers seem to want to ignore the spec and put
86	 * 0 in the intline rather than 255 to indicate none.  Some use
87	 * numbers in the range 128-254 to indicate something strange and
88	 * apparently undocumented anywhere.  Assume these are completely bogus
89	 * and map them to 255, which the rest of the PCI code recognizes as
90	 * as an invalid IRQ.
91	 */
92	if (reg == PCIR_INTLINE && bytes == 1) {
93		line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1);
94		if (line == 0 || line >= 128)
95			line = PCI_INVALID_IRQ;
96		return (line);
97	}
98	return (pcireg_cfgread(bus, slot, func, reg, bytes));
99}
100
101/*
102 * Write configuration space register
103 */
104void
105pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
106{
107
108	pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
109}
110
111/*
112 * Route a PCI interrupt
113 */
114int
115pci_cfgintr(int bus, int device, int pin, int oldirq)
116{
117
118	printf("pci_cfgintr: can't route an interrupt to %d:%d INT%c without ACPI\n", bus,
119	    device, 'A' + pin - 1);
120	return (PCI_INVALID_IRQ);
121}
122
123/*
124 * Configuration space access using direct register operations
125 */
126
127/* enable configuration space accesses and return data port address */
128static int
129pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
130{
131	int dataport = 0;
132
133	if (bus <= PCI_BUSMAX
134	    && slot < devmax
135	    && func <= PCI_FUNCMAX
136	    && reg <= PCI_REGMAX
137	    && bytes != 3
138	    && (unsigned) bytes <= 4
139	    && (reg & (bytes - 1)) == 0) {
140		switch (cfgmech) {
141		case 1:
142			outl(CONF1_ADDR_PORT, (1 << 31)
143			    | (bus << 16) | (slot << 11)
144			    | (func << 8) | (reg & ~0x03));
145			dataport = CONF1_DATA_PORT + (reg & 0x03);
146			break;
147		case 2:
148			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
149			outb(CONF2_FORWARD_PORT, bus);
150			dataport = 0xc000 | (slot << 8) | reg;
151			break;
152		}
153	}
154	return (dataport);
155}
156
157/* disable configuration space accesses */
158static void
159pci_cfgdisable(void)
160{
161	switch (cfgmech) {
162	case 1:
163		outl(CONF1_ADDR_PORT, 0);
164		break;
165	case 2:
166		outb(CONF2_ENABLE_PORT, 0);
167		outb(CONF2_FORWARD_PORT, 0);
168		break;
169	}
170}
171
172static int
173pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
174{
175	int data = -1;
176	int port;
177
178	mtx_lock_spin(&pcicfg_mtx);
179	port = pci_cfgenable(bus, slot, func, reg, bytes);
180	if (port != 0) {
181		switch (bytes) {
182		case 1:
183			data = inb(port);
184			break;
185		case 2:
186			data = inw(port);
187			break;
188		case 4:
189			data = inl(port);
190			break;
191		}
192		pci_cfgdisable();
193	}
194	mtx_unlock_spin(&pcicfg_mtx);
195	return (data);
196}
197
198static void
199pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
200{
201	int port;
202
203	mtx_lock_spin(&pcicfg_mtx);
204	port = pci_cfgenable(bus, slot, func, reg, bytes);
205	if (port != 0) {
206		switch (bytes) {
207		case 1:
208			outb(port, data);
209			break;
210		case 2:
211			outw(port, data);
212			break;
213		case 4:
214			outl(port, data);
215			break;
216		}
217		pci_cfgdisable();
218	}
219	mtx_unlock_spin(&pcicfg_mtx);
220}
221
222/* check whether the configuration mechanism has been correctly identified */
223static int
224pci_cfgcheck(int maxdev)
225{
226	uint32_t id, class;
227	uint8_t header;
228	uint8_t device;
229	int port;
230
231	if (bootverbose)
232		printf("pci_cfgcheck:\tdevice ");
233
234	for (device = 0; device < maxdev; device++) {
235		if (bootverbose)
236			printf("%d ", device);
237
238		port = pci_cfgenable(0, device, 0, 0, 4);
239		id = inl(port);
240		if (id == 0 || id == 0xffffffff)
241			continue;
242
243		port = pci_cfgenable(0, device, 0, 8, 4);
244		class = inl(port) >> 8;
245		if (bootverbose)
246			printf("[class=%06x] ", class);
247		if (class == 0 || (class & 0xf870ff) != 0)
248			continue;
249
250		port = pci_cfgenable(0, device, 0, 14, 1);
251		header = inb(port);
252		if (bootverbose)
253			printf("[hdr=%02x] ", header);
254		if ((header & 0x7e) != 0)
255			continue;
256
257		if (bootverbose)
258			printf("is there (id=%08x)\n", id);
259
260		pci_cfgdisable();
261		return (1);
262	}
263	if (bootverbose)
264		printf("-- nothing found\n");
265
266	pci_cfgdisable();
267	return (0);
268}
269
270static int
271pcireg_cfgopen(void)
272{
273	uint32_t mode1res, oldval1;
274	uint8_t mode2res, oldval2;
275
276	oldval1 = inl(CONF1_ADDR_PORT);
277
278	if (bootverbose) {
279		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n",
280		    oldval1);
281	}
282
283	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
284
285		cfgmech = 1;
286		devmax = 32;
287
288		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
289		outb(CONF1_ADDR_PORT + 3, 0);
290		mode1res = inl(CONF1_ADDR_PORT);
291		outl(CONF1_ADDR_PORT, oldval1);
292
293		if (bootverbose)
294			printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n",
295			    mode1res, CONF1_ENABLE_CHK);
296
297		if (mode1res) {
298			if (pci_cfgcheck(32))
299				return (cfgmech);
300		}
301
302		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
303		mode1res = inl(CONF1_ADDR_PORT);
304		outl(CONF1_ADDR_PORT, oldval1);
305
306		if (bootverbose)
307			printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n",
308			    mode1res, CONF1_ENABLE_CHK1);
309
310		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
311			if (pci_cfgcheck(32))
312				return (cfgmech);
313		}
314	}
315
316	oldval2 = inb(CONF2_ENABLE_PORT);
317
318	if (bootverbose) {
319		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
320		    oldval2);
321	}
322
323	if ((oldval2 & 0xf0) == 0) {
324
325		cfgmech = 2;
326		devmax = 16;
327
328		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
329		mode2res = inb(CONF2_ENABLE_PORT);
330		outb(CONF2_ENABLE_PORT, oldval2);
331
332		if (bootverbose)
333			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
334			    mode2res, CONF2_ENABLE_CHK);
335
336		if (mode2res == CONF2_ENABLE_RES) {
337			if (bootverbose)
338				printf("pci_open(2a):\tnow trying mechanism 2\n");
339
340			if (pci_cfgcheck(16))
341				return (cfgmech);
342		}
343	}
344
345	cfgmech = 0;
346	devmax = 0;
347	return (cfgmech);
348}
349
350