xref: /freebsd/sys/i386/pci/pci_pir.c (revision ce834215a70ff69e7e222827437116eee2f9ac6f)
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.38 1997/05/26 21:25:24 se Exp $
27  *
28  */
29 
30 #include <sys/types.h>
31 #include <sys/systm.h>
32 
33 #include <pci/pcireg.h>
34 #include <pci/pcivar.h>
35 #include <i386/isa/pcibus.h>
36 
37 #ifdef PCI_COMPAT
38 /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */
39 #define cfgmech pci_mechanism
40 int cfgmech;
41 #else
42 static int cfgmech;
43 #endif /* PCI_COMPAT */
44 static int devmax;
45 
46 /* enable configuration space accesses and return data port address */
47 
48 static int
49 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
50 {
51 	int dataport = 0;
52 
53 	if (bus <= PCI_BUSMAX
54 	    && slot < devmax
55 	    && func <= PCI_FUNCMAX
56 	    && reg <= PCI_REGMAX
57 	    && bytes != 3
58 	    && (unsigned) bytes <= 4
59 	    && (reg & (bytes -1)) == 0) {
60 		switch (cfgmech) {
61 		case 1:
62 			outl(CONF1_ADDR_PORT, (1 << 31)
63 			     | (bus << 16) | (slot << 11)
64 			     | (func << 8) | (reg & ~0x03));
65 			dataport = CONF1_DATA_PORT + (reg & 0x03);
66 			break;
67 		case 2:
68 			outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1));
69 			outb(CONF2_FORWARD_PORT, bus);
70 			dataport = 0xc000 | (slot << 8) | reg;
71 			break;
72 		}
73 	}
74 	return (dataport);
75 }
76 
77 /* disable configuration space accesses */
78 
79 static void
80 pci_cfgdisable(void)
81 {
82 	switch (cfgmech) {
83 	case 1:
84 		outl(CONF1_ADDR_PORT, 0);
85 		break;
86 	case 2:
87 		outb(CONF2_ENABLE_PORT, 0);
88 		outb(CONF2_FORWARD_PORT, 0);
89 		break;
90 	}
91 }
92 
93 /* read configuration space register */
94 
95 int
96 pci_cfgread(pcicfgregs *cfg, int reg, int bytes)
97 {
98 	int data = -1;
99 	int port;
100 
101 	port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes);
102 
103 	if (port != 0) {
104 		switch (bytes) {
105 		case 1:
106 			data = inb(port);
107 			break;
108 		case 2:
109 			data = inw(port);
110 			break;
111 		case 4:
112 			data = inl(port);
113 			break;
114 		}
115 		pci_cfgdisable();
116 	}
117 	return (data);
118 }
119 
120 /* write configuration space register */
121 
122 void
123 pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes)
124 {
125 	int port;
126 
127 	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 
146 static int
147 pci_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 
188 int
189 pci_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