xref: /freebsd/sys/i386/pci/pci_pir.c (revision 6e8394b8baa7d5d9153ab90de6824bcd19b3b4e1)
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.41 1997/12/20 09:04:25 se Exp $
27  *
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 
35 #include <pci/pcivar.h>
36 #include <i386/isa/pcibus.h>
37 
38 #ifdef PCI_COMPAT
39 /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */
40 #define cfgmech pci_mechanism
41 int cfgmech;
42 #else
43 static int cfgmech;
44 #endif /* PCI_COMPAT */
45 static int devmax;
46 
47 /* enable configuration space accesses and return data port address */
48 
49 static int
50 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
51 {
52 	int dataport = 0;
53 
54 	if (bus <= PCI_BUSMAX
55 	    && slot < devmax
56 	    && func <= PCI_FUNCMAX
57 	    && reg <= PCI_REGMAX
58 	    && bytes != 3
59 	    && (unsigned) bytes <= 4
60 	    && (reg & (bytes -1)) == 0) {
61 		switch (cfgmech) {
62 		case 1:
63 			outl(CONF1_ADDR_PORT, (1 << 31)
64 			     | (bus << 16) | (slot << 11)
65 			     | (func << 8) | (reg & ~0x03));
66 			dataport = CONF1_DATA_PORT + (reg & 0x03);
67 			break;
68 		case 2:
69 			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
70 			outb(CONF2_FORWARD_PORT, bus);
71 			dataport = 0xc000 | (slot << 8) | reg;
72 			break;
73 		}
74 	}
75 	return (dataport);
76 }
77 
78 /* disable configuration space accesses */
79 
80 static void
81 pci_cfgdisable(void)
82 {
83 	switch (cfgmech) {
84 	case 1:
85 		outl(CONF1_ADDR_PORT, 0);
86 		break;
87 	case 2:
88 		outb(CONF2_ENABLE_PORT, 0);
89 		outb(CONF2_FORWARD_PORT, 0);
90 		break;
91 	}
92 }
93 
94 /* read configuration space register */
95 
96 int
97 pci_cfgread(pcicfgregs *cfg, int reg, int bytes)
98 {
99 	int data = -1;
100 	int port;
101 
102 	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
103 
104 	if (port != 0) {
105 		switch (bytes) {
106 		case 1:
107 			data = inb(port);
108 			break;
109 		case 2:
110 			data = inw(port);
111 			break;
112 		case 4:
113 			data = inl(port);
114 			break;
115 		}
116 		pci_cfgdisable();
117 	}
118 	return (data);
119 }
120 
121 /* write configuration space register */
122 
123 void
124 pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
125 {
126 	int port;
127 
128 	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
129 	if (port != 0) {
130 		switch (bytes) {
131 		case 1:
132 			outb(port, data);
133 			break;
134 		case 2:
135 			outw(port, data);
136 			break;
137 		case 4:
138 			outl(port, data);
139 			break;
140 		}
141 		pci_cfgdisable();
142 	}
143 }
144 
145 /* check whether the configuration mechanism has been correct identified */
146 
147 static int
148 pci_cfgcheck(int maxdev)
149 {
150 	u_char device;
151 
152 	if (bootverbose)
153 		printf("pci_cfgcheck:\tdevice ");
154 
155 	for (device = 0; device < maxdev; device++) {
156 		unsigned id, class, header;
157 		if (bootverbose)
158 			printf("%d ", device);
159 
160 		id = inl(pci_cfgenable(0, device, 0, 0, 4));
161 		if (id == 0 || id == -1)
162 			continue;
163 
164 		class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8;
165 		if (bootverbose)
166 			printf("[class=%06x] ", class);
167 		if (class == 0 || (class & 0xf870ff) != 0)
168 			continue;
169 
170 		header = inb(pci_cfgenable(0, device, 0, 14, 1));
171 		if (bootverbose)
172 			printf("[hdr=%02x] ", header);
173 		if ((header & 0x7e) != 0)
174 			continue;
175 
176 		if (bootverbose)
177 			printf("is there (id=%08x)\n", id);
178 
179 		pci_cfgdisable();
180 		return (1);
181 	}
182 	if (bootverbose)
183 		printf("-- nothing found\n");
184 
185 	pci_cfgdisable();
186 	return (0);
187 }
188 
189 static int
190 pci_cfgopen(void)
191 {
192 	unsigned long mode1res,oldval1;
193 	unsigned char mode2res,oldval2;
194 
195 	oldval1 = inl(CONF1_ADDR_PORT);
196 
197 	if (bootverbose) {
198 		printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n",
199 		       oldval1);
200 	}
201 
202 	if ((oldval1 & CONF1_ENABLE_MSK) == 0) {
203 
204 		cfgmech = 1;
205 		devmax = 32;
206 
207 		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK);
208 		outb(CONF1_ADDR_PORT +3, 0);
209 		mode1res = inl(CONF1_ADDR_PORT);
210 		outl(CONF1_ADDR_PORT, oldval1);
211 
212 		if (bootverbose)
213 			printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n",
214 			       mode1res, CONF1_ENABLE_CHK);
215 
216 		if (mode1res) {
217 			if (pci_cfgcheck(32))
218 				return (cfgmech);
219 		}
220 
221 		outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1);
222 		mode1res = inl(CONF1_ADDR_PORT);
223 		outl(CONF1_ADDR_PORT, oldval1);
224 
225 		if (bootverbose)
226 			printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n",
227 			       mode1res, CONF1_ENABLE_CHK1);
228 
229 		if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) {
230 			if (pci_cfgcheck(32))
231 				return (cfgmech);
232 		}
233 	}
234 
235 	oldval2 = inb(CONF2_ENABLE_PORT);
236 
237 	if (bootverbose) {
238 		printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n",
239 		       oldval2);
240 	}
241 
242 	if ((oldval2 & 0xf0) == 0) {
243 
244 		cfgmech = 2;
245 		devmax = 16;
246 
247 		outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK);
248 		mode2res = inb(CONF2_ENABLE_PORT);
249 		outb(CONF2_ENABLE_PORT, oldval2);
250 
251 		if (bootverbose)
252 			printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n",
253 			       mode2res, CONF2_ENABLE_CHK);
254 
255 		if (mode2res == CONF2_ENABLE_RES) {
256 			if (bootverbose)
257 				printf("pci_open(2a):\tnow trying mechanism 2\n");
258 
259 			if (pci_cfgcheck(16))
260 				return (cfgmech);
261 		}
262 	}
263 
264 	cfgmech = 0;
265 	devmax = 0;
266 	return (cfgmech);
267 }
268 
269 static devclass_t	pcib_devclass;
270 
271 static int
272 nexus_pcib_probe(device_t dev)
273 {
274 	if (pci_cfgopen() != 0) {
275 		device_set_desc(dev, "PCI host bus adapter");
276 
277 		device_add_child(dev, "pci", 0, 0);
278 		return 0;
279 	}
280 	return ENXIO;
281 }
282 
283 static device_method_t nexus_pcib_methods[] = {
284 	/* Device interface */
285 	DEVMETHOD(device_probe,		nexus_pcib_probe),
286 	DEVMETHOD(device_attach,	bus_generic_attach),
287 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
288 	DEVMETHOD(device_suspend,	bus_generic_suspend),
289 	DEVMETHOD(device_resume,	bus_generic_resume),
290 
291 	/* Bus interface */
292 	DEVMETHOD(bus_print_child,	bus_generic_print_child),
293 	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
294 	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
295 	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
296 	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
297 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
298 	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
299 
300 	{ 0, 0 }
301 };
302 
303 static driver_t nexus_pcib_driver = {
304 	"pcib",
305 	nexus_pcib_methods,
306 	1,
307 };
308 
309 DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0);
310