Files
openbios/drivers/ide.c
Alexander Graf b2036c0313 PPC: Fix interrupt numbers
We changed several interrupt numbers in Qemu to better reflect real
world hardware. Also, since we're now using proper interrupt maps, we
need to make sure we specify interrupts in the way the respective map
requires it.

Signed-off-by: Alexander Graf <agraf@suse.de>

git-svn-id: svn://coreboot.org/openbios/trunk/openbios-devel@680 f158a5a8-5612-0410-a976-696ce0be7e32
2010-02-22 18:52:28 +00:00

1672 lines
37 KiB
C

/*
* OpenBIOS polled ide driver
*
* Copyright (C) 2004 Jens Axboe <axboe@suse.de>
* Copyright (C) 2005 Stefan Reinauer <stepan@openbios.org>
*
* 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 "openbios/config.h"
#include "openbios/bindings.h"
#include "openbios/kernel.h"
#include "libc/byteorder.h"
#include "libc/vsprintf.h"
#include "openbios/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(const char *name)
{
struct ide_channel *current;
current = channels;
while (current) {
if (!strcmp(current->name, name))
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)
{
struct ide_channel *chan = drive->channel;
unsigned char err;
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) {
err = ob_ide_pio_readb(drive, IDEREG_ERROR);
IDE_DPRINTF(", err=%x", err);
}
IDE_DPRINTF("\n");
/*
* 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");
}
}
/*
* 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 *) &cap;
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 *)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, len;
phandle_t ph;
struct ide_drive *drive;
struct ide_channel *chan;
char *idename;
int unit;
fword("my-unit");
unit = POP();
fword("my-parent");
fword("ihandle>phandle");
ph=(phandle_t)POP();
idename=get_property(ph, "name", &len);
chan = ide_seek_channel(idename);
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];
cell props[6];
io_ports[0] = io_port0;
ctl_ports[0] = ctl_port0 + 2;
io_ports[1] = io_port1;
ctl_ports[1] = ctl_port1 + 2;
for (i = 0; i < IDE_NUM_CHANNELS; i++, current_channel++) {
chan = malloc(sizeof(struct ide_channel));
snprintf(chan->name, sizeof(chan->name),
DEV_NAME, current_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(ob_ide_ctrl, nodebuff);
dnode = find_dev(nodebuff);
#ifndef CONFIG_PPC
props[0]=14; props[1]=0;
set_property(dnode, "interrupts",
(char *)&props, 2*sizeof(cell));
#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(cell));
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/" DEV_NAME "/%s", path, current_channel,
media);
REGISTER_NAMED_NODE(ob_ide, nodebuff);
dnode=find_dev(nodebuff);
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;
}
#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;
cell props[8];
struct ide_channel *chan;
for (i = 0; i < nb_channels; i++, current_channel++) {
chan = malloc(sizeof(struct ide_channel));
snprintf(chan->name, sizeof(chan->name),
DEV_NAME, current_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(ob_ide_ctrl, nodebuff);
dnode = find_dev(nodebuff);
set_property(dnode, "compatible", "heathrow-ata", 13);
props[0] = 0x00000526;
props[1] = 0x00000085;
props[2] = 0x00000025;
props[3] = 0x00000025;
props[4] = 0x00000025;
props[5] = 0x00000000;
props[6] = 0x00000000;
props[7] = 0x00000000;
OLDWORLD(set_property(dnode, "AAPL,pio-timing",
(char *)&props, 8*sizeof(cell)));
/* The first interrupt entry is the ide interrupt, the second
the dbdma interrupt */
switch (current_channel) {
case 1:
props[0] = 0x0000000d;
props[2] = 0x00000002;
break;
case 2:
props[0] = 0x0000000e;
props[2] = 0x00000003;
break;
case 3:
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;
set_property(dnode, "interrupts",
(char *)&props, 4*sizeof(cell));
set_int_property(dnode, "#interrupt-cells", 2);
OLDWORLD(set_property(dnode, "AAPL,interrupts",
(char *)&props, 2*sizeof(cell)));
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(cell));
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(cell)));
props[0] = 0;
OLDWORLD(set_property(dnode, "AAPL,bus-id", (char*)props,
1 * sizeof(cell)));
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/" DEV_NAME "/%s", path, current_channel,
media);
REGISTER_NAMED_NODE(ob_ide, nodebuff);
dnode = find_dev(nodebuff);
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 */