xref: /freebsd/sys/i386/pci/pci_pir.c (revision 0de89efe5c443f213c7ea28773ef2dc6cf3af2ed)
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.39 1997/05/26 21:52:41 se 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
39 int cfgmech;
40 #else
41 static int cfgmech;
42 #endif /* PCI_COMPAT */
43 static int devmax;
44 
45 /* enable configuration space accesses and return data port address */
46 
47 static int
48 pci_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 
78 static void
79 pci_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 
94 int
95 pci_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 
121 void
122 pci_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 
145 static int
146 pci_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 & 0xf8f0ff) != 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 
187 int
188 pci_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