/* * OpenBIOS polled ide driver * * Copyright (C) 2004 Jens Axboe * Copyright (C) 2005 Stefan Reinauer * * Credit goes to Hale Landis for his excellent ata demo software * OF node handling and some fixes by Stefan Reinauer * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 * */ #include "config.h" #include "libopenbios/bindings.h" #include "kernel/kernel.h" #include "libc/byteorder.h" #include "libc/vsprintf.h" #include "drivers/drivers.h" #include "ide.h" #include "hdreg.h" #include "timer.h" #ifdef CONFIG_DEBUG_IDE #define IDE_DPRINTF(fmt, args...) \ do { printk("IDE - %s: " fmt, __func__ , ##args); } while (0) #else #define IDE_DPRINTF(fmt, args...) do { } while (0) #endif /* DECLARE data structures for the nodes. */ DECLARE_UNNAMED_NODE( ob_ide, INSTALL_OPEN, sizeof(struct ide_drive*) ); DECLARE_UNNAMED_NODE( ob_ide_ctrl, INSTALL_OPEN, sizeof(int)); /* * define to 2 for the standard 2 channels only */ #ifndef CONFIG_IDE_NUM_CHANNELS #define IDE_NUM_CHANNELS 4 #else #define IDE_NUM_CHANNELS CONFIG_IDE_NUM_CHANNELS #endif #define IDE_MAX_CHANNELS 4 #ifndef CONFIG_IDE_FIRST_UNIT #define FIRST_UNIT 0 #else #define FIRST_UNIT CONFIG_IDE_FIRST_UNIT #endif #ifndef CONFIG_IDE_DEV_TYPE #define DEV_TYPE "ide" #else #define DEV_TYPE CONFIG_IDE_DEV_TYPE #endif #ifndef CONFIG_IDE_DEV_NAME #define DEV_NAME "ide%d" #else #define DEV_NAME CONFIG_IDE_DEV_NAME #endif static int current_channel = FIRST_UNIT; static struct ide_channel *channels = NULL; static inline void ide_add_channel(struct ide_channel *chan) { chan->next = channels; channels = chan; } static struct ide_channel *ide_seek_channel(phandle_t ph) { struct ide_channel *current; current = channels; while (current) { if (current->ph == ph) return current; current = current->next; } return NULL; } /* * don't be pedantic */ #undef ATA_PEDANTIC static void dump_drive(struct ide_drive *drive) { #ifdef CONFIG_DEBUG_IDE printk("IDE DRIVE @%lx:\n", (unsigned long)drive); printk("unit: %d\n",drive->unit); printk("present: %d\n",drive->present); printk("type: %d\n",drive->type); printk("media: %d\n",drive->media); printk("model: %s\n",drive->model); printk("nr: %d\n",drive->nr); printk("cyl: %d\n",drive->cyl); printk("head: %d\n",drive->head); printk("sect: %d\n",drive->sect); printk("bs: %d\n",drive->bs); #endif } /* * old style io port operations */ static unsigned char ob_ide_inb(struct ide_channel *chan, unsigned int port) { return inb(chan->io_regs[port]); } static void ob_ide_outb(struct ide_channel *chan, unsigned char data, unsigned int port) { outb(data, chan->io_regs[port]); } static void ob_ide_insw(struct ide_channel *chan, unsigned int port, unsigned char *addr, unsigned int count) { insw(chan->io_regs[port], addr, count); } static void ob_ide_outsw(struct ide_channel *chan, unsigned int port, unsigned char *addr, unsigned int count) { outsw(chan->io_regs[port], addr, count); } static inline unsigned char ob_ide_pio_readb(struct ide_drive *drive, unsigned int offset) { struct ide_channel *chan = drive->channel; return chan->obide_inb(chan, offset); } static inline void ob_ide_pio_writeb(struct ide_drive *drive, unsigned int offset, unsigned char data) { struct ide_channel *chan = drive->channel; chan->obide_outb(chan, data, offset); } static inline void ob_ide_pio_insw(struct ide_drive *drive, unsigned int offset, unsigned char *addr, unsigned int len) { struct ide_channel *chan = drive->channel; if (len & 1) { IDE_DPRINTF("%d: command not word aligned\n", drive->nr); return; } chan->obide_insw(chan, offset, addr, len / 2); } static inline void ob_ide_pio_outsw(struct ide_drive *drive, unsigned int offset, unsigned char *addr, unsigned int len) { struct ide_channel *chan = drive->channel; if (len & 1) { IDE_DPRINTF("%d: command not word aligned\n", drive->nr); return; } chan->obide_outsw(chan, offset, addr, len / 2); } static void ob_ide_400ns_delay(struct ide_drive *drive) { (void) ob_ide_pio_readb(drive, IDEREG_ASTATUS); (void) ob_ide_pio_readb(drive, IDEREG_ASTATUS); (void) ob_ide_pio_readb(drive, IDEREG_ASTATUS); (void) ob_ide_pio_readb(drive, IDEREG_ASTATUS); udelay(1); } static void ob_ide_error(struct ide_drive *drive, unsigned char stat, const char *msg) { #ifdef CONFIG_DEBUG_IDE struct ide_channel *chan = drive->channel; unsigned char err; #endif if (!stat) stat = ob_ide_pio_readb(drive, IDEREG_STATUS); IDE_DPRINTF("ob_ide_error drive<%d>: %s:\n", drive->nr, msg); IDE_DPRINTF(" cmd=%x, stat=%x", chan->ata_cmd.command, stat); if ((stat & (BUSY_STAT | ERR_STAT)) == ERR_STAT) { #ifdef CONFIG_DEBUG_IDE err = #endif ob_ide_pio_readb(drive, IDEREG_ERROR); IDE_DPRINTF(", err=%x", err); } IDE_DPRINTF("\n"); #ifdef CONFIG_DEBUG_IDE /* * see if sense is valid and dump that */ if (chan->ata_cmd.command == WIN_PACKET) { struct atapi_command *cmd = &chan->atapi_cmd; unsigned char old_cdb = cmd->cdb[0]; if (cmd->cdb[0] == ATAPI_REQ_SENSE) { old_cdb = cmd->old_cdb; IDE_DPRINTF(" atapi opcode=%02x", old_cdb); } else { int i; IDE_DPRINTF(" cdb: "); for (i = 0; i < sizeof(cmd->cdb); i++) IDE_DPRINTF("%02x ", cmd->cdb[i]); } if (cmd->sense_valid) IDE_DPRINTF(", sense: %02x/%02x/%02x", cmd->sense.sense_key, cmd->sense.asc, cmd->sense.ascq); else IDE_DPRINTF(", no sense"); IDE_DPRINTF("\n"); } #endif } /* * wait for 'stat' to be set. returns 1 if failed, 0 if succesful */ static int ob_ide_wait_stat(struct ide_drive *drive, unsigned char ok_stat, unsigned char bad_stat, unsigned char *ret_stat) { unsigned char stat; int i; ob_ide_400ns_delay(drive); for (i = 0; i < 5000; i++) { stat = ob_ide_pio_readb(drive, IDEREG_STATUS); if (!(stat & BUSY_STAT)) break; udelay(1000); } if (ret_stat) *ret_stat = stat; if (stat & bad_stat) return 1; if ((stat & ok_stat) || !ok_stat) return 0; return 1; } static int ob_ide_select_drive(struct ide_drive *drive) { struct ide_channel *chan = drive->channel; unsigned char control = IDEHEAD_DEV0; if (ob_ide_wait_stat(drive, 0, BUSY_STAT, NULL)) { IDE_DPRINTF("select_drive: timed out\n"); return 1; } /* * don't select drive if already active. Note: we always * wait for BUSY clear */ if (drive->unit == chan->selected) return 0; if (drive->unit) control = IDEHEAD_DEV1; ob_ide_pio_writeb(drive, IDEREG_CURRENT, control); ob_ide_400ns_delay(drive); if (ob_ide_wait_stat(drive, 0, BUSY_STAT, NULL)) { IDE_DPRINTF("select_drive: timed out\n"); return 1; } chan->selected = drive->unit; return 0; } static void ob_ide_write_tasklet(struct ide_drive *drive, struct ata_command *cmd) { ob_ide_pio_writeb(drive, IDEREG_FEATURE, cmd->task[1]); ob_ide_pio_writeb(drive, IDEREG_NSECTOR, cmd->task[3]); ob_ide_pio_writeb(drive, IDEREG_SECTOR, cmd->task[7]); ob_ide_pio_writeb(drive, IDEREG_LCYL, cmd->task[8]); ob_ide_pio_writeb(drive, IDEREG_HCYL, cmd->task[9]); ob_ide_pio_writeb(drive, IDEREG_FEATURE, cmd->task[0]); ob_ide_pio_writeb(drive, IDEREG_NSECTOR, cmd->task[2]); ob_ide_pio_writeb(drive, IDEREG_SECTOR, cmd->task[4]); ob_ide_pio_writeb(drive, IDEREG_LCYL, cmd->task[5]); ob_ide_pio_writeb(drive, IDEREG_HCYL, cmd->task[6]); if (drive->unit) cmd->device_head |= IDEHEAD_DEV1; ob_ide_pio_writeb(drive, IDEREG_CURRENT, cmd->device_head); ob_ide_pio_writeb(drive, IDEREG_COMMAND, cmd->command); ob_ide_400ns_delay(drive); } static void ob_ide_write_registers(struct ide_drive *drive, struct ata_command *cmd) { /* * we are _always_ polled */ ob_ide_pio_writeb(drive, IDEREG_CONTROL, cmd->control | IDECON_NIEN); ob_ide_pio_writeb(drive, IDEREG_FEATURE, cmd->feature); ob_ide_pio_writeb(drive, IDEREG_NSECTOR, cmd->nsector); ob_ide_pio_writeb(drive, IDEREG_SECTOR, cmd->sector); ob_ide_pio_writeb(drive, IDEREG_LCYL, cmd->lcyl); ob_ide_pio_writeb(drive, IDEREG_HCYL, cmd->hcyl); if (drive->unit) cmd->device_head |= IDEHEAD_DEV1; ob_ide_pio_writeb(drive, IDEREG_CURRENT, cmd->device_head); ob_ide_pio_writeb(drive, IDEREG_COMMAND, cmd->command); ob_ide_400ns_delay(drive); } /* * execute command with "pio non data" protocol */ #if 0 static int ob_ide_pio_non_data(struct ide_drive *drive, struct ata_command *cmd) { if (ob_ide_select_drive(drive)) return 1; ob_ide_write_registers(drive, cmd); if (ob_ide_wait_stat(drive, 0, BUSY_STAT, NULL)) return 1; return 0; } #endif /* * execute given command with a pio data-in phase. */ static int ob_ide_pio_data_in(struct ide_drive *drive, struct ata_command *cmd) { unsigned char stat; unsigned int bytes, timeout; if (ob_ide_select_drive(drive)) return 1; /* * ATA must set ready and seek stat, ATAPI need only clear busy */ timeout = 0; do { stat = ob_ide_pio_readb(drive, IDEREG_STATUS); if (drive->type == ide_type_ata) { /* * this is BIOS code, don't be too pedantic */ #ifdef ATA_PEDANTIC if ((stat & (BUSY_STAT | READY_STAT | SEEK_STAT)) == (READY_STAT | SEEK_STAT)) break; #else if ((stat & (BUSY_STAT | READY_STAT)) == READY_STAT) break; #endif } else { if (!(stat & BUSY_STAT)) break; } ob_ide_400ns_delay(drive); } while (timeout++ < 1000); if (timeout >= 1000) { ob_ide_error(drive, stat, "drive timed out"); cmd->stat = stat; return 1; } ob_ide_write_registers(drive, cmd); /* * now read the data */ bytes = cmd->buflen; do { unsigned count = cmd->buflen; if (count > drive->bs) count = drive->bs; /* delay 100ms for ATAPI? */ /* * wait for BUSY clear */ if (ob_ide_wait_stat(drive, 0, BUSY_STAT | ERR_STAT, &stat)) { ob_ide_error(drive, stat, "timed out waiting for BUSY clear"); cmd->stat = stat; break; } /* * transfer the data */ if ((stat & (BUSY_STAT | DRQ_STAT)) == DRQ_STAT) { ob_ide_pio_insw(drive, IDEREG_DATA, cmd->buffer, count); cmd->bytes -= count; cmd->buffer += count; bytes -= count; ob_ide_400ns_delay(drive); } if (stat & (BUSY_STAT | WRERR_STAT | ERR_STAT)) { cmd->stat = stat; break; } if (!(stat & DRQ_STAT)) { cmd->stat = stat; break; } } while (bytes); if (bytes) IDE_DPRINTF("bytes=%d, stat=%x\n", bytes, stat); return bytes ? 1 : 0; } /* * execute ata command with pio packet protocol */ static int ob_ide_pio_packet(struct ide_drive *drive, struct atapi_command *cmd) { unsigned char stat, reason, lcyl, hcyl; struct ata_command *acmd = &drive->channel->ata_cmd; unsigned char *buffer; unsigned int bytes; if (ob_ide_select_drive(drive)) return 1; if (cmd->buflen && cmd->data_direction == atapi_ddir_none) IDE_DPRINTF("non-zero buflen but no data direction\n"); memset(acmd, 0, sizeof(*acmd)); acmd->lcyl = cmd->buflen & 0xff; acmd->hcyl = (cmd->buflen >> 8) & 0xff; acmd->command = WIN_PACKET; ob_ide_write_registers(drive, acmd); /* * BUSY must be set, _or_ DRQ | ERR */ stat = ob_ide_pio_readb(drive, IDEREG_ASTATUS); if ((stat & BUSY_STAT) == 0) { if (!(stat & (DRQ_STAT | ERR_STAT))) { ob_ide_error(drive, stat, "bad stat in atapi cmd"); cmd->stat = stat; return 1; } } if (ob_ide_wait_stat(drive, 0, BUSY_STAT | ERR_STAT, &stat)) { ob_ide_error(drive, stat, "timeout, ATAPI BUSY clear"); cmd->stat = stat; return 1; } if ((stat & (BUSY_STAT | DRQ_STAT | ERR_STAT)) != DRQ_STAT) { /* * if command isn't request sense, then we have a problem. if * we are doing a sense, ERR_STAT == CHECK_CONDITION */ if (cmd->cdb[0] != ATAPI_REQ_SENSE) { IDE_DPRINTF("odd, drive didn't want to transfer %x\n", stat); return 1; } } /* * transfer cdb */ ob_ide_pio_outsw(drive, IDEREG_DATA, cmd->cdb,sizeof(cmd->cdb)); ob_ide_400ns_delay(drive); /* * ok, cdb was sent to drive, now do data transfer (if any) */ bytes = cmd->buflen; buffer = cmd->buffer; do { unsigned int bc; if (ob_ide_wait_stat(drive, 0, BUSY_STAT | ERR_STAT, &stat)) { ob_ide_error(drive, stat, "busy not clear after cdb"); cmd->stat = stat; break; } /* * transfer complete! */ if ((stat & (BUSY_STAT | DRQ_STAT)) == 0) break; if ((stat & (BUSY_STAT | DRQ_STAT)) != DRQ_STAT) break; reason = ob_ide_pio_readb(drive, IDEREG_NSECTOR); lcyl = ob_ide_pio_readb(drive, IDEREG_LCYL); hcyl = ob_ide_pio_readb(drive, IDEREG_HCYL); /* * check if the drive wants to transfer data in the same * direction as we do... */ if ((reason & IREASON_CD) && cmd->data_direction != atapi_ddir_read) { ob_ide_error(drive, stat, "atapi, bad transfer ddir"); break; } bc = (hcyl << 8) | lcyl; if (!bc) break; if (bc > bytes) bc = bytes; if (cmd->data_direction == atapi_ddir_read) ob_ide_pio_insw(drive, IDEREG_DATA, buffer, bc); else ob_ide_pio_outsw(drive, IDEREG_DATA, buffer, bc); bytes -= bc; buffer += bc; ob_ide_400ns_delay(drive); } while (bytes); if (cmd->data_direction != atapi_ddir_none) (void) ob_ide_wait_stat(drive, 0, BUSY_STAT, &stat); if (bytes) IDE_DPRINTF("cdb failed, bytes=%d, stat=%x\n", bytes, stat); return (stat & ERR_STAT) || bytes; } /* * execute a packet command, with retries if appropriate */ static int ob_ide_atapi_packet(struct ide_drive *drive, struct atapi_command *cmd) { int retries = 5, ret; if (drive->type != ide_type_atapi) return 1; if (cmd->buflen > 0xffff) return 1; /* * retry loop */ do { ret = ob_ide_pio_packet(drive, cmd); if (!ret) break; /* * request sense failed, bummer */ if (cmd->cdb[0] == ATAPI_REQ_SENSE) break; if (ob_ide_atapi_request_sense(drive)) break; /* * we know sense is valid. retry if the drive isn't ready, * otherwise don't bother. */ if (cmd->sense.sense_key != ATAPI_SENSE_NOT_READY) break; /* * ... except 'medium not present' */ if (cmd->sense.asc == 0x3a) break; udelay(1000000); } while (retries--); if (ret) ob_ide_error(drive, 0, "atapi command"); return ret; } static int ob_ide_atapi_request_sense(struct ide_drive *drive) { struct atapi_command *cmd = &drive->channel->atapi_cmd; unsigned char old_cdb; /* * save old cdb for debug error */ old_cdb = cmd->cdb[0]; memset(cmd, 0, sizeof(*cmd)); cmd->cdb[0] = ATAPI_REQ_SENSE; cmd->cdb[4] = 18; cmd->buffer = (unsigned char *) &cmd->sense; cmd->buflen = 18; cmd->data_direction = atapi_ddir_read; cmd->old_cdb = old_cdb; if (ob_ide_atapi_packet(drive, cmd)) return 1; cmd->sense_valid = 1; return 0; } /* * make sure drive is ready and media loaded */ static int ob_ide_atapi_drive_ready(struct ide_drive *drive) { struct atapi_command *cmd = &drive->channel->atapi_cmd; struct atapi_capacity cap; IDE_DPRINTF("ob_ide_atapi_drive_ready\n"); /* * Test Unit Ready is like a ping */ memset(cmd, 0, sizeof(*cmd)); cmd->cdb[0] = ATAPI_TUR; if (ob_ide_atapi_packet(drive, cmd)) { IDE_DPRINTF("%d: TUR failed\n", drive->nr); return 1; } /* * don't force load of tray (bit 2 in byte 4 of cdb), it's * annoying and we don't want to deal with errors from drives * that cannot do it */ memset(cmd, 0, sizeof(*cmd)); cmd->cdb[0] = ATAPI_START_STOP_UNIT; cmd->cdb[4] = 0x01; if (ob_ide_atapi_packet(drive, cmd)) { IDE_DPRINTF("%d: START_STOP unit failed\n", drive->nr); return 1; } /* * finally, get capacity and block size */ memset(cmd, 0, sizeof(*cmd)); memset(&cap, 0, sizeof(cap)); cmd->cdb[0] = ATAPI_READ_CAPACITY; cmd->buffer = (unsigned char *) ∩ cmd->buflen = sizeof(cap); cmd->data_direction = atapi_ddir_read; if (ob_ide_atapi_packet(drive, cmd)) { drive->sectors = 0x1fffff; drive->bs = 2048; return 1; } drive->sectors = __be32_to_cpu(cap.lba) + 1; drive->bs = __be32_to_cpu(cap.block_size); return 0; } /* * read from an atapi device, using READ_10 */ static int ob_ide_read_atapi(struct ide_drive *drive, unsigned long long block, unsigned char *buf, unsigned int sectors) { struct atapi_command *cmd = &drive->channel->atapi_cmd; if (ob_ide_atapi_drive_ready(drive)) return 1; memset(cmd, 0, sizeof(*cmd)); /* * READ_10 should work on generally any atapi device */ cmd->cdb[0] = ATAPI_READ_10; cmd->cdb[2] = (block >> 24) & 0xff; cmd->cdb[3] = (block >> 16) & 0xff; cmd->cdb[4] = (block >> 8) & 0xff; cmd->cdb[5] = block & 0xff; cmd->cdb[7] = (sectors >> 8) & 0xff; cmd->cdb[8] = sectors & 0xff; cmd->buffer = buf; cmd->buflen = sectors * 2048; cmd->data_direction = atapi_ddir_read; return ob_ide_atapi_packet(drive, cmd); } static int ob_ide_read_ata_chs(struct ide_drive *drive, unsigned long long block, unsigned char *buf, unsigned int sectors) { struct ata_command *cmd = &drive->channel->ata_cmd; unsigned int track = (block / drive->sect); unsigned int sect = (block % drive->sect) + 1; unsigned int head = (track % drive->head); unsigned int cyl = (track / drive->head); /* * fill in chs command to read from disk at given location */ cmd->buffer = buf; cmd->buflen = sectors * 512; cmd->nsector = sectors & 0xff; cmd->sector = sect; cmd->lcyl = cyl; cmd->hcyl = cyl >> 8; cmd->device_head = head; cmd->command = WIN_READ; return ob_ide_pio_data_in(drive, cmd); } static int ob_ide_read_ata_lba28(struct ide_drive *drive, unsigned long long block, unsigned char *buf, unsigned int sectors) { struct ata_command *cmd = &drive->channel->ata_cmd; memset(cmd, 0, sizeof(*cmd)); /* * fill in 28-bit lba command to read from disk at given location */ cmd->buffer = buf; cmd->buflen = sectors * 512; cmd->nsector = sectors; cmd->sector = block; cmd->lcyl = block >>= 8; cmd->hcyl = block >>= 8; cmd->device_head = ((block >> 8) & 0x0f); cmd->device_head |= (1 << 6); cmd->command = WIN_READ; return ob_ide_pio_data_in(drive, cmd); } static int ob_ide_read_ata_lba48(struct ide_drive *drive, unsigned long long block, unsigned char *buf, unsigned int sectors) { struct ata_command *cmd = &drive->channel->ata_cmd; memset(cmd, 0, sizeof(*cmd)); cmd->buffer = buf; cmd->buflen = sectors * 512; /* * we are using tasklet addressing here */ cmd->task[2] = sectors; cmd->task[3] = sectors >> 8; cmd->task[4] = block; cmd->task[5] = block >> 8; cmd->task[6] = block >> 16; cmd->task[7] = block >> 24; cmd->task[8] = (u64) block >> 32; cmd->task[9] = (u64) block >> 40; cmd->command = WIN_READ_EXT; ob_ide_write_tasklet(drive, cmd); return ob_ide_pio_data_in(drive, cmd); } /* * read 'sectors' sectors from ata device */ static int ob_ide_read_ata(struct ide_drive *drive, unsigned long long block, unsigned char *buf, unsigned int sectors) { unsigned long long end_block = block + sectors; const int need_lba48 = (end_block > (1ULL << 28)) || (sectors > 255); if (end_block > drive->sectors) return 1; if (need_lba48 && drive->addressing != ide_lba48) return 1; /* * use lba48 if we have to, otherwise use the faster lba28 */ if (need_lba48) return ob_ide_read_ata_lba48(drive, block, buf, sectors); else if (drive->addressing != ide_chs) return ob_ide_read_ata_lba28(drive, block, buf, sectors); return ob_ide_read_ata_chs(drive, block, buf, sectors); } static int ob_ide_read_sectors(struct ide_drive *drive, unsigned long long block, unsigned char *buf, unsigned int sectors) { if (!sectors) return 1; if (block + sectors > drive->sectors) return 1; IDE_DPRINTF("ob_ide_read_sectors: block=%lu sectors=%u\n", (unsigned long) block, sectors); if (drive->type == ide_type_ata) return ob_ide_read_ata(drive, block, buf, sectors); else return ob_ide_read_atapi(drive, block, buf, sectors); } /* * byte swap the string if necessay, and strip leading/trailing blanks */ static void ob_ide_fixup_string(unsigned char *s, unsigned int len) { unsigned char *p = s, *end = &s[len & ~1]; /* * if big endian arch, byte swap the string */ #ifdef CONFIG_BIG_ENDIAN for (p = end ; p != s;) { unsigned short *pp = (unsigned short *) (p -= 2); *pp = __le16_to_cpu(*pp); } #endif while (s != end && *s == ' ') ++s; while (s != end && *s) if (*s++ != ' ' || (s != end && *s && *s != ' ')) *p++ = *(s-1); while (p != end) *p++ = '\0'; } /* * it's big endian, we need to swap (if on little endian) the items we use */ static int ob_ide_fixup_id(struct hd_driveid *id) { ob_ide_fixup_string(id->model, 40); id->config = __le16_to_cpu(id->config); id->lba_capacity = __le32_to_cpu(id->lba_capacity); id->cyls = __le16_to_cpu(id->cyls); id->heads = __le16_to_cpu(id->heads); id->sectors = __le16_to_cpu(id->sectors); id->command_set_2 = __le16_to_cpu(id->command_set_2); id->cfs_enable_2 = __le16_to_cpu(id->cfs_enable_2); return 0; } static int ob_ide_identify_drive(struct ide_drive *drive) { struct ata_command *cmd = &drive->channel->ata_cmd; struct hd_driveid id; memset(cmd, 0, sizeof(*cmd)); cmd->buffer = (unsigned char *) &id; cmd->buflen = 512; if (drive->type == ide_type_ata) cmd->command = WIN_IDENTIFY; else if (drive->type == ide_type_atapi) cmd->command = WIN_IDENTIFY_PACKET; else { IDE_DPRINTF("%s: called with bad device type %d\n", __FUNCTION__, drive->type); return 1; } if (ob_ide_pio_data_in(drive, cmd)) return 1; ob_ide_fixup_id(&id); if (drive->type == ide_type_atapi) { drive->media = (id.config >> 8) & 0x1f; drive->sectors = 0x7fffffff; drive->bs = 2048; drive->max_sectors = 31; } else { drive->media = ide_media_disk; drive->sectors = id.lba_capacity; drive->bs = 512; drive->max_sectors = 255; #ifdef CONFIG_IDE_LBA48 if ((id.command_set_2 & 0x0400) && (id.cfs_enable_2 & 0x0400)) { drive->addressing = ide_lba48; drive->max_sectors = 65535; } else #endif if (id.capability & 2) drive->addressing = ide_lba28; else { drive->addressing = ide_chs; } /* only set these in chs mode? */ drive->cyl = id.cyls; drive->head = id.heads; drive->sect = id.sectors; } strncpy(drive->model, (char*)id.model, sizeof(id.model)); drive->model[40] = '\0'; return 0; } /* * identify type of devices on channel. must have already been probed. */ static void ob_ide_identify_drives(struct ide_channel *chan) { struct ide_drive *drive; int i; for (i = 0; i < 2; i++) { drive = &chan->drives[i]; if (!drive->present) continue; ob_ide_identify_drive(drive); } } /* * software reset (ATA-4, section 8.3) */ static void ob_ide_software_reset(struct ide_drive *drive) { struct ide_channel *chan = drive->channel; ob_ide_pio_writeb(drive, IDEREG_CONTROL, IDECON_NIEN | IDECON_SRST); ob_ide_400ns_delay(drive); ob_ide_pio_writeb(drive, IDEREG_CONTROL, IDECON_NIEN); ob_ide_400ns_delay(drive); /* * if master is present, wait for BUSY clear */ if (chan->drives[0].present) ob_ide_wait_stat(drive, 0, BUSY_STAT, NULL); /* * if slave is present, wait until it allows register access */ if (chan->drives[1].present) { unsigned char sectorn, sectorc; int timeout = 1000; do { /* * select it */ ob_ide_pio_writeb(drive, IDEREG_CURRENT, IDEHEAD_DEV1); ob_ide_400ns_delay(drive); sectorn = ob_ide_pio_readb(drive, IDEREG_SECTOR); sectorc = ob_ide_pio_readb(drive, IDEREG_NSECTOR); if (sectorc == 0x01 && sectorn == 0x01) break; } while (--timeout); } /* * reset done, reselect original device */ drive->channel->selected = -1; ob_ide_select_drive(drive); } /* * this serves as both a device check, and also to verify that the drives * we initially "found" are really there */ static void ob_ide_device_type_check(struct ide_drive *drive) { unsigned char sc, sn, cl, ch, st; if (ob_ide_select_drive(drive)) return; sc = ob_ide_pio_readb(drive, IDEREG_NSECTOR); sn = ob_ide_pio_readb(drive, IDEREG_SECTOR); if (sc == 0x01 && sn == 0x01) { /* * read device signature */ cl = ob_ide_pio_readb(drive, IDEREG_LCYL); ch = ob_ide_pio_readb(drive, IDEREG_HCYL); st = ob_ide_pio_readb(drive, IDEREG_STATUS); if (cl == 0x14 && ch == 0xeb) drive->type = ide_type_atapi; else if (cl == 0x00 && ch == 0x00 && st != 0x00) drive->type = ide_type_ata; else drive->present = 0; } else drive->present = 0; } /* * pure magic */ static void ob_ide_device_check(struct ide_drive *drive) { unsigned char sc, sn; /* * non-existing io port should return 0xff, don't probe this * channel at all then */ if (ob_ide_pio_readb(drive, IDEREG_STATUS) == 0xff) { drive->channel->present = 0; return; } if (ob_ide_select_drive(drive)) return; ob_ide_pio_writeb(drive, IDEREG_NSECTOR, 0x55); ob_ide_pio_writeb(drive, IDEREG_SECTOR, 0xaa); ob_ide_pio_writeb(drive, IDEREG_NSECTOR, 0xaa); ob_ide_pio_writeb(drive, IDEREG_SECTOR, 0x55); ob_ide_pio_writeb(drive, IDEREG_NSECTOR, 0x55); ob_ide_pio_writeb(drive, IDEREG_SECTOR, 0xaa); sc = ob_ide_pio_readb(drive, IDEREG_NSECTOR); sn = ob_ide_pio_readb(drive, IDEREG_SECTOR); /* * we _think_ the device is there, we will make sure later */ if (sc == 0x55 && sn == 0xaa) { drive->present = 1; drive->type = ide_type_unknown; } } /* * probe the legacy ide ports and find attached devices. */ static void ob_ide_probe(struct ide_channel *chan) { struct ide_drive *drive; int i; for (i = 0; i < 2; i++) { drive = &chan->drives[i]; ob_ide_device_check(drive); /* * no point in continuing */ if (!chan->present) break; if (!drive->present) continue; /* * select and reset device */ if (ob_ide_select_drive(drive)) continue; ob_ide_software_reset(drive); ob_ide_device_type_check(drive); } } /* * The following functions are interfacing with OpenBIOS. They * are device node methods. Thus they have to do proper stack handling. * */ /* * 255 sectors for ata lba28, 65535 for lba48, and 31 sectors for atapi */ static void ob_ide_max_transfer(int *idx) { struct ide_drive *drive = *(struct ide_drive **)idx; IDE_DPRINTF("max_transfer %x\n", drive->max_sectors * drive->bs); PUSH(drive->max_sectors * drive->bs); } static void ob_ide_read_blocks(int *idx) { cell n = POP(), cnt=n; ucell blk = POP(); unsigned char *dest = (unsigned char *)cell2pointer(POP()); struct ide_drive *drive = *(struct ide_drive **)idx; IDE_DPRINTF("ob_ide_read_blocks %lx block=%ld n=%ld\n", (unsigned long)dest, (unsigned long)blk, (long)n); while (n) { int len = n; if (len > drive->max_sectors) len = drive->max_sectors; if (ob_ide_read_sectors(drive, blk, dest, len)) { IDE_DPRINTF("ob_ide_read_blocks: error\n"); RET(0); } dest += len * drive->bs; n -= len; blk += len; } PUSH(cnt); } static void ob_ide_block_size(int *idx) { struct ide_drive *drive = *(struct ide_drive **)idx; IDE_DPRINTF("ob_ide_block_size: block size %x\n", drive->bs); PUSH(drive->bs); } static void ob_ide_initialize(int *idx) { int props[3]; phandle_t ph=get_cur_dev(); push_str("block"); fword("device-type"); // Set dummy reg properties set_int_property(ph, "#address-cells", 1); set_int_property(ph, "#size-cells", 0); props[0] = __cpu_to_be32(0); props[1] = __cpu_to_be32(0); props[2] = __cpu_to_be32(0); set_property(ph, "reg", (char *)&props, 3*sizeof(int)); fword("is-deblocker"); } static void ob_ide_open(int *idx) { int ret=1; phandle_t ph; struct ide_drive *drive; struct ide_channel *chan; int unit; fword("my-unit"); unit = POP(); fword("my-parent"); fword("ihandle>phandle"); ph=(phandle_t)POP(); chan = ide_seek_channel(ph); drive = &chan->drives[unit]; *(struct ide_drive **)idx = drive; IDE_DPRINTF("opening channel %d unit %d\n", idx[1], idx[0]); dump_drive(drive); if (drive->type != ide_type_ata) ret= !ob_ide_atapi_drive_ready(drive); selfword("open-deblocker"); /* interpose disk-label */ ph = find_dev("/packages/disk-label"); fword("my-args"); PUSH_ph( ph ); fword("interpose"); RET ( -ret ); } static void ob_ide_close(struct ide_drive *drive) { selfword("close-deblocker"); } NODE_METHODS(ob_ide) = { { NULL, ob_ide_initialize }, { "open", ob_ide_open }, { "close", ob_ide_close }, { "read-blocks", ob_ide_read_blocks }, { "block-size", ob_ide_block_size }, { "max-transfer", ob_ide_max_transfer }, }; static void ob_ide_ctrl_initialize(int *idx) { phandle_t ph=get_cur_dev(); /* set device type */ push_str(DEV_TYPE); fword("device-type"); set_int_property(ph, "#address-cells", 1); set_int_property(ph, "#size-cells", 0); } static void ob_ide_ctrl_decodeunit(int *idx) { fword("parse-hex"); } NODE_METHODS(ob_ide_ctrl) = { { NULL, ob_ide_ctrl_initialize }, { "decode-unit", ob_ide_ctrl_decodeunit }, }; static void set_cd_alias(const char *path) { phandle_t aliases; aliases = find_dev("/aliases"); if (get_property(aliases, "cd", NULL)) return; set_property(aliases, "cd", path, strlen(path) + 1); set_property(aliases, "cdrom", path, strlen(path) + 1); } static void set_hd_alias(const char *path) { phandle_t aliases; aliases = find_dev("/aliases"); if (get_property(aliases, "hd", NULL)) return; set_property(aliases, "hd", path, strlen(path) + 1); set_property(aliases, "disk", path, strlen(path) + 1); } static void set_ide_alias(const char *path) { phandle_t aliases; static int ide_counter = 0; char idestr[8]; aliases = find_dev("/aliases"); snprintf(idestr, sizeof(idestr), "ide%d", ide_counter++); set_property(aliases, idestr, path, strlen(path) + 1); } int ob_ide_init(const char *path, uint32_t io_port0, uint32_t ctl_port0, uint32_t io_port1, uint32_t ctl_port1) { int i, j; char nodebuff[128]; phandle_t dnode; struct ide_channel *chan; int io_ports[IDE_MAX_CHANNELS]; int ctl_ports[IDE_MAX_CHANNELS]; u32 props[6]; io_ports[0] = io_port0; ctl_ports[0] = ctl_port0; io_ports[1] = io_port1; ctl_ports[1] = ctl_port1; for (i = 0; i < IDE_NUM_CHANNELS; i++, current_channel++) { chan = malloc(sizeof(struct ide_channel)); chan->mmio = 0; for (j = 0; j < 8; j++) chan->io_regs[j] = io_ports[i] + j; chan->io_regs[8] = ctl_ports[i]; chan->io_regs[9] = ctl_ports[i] + 1; chan->obide_inb = ob_ide_inb; chan->obide_insw = ob_ide_insw; chan->obide_outb = ob_ide_outb; chan->obide_outsw = ob_ide_outsw; chan->selected = -1; /* * assume it's there, if not io port dead check will clear */ chan->present = 1; for (j = 0; j < 2; j++) { chan->drives[j].present = 0; chan->drives[j].unit = j; chan->drives[j].channel = chan; /* init with a decent value */ chan->drives[j].bs = 512; chan->drives[j].nr = i * 2 + j; } ide_add_channel(chan); ob_ide_probe(chan); if (!chan->present) continue; ob_ide_identify_drives(chan); snprintf(nodebuff, sizeof(nodebuff), "%s/" DEV_NAME, path, current_channel); REGISTER_NAMED_NODE_PHANDLE(ob_ide_ctrl, nodebuff, dnode); chan->ph = dnode; #if !defined(CONFIG_PPC) && !defined(CONFIG_SPARC64) props[0]=14; props[1]=0; set_property(dnode, "interrupts", (char *)&props, 2*sizeof(props[0])); #endif props[0] = __cpu_to_be32(chan->io_regs[0]); props[1] = __cpu_to_be32(1); props[2] = __cpu_to_be32(8); props[3] = __cpu_to_be32(chan->io_regs[8]); props[4] = __cpu_to_be32(1); props[5] = __cpu_to_be32(2); set_property(dnode, "reg", (char *)&props, 6*sizeof(props[0])); IDE_DPRINTF(DEV_NAME": [io ports 0x%x-0x%x,0x%x]\n", current_channel, chan->io_regs[0], chan->io_regs[0] + 7, chan->io_regs[8]); for (j = 0; j < 2; j++) { struct ide_drive *drive = &chan->drives[j]; const char *media = "UNKNOWN"; if (!drive->present) continue; IDE_DPRINTF(" drive%d [ATA%s ", j, drive->type == ide_type_atapi ? "PI" : ""); switch (drive->media) { case ide_media_floppy: media = "floppy"; break; case ide_media_cdrom: media = "cdrom"; break; case ide_media_optical: media = "mo"; break; case ide_media_disk: media = "disk"; break; } IDE_DPRINTF("%s]: %s\n", media, drive->model); snprintf(nodebuff, sizeof(nodebuff), "%s/%s", get_path_from_ph(dnode), media); REGISTER_NAMED_NODE_PHANDLE(ob_ide, nodebuff, dnode); set_int_property(dnode, "reg", j); /* create aliases */ set_ide_alias(nodebuff); if (drive->media == ide_media_cdrom) set_cd_alias(nodebuff); if (drive->media == ide_media_disk) set_hd_alias(nodebuff); } } return 0; } void ob_ide_quiesce(void) { struct ide_channel *channel; int i; channel = channels; while (channel) { for (i = 0; i < 2; i++) { struct ide_drive *drive = &channel->drives[i]; if (!drive->present) continue; ob_ide_select_drive(drive); ob_ide_software_reset(drive); ob_ide_device_type_check(drive); } channel = channel->next; } } #if defined(CONFIG_DRIVER_MACIO) static unsigned char macio_ide_inb(struct ide_channel *chan, unsigned int port) { return in_8((unsigned char*)(chan->mmio + (port << 4))); } static void macio_ide_outb(struct ide_channel *chan, unsigned char data, unsigned int port) { out_8((unsigned char*)(chan->mmio + (port << 4)), data); } static void macio_ide_insw(struct ide_channel *chan, unsigned int port, unsigned char *addr, unsigned int count) { _insw((uint16_t*)(chan->mmio + (port << 4)), addr, count); } static void macio_ide_outsw(struct ide_channel *chan, unsigned int port, unsigned char *addr, unsigned int count) { _outsw((uint16_t*)(chan->mmio + (port << 4)), addr, count); } #define MACIO_IDE_OFFSET 0x00020000 #define MACIO_IDE_SIZE 0x00001000 int macio_ide_init(const char *path, uint32_t addr, int nb_channels) { int i, j; char nodebuff[128]; phandle_t dnode; u32 props[8]; struct ide_channel *chan; /* IDE ports on Macs are numbered from 3. * Also see comments in pci.c:ob_pci_host_set_interrupt_map() */ current_channel = 3; for (i = 0; i < nb_channels; i++) { chan = malloc(sizeof(struct ide_channel)); chan->mmio = addr + MACIO_IDE_OFFSET + i * MACIO_IDE_SIZE; chan->obide_inb = macio_ide_inb; chan->obide_insw = macio_ide_insw; chan->obide_outb = macio_ide_outb; chan->obide_outsw = macio_ide_outsw; chan->selected = -1; /* * assume it's there, if not io port dead check will clear */ chan->present = 1; for (j = 0; j < 2; j++) { chan->drives[j].present = 0; chan->drives[j].unit = j; chan->drives[j].channel = chan; /* init with a decent value */ chan->drives[j].bs = 512; chan->drives[j].nr = i * 2 + j; } ob_ide_probe(chan); if (!chan->present) { free(chan); continue; } ide_add_channel(chan); ob_ide_identify_drives(chan); snprintf(nodebuff, sizeof(nodebuff), "%s/" DEV_NAME, path, current_channel); REGISTER_NAMED_NODE_PHANDLE(ob_ide_ctrl, nodebuff, dnode); chan->ph = dnode; set_property(dnode, "compatible", (is_oldworld() ? "heathrow-ata" : "keylargo-ata"), 13); set_property(dnode, "model", ((current_channel == 3) ? "ata-3" : "ata-4"), strlen("ata-*") + 1); set_property(dnode, "AAPL,connector", "ata", strlen("ata") + 1); props[0] = 0x00000526; props[1] = 0x00000085; props[2] = 0x00000025; props[3] = 0x00000025; props[4] = 0x00000025; props[5] = 0x00000000; props[6] = 0x00000000; props[7] = 0x00000000; set_property(dnode, "AAPL,pio-timing", (char *)&props, 8*sizeof(props[0])); /* The first interrupt entry is the ide interrupt, the second the dbdma interrupt */ switch (i) { case 0: props[0] = 0x0000000d; props[2] = 0x00000002; break; case 1: props[0] = 0x0000000e; props[2] = 0x00000003; break; case 2: props[0] = 0x0000000f; props[2] = 0x00000004; break; default: props[0] = 0x00000000; props[2] = 0x00000000; break; } props[1] = 0x00000000; /* XXX level triggered on real hw */ props[3] = 0x00000000; NEWWORLD(set_property(dnode, "interrupts", (char *)&props, 4*sizeof(props[0]))); NEWWORLD(set_int_property(dnode, "#interrupt-cells", 2)); props[1] = props[2]; OLDWORLD(set_property(dnode, "AAPL,interrupts", (char *)&props, 2*sizeof(props[0]))); props[0] = MACIO_IDE_OFFSET + i * MACIO_IDE_SIZE; props[1] = MACIO_IDE_SIZE; props[2] = 0x00008b00 + i * 0x0200; props[3] = 0x0200; set_property(dnode, "reg", (char *)&props, 4*sizeof(props[0])); props[0] = addr + MACIO_IDE_OFFSET + i * MACIO_IDE_SIZE; props[1] = addr + 0x00008b00 + i * 0x0200; OLDWORLD(set_property(dnode, "AAPL,address", (char *)&props, 2*sizeof(props[0]))); props[0] = i; set_property(dnode, "AAPL,bus-id", (char*)props, 1 * sizeof(props[0])); IDE_DPRINTF(DEV_NAME": [io ports 0x%lx]\n", current_channel, chan->mmio); for (j = 0; j < 2; j++) { struct ide_drive *drive = &chan->drives[j]; const char *media = "UNKNOWN"; if (!drive->present) continue; IDE_DPRINTF(" drive%d [ATA%s ", j, drive->type == ide_type_atapi ? "PI" : ""); switch (drive->media) { case ide_media_floppy: media = "floppy"; break; case ide_media_cdrom: media = "cdrom"; break; case ide_media_optical: media = "mo"; break; case ide_media_disk: media = "disk"; break; } IDE_DPRINTF("%s]: %s\n", media, drive->model); snprintf(nodebuff, sizeof(nodebuff), "%s/%s", get_path_from_ph(dnode), media); REGISTER_NAMED_NODE_PHANDLE(ob_ide, nodebuff, dnode); set_int_property(dnode, "reg", j); /* create aliases */ set_ide_alias(nodebuff); if (drive->media == ide_media_cdrom) set_cd_alias(nodebuff); if (drive->media == ide_media_disk) set_hd_alias(nodebuff); } } return 0; } #endif /* CONFIG_DRIVER_MACIO */