xref: /illumos-gate/usr/src/grub/grub-0.97/netboot/pci_io.c (revision bb9b6b3f59b8820022416cea99b49c50fef6e391)
1 /*
2 ** Support for NE2000 PCI clones added David Monro June 1997
3 ** Generalised to other NICs by Ken Yap July 1997
4 **
5 ** Most of this is taken from:
6 **
7 ** /usr/src/linux/drivers/pci/pci.c
8 ** /usr/src/linux/include/linux/pci.h
9 ** /usr/src/linux/arch/i386/bios32.c
10 ** /usr/src/linux/include/linux/bios32.h
11 ** /usr/src/linux/drivers/net/ne.c
12 */
13 #define PCBIOS
14 #include "grub.h"
15 #include "pci.h"
16 
17 #ifdef	CONFIG_PCI_DIRECT
18 #define  PCIBIOS_SUCCESSFUL                0x00
19 
20 #define DEBUG 0
21 
22 /*
23  * Functions for accessing PCI configuration space with type 1 accesses
24  */
25 
26 #define CONFIG_CMD(bus, device_fn, where)   (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
27 
28 int pcibios_read_config_byte(unsigned int bus, unsigned int device_fn,
29 			       unsigned int where, uint8_t *value)
30 {
31     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
32     *value = inb(0xCFC + (where&3));
33     return PCIBIOS_SUCCESSFUL;
34 }
35 
36 int pcibios_read_config_word (unsigned int bus,
37     unsigned int device_fn, unsigned int where, uint16_t *value)
38 {
39     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
40     *value = inw(0xCFC + (where&2));
41     return PCIBIOS_SUCCESSFUL;
42 }
43 
44 int pcibios_read_config_dword (unsigned int bus, unsigned int device_fn,
45 				 unsigned int where, uint32_t *value)
46 {
47     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
48     *value = inl(0xCFC);
49     return PCIBIOS_SUCCESSFUL;
50 }
51 
52 int pcibios_write_config_byte (unsigned int bus, unsigned int device_fn,
53 				 unsigned int where, uint8_t value)
54 {
55     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
56     outb(value, 0xCFC + (where&3));
57     return PCIBIOS_SUCCESSFUL;
58 }
59 
60 int pcibios_write_config_word (unsigned int bus, unsigned int device_fn,
61 				 unsigned int where, uint16_t value)
62 {
63     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
64     outw(value, 0xCFC + (where&2));
65     return PCIBIOS_SUCCESSFUL;
66 }
67 
68 int pcibios_write_config_dword (unsigned int bus, unsigned int device_fn, unsigned int where, uint32_t value)
69 {
70     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
71     outl(value, 0xCFC);
72     return PCIBIOS_SUCCESSFUL;
73 }
74 
75 #undef CONFIG_CMD
76 
77 #else	 /* CONFIG_PCI_DIRECT  not defined */
78 
79 #if !defined(PCBIOS)
80 #error "The pcibios can only be used when the PCBIOS support is compiled in"
81 #endif
82 
83 
84 #define KERN_CODE_SEG 0X8
85 /* Stuff for asm */
86 #define save_flags(x) \
87 __asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */ :"memory")
88 
89 #define cli() __asm__ __volatile__ ("cli": : :"memory")
90 
91 #define restore_flags(x) \
92 __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
93 
94 
95 
96 static struct {
97 	unsigned long address;
98 	unsigned short segment;
99 } bios32_indirect = { 0, KERN_CODE_SEG };
100 
101 static long pcibios_entry = 0;
102 static struct {
103 	unsigned long address;
104 	unsigned short segment;
105 } pci_indirect = { 0, KERN_CODE_SEG };
106 
107 static unsigned long bios32_service(unsigned long service)
108 {
109 	unsigned char return_code;	/* %al */
110 	unsigned long address;		/* %ebx */
111 	unsigned long length;		/* %ecx */
112 	unsigned long entry;		/* %edx */
113 	unsigned long flags;
114 
115 	save_flags(flags);
116 	__asm__(
117 #ifdef ABSOLUTE_WITHOUT_ASTERISK
118 		"lcall (%%edi)"
119 #else
120 		"lcall *(%%edi)"
121 #endif
122 		: "=a" (return_code),
123 		  "=b" (address),
124 		  "=c" (length),
125 		  "=d" (entry)
126 		: "0" (service),
127 		  "1" (0),
128 		  "D" (&bios32_indirect));
129 	restore_flags(flags);
130 
131 	switch (return_code) {
132 		case 0:
133 			return address + entry;
134 		case 0x80:	/* Not present */
135 			printf("bios32_service(%d) : not present\n", service);
136 			return 0;
137 		default: /* Shouldn't happen */
138 			printf("bios32_service(%d) : returned %#X, mail drew@colorado.edu\n",
139 				service, return_code);
140 			return 0;
141 	}
142 }
143 
144 int pcibios_read_config_byte(unsigned int bus,
145         unsigned int device_fn, unsigned int where, uint8_t *value)
146 {
147         unsigned long ret;
148         unsigned long bx = (bus << 8) | device_fn;
149         unsigned long flags;
150 
151         save_flags(flags);
152         __asm__(
153 #ifdef ABSOLUTE_WITHOUT_ASTERISK
154 		"lcall (%%esi)\n\t"
155 #else
156 		"lcall *(%%esi)\n\t"
157 #endif
158                 "jc 1f\n\t"
159                 "xor %%ah, %%ah\n"
160                 "1:"
161                 : "=c" (*value),
162                   "=a" (ret)
163                 : "1" (PCIBIOS_READ_CONFIG_BYTE),
164                   "b" (bx),
165                   "D" ((long) where),
166                   "S" (&pci_indirect));
167         restore_flags(flags);
168         return (int) (ret & 0xff00) >> 8;
169 }
170 
171 int pcibios_read_config_word(unsigned int bus,
172         unsigned int device_fn, unsigned int where, uint16_t *value)
173 {
174         unsigned long ret;
175         unsigned long bx = (bus << 8) | device_fn;
176         unsigned long flags;
177 
178         save_flags(flags);
179         __asm__(
180 #ifdef ABSOLUTE_WITHOUT_ASTERISK
181 		"lcall (%%esi)\n\t"
182 #else
183 		"lcall *(%%esi)\n\t"
184 #endif
185                 "jc 1f\n\t"
186                 "xor %%ah, %%ah\n"
187                 "1:"
188                 : "=c" (*value),
189                   "=a" (ret)
190                 : "1" (PCIBIOS_READ_CONFIG_WORD),
191                   "b" (bx),
192                   "D" ((long) where),
193                   "S" (&pci_indirect));
194         restore_flags(flags);
195         return (int) (ret & 0xff00) >> 8;
196 }
197 
198 int pcibios_read_config_dword(unsigned int bus,
199         unsigned int device_fn, unsigned int where, uint32_t *value)
200 {
201         unsigned long ret;
202         unsigned long bx = (bus << 8) | device_fn;
203         unsigned long flags;
204 
205         save_flags(flags);
206         __asm__(
207 #ifdef ABSOLUTE_WITHOUT_ASTERISK
208 		"lcall (%%esi)\n\t"
209 #else
210 		"lcall *(%%esi)\n\t"
211 #endif
212                 "jc 1f\n\t"
213                 "xor %%ah, %%ah\n"
214                 "1:"
215                 : "=c" (*value),
216                   "=a" (ret)
217                 : "1" (PCIBIOS_READ_CONFIG_DWORD),
218                   "b" (bx),
219                   "D" ((long) where),
220                   "S" (&pci_indirect));
221         restore_flags(flags);
222         return (int) (ret & 0xff00) >> 8;
223 }
224 
225 int pcibios_write_config_byte (unsigned int bus,
226 	unsigned int device_fn, unsigned int where, uint8_t value)
227 {
228 	unsigned long ret;
229 	unsigned long bx = (bus << 8) | device_fn;
230 	unsigned long flags;
231 
232 	save_flags(flags); cli();
233 	__asm__(
234 #ifdef ABSOLUTE_WITHOUT_ASTERISK
235 		"lcall (%%esi)\n\t"
236 #else
237 		"lcall *(%%esi)\n\t"
238 #endif
239 		"jc 1f\n\t"
240 		"xor %%ah, %%ah\n"
241 		"1:"
242 		: "=a" (ret)
243 		: "0" (PCIBIOS_WRITE_CONFIG_BYTE),
244 		  "c" (value),
245 		  "b" (bx),
246 		  "D" ((long) where),
247 		  "S" (&pci_indirect));
248 	restore_flags(flags);
249 	return (int) (ret & 0xff00) >> 8;
250 }
251 
252 int pcibios_write_config_word (unsigned int bus,
253 	unsigned int device_fn, unsigned int where, uint16_t value)
254 {
255 	unsigned long ret;
256 	unsigned long bx = (bus << 8) | device_fn;
257 	unsigned long flags;
258 
259 	save_flags(flags); cli();
260 	__asm__(
261 #ifdef ABSOLUTE_WITHOUT_ASTERISK
262 		"lcall (%%esi)\n\t"
263 #else
264 		"lcall *(%%esi)\n\t"
265 #endif
266 		"jc 1f\n\t"
267 		"xor %%ah, %%ah\n"
268 		"1:"
269 		: "=a" (ret)
270 		: "0" (PCIBIOS_WRITE_CONFIG_WORD),
271 		  "c" (value),
272 		  "b" (bx),
273 		  "D" ((long) where),
274 		  "S" (&pci_indirect));
275 	restore_flags(flags);
276 	return (int) (ret & 0xff00) >> 8;
277 }
278 
279 int pcibios_write_config_dword (unsigned int bus,
280 	unsigned int device_fn, unsigned int where, uint32_t value)
281 {
282 	unsigned long ret;
283 	unsigned long bx = (bus << 8) | device_fn;
284 	unsigned long flags;
285 
286 	save_flags(flags); cli();
287 	__asm__(
288 #ifdef ABSOLUTE_WITHOUT_ASTERISK
289 		"lcall (%%esi)\n\t"
290 #else
291 		"lcall *(%%esi)\n\t"
292 #endif
293 		"jc 1f\n\t"
294 		"xor %%ah, %%ah\n"
295 		"1:"
296 		: "=a" (ret)
297 		: "0" (PCIBIOS_WRITE_CONFIG_DWORD),
298 		  "c" (value),
299 		  "b" (bx),
300 		  "D" ((long) where),
301 		  "S" (&pci_indirect));
302 	restore_flags(flags);
303 	return (int) (ret & 0xff00) >> 8;
304 }
305 
306 static void check_pcibios(void)
307 {
308 	unsigned long signature;
309 	unsigned char present_status;
310 	unsigned char major_revision;
311 	unsigned char minor_revision;
312 	unsigned long flags;
313 	int pack;
314 
315 	if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
316 		pci_indirect.address = pcibios_entry;
317 
318 		save_flags(flags);
319 		__asm__(
320 #ifdef ABSOLUTE_WITHOUT_ASTERISK
321 			"lcall (%%edi)\n\t"
322 #else
323 			"lcall *(%%edi)\n\t"
324 #endif
325 			"jc 1f\n\t"
326 			"xor %%ah, %%ah\n"
327 			"1:\tshl $8, %%eax\n\t"
328 			"movw %%bx, %%ax"
329 			: "=d" (signature),
330 			  "=a" (pack)
331 			: "1" (PCIBIOS_PCI_BIOS_PRESENT),
332 			  "D" (&pci_indirect)
333 			: "bx", "cx");
334 		restore_flags(flags);
335 
336 		present_status = (pack >> 16) & 0xff;
337 		major_revision = (pack >> 8) & 0xff;
338 		minor_revision = pack & 0xff;
339 		if (present_status || (signature != PCI_SIGNATURE)) {
340 			printf("ERROR: BIOS32 says PCI BIOS, but no PCI "
341 				"BIOS????\n");
342 			pcibios_entry = 0;
343 		}
344 #if	DEBUG
345 		if (pcibios_entry) {
346 			printf ("pcibios_init : PCI BIOS revision %hhX.%hhX"
347 				" entry at %#X\n", major_revision,
348 				minor_revision, pcibios_entry);
349 		}
350 #endif
351 	}
352 }
353 
354 static void pcibios_init(void)
355 {
356 	union bios32 *check;
357 	unsigned char sum;
358 	int i, length;
359 	unsigned long bios32_entry = 0;
360 
361 	EnterFunction("pcibios_init");
362 	/*
363 	 * Follow the standard procedure for locating the BIOS32 Service
364 	 * directory by scanning the permissible address range from
365 	 * 0xe0000 through 0xfffff for a valid BIOS32 structure.
366 	 *
367 	 */
368 
369 	for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) {
370 		if (check->fields.signature != BIOS32_SIGNATURE)
371 			continue;
372 		length = check->fields.length * 16;
373 		if (!length)
374 			continue;
375 		sum = 0;
376 		for (i = 0; i < length ; ++i)
377 			sum += check->chars[i];
378 		if (sum != 0)
379 			continue;
380 		if (check->fields.revision != 0) {
381 			printf("pcibios_init : unsupported revision %d at %#X, mail drew@colorado.edu\n",
382 				check->fields.revision, check);
383 			continue;
384 		}
385 #if	DEBUG
386 		printf("pcibios_init : BIOS32 Service Directory "
387 			"structure at %#X\n", check);
388 #endif
389 		if (!bios32_entry) {
390 			if (check->fields.entry >= 0x100000) {
391 				printf("pcibios_init: entry in high "
392 					"memory, giving up\n");
393 				return;
394 			} else {
395 				bios32_entry = check->fields.entry;
396 #if	DEBUG
397 				printf("pcibios_init : BIOS32 Service Directory"
398 					" entry at %#X\n", bios32_entry);
399 #endif
400 				bios32_indirect.address = bios32_entry;
401 			}
402 		}
403 	}
404 	if (bios32_entry)
405 		check_pcibios();
406 	LeaveFunction("pcibios_init");
407 }
408 
409 #endif	/* CONFIG_PCI_DIRECT not defined*/
410 
411 unsigned long pcibios_bus_base(unsigned int bus __unused)
412 {
413 	/* architecturally this must be 0 */
414 	return 0;
415 }
416 
417 void find_pci(int type, struct pci_device *dev)
418 {
419 	EnterFunction("find_pci");
420 #ifndef	CONFIG_PCI_DIRECT
421 	if (!pcibios_entry) {
422 		pcibios_init();
423 	}
424 	if (!pcibios_entry) {
425 		printf("pci_init: no BIOS32 detected\n");
426 		return;
427 	}
428 #endif
429 	LeaveFunction("find_pci");
430 	return scan_pci_bus(type, dev);
431 }
432