pci_cfgreg.c revision 31893
1/*
2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $Id: pcibus.c,v 1.40 1997/07/20 14:10:08 bde Exp $
27 *
28 */
29
30#include <sys/types.h>
31#include <sys/systm.h>
32
33#include <pci/pcivar.h>
34#include <i386/isa/pcibus.h>
35
36#ifdef PCI_COMPAT
37/* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */
38#define cfgmech pci_mechanism
39int cfgmech;
40#else
41static int cfgmech;
42#endif /* PCI_COMPAT */
43static int devmax;
44
45/* enable configuration space accesses and return data port address */
46
47static int
48pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
49{
50	int dataport = 0;
51
52	if (bus <= PCI_BUSMAX
53	    && slot < devmax
54	    && func <= PCI_FUNCMAX
55	    && reg <= PCI_REGMAX
56	    && bytes != 3
57	    && (unsigned) bytes <= 4
58	    && (reg & (bytes -1)) == 0) {
59		switch (cfgmech) {
60		case 1:
61			outl(CONF1_ADDR_PORT, (1 << 31)
62			     | (bus << 16) | (slot << 11)
63			     | (func << 8) | (reg & ~0x03));
64			dataport = CONF1_DATA_PORT + (reg & 0x03);
65			break;
66		case 2:
67			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
68			outb(CONF2_FORWARD_PORT, bus);
69			dataport = 0xc000 | (slot << 8) | reg;
70			break;
71		}
72	}
73	return (dataport);
74}
75
76/* disable configuration space accesses */
77
78static void
79pci_cfgdisable(void)
80{
81	switch (cfgmech) {
82	case 1:
83		outl(CONF1_ADDR_PORT, 0);
84		break;
85	case 2:
86		outb(CONF2_ENABLE_PORT, 0);
87		outb(CONF2_FORWARD_PORT, 0);
88		break;
89	}
90}
91
92/* read configuration space register */
93
94int
95pci_cfgread(pcicfgregs *cfg, int reg, int bytes)
96{
97	int data = -1;
98	int port;
99
100	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
101
102	if (port != 0) {
103		switch (bytes) {
104		case 1:
105			data = inb(port);
106			break;
107		case 2:
108			data = inw(port);
109			break;
110		case 4:
111			data = inl(port);
112			break;
113		}
114		pci_cfgdisable();
115	}
116	return (data);
117}
118
119/* write configuration space register */
120
121void
122pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
123{
124	int port;
125
126	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
127	if (port != 0) {
128		switch (bytes) {
129		case 1:
130			outb(port, data);
131			break;
132		case 2:
133			outw(port, data);
134			break;
135		case 4:
136			outl(port, data);
137			break;
138		}
139		pci_cfgdisable();
140	}
141}
142
143/* check whether the configuration mechanism has been correct identified */
144
145static int
146pci_cfgcheck(int maxdev)
147{
148	u_char device;
149
150	if (bootverbose)
151		printf("pci_cfgcheck:\tdevice ");
152
153	for (device = 0; device < maxdev; device++) {
154		unsigned id, class, header;
155		if (bootverbose)
156			printf("%d ", device);
157
158		id = inl(pci_cfgenable(0, device, 0, 0, 4));
159		if (id == 0 || id == -1)
160			continue;
161
162		class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
163		if (bootverbose)
164			printf("[class=%06x] ", class);
165		if (class == 0 || (class & 0xf870ff) != 0)
166			continue;
167
168		header = inb(pci_cfgenable(0, device, 0, 14, 1));
169		if (bootverbose)
170			printf("[hdr=%02x] ", header);
171		if ((header & 0x7e) != 0)
172			continue;
173
174		if (bootverbose)
175			printf("is there (id=%08x)\n", id);
176
177		pci_cfgdisable();
178		return (1);
179	}
180	if (bootverbose)
181		printf("-- nothing found\n");
182
183	pci_cfgdisable();
184	return (0);
185}
186
187int
188pci_cfgopen(void)
189{
190	unsigned long mode1res,oldval1;
191	unsigned char mode2res,oldval2;
192
193	oldval1 = inl(CONF1_ADDR_PORT);
194
195	if (bootverbose) {
196		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
197		       oldval1);
198	}
199
200	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
201
202		cfgmech = 1;
203		devmax = 32;
204
205		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
206		outb(CONF1_ADDR_PORT +3, 0);
207		mode1res = inl(CONF1_ADDR_PORT);
208		outl(CONF1_ADDR_PORT, oldval1);
209
210		if (bootverbose)
211			printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
212			       mode1res, CONF1_ENABLE_CHK);
213
214		if (mode1res) {
215			if (pci_cfgcheck(32))
216				return (cfgmech);
217		}
218
219		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
220		mode1res = inl(CONF1_ADDR_PORT);
221		outl(CONF1_ADDR_PORT, oldval1);
222
223		if (bootverbose)
224			printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
225			       mode1res, CONF1_ENABLE_CHK1);
226
227		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
228			if (pci_cfgcheck(32))
229				return (cfgmech);
230		}
231	}
232
233	oldval2 = inb(CONF2_ENABLE_PORT);
234
235	if (bootverbose) {
236		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
237		       oldval2);
238	}
239
240	if ((oldval2 & 0xf0) == 0) {
241
242		cfgmech = 2;
243		devmax = 16;
244
245		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
246		mode2res = inb(CONF2_ENABLE_PORT);
247		outb(CONF2_ENABLE_PORT, oldval2);
248
249		if (bootverbose)
250			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
251			       mode2res, CONF2_ENABLE_CHK);
252
253		if (mode2res == CONF2_ENABLE_RES) {
254			if (bootverbose)
255				printf("pci_open(2a):\tnow trying mechanism 2\n");
256
257			if (pci_cfgcheck(16))
258				return (cfgmech);
259		}
260	}
261
262	cfgmech = 0;
263	devmax = 0;
264	return (cfgmech);
265}
266