pci_cfgreg.c revision 26174
1274955Ssvnmir/*
2274955Ssvnmir * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3274955Ssvnmir * All rights reserved.
4274955Ssvnmir *
5274955Ssvnmir * Redistribution and use in source and binary forms, with or without
6274955Ssvnmir * modification, are permitted provided that the following conditions
7274955Ssvnmir * are met:
8274955Ssvnmir * 1. Redistributions of source code must retain the above copyright
9274955Ssvnmir *    notice unmodified, this list of conditions, and the following
10274955Ssvnmir *    disclaimer.
11274955Ssvnmir * 2. Redistributions in binary form must reproduce the above copyright
12274955Ssvnmir *    notice, this list of conditions and the following disclaimer in the
13274955Ssvnmir *    documentation and/or other materials provided with the distribution.
14274955Ssvnmir *
15274955Ssvnmir * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16274955Ssvnmir * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17274955Ssvnmir * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18274955Ssvnmir * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19274955Ssvnmir * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20274955Ssvnmir * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21274955Ssvnmir * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22274955Ssvnmir * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23274955Ssvnmir * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24274955Ssvnmir * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25274955Ssvnmir *
26274955Ssvnmir * $Id: pcibus.c,v 1.38 1997/05/26 21:25:24 se Exp $
27274955Ssvnmir *
28274955Ssvnmir */
29274955Ssvnmir
30274955Ssvnmir#include <sys/types.h>
31274955Ssvnmir#include <sys/systm.h>
32274955Ssvnmir
33274955Ssvnmir#include <pci/pcireg.h>
34274955Ssvnmir#include <pci/pcivar.h>
35274955Ssvnmir#include <i386/isa/pcibus.h>
36274955Ssvnmir
37274955Ssvnmir#ifdef PCI_COMPAT
38274955Ssvnmir/* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */
39274955Ssvnmir#define cfgmech pci_mechanism
40274955Ssvnmirint cfgmech;
41274955Ssvnmir#else
42274955Ssvnmirstatic int cfgmech;
43274955Ssvnmir#endif /* PCI_COMPAT */
44274955Ssvnmirstatic int devmax;
45274955Ssvnmir
46274955Ssvnmir/* enable configuration space accesses and return data port address */
47274955Ssvnmir
48274955Ssvnmirstatic int
49274955Ssvnmirpci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
50274955Ssvnmir{
51274955Ssvnmir	int dataport = 0;
52274955Ssvnmir
53274955Ssvnmir	if (bus <= PCI_BUSMAX
54274955Ssvnmir	    && slot < devmax
55274955Ssvnmir	    && func <= PCI_FUNCMAX
56274955Ssvnmir	    && reg <= PCI_REGMAX
57274955Ssvnmir	    && bytes != 3
58274955Ssvnmir	    && (unsigned) bytes <= 4
59274955Ssvnmir	    && (reg & (bytes -1)) == 0) {
60274955Ssvnmir		switch (cfgmech) {
61274955Ssvnmir		case 1:
62274955Ssvnmir			outl(CONF1_ADDR_PORT, (1 << 31)
63274955Ssvnmir			     | (bus << 16) | (slot << 11)
64274955Ssvnmir			     | (func << 8) | (reg & ~0x03));
65274955Ssvnmir			dataport = CONF1_DATA_PORT + (reg & 0x03);
66274955Ssvnmir			break;
67274955Ssvnmir		case 2:
68274955Ssvnmir			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
69274955Ssvnmir			outb(CONF2_FORWARD_PORT, bus);
70274955Ssvnmir			dataport = 0xc000 | (slot << 8) | reg;
71274955Ssvnmir			break;
72274955Ssvnmir		}
73274955Ssvnmir	}
74274955Ssvnmir	return (dataport);
75274955Ssvnmir}
76274955Ssvnmir
77274955Ssvnmir/* disable configuration space accesses */
78274955Ssvnmir
79274955Ssvnmirstatic void
80274955Ssvnmirpci_cfgdisable(void)
81274955Ssvnmir{
82274955Ssvnmir	switch (cfgmech) {
83274955Ssvnmir	case 1:
84274955Ssvnmir		outl(CONF1_ADDR_PORT, 0);
85274955Ssvnmir		break;
86274955Ssvnmir	case 2:
87274955Ssvnmir		outb(CONF2_ENABLE_PORT, 0);
88274955Ssvnmir		outb(CONF2_FORWARD_PORT, 0);
89274955Ssvnmir		break;
90274955Ssvnmir	}
91274955Ssvnmir}
92274955Ssvnmir
93274955Ssvnmir/* read configuration space register */
94274955Ssvnmir
95274955Ssvnmirint
96274955Ssvnmirpci_cfgread(pcicfgregs *cfg, int reg, int bytes)
97274955Ssvnmir{
98274955Ssvnmir	int data = -1;
99274955Ssvnmir	int port;
100274955Ssvnmir
101274955Ssvnmir	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
102274955Ssvnmir
103274955Ssvnmir	if (port != 0) {
104274955Ssvnmir		switch (bytes) {
105274955Ssvnmir		case 1:
106274955Ssvnmir			data = inb(port);
107274955Ssvnmir			break;
108274955Ssvnmir		case 2:
109274955Ssvnmir			data = inw(port);
110274955Ssvnmir			break;
111274955Ssvnmir		case 4:
112274955Ssvnmir			data = inl(port);
113274955Ssvnmir			break;
114274955Ssvnmir		}
115274955Ssvnmir		pci_cfgdisable();
116274955Ssvnmir	}
117274955Ssvnmir	return (data);
118274955Ssvnmir}
119274955Ssvnmir
120274955Ssvnmir/* write configuration space register */
121274955Ssvnmir
122274955Ssvnmirvoid
123274955Ssvnmirpci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
124274955Ssvnmir{
125274955Ssvnmir	int port;
126274955Ssvnmir
127274955Ssvnmir	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
128	if (port != 0) {
129		switch (bytes) {
130		case 1:
131			outb(port, data);
132			break;
133		case 2:
134			outw(port, data);
135			break;
136		case 4:
137			outl(port, data);
138			break;
139		}
140		pci_cfgdisable();
141	}
142}
143
144/* check whether the configuration mechanism has been correct identified */
145
146static int
147pci_cfgcheck(int maxdev)
148{
149	u_char device;
150
151	if (bootverbose)
152		printf("pci_cfgcheck:\tdevice ");
153
154	for (device = 0; device < maxdev; device++) {
155		unsigned id, class, header;
156		if (bootverbose)
157			printf("%d ", device);
158
159		id = inl(pci_cfgenable(0, device, 0, 0, 4));
160		if (id == 0 || id == -1)
161			continue;
162
163		class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
164		if (bootverbose)
165			printf("[class=%06x] ", class);
166		if (class == 0 || (class & 0xf8f0ff) != 0)
167			continue;
168
169		header = inb(pci_cfgenable(0, device, 0, 14, 1));
170		if (bootverbose)
171			printf("[hdr=%02x] ", header);
172		if ((header & 0x7e) != 0)
173			continue;
174
175		if (bootverbose)
176			printf("is there (id=%08x)\n", id);
177
178		pci_cfgdisable();
179		return (1);
180	}
181	if (bootverbose)
182		printf("-- nothing found\n");
183
184	pci_cfgdisable();
185	return (0);
186}
187
188int
189pci_cfgopen(void)
190{
191	unsigned long mode1res,oldval1;
192	unsigned char mode2res,oldval2;
193
194	oldval1 = inl(CONF1_ADDR_PORT);
195
196	if (bootverbose) {
197		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
198		       oldval1);
199	}
200
201	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
202
203		cfgmech = 1;
204		devmax = 32;
205
206		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
207		outb(CONF1_ADDR_PORT +3, 0);
208		mode1res = inl(CONF1_ADDR_PORT);
209		outl(CONF1_ADDR_PORT, oldval1);
210
211		if (bootverbose)
212			printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
213			       mode1res, CONF1_ENABLE_CHK);
214
215		if (mode1res) {
216			if (pci_cfgcheck(32))
217				return (cfgmech);
218		}
219
220		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
221		mode1res = inl(CONF1_ADDR_PORT);
222		outl(CONF1_ADDR_PORT, oldval1);
223
224		if (bootverbose)
225			printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
226			       mode1res, CONF1_ENABLE_CHK1);
227
228		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
229			if (pci_cfgcheck(32))
230				return (cfgmech);
231		}
232	}
233
234	oldval2 = inb(CONF2_ENABLE_PORT);
235
236	if (bootverbose) {
237		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
238		       oldval2);
239	}
240
241	if ((oldval2 & 0xf0) == 0) {
242
243		cfgmech = 2;
244		devmax = 16;
245
246		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
247		mode2res = inb(CONF2_ENABLE_PORT);
248		outb(CONF2_ENABLE_PORT, oldval2);
249
250		if (bootverbose)
251			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
252			       mode2res, CONF2_ENABLE_CHK);
253
254		if (mode2res == CONF2_ENABLE_RES) {
255			if (bootverbose)
256				printf("pci_open(2a):\tnow trying mechanism 2\n");
257
258			if (pci_cfgcheck(16))
259				return (cfgmech);
260		}
261	}
262
263	cfgmech = 0;
264	devmax = 0;
265	return (cfgmech);
266}
267