nubus.c revision 1.12 1 /* $NetBSD: nubus.c,v 1.12 1995/06/21 02:59:08 briggs Exp $ */
2
3 /*
4 * Copyright (c) 1995 Allen Briggs. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following 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 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Allen Briggs.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/device.h>
35
36 #include <machine/cpu.h>
37
38 #include <vm/vm.h>
39
40 #include "nubus.h"
41
42 #if DEBUG
43 static int nubus_debug = 0x01;
44 #define NDB_PROBE 0x1
45 #define NDB_FOLLOW 0x2
46 #define NDB_ARITH 0x4
47 #endif
48
49 extern int matchbyname();
50
51 static int nubusprint __P((void *aux, char *name));
52 static void nubusattach __P((struct device *parent, struct device *self,
53 void *aux));
54
55 static int probe_slot __P((int slot, nubus_slot *fmt));
56 static u_long IncPtr __P((nubus_slot *fmt, u_long base, long amt));
57 static u_long nubus_calc_CRC __P((nubus_slot *fmt));
58 static u_char GetByte __P((nubus_slot *fmt, u_long ptr));
59 static u_short GetWord __P((nubus_slot *fmt, u_long ptr));
60 static u_long GetLong __P((nubus_slot *fmt, u_long ptr));
61
62 struct cfdriver nubuscd = {
63 NULL, "nubus", matchbyname, nubusattach,
64 DV_DULL, sizeof(struct nubus_softc), 1
65 };
66
67 static void
68 nubusattach(parent, self, aux)
69 struct device *parent, *self;
70 void *aux;
71 {
72 extern u_long int_video_start;
73 nubus_slot fmtblock;
74 int i;
75
76 printf("\n");
77
78 for ( i = NUBUS_MIN_SLOT; i <= NUBUS_MAX_SLOT; i++) {
79 if (probe_slot(i, &fmtblock)) {
80 config_found(self, &fmtblock, nubusprint);
81 }
82 }
83
84 /*
85 * Kludge for internal video.
86 */
87 if (int_video_start) {
88 int int_video_slot = 0xe;
89 fmtblock.top = NUBUS_SLOT_TO_BASE(int_video_slot+1);
90 fmtblock.slot = int_video_slot;
91 fmtblock.bytelanes = 0x0F;
92 fmtblock.step = 4;
93 fmtblock.test_pattern = ~NUBUS_ROM_TEST_PATTERN;
94 fmtblock.format = 1;
95 fmtblock.revision_level = 1;
96 fmtblock.crc = 1;
97 fmtblock.length = 0;
98 fmtblock.directory_offset = 0;
99 config_found(self, &fmtblock, nubusprint);
100 }
101 }
102
103 static int
104 nubusprint(aux, name)
105 void *aux;
106 char *name;
107 {
108 nubus_slot *fmt;
109 int slot;
110 char *info;
111
112 fmt = (nubus_slot *) aux;
113 if (name) {
114 printf("%s: slot %x: %s ", name, fmt->slot,
115 nubus_get_card_name(fmt));
116 printf("(Vendor: %s, ",
117 nubus_get_vendor(fmt, NUBUS_RSRC_VEND_ID));
118 printf("Part: %s) ",
119 nubus_get_vendor(fmt, NUBUS_RSRC_VEND_PART));
120 }
121 return (UNCONF);
122 }
123
124 /*
125 * Probe a given nubus slot. If a card is there and we can get the
126 * format block from it's clutching decl. ROMs, fill the format block
127 * and return non-zero. If we can't find a card there with a valid
128 * decl. ROM, return 0.
129 *
130 * First, we check to see if we can access the memory at the tail
131 * end of the slot. If so, then we check for a bytelanes byte. We
132 * could probably just return a failure status if we bus error on
133 * the first try, but there really is little reason not to go ahead
134 * and check the other three locations in case there's a wierd card
135 * out there.
136 *
137 * Checking for a card involves locating the "bytelanes" byte which
138 * tells us how to interpret the declaration ROM's data. The format
139 * block is at the top of the card's standard memory space and the
140 * bytelanes byte is at the end of that block.
141 *
142 * After some inspection of the bytelanes byte, it appears that it
143 * takes the form 0xXY where Y is a bitmask of the bytelanes in use
144 * and X is a bitmask of the lanes to ignore. Hence, (X ^ Y) == 0
145 * and (less obviously), Y will have the upper N bits clear if it is
146 * found N bytes from the last possible location. Both that and
147 * the exclusive-or check are made.
148 *
149 * If a valid
150 */
151 static u_char nbits[]={0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
152 static int
153 probe_slot(slot, fmt)
154 int slot;
155 nubus_slot *fmt;
156 {
157 caddr_t rom_probe;
158 u_long hdr;
159 u_long phys;
160 u_char data;
161 int hdr_size, i;
162
163 fmt->bytelanes = 0;
164 fmt->slot = (u_long)slot;
165
166 rom_probe = (caddr_t) (NUBUS_SLOT_TO_BASE(fmt->slot) + NBMEMSIZE);
167
168 #ifdef DEBUG
169 if (nubus_debug & NDB_PROBE) {
170 phys = pmap_extract(pmap_kernel(), rom_probe-1);
171 printf("probing slot %d, first probe at 0x%x (phys 0x%x).\n",
172 slot, rom_probe-1, phys);
173 }
174 #endif
175
176 for (i = 4; i && (fmt->bytelanes == 0); i--) {
177
178 rom_probe--;
179
180 if (badbaddr(rom_probe))
181 continue;
182
183 if ((data = *rom_probe) == 0)
184 continue;
185
186 if ( ((((data & 0xf0) >> 4) ^ (data & 0x0f)) == 0x0f)
187 && ((data & 0x0f) < (1 << i)) ) {
188 fmt->bytelanes = data;
189 fmt->step = nbits[(data & 0x0f)];
190 }
191 }
192 #ifdef DEBUG
193 if (nubus_debug & NDB_PROBE)
194 if (fmt->bytelanes == 0)
195 printf("bytelanes not found for slot 0x%x.\n", slot);
196 #endif
197
198 if (fmt->bytelanes == 0)
199 return 0;
200
201 #ifdef DEBUG
202 if (nubus_debug & NDB_PROBE)
203 printf("bytelanes of 0x%x found for slot 0x%x (base 0x%x).\n",
204 fmt->bytelanes, slot, NUBUS_SLOT_TO_BASE(slot));
205 #endif
206
207 hdr_size = 20;
208
209 /*
210 * Go ahead and attempt to load format header.
211 * First, we need to find the first byte beyond memory that
212 * would be valid. This is necessary for NUBUS_ROM_offset()
213 * to work.
214 */
215 hdr = NUBUS_SLOT_TO_BASE(fmt->slot) + NBMEMSIZE;
216 i = 0x10 | (fmt->bytelanes & 0x0f);
217 while ((i & 1) == 0) {
218 hdr++;
219 i >>= 1;
220 }
221 fmt->top = hdr;
222 hdr = IncPtr(fmt, hdr, -hdr_size);
223 #ifdef DEBUG
224 if (nubus_debug & NDB_PROBE)
225 printf("fmt->top is 0x%x, that minus 0x%x puts us at 0x%x.\n",
226 fmt->top, hdr_size, hdr);
227 #if 0
228 for (i=1 ; i < 8 ; i++) {
229 printf("0x%x - 0x%x = 0x%x, + 0x%x = 0x%x.\n",
230 hdr, i, IncPtr(fmt, hdr, -i),
231 i, IncPtr(fmt, hdr, i));
232 }
233 #endif
234 #endif
235
236 fmt->directory_offset = 0xff000000 | GetLong(fmt, hdr);
237 hdr = IncPtr(fmt, hdr, 4);
238 fmt->length = GetLong(fmt, hdr);
239 hdr = IncPtr(fmt, hdr, 4);
240 fmt->crc = GetLong(fmt, hdr);
241 hdr = IncPtr(fmt, hdr, 4);
242 fmt->revision_level = GetByte(fmt, hdr);
243 hdr = IncPtr(fmt, hdr, 1);
244 fmt->format = GetByte(fmt, hdr);
245 hdr = IncPtr(fmt, hdr, 1);
246 fmt->test_pattern = GetLong(fmt, hdr);
247
248 #if DEBUG
249 if (nubus_debug & NDB_PROBE) {
250 printf("Directory offset 0x%x\t", fmt->directory_offset);
251 printf("Length 0x%x\t", fmt->length);
252 printf("CRC 0x%x\n", fmt->crc);
253 printf("Revision level 0x%x\t", fmt->revision_level);
254 printf("Format 0x%x\t", fmt->format);
255 printf("Test Pattern 0x%x\n", fmt->test_pattern);
256 }
257 #endif
258
259 if ((fmt->directory_offset & 0x00ff0000) == 0) {
260 printf("Invalid looking directory offset (0x%x)!\n",
261 fmt->directory_offset);
262 return 0;
263 }
264 if (fmt->test_pattern != NUBUS_ROM_TEST_PATTERN) {
265 printf("Nubus--test pattern invalid:\n");
266 printf(" slot 0x%x, bytelanes 0x%x?\n",
267 fmt->slot, fmt->bytelanes);
268 printf(" read test 0x%x, compare with 0x%x.\n",
269 fmt->test_pattern, NUBUS_ROM_TEST_PATTERN);
270 return 0;
271 }
272
273 /* Perform CRC */
274 if (fmt->crc != nubus_calc_CRC(fmt)) {
275 printf("Nubus--crc check failed, slot 0x%x.\n",
276 fmt->slot);
277 return 0;
278 }
279
280 return 1;
281 }
282
283 /*
284 * Compute byte offset on card, taking into account bytelanes.
285 * Base must be on a valid bytelane for this function to work.
286 * Return the new address.
287 *
288 * XXX -- There has GOT to be a better way to do this.
289 */
290 static u_long
291 IncPtr(fmt, base, amt)
292 nubus_slot *fmt;
293 u_long base;
294 long amt;
295 {
296 u_char b, t;
297
298 if (!amt)
299 return base;
300
301 if (amt < 0) {
302 amt = -amt;
303 b = fmt->bytelanes;
304 t = (b << 4);
305 b <<= (3 - (base & 0x3));
306 while (amt) {
307 b <<= 1;
308 if (b == t)
309 b = fmt->bytelanes;
310 if (b & 0x08)
311 amt--;
312 base--;
313 }
314 return base;
315 }
316
317 t = (fmt->bytelanes & 0xf) | 0x10;
318 b = t >> (base & 0x3);
319 while (amt) {
320 b >>= 1;
321 if (b == 1)
322 b = t;
323 if (b & 1)
324 amt--;
325 base++;
326 }
327
328 return base;
329 }
330
331 static u_long
332 nubus_calc_CRC(fmt)
333 nubus_slot *fmt;
334 {
335 #if 0
336 u_long base, ptr, crc_loc, sum;
337 int i;
338
339 base = fmt->top;
340 crc_loc = NUBUS_ROM_offset(fmt, base, -12);
341 ptr = NUBUS_ROM_offset(fmt, base, -fmt->length);
342
343 sum = 0;
344 while (ptr < base)
345 roll #1, sum
346 if (ptr == crc_loc) {
347 roll #3, sum
348 ptr = IncPtr(fmt, ptr, 3);
349 } else {
350 sum += GetByte(fmt, ptr);
351 }
352 ptr = IncPtr(fmt, ptr, 1);
353 }
354
355 return sum;
356 #endif
357 return fmt->crc;
358 }
359
360 static u_char
361 GetByte(fmt, ptr)
362 nubus_slot *fmt;
363 u_long ptr;
364 {
365 return *(caddr_t)ptr;
366 }
367
368 static u_short
369 GetWord(fmt, ptr)
370 nubus_slot *fmt;
371 u_long ptr;
372 {
373 u_short s;
374
375 s = (GetByte(fmt, ptr) << 8);
376 ptr = IncPtr(fmt, ptr, 1);
377 s |= GetByte(fmt, ptr);
378 return s;
379 }
380
381 static u_long
382 GetLong(fmt, ptr)
383 nubus_slot *fmt;
384 u_long ptr;
385 {
386 register u_long l;
387 register int i;
388
389 l = 0;
390 for ( i = 0; i < 4; i++) {
391 l = (l << 8) | GetByte(fmt, ptr);
392 ptr = IncPtr(fmt, ptr, 1);
393 }
394 return l;
395 }
396
397 void
398 nubus_get_main_dir(slot, dir_return)
399 nubus_slot *slot;
400 nubus_dir *dir_return;
401 {
402 u_long off;
403
404 #if DEBUG
405 if (nubus_debug & NDB_FOLLOW)
406 printf("nubus_get_main_dir(0x%x, 0x%x)\n",
407 (u_int) slot, (u_int) dir_return);
408 #endif
409 dir_return->dirbase = IncPtr(slot, slot->top,
410 slot->directory_offset - 20);
411 dir_return->curr_ent = dir_return->dirbase;
412 }
413
414 int
415 nubus_find_rsrc(slot, dir, rsrcid, dirent_return)
416 nubus_slot *slot;
417 nubus_dir *dir;
418 u_int8_t rsrcid;
419 nubus_dirent *dirent_return;
420 {
421 u_long entry;
422 u_char byte;
423
424 #if DEBUG
425 if (nubus_debug & NDB_FOLLOW)
426 printf("nubus_find_rsrc(0x%x, 0x%x, 0x%x, 0x%x)\n",
427 (u_int) slot, (u_int) dir, (u_int) rsrcid,
428 (u_int) dirent_return);
429 #endif
430 if (slot->test_pattern != NUBUS_ROM_TEST_PATTERN)
431 return -1;
432
433 entry = dir->curr_ent;
434 do {
435 byte = GetByte(slot, entry);
436 #if DEBUG
437 if (nubus_debug & NDB_FOLLOW)
438 printf("\tFound rsrc 0x%x.\n", byte);
439 #endif
440 if (byte == rsrcid) {
441 dirent_return->myloc = entry;
442 dirent_return->rsrc_id = rsrcid;
443 entry = GetLong(slot, entry);
444 dirent_return->offset = (entry & 0x00ffffff);
445 return 1;
446 }
447 if (byte == 0xff) {
448 entry = dir->dirbase;
449 } else {
450 entry = IncPtr(slot, entry, 4);
451 }
452 } while (entry != (u_long) dir->curr_ent);
453 return 0;
454 }
455
456 void
457 nubus_get_dir_from_rsrc(slot, dirent, dir_return)
458 nubus_slot *slot;
459 nubus_dirent *dirent;
460 nubus_dir *dir_return;
461 {
462 u_long loc;
463
464 #if DEBUG
465 if (nubus_debug & NDB_FOLLOW)
466 printf("nubus_get_dir_from_rsrc(0x%x, 0x%x, 0x%x).\n",
467 (u_int) slot, (u_int) dirent, (u_int) dir_return);
468 #endif
469 if ((loc = dirent->offset) & 0x800000) {
470 loc |= 0xff000000;
471 }
472 dir_return->dirbase = IncPtr(slot, dirent->myloc, loc);
473 dir_return->curr_ent = dir_return->dirbase;
474 }
475
476 int
477 nubus_get_ind_data(slot, dirent, data_return, nbytes)
478 nubus_slot *slot;
479 nubus_dirent *dirent;
480 caddr_t data_return;
481 int nbytes;
482 {
483 u_long loc;
484
485 #if DEBUG
486 if (nubus_debug & NDB_FOLLOW)
487 printf("nubus_get_ind_data(0x%x, 0x%x, 0x%x, %d).\n",
488 (u_int) slot, (u_int) dirent, (u_int) data_return,
489 nbytes);
490 #endif
491 if ((loc = dirent->offset) & 0x800000) {
492 loc |= 0xff000000;
493 }
494 loc = IncPtr(slot, dirent->myloc, loc);
495
496 while (nbytes--) {
497 *data_return++ = GetByte(slot, loc);
498 loc = IncPtr(slot, loc, 1);
499 }
500 return 1;
501 }
502
503 int
504 nubus_get_c_string(slot, dirent, data_return, max_bytes)
505 nubus_slot *slot;
506 nubus_dirent *dirent;
507 caddr_t data_return;
508 int max_bytes;
509 {
510 u_long loc;
511
512 #if DEBUG
513 if (nubus_debug & NDB_FOLLOW)
514 printf("nubus_get_c_string(0x%x, 0x%x, 0x%x, %d).\n",
515 (u_int) slot, (u_int) dirent, (u_int) data_return,
516 max_bytes);
517 #endif
518 if ((loc = dirent->offset) & 0x800000) {
519 loc |= 0xff000000;
520 }
521 loc = IncPtr(slot, dirent->myloc, loc);
522
523 *data_return = '\0';
524 while (max_bytes--) {
525 if ((*data_return++ = GetByte(slot, loc)) == 0)
526 return 1;
527 loc = IncPtr(slot, loc, 1);
528 }
529 return 0;
530 }
531
532 static char *huh = "???";
533
534 char *
535 nubus_get_vendor(slot, rsrc)
536 nubus_slot *slot;
537 int rsrc;
538 {
539 static char str_ret[64];
540 nubus_dir dir;
541 nubus_dirent ent;
542
543 #if DEBUG
544 if (nubus_debug & NDB_FOLLOW)
545 printf("nubus_get_vendor(0x%x, 0x%x).\n", (u_int) slot, rsrc);
546 #endif
547 nubus_get_main_dir(slot, &dir);
548 if (nubus_find_rsrc(slot, &dir, 1, &ent) <= 0)
549 return huh;
550 nubus_get_dir_from_rsrc(slot, &ent, &dir);
551
552 if (nubus_find_rsrc(slot, &dir, NUBUS_RSRC_VENDORINFO, &ent) <= 0)
553 return huh;
554 nubus_get_dir_from_rsrc(slot, &ent, &dir);
555
556 if (nubus_find_rsrc(slot, &dir, rsrc, &ent) <= 0)
557 return huh;
558
559 nubus_get_c_string(slot, &ent, str_ret, 64);
560
561 return str_ret;
562 }
563
564 char *
565 nubus_get_card_name(slot)
566 nubus_slot *slot;
567 {
568 static char name_ret[64];
569 nubus_dir dir;
570 nubus_dirent ent;
571
572 #if DEBUG
573 if (nubus_debug & NDB_FOLLOW)
574 printf("nubus_get_card_name(0x%x).\n", (u_long) slot);
575 #endif
576 nubus_get_main_dir(slot, &dir);
577
578 if (nubus_find_rsrc(slot, &dir, 1, &ent) <= 0)
579 return huh;
580
581 nubus_get_dir_from_rsrc(slot, &ent, &dir);
582
583 if (nubus_find_rsrc(slot, &dir, NUBUS_RSRC_NAME, &ent) <= 0)
584 return huh;
585
586 nubus_get_c_string(slot, &ent, name_ret, 64);
587
588 return name_ret;
589 }
590