|
|
1.1 ! root 1: /* ! 2: * OpenBIOS ESP driver ! 3: * ! 4: * Copyright (C) 2004 Jens Axboe <[email protected]> ! 5: * Copyright (C) 2005 Stefan Reinauer <[email protected]> ! 6: * ! 7: * Credit goes to Hale Landis for his excellent ata demo software ! 8: * OF node handling and some fixes by Stefan Reinauer ! 9: * ! 10: * This program is free software; you can redistribute it and/or ! 11: * modify it under the terms of the GNU General Public License ! 12: * version 2 ! 13: * ! 14: */ ! 15: ! 16: #include "config.h" ! 17: #include "libopenbios/bindings.h" ! 18: #include "kernel/kernel.h" ! 19: #include "libc/byteorder.h" ! 20: #include "libc/vsprintf.h" ! 21: ! 22: #include "drivers/drivers.h" ! 23: #include "asm/io.h" ! 24: #include "scsi.h" ! 25: #include "asm/dma.h" ! 26: #include "esp.h" ! 27: #include "libopenbios/ofmem.h" ! 28: ! 29: #define BUFSIZE 4096 ! 30: ! 31: #ifdef CONFIG_DEBUG_ESP ! 32: #define DPRINTF(fmt, args...) \ ! 33: do { printk(fmt , ##args); } while (0) ! 34: #else ! 35: #define DPRINTF(fmt, args...) ! 36: #endif ! 37: ! 38: struct esp_dma { ! 39: volatile struct sparc_dma_registers *regs; ! 40: enum dvma_rev revision; ! 41: }; ! 42: ! 43: typedef struct sd_private { ! 44: unsigned int bs; ! 45: const char *media_str[2]; ! 46: uint32_t sectors; ! 47: uint8_t media; ! 48: uint8_t id; ! 49: uint8_t present; ! 50: char model[40]; ! 51: } sd_private_t; ! 52: ! 53: struct esp_regs { ! 54: unsigned char regs[ESP_REG_SIZE]; ! 55: }; ! 56: ! 57: typedef struct esp_private { ! 58: volatile struct esp_regs *ll; ! 59: uint32_t buffer_dvma; ! 60: unsigned int irq; /* device IRQ number */ ! 61: struct esp_dma espdma; ! 62: unsigned char *buffer; ! 63: sd_private_t sd[8]; ! 64: } esp_private_t; ! 65: ! 66: static esp_private_t *global_esp; ! 67: ! 68: /* DECLARE data structures for the nodes. */ ! 69: DECLARE_UNNAMED_NODE(ob_sd, INSTALL_OPEN, sizeof(sd_private_t *)); ! 70: DECLARE_UNNAMED_NODE(ob_esp, INSTALL_OPEN, sizeof(esp_private_t *)); ! 71: ! 72: #ifdef CONFIG_DEBUG_ESP ! 73: static void dump_drive(sd_private_t *drive) ! 74: { ! 75: printk("SCSI DRIVE @%lx:\n", (unsigned long)drive); ! 76: printk("id: %d\n", drive->id); ! 77: printk("media: %s\n", drive->media_str[0]); ! 78: printk("media: %s\n", drive->media_str[1]); ! 79: printk("model: %s\n", drive->model); ! 80: printk("sectors: %d\n", drive->sectors); ! 81: printk("present: %d\n", drive->present); ! 82: printk("bs: %d\n", drive->bs); ! 83: } ! 84: #endif ! 85: ! 86: static int ! 87: do_command(esp_private_t *esp, sd_private_t *sd, int cmdlen, int replylen) ! 88: { ! 89: int status; ! 90: ! 91: // Set SCSI target ! 92: esp->ll->regs[ESP_BUSID] = sd->id & 7; ! 93: // Set DMA address ! 94: esp->espdma.regs->st_addr = esp->buffer_dvma; ! 95: // Set DMA length ! 96: esp->ll->regs[ESP_TCLOW] = cmdlen & 0xff; ! 97: esp->ll->regs[ESP_TCMED] = (cmdlen >> 8) & 0xff; ! 98: // Set DMA direction and enable DMA ! 99: esp->espdma.regs->cond_reg = DMA_ENABLE; ! 100: // Set ATN, issue command ! 101: esp->ll->regs[ESP_CMD] = ESP_CMD_SELA | ESP_CMD_DMA; ! 102: // Wait for DMA to complete. Can this fail? ! 103: while ((esp->espdma.regs->cond_reg & DMA_HNDL_INTR) == 0) /* no-op */; ! 104: // Check status ! 105: status = esp->ll->regs[ESP_STATUS]; ! 106: // Clear interrupts to avoid guests seeing spurious interrupts ! 107: (void)esp->ll->regs[ESP_INTRPT]; ! 108: ! 109: DPRINTF("do_command: id %d, cmd[0] 0x%x, status 0x%x\n", sd->id, esp->buffer[0], status); ! 110: ! 111: /* Target didn't want all command data? */ ! 112: if ((status & ESP_STAT_TCNT) != ESP_STAT_TCNT) { ! 113: return status; ! 114: } ! 115: if (replylen == 0) { ! 116: return 0; ! 117: } ! 118: /* Target went to status phase instead of data phase? */ ! 119: if ((status & ESP_STAT_PMASK) == ESP_STATP) { ! 120: return status; ! 121: } ! 122: ! 123: // Get reply ! 124: // Set DMA address ! 125: esp->espdma.regs->st_addr = esp->buffer_dvma; ! 126: // Set DMA length ! 127: esp->ll->regs[ESP_TCLOW] = replylen & 0xff; ! 128: esp->ll->regs[ESP_TCMED] = (replylen >> 8) & 0xff; ! 129: // Set DMA direction ! 130: esp->espdma.regs->cond_reg = DMA_ST_WRITE | DMA_ENABLE; ! 131: // Transfer ! 132: esp->ll->regs[ESP_CMD] = ESP_CMD_TI | ESP_CMD_DMA; ! 133: // Wait for DMA to complete ! 134: while ((esp->espdma.regs->cond_reg & DMA_HNDL_INTR) == 0) /* no-op */; ! 135: // Check status ! 136: status = esp->ll->regs[ESP_STATUS]; ! 137: // Clear interrupts to avoid guests seeing spurious interrupts ! 138: (void)esp->ll->regs[ESP_INTRPT]; ! 139: ! 140: DPRINTF("do_command_reply: status 0x%x\n", status); ! 141: ! 142: if ((status & ESP_STAT_TCNT) != ESP_STAT_TCNT) ! 143: return status; ! 144: else ! 145: return 0; // OK ! 146: } ! 147: ! 148: // offset is in sectors ! 149: static int ! 150: ob_sd_read_sector(esp_private_t *esp, sd_private_t *sd, int offset) ! 151: { ! 152: DPRINTF("ob_sd_read_sector id %d sector=%d\n", ! 153: sd->id, offset); ! 154: ! 155: // Setup command = Read(10) ! 156: memset(esp->buffer, 0, 11); ! 157: esp->buffer[0] = 0x80; ! 158: esp->buffer[1] = READ_10; ! 159: ! 160: esp->buffer[3] = (offset >> 24) & 0xff; ! 161: esp->buffer[4] = (offset >> 16) & 0xff; ! 162: esp->buffer[5] = (offset >> 8) & 0xff; ! 163: esp->buffer[6] = offset & 0xff; ! 164: ! 165: esp->buffer[8] = 0; ! 166: esp->buffer[9] = 1; ! 167: ! 168: if (do_command(esp, sd, 11, sd->bs)) ! 169: return 0; ! 170: ! 171: return 0; ! 172: } ! 173: ! 174: static unsigned int ! 175: read_capacity(esp_private_t *esp, sd_private_t *sd) ! 176: { ! 177: // Setup command = Read Capacity ! 178: memset(esp->buffer, 0, 11); ! 179: esp->buffer[0] = 0x80; ! 180: esp->buffer[1] = READ_CAPACITY; ! 181: ! 182: if (do_command(esp, sd, 11, 8)) { ! 183: sd->sectors = 0; ! 184: sd->bs = 0; ! 185: DPRINTF("read_capacity id %d failed\n", sd->id); ! 186: return 0; ! 187: } ! 188: sd->bs = (esp->buffer[4] << 24) | (esp->buffer[5] << 16) | (esp->buffer[6] << 8) | esp->buffer[7]; ! 189: sd->sectors = ((esp->buffer[0] << 24) | (esp->buffer[1] << 16) | (esp->buffer[2] << 8) | esp->buffer[3]) * (sd->bs / 512); ! 190: ! 191: DPRINTF("read_capacity id %d bs %d sectors %d\n", sd->id, sd->bs, ! 192: sd->sectors); ! 193: return 1; ! 194: } ! 195: ! 196: static unsigned int ! 197: test_unit_ready(esp_private_t *esp, sd_private_t *sd) ! 198: { ! 199: /* Setup command = Test Unit Ready */ ! 200: memset(esp->buffer, 0, 6); ! 201: esp->buffer[0] = 0x80; ! 202: esp->buffer[1] = TEST_UNIT_READY; ! 203: ! 204: if (do_command(esp, sd, 6, 0)) { ! 205: DPRINTF("test_unit_ready id %d failed\n", sd->id); ! 206: return 0; ! 207: } ! 208: ! 209: DPRINTF("test_unit_ready id %d success\n", sd->id); ! 210: return 1; ! 211: } ! 212: ! 213: static unsigned int ! 214: inquiry(esp_private_t *esp, sd_private_t *sd) ! 215: { ! 216: const char *media[2] = { "UNKNOWN", "UNKNOWN"}; ! 217: ! 218: // Setup command = Inquiry ! 219: memset(esp->buffer, 0, 7); ! 220: esp->buffer[0] = 0x80; ! 221: esp->buffer[1] = INQUIRY; ! 222: ! 223: esp->buffer[5] = 36; ! 224: ! 225: if (do_command(esp, sd, 7, 36)) { ! 226: sd->present = 0; ! 227: sd->media = -1; ! 228: return 0; ! 229: } ! 230: sd->present = 1; ! 231: sd->media = esp->buffer[0]; ! 232: ! 233: switch (sd->media) { ! 234: case TYPE_DISK: ! 235: media[0] = "disk"; ! 236: media[1] = "hd"; ! 237: break; ! 238: case TYPE_ROM: ! 239: media[0] = "cdrom"; ! 240: media[1] = "cd"; ! 241: break; ! 242: } ! 243: sd->media_str[0] = media[0]; ! 244: sd->media_str[1] = media[1]; ! 245: memcpy(sd->model, &esp->buffer[16], 16); ! 246: sd->model[17] = '\0'; ! 247: ! 248: return 1; ! 249: } ! 250: ! 251: ! 252: static void ! 253: ob_sd_read_blocks(sd_private_t **sd) ! 254: { ! 255: cell n = POP(), cnt = n; ! 256: ucell blk = POP(); ! 257: char *dest = (char*)POP(); ! 258: int pos, spb, sect_offset; ! 259: ! 260: DPRINTF("ob_sd_read_blocks id %d %lx block=%d n=%d\n", (*sd)->id, (unsigned long)dest, blk, n ); ! 261: ! 262: if ((*sd)->bs == 0) { ! 263: PUSH(0); ! 264: return; ! 265: } ! 266: spb = (*sd)->bs / 512; ! 267: while (n) { ! 268: sect_offset = blk / spb; ! 269: pos = (blk - sect_offset * spb) * 512; ! 270: ! 271: if (ob_sd_read_sector(global_esp, *sd, sect_offset)) { ! 272: DPRINTF("ob_sd_read_blocks: error\n"); ! 273: RET(0); ! 274: } ! 275: while (n && pos < spb * 512) { ! 276: memcpy(dest, global_esp->buffer + pos, 512); ! 277: pos += 512; ! 278: dest += 512; ! 279: n--; ! 280: blk++; ! 281: } ! 282: } ! 283: PUSH(cnt); ! 284: } ! 285: ! 286: static void ! 287: ob_sd_block_size(__attribute__((unused))sd_private_t **sd) ! 288: { ! 289: PUSH(512); ! 290: } ! 291: ! 292: static void ! 293: ob_sd_open(__attribute__((unused))sd_private_t **sd) ! 294: { ! 295: int ret = 1, id; ! 296: phandle_t ph; ! 297: ! 298: fword("my-unit"); ! 299: id = POP(); ! 300: POP(); // unit id is 2 ints but we only need one. ! 301: *sd = &global_esp->sd[id]; ! 302: ! 303: #ifdef CONFIG_DEBUG_ESP ! 304: { ! 305: char *args; ! 306: ! 307: fword("my-args"); ! 308: args = pop_fstr_copy(); ! 309: DPRINTF("opening drive %d args %s\n", id, args); ! 310: free(args); ! 311: } ! 312: #endif ! 313: ! 314: selfword("open-deblocker"); ! 315: ! 316: /* interpose disk-label */ ! 317: ph = find_dev("/packages/disk-label"); ! 318: fword("my-args"); ! 319: PUSH_ph( ph ); ! 320: fword("interpose"); ! 321: ! 322: RET ( -ret ); ! 323: } ! 324: ! 325: static void ! 326: ob_sd_close(__attribute__((unused)) sd_private_t **sd) ! 327: { ! 328: selfword("close-deblocker"); ! 329: } ! 330: ! 331: NODE_METHODS(ob_sd) = { ! 332: { "open", ob_sd_open }, ! 333: { "close", ob_sd_close }, ! 334: { "read-blocks", ob_sd_read_blocks }, ! 335: { "block-size", ob_sd_block_size }, ! 336: }; ! 337: ! 338: ! 339: static int ! 340: espdma_init(unsigned int slot, uint64_t base, unsigned long offset, ! 341: struct esp_dma *espdma) ! 342: { ! 343: espdma->regs = (void *)ofmem_map_io(base + (uint64_t)offset, 0x10); ! 344: ! 345: if (espdma->regs == NULL) { ! 346: DPRINTF("espdma_init: cannot map registers\n"); ! 347: return -1; ! 348: } ! 349: ! 350: DPRINTF("dma1: "); ! 351: ! 352: switch ((espdma->regs->cond_reg) & DMA_DEVICE_ID) { ! 353: case DMA_VERS0: ! 354: espdma->revision = dvmarev0; ! 355: DPRINTF("Revision 0 "); ! 356: break; ! 357: case DMA_ESCV1: ! 358: espdma->revision = dvmaesc1; ! 359: DPRINTF("ESC Revision 1 "); ! 360: break; ! 361: case DMA_VERS1: ! 362: espdma->revision = dvmarev1; ! 363: DPRINTF("Revision 1 "); ! 364: break; ! 365: case DMA_VERS2: ! 366: espdma->revision = dvmarev2; ! 367: DPRINTF("Revision 2 "); ! 368: break; ! 369: case DMA_VERHME: ! 370: espdma->revision = dvmahme; ! 371: DPRINTF("HME DVMA gate array "); ! 372: break; ! 373: case DMA_VERSPLUS: ! 374: espdma->revision = dvmarevplus; ! 375: DPRINTF("Revision 1 PLUS "); ! 376: break; ! 377: default: ! 378: DPRINTF("unknown dma version %x", ! 379: (espdma->regs->cond_reg) & DMA_DEVICE_ID); ! 380: /* espdma->allocated = 1; */ ! 381: break; ! 382: } ! 383: DPRINTF("\n"); ! 384: ! 385: push_str("/iommu/sbus/espdma"); ! 386: fword("find-device"); ! 387: ! 388: /* set reg */ ! 389: PUSH(slot); ! 390: fword("encode-int"); ! 391: PUSH(offset); ! 392: fword("encode-int"); ! 393: fword("encode+"); ! 394: PUSH(0x00000010); ! 395: fword("encode-int"); ! 396: fword("encode+"); ! 397: push_str("reg"); ! 398: fword("property"); ! 399: ! 400: return 0; ! 401: } ! 402: ! 403: static void ! 404: ob_esp_initialize(__attribute__((unused)) esp_private_t **esp) ! 405: { ! 406: phandle_t ph = get_cur_dev(); ! 407: ! 408: set_int_property(ph, "#address-cells", 2); ! 409: set_int_property(ph, "#size-cells", 0); ! 410: ! 411: /* set device type */ ! 412: push_str("scsi"); ! 413: fword("device-type"); ! 414: ! 415: /* QEMU's ESP emulation does not support mixing DMA and FIFO messages. By ! 416: setting this attribute, we prevent the Solaris ESP kernel driver from ! 417: trying to use this feature when booting a disk image (and failing) */ ! 418: PUSH(0x58); ! 419: fword("encode-int"); ! 420: push_str("scsi-options"); ! 421: fword("property"); ! 422: ! 423: PUSH(0x24); ! 424: fword("encode-int"); ! 425: PUSH(0); ! 426: fword("encode-int"); ! 427: fword("encode+"); ! 428: push_str("intr"); ! 429: fword("property"); ! 430: } ! 431: ! 432: static void ! 433: ob_esp_decodeunit(__attribute__((unused)) esp_private_t **esp) ! 434: { ! 435: fword("decode-unit-scsi"); ! 436: } ! 437: ! 438: ! 439: static void ! 440: ob_esp_encodeunit(__attribute__((unused)) esp_private_t **esp) ! 441: { ! 442: fword("encode-unit-scsi"); ! 443: } ! 444: ! 445: NODE_METHODS(ob_esp) = { ! 446: { NULL, ob_esp_initialize }, ! 447: { "decode-unit", ob_esp_decodeunit }, ! 448: { "encode-unit", ob_esp_encodeunit }, ! 449: }; ! 450: ! 451: static void ! 452: add_alias(const char *device, const char *alias) ! 453: { ! 454: DPRINTF("add_alias dev \"%s\" = alias \"%s\"\n", device, alias); ! 455: push_str("/aliases"); ! 456: fword("find-device"); ! 457: push_str(device); ! 458: fword("encode-string"); ! 459: push_str(alias); ! 460: fword("property"); ! 461: } ! 462: ! 463: int ! 464: ob_esp_init(unsigned int slot, uint64_t base, unsigned long espoffset, ! 465: unsigned long dmaoffset) ! 466: { ! 467: int id, diskcount = 0, cdcount = 0, *counter_ptr; ! 468: char nodebuff[256], aliasbuff[256]; ! 469: esp_private_t *esp; ! 470: unsigned int i; ! 471: ! 472: DPRINTF("Initializing SCSI..."); ! 473: ! 474: esp = malloc(sizeof(esp_private_t)); ! 475: if (!esp) { ! 476: DPRINTF("Can't allocate ESP private structure\n"); ! 477: return -1; ! 478: } ! 479: ! 480: global_esp = esp; ! 481: ! 482: if (espdma_init(slot, base, dmaoffset, &esp->espdma) != 0) { ! 483: return -1; ! 484: } ! 485: /* Get the IO region */ ! 486: esp->ll = (void *)ofmem_map_io(base + (uint64_t)espoffset, ! 487: sizeof(struct esp_regs)); ! 488: if (esp->ll == NULL) { ! 489: DPRINTF("Can't map ESP registers\n"); ! 490: return -1; ! 491: } ! 492: ! 493: esp->buffer = (void *)dvma_alloc(BUFSIZE, &esp->buffer_dvma); ! 494: if (!esp->buffer || !esp->buffer_dvma) { ! 495: DPRINTF("Can't get a DVMA buffer\n"); ! 496: return -1; ! 497: } ! 498: ! 499: // Chip reset ! 500: esp->ll->regs[ESP_CMD] = ESP_CMD_RC; ! 501: ! 502: DPRINTF("ESP at 0x%lx, buffer va 0x%lx dva 0x%lx\n", (unsigned long)esp, ! 503: (unsigned long)esp->buffer, (unsigned long)esp->buffer_dvma); ! 504: DPRINTF("done\n"); ! 505: DPRINTF("Initializing SCSI devices..."); ! 506: ! 507: for (id = 0; id < 8; id++) { ! 508: esp->sd[id].id = id; ! 509: if (!inquiry(esp, &esp->sd[id])) { ! 510: DPRINTF("Unit %d not present\n", id); ! 511: continue; ! 512: } ! 513: /* Clear Unit Attention condition from reset */ ! 514: for (i = 0; i < 5; i++) { ! 515: if (test_unit_ready(esp, &esp->sd[id])) { ! 516: break; ! 517: } ! 518: } ! 519: if (i == 5) { ! 520: DPRINTF("Unit %d present but won't become ready\n", id); ! 521: continue; ! 522: } ! 523: DPRINTF("Unit %d present\n", id); ! 524: read_capacity(esp, &esp->sd[id]); ! 525: ! 526: #ifdef CONFIG_DEBUG_ESP ! 527: dump_drive(&esp->sd[id]); ! 528: #endif ! 529: } ! 530: ! 531: REGISTER_NAMED_NODE(ob_esp, "/iommu/sbus/espdma/esp"); ! 532: device_end(); ! 533: /* set reg */ ! 534: push_str("/iommu/sbus/espdma/esp"); ! 535: fword("find-device"); ! 536: PUSH(slot); ! 537: fword("encode-int"); ! 538: PUSH(espoffset); ! 539: fword("encode-int"); ! 540: fword("encode+"); ! 541: PUSH(0x00000010); ! 542: fword("encode-int"); ! 543: fword("encode+"); ! 544: push_str("reg"); ! 545: fword("property"); ! 546: ! 547: PUSH(0x02625a00); ! 548: fword("encode-int"); ! 549: push_str("clock-frequency"); ! 550: fword("property"); ! 551: ! 552: for (id = 0; id < 8; id++) { ! 553: if (!esp->sd[id].present) ! 554: continue; ! 555: push_str("/iommu/sbus/espdma/esp"); ! 556: fword("find-device"); ! 557: fword("new-device"); ! 558: push_str("sd"); ! 559: fword("device-name"); ! 560: push_str("block"); ! 561: fword("device-type"); ! 562: fword("is-deblocker"); ! 563: PUSH(id); ! 564: fword("encode-int"); ! 565: PUSH(0); ! 566: fword("encode-int"); ! 567: fword("encode+"); ! 568: push_str("reg"); ! 569: fword("property"); ! 570: fword("finish-device"); ! 571: snprintf(nodebuff, sizeof(nodebuff), "/iommu/sbus/espdma/esp/sd@%d,0", ! 572: id); ! 573: REGISTER_NODE_METHODS(ob_sd, nodebuff); ! 574: if (esp->sd[id].media == TYPE_ROM) { ! 575: counter_ptr = &cdcount; ! 576: } else { ! 577: counter_ptr = &diskcount; ! 578: } ! 579: if (*counter_ptr == 0) { ! 580: add_alias(nodebuff, esp->sd[id].media_str[0]); ! 581: add_alias(nodebuff, esp->sd[id].media_str[1]); ! 582: } ! 583: snprintf(aliasbuff, sizeof(aliasbuff), "%s%d", ! 584: esp->sd[id].media_str[0], *counter_ptr); ! 585: add_alias(nodebuff, aliasbuff); ! 586: snprintf(aliasbuff, sizeof(aliasbuff), "%s%d", ! 587: esp->sd[id].media_str[1], *counter_ptr); ! 588: add_alias(nodebuff, aliasbuff); ! 589: snprintf(aliasbuff, sizeof(aliasbuff), "sd(0,%d,0)", id); ! 590: add_alias(nodebuff, aliasbuff); ! 591: snprintf(aliasbuff, sizeof(aliasbuff), "sd(0,%d,0)@0,0", id); ! 592: add_alias(nodebuff, aliasbuff); ! 593: (*counter_ptr)++; ! 594: } ! 595: DPRINTF("done\n"); ! 596: ! 597: return 0; ! 598: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.