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