Author: stepan Date: 2009-03-11 04:07:08 +0100 (Wed, 11 Mar 2009) New Revision: 86
Added: trunk/filo/drivers/hdreg.h trunk/filo/drivers/ide_new.c trunk/filo/drivers/ide_new.h Modified: trunk/filo/Config.in trunk/filo/Makefile trunk/filo/drivers/Makefile.inc trunk/filo/drivers/ide.c trunk/filo/drivers/intel.c trunk/filo/fs/blockdev.c trunk/filo/include/fs.h trunk/filo/main/Makefile.inc trunk/filo/main/grub/builtins.c Log: * move openbios IDE driver over to filo. It works a lot more reliable than the old one. (Lacks PCI BAR reading though) * some helper commands * push version to 0.6.0 final
Modified: trunk/filo/Config.in =================================================================== --- trunk/filo/Config.in 2008-12-20 11:23:33 UTC (rev 85) +++ trunk/filo/Config.in 2009-03-11 03:07:08 UTC (rev 86) @@ -118,15 +118,23 @@ Use PCMCIA compact flash on Via Epia MII10000 and MII6000E This device is referred to as hde.
+config IDE_NEW_DISK + bool "New IDE driver" + default n + depends on !IDE_DISK + help + Jens Axboe's fine IDE driver + config USB_NEW_DISK - bool "new USB Stack" + bool "USB Stack" default y help Driver for USB Storage
config USB_DISK - bool "USB Stack (obsolete?)" + bool "Old USB Stack (obsolete?)" default n + depends on !USB_NEW_DISK help Driver for USB Storage
Modified: trunk/filo/Makefile =================================================================== --- trunk/filo/Makefile 2008-12-20 11:23:33 UTC (rev 85) +++ trunk/filo/Makefile 2009-03-11 03:07:08 UTC (rev 86) @@ -17,7 +17,7 @@ #
export PROGRAM_NAME := FILO -export PROGRAM_VERSION := 0.6.0rc1 +export PROGRAM_VERSION := 0.6.0
export src := $(shell pwd) export srctree := $(src)
Modified: trunk/filo/drivers/Makefile.inc =================================================================== --- trunk/filo/drivers/Makefile.inc 2008-12-20 11:23:33 UTC (rev 85) +++ trunk/filo/drivers/Makefile.inc 2009-03-11 03:07:08 UTC (rev 86) @@ -17,6 +17,7 @@ #
TARGETS-$(CONFIG_IDE_DISK) += drivers/ide.o +TARGETS-$(CONFIG_IDE_NEW_DISK) += drivers/ide_new.o TARGETS-$(CONFIG_VIA_SOUND) += drivers/via-sound.o TARGETS-y += drivers/intel.o
Added: trunk/filo/drivers/hdreg.h =================================================================== --- trunk/filo/drivers/hdreg.h (rev 0) +++ trunk/filo/drivers/hdreg.h 2009-03-11 03:07:08 UTC (rev 86) @@ -0,0 +1,297 @@ +/* + * this header holds data structures as dictated by spec + */ +#ifndef HDREG_H +#define HDREG_H +#include <arch/types.h> +//#include <misc.h> + +#define CONFIG_LITTLE_ENDIAN + +#define u8 uint8_t +#define u16 uint16_t +#define u32 uint32_t + +struct hd_driveid { + unsigned short config; /* lots of obsolete bit flags */ + unsigned short cyls; /* Obsolete, "physical" cyls */ + unsigned short reserved2; /* reserved (word 2) */ + unsigned short heads; /* Obsolete, "physical" heads */ + unsigned short track_bytes; /* unformatted bytes per track */ + unsigned short sector_bytes; /* unformatted bytes per sector */ + unsigned short sectors; /* Obsolete, "physical" sectors per track */ + unsigned short vendor0; /* vendor unique */ + unsigned short vendor1; /* vendor unique */ + unsigned short vendor2; /* Retired vendor unique */ + unsigned char serial_no[20]; /* 0 = not_specified */ + unsigned short buf_type; /* Retired */ + unsigned short buf_size; /* Retired, 512 byte increments + * 0 = not_specified + */ + unsigned short ecc_bytes; /* for r/w long cmds; 0 = not_specified */ + unsigned char fw_rev[8]; /* 0 = not_specified */ + unsigned char model[40]; /* 0 = not_specified */ + unsigned char max_multsect; /* 0=not_implemented */ + unsigned char vendor3; /* vendor unique */ + unsigned short dword_io; /* 0=not_implemented; 1=implemented */ + unsigned char vendor4; /* vendor unique */ + unsigned char capability; /* (upper byte of word 49) + * 3: IORDYsup + * 2: IORDYsw + * 1: LBA + * 0: DMA + */ + unsigned short reserved50; /* reserved (word 50) */ + unsigned char vendor5; /* Obsolete, vendor unique */ + unsigned char tPIO; /* Obsolete, 0=slow, 1=medium, 2=fast */ + unsigned char vendor6; /* Obsolete, vendor unique */ + unsigned char tDMA; /* Obsolete, 0=slow, 1=medium, 2=fast */ + unsigned short field_valid; /* (word 53) + * 2: ultra_ok word 88 + * 1: eide_ok words 64-70 + * 0: cur_ok words 54-58 + */ + unsigned short cur_cyls; /* Obsolete, logical cylinders */ + unsigned short cur_heads; /* Obsolete, l heads */ + unsigned short cur_sectors; /* Obsolete, l sectors per track */ + unsigned short cur_capacity0; /* Obsolete, l total sectors on drive */ + unsigned short cur_capacity1; /* Obsolete, (2 words, misaligned int) */ + unsigned char multsect; /* current multiple sector count */ + unsigned char multsect_valid; /* when (bit0==1) multsect is ok */ + unsigned int lba_capacity; /* Obsolete, total number of sectors */ + unsigned short dma_1word; /* Obsolete, single-word dma info */ + unsigned short dma_mword; /* multiple-word dma info */ + unsigned short eide_pio_modes; /* bits 0:mode3 1:mode4 */ + unsigned short eide_dma_min; /* min mword dma cycle time (ns) */ + unsigned short eide_dma_time; /* recommended mword dma cycle time (ns) */ + unsigned short eide_pio; /* min cycle time (ns), no IORDY */ + unsigned short eide_pio_iordy; /* min cycle time (ns), with IORDY */ + unsigned short words69_70[2]; /* reserved words 69-70 + * future command overlap and queuing + */ + /* HDIO_GET_IDENTITY currently returns only words 0 through 70 */ + unsigned short words71_74[4]; /* reserved words 71-74 + * for IDENTIFY PACKET DEVICE command + */ + unsigned short queue_depth; /* (word 75) + * 15:5 reserved + * 4:0 Maximum queue depth -1 + */ + unsigned short words76_79[4]; /* reserved words 76-79 */ + unsigned short major_rev_num; /* (word 80) */ + unsigned short minor_rev_num; /* (word 81) */ + unsigned short command_set_1; /* (word 82) supported + * 15: Obsolete + * 14: NOP command + * 13: READ_BUFFER + * 12: WRITE_BUFFER + * 11: Obsolete + * 10: Host Protected Area + * 9: DEVICE Reset + * 8: SERVICE Interrupt + * 7: Release Interrupt + * 6: look-ahead + * 5: write cache + * 4: PACKET Command + * 3: Power Management Feature Set + * 2: Removable Feature Set + * 1: Security Feature Set + * 0: SMART Feature Set + */ + unsigned short command_set_2; /* (word 83) + * 15: Shall be ZERO + * 14: Shall be ONE + * 13: FLUSH CACHE EXT + * 12: FLUSH CACHE + * 11: Device Configuration Overlay + * 10: 48-bit Address Feature Set + * 9: Automatic Acoustic Management + * 8: SET MAX security + * 7: reserved 1407DT PARTIES + * 6: SetF sub-command Power-Up + * 5: Power-Up in Standby Feature Set + * 4: Removable Media Notification + * 3: APM Feature Set + * 2: CFA Feature Set + * 1: READ/WRITE DMA QUEUED + * 0: Download MicroCode + */ + unsigned short cfsse; /* (word 84) + * cmd set-feature supported extensions + * 15: Shall be ZERO + * 14: Shall be ONE + * 13:6 reserved + * 5: General Purpose Logging + * 4: Streaming Feature Set + * 3: Media Card Pass Through + * 2: Media Serial Number Valid + * 1: SMART selt-test supported + * 0: SMART error logging + */ + unsigned short cfs_enable_1; /* (word 85) + * command set-feature enabled + * 15: Obsolete + * 14: NOP command + * 13: READ_BUFFER + * 12: WRITE_BUFFER + * 11: Obsolete + * 10: Host Protected Area + * 9: DEVICE Reset + * 8: SERVICE Interrupt + * 7: Release Interrupt + * 6: look-ahead + * 5: write cache + * 4: PACKET Command + * 3: Power Management Feature Set + * 2: Removable Feature Set + * 1: Security Feature Set + * 0: SMART Feature Set + */ + unsigned short cfs_enable_2; /* (word 86) + * command set-feature enabled + * 15: Shall be ZERO + * 14: Shall be ONE + * 13: FLUSH CACHE EXT + * 12: FLUSH CACHE + * 11: Device Configuration Overlay + * 10: 48-bit Address Feature Set + * 9: Automatic Acoustic Management + * 8: SET MAX security + * 7: reserved 1407DT PARTIES + * 6: SetF sub-command Power-Up + * 5: Power-Up in Standby Feature Set + * 4: Removable Media Notification + * 3: APM Feature Set + * 2: CFA Feature Set + * 1: READ/WRITE DMA QUEUED + * 0: Download MicroCode + */ + unsigned short csf_default; /* (word 87) + * command set-feature default + * 15: Shall be ZERO + * 14: Shall be ONE + * 13:6 reserved + * 5: General Purpose Logging enabled + * 4: Valid CONFIGURE STREAM executed + * 3: Media Card Pass Through enabled + * 2: Media Serial Number Valid + * 1: SMART selt-test supported + * 0: SMART error logging + */ + unsigned short dma_ultra; /* (word 88) */ + unsigned short trseuc; /* time required for security erase */ + unsigned short trsEuc; /* time required for enhanced erase */ + unsigned short CurAPMvalues; /* current APM values */ + unsigned short mprc; /* master password revision code */ + unsigned short hw_config; /* hardware config (word 93) + * 15: Shall be ZERO + * 14: Shall be ONE + * 13: + * 12: + * 11: + * 10: + * 9: + * 8: + * 7: + * 6: + * 5: + * 4: + * 3: + * 2: + * 1: + * 0: Shall be ONE + */ + unsigned short acoustic; /* (word 94) + * 15:8 Vendor's recommended value + * 7:0 current value + */ + unsigned short msrqs; /* min stream request size */ + unsigned short sxfert; /* stream transfer time */ + unsigned short sal; /* stream access latency */ + unsigned int spg; /* stream performance granularity */ + unsigned long long lba_capacity_2;/* 48-bit total number of sectors */ + unsigned short words104_125[22];/* reserved words 104-125 */ + unsigned short last_lun; /* (word 126) */ + unsigned short word127; /* (word 127) Feature Set + * Removable Media Notification + * 15:2 reserved + * 1:0 00 = not supported + * 01 = supported + * 10 = reserved + * 11 = reserved + */ + unsigned short dlf; /* (word 128) + * device lock function + * 15:9 reserved + * 8 security level 1:max 0:high + * 7:6 reserved + * 5 enhanced erase + * 4 expire + * 3 frozen + * 2 locked + * 1 en/disabled + * 0 capability + */ + unsigned short csfo; /* (word 129) + * current set features options + * 15:4 reserved + * 3: auto reassign + * 2: reverting + * 1: read-look-ahead + * 0: write cache + */ + unsigned short words130_155[26];/* reserved vendor words 130-155 */ + unsigned short word156; /* reserved vendor word 156 */ + unsigned short words157_159[3];/* reserved vendor words 157-159 */ + unsigned short cfa_power; /* (word 160) CFA Power Mode + * 15 word 160 supported + * 14 reserved + * 13 + * 12 + * 11:0 + */ + unsigned short words161_175[15];/* Reserved for CFA */ + unsigned short words176_205[30];/* Current Media Serial Number */ + unsigned short words206_254[49];/* reserved words 206-254 */ + unsigned short integrity_word; /* (word 255) + * 15:8 Checksum + * 7:0 Signature + */ +}; + +struct request_sense { +#if defined(CONFIG_BIG_ENDIAN) + u8 valid : 1; + u8 error_code : 7; +#elif defined(CONFIG_LITTLE_ENDIAN) + u8 error_code : 7; + u8 valid : 1; +#endif + u8 segment_number; +#if defined(CONFIG_BIG_ENDIAN) + u8 reserved1 : 2; + u8 ili : 1; + u8 reserved2 : 1; + u8 sense_key : 4; +#elif defined(CONFIG_LITTLE_ENDIAN) + u8 sense_key : 4; + u8 reserved2 : 1; + u8 ili : 1; + u8 reserved1 : 2; +#endif + u8 information[4]; + u8 add_sense_len; + u8 command_info[4]; + u8 asc; + u8 ascq; + u8 fruc; + u8 sks[3]; + u8 asb[46]; +}; + +struct atapi_capacity { + u32 lba; + u32 block_size; +}; + +#endif
Modified: trunk/filo/drivers/ide.c =================================================================== --- trunk/filo/drivers/ide.c 2008-12-20 11:23:33 UTC (rev 85) +++ trunk/filo/drivers/ide.c 2009-03-11 03:07:08 UTC (rev 86) @@ -1132,6 +1132,11 @@ if (hdr == HEADER_TYPE_BRIDGE || hdr == HEADER_TYPE_CARDBUS) { unsigned int new_bus; new_bus = (pci_read_config32(currdev, REG_PRIMARY_BUS) >> 8) & 0xff; + if (new_bus == 0) { + debug("Misconfigured bridge at %02x:%02x.%02x skipped.\n", + bus, slot, func); + continue; + } if (pci_find_ata_device_on_bus(new_bus, dev, index, sata, pata)) return 1; }
Added: trunk/filo/drivers/ide_new.c =================================================================== --- trunk/filo/drivers/ide_new.c (rev 0) +++ trunk/filo/drivers/ide_new.c 2009-03-11 03:07:08 UTC (rev 86) @@ -0,0 +1,1302 @@ +/* + * 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 + * + */ + +/* + * TODO: + * - Really probe for interfaces, don't just rely on legacy + */ + +#define GRUB + +#include <libpayload.h> +#include <config.h> +#include <arch/byteorder.h> + +#include <fs.h> +#include <debug.h> +#include "ide_new.h" +#include "hdreg.h" + +#if 0 +#define dprintf printf +#else +#define dprintf(x...) +#endif + +/* + * 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 + +static struct ide_channel ob_ide_channels[IDE_MAX_CHANNELS]; + +/* + * FIXME: probe, we just hardwire legacy ports for now + */ +static const int io_ports[IDE_MAX_CHANNELS] = { 0x1f0, 0x170, 0x1e8, 0x168 }; +static const int ctl_ports[IDE_MAX_CHANNELS] = { 0x3f6, 0x376, 0x3ee, 0x36e }; + +/* + * 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(unsigned long port) +{ + return inb(port); +} + +static void +ob_ide_outb(unsigned char data, unsigned long port) +{ + outb(data, port); +} + +static void +ob_ide_insw(unsigned long port, unsigned char *addr, unsigned int count) +{ + insw(port, addr, count); +} + +static void +ob_ide_outsw(unsigned long port, unsigned char *addr, unsigned int count) +{ + outsw(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->io_regs[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(data, chan->io_regs[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) { + printf("%d: command not word aligned\n", drive->nr); + return; + } + + chan->obide_insw(chan->io_regs[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) { + printf("%d: command not word aligned\n", drive->nr); + return; + } + + chan->obide_outsw(chan->io_regs[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, char *msg) +{ + struct ide_channel *chan = drive->channel; + unsigned char err; + + if (!stat) + stat = ob_ide_pio_readb(drive, IDEREG_STATUS); + + printf("ob_ide_error drive<%d>: %s:\n", drive->nr, msg); + printf(" 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); + printf(", err=%x", err); + } + printf("\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; + + printf(" atapi opcode=%02x", old_cdb); + } else { + int i; + + printf(" cdb: "); + for (i = 0; i < sizeof(cmd->cdb); i++) + printf("%02x ", cmd->cdb[i]); + } + if (cmd->sense_valid) + printf(", sense: %02x/%02x/%02x", cmd->sense.sense_key, cmd->sense.asc, cmd->sense.ascq); + else + printf(", no sense"); + printf("\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)) { + printf("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)) { + printf("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 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) + printf("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) + printf("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) { + printf("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) + printf("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; + + /* + * Test Unit Ready is like a ping + */ + memset(cmd, 0, sizeof(*cmd)); + cmd->cdb[0] = ATAPI_TUR; + + if (ob_ide_atapi_packet(drive, cmd)) { + printf("%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)) { + printf("%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, 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, + 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); + struct ata_sector ata_sector; + + /* + * fill in chs command to read from disk at given location + */ + cmd->buffer = buf; + cmd->buflen = sectors * 512; + + ata_sector.all = sectors; + cmd->nsector = ata_sector.low; + 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, + 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, + char *buf, unsigned int sectors) +{ + struct ata_command *cmd = &drive->channel->ata_cmd; + struct ata_sector ata_sector; + + memset(cmd, 0, sizeof(*cmd)); + + cmd->buffer = buf; + cmd->buflen = sectors * 512; + ata_sector.all = sectors; + + /* + * we are using tasklet addressing here + */ + cmd->task[2] = ata_sector.low; + cmd->task[3] = ata_sector.high; + 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, 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, + char *buf, unsigned int sectors) +{ + if (!sectors) + return 1; + if (block + sectors > drive->sectors) + return 1; + +#ifdef CONFIG_DEBUG_IDE + printf("ob_ide_read_sectors: block=%Ld sectors=%u\n", (unsigned long) block, sectors); +#endif + + 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 little endian arch, byte swap the string + */ +#ifdef CONFIG_LITTLE_ENDIAN + for (p = end ; p != s;) { + unsigned short *pp = (unsigned short *) (p -= 2); + *pp = __be16_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 { + printf("%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; + } + + strcpy(drive->model, id.model); + 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 int +ob_ide_max_transfer(int *idx) +{ + struct ide_drive *drive=&ob_ide_channels[idx[1]].drives[idx[0]]; + + return (drive->max_sectors * drive->bs); +} + +int +ob_ide_read_blocks(struct ide_drive *drive, int n, u32 blk, char* dest) +{ + int cnt = n; + while (n) { + int len = n; + if (len > drive->max_sectors) + len = drive->max_sectors; + + dprintf("reading %d sectors from blk %d\n",len, blk); + if (ob_ide_read_sectors(drive, blk, dest, len)) { + return n-1; + } + dprintf("done\n"); + + dest += len * drive->bs; + n -= len; + blk += len; + } + + return (cnt); +} + +static int +ob_ide_block_size(int *idx) +{ + struct ide_drive *drive=&ob_ide_channels[idx[1]].drives[idx[0]]; + return(drive->bs); +} + +//int ob_ide_init(int (*func)(struct ide_drive*)) +int ob_ide_init(void) +{ + int i, j; + + for (i = 0; i < IDE_NUM_CHANNELS; i++) { + struct ide_channel *chan = &ob_ide_channels[i]; + + 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; + } + + ob_ide_probe(chan); + + if (!chan->present) + continue; + + ob_ide_identify_drives(chan); + + printf("ata-%d: [io ports 0x%x-0x%x,0x%x]\n", i, + 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]; + char *media = "UNKNOWN"; + if (!drive->present) + continue; + printf(" 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; + } + printf("%s]: %s\n", media, drive->model); + } + + } + + return 0; +} + +static int inited=0; + +int ide_probe(int drive) +{ + struct ide_drive *curr_drive = & ob_ide_channels[drive / 2].drives[drive % 2]; + if (!inited) { + ob_ide_init(); + inited = 1; + } + + if (!curr_drive->present) + return -1; + + return 0; + +} + +int ide_probe_verbose(int drive) +{ + struct ide_drive *curr_drive = & ob_ide_channels[drive / 2].drives[drive % 2]; + char *media = "UNKNOWN"; + + if (!inited) { + ob_ide_init(); + inited = 1; + } + + if (!curr_drive->present) { + return -1; + } + printf("ata%d %s ", drive / 2, (drive % 2)?"slave ":"master"); + + + printf("[ATA%s ", curr_drive->type == ide_type_atapi ? "PI" : ""); + switch (curr_drive->media) { + case ide_media_floppy: + media = "Floppy"; + break; + case ide_media_cdrom: + media = "CDROM/DVD"; + break; + case ide_media_optical: + media = "MO"; + break; + case ide_media_disk: + media = "DISK"; + break; + } + printf("%s]: %s\n", media, curr_drive->model); + + return 0; +} + +int ide_read(int drive, sector_t sector, void *buffer) +{ + int ret; + ret = ob_ide_read_blocks(&ob_ide_channels[drive / 2].drives[drive % 2], 1, sector, buffer); + if (ret!=1) { // right now only one block at a time.. bummer + return -1; + } + return 0; +} + +int ide_read_blocks(int drive, sector_t sector, int count, void *buffer) +{ + int ret; + ret = ob_ide_read_blocks(&ob_ide_channels[drive / 2].drives[drive % 2], + count, sector, buffer); + if (ret!=count) { // right now only one block at a time.. bummer + return -1; + } + return 0; +}
Added: trunk/filo/drivers/ide_new.h =================================================================== --- trunk/filo/drivers/ide_new.h (rev 0) +++ trunk/filo/drivers/ide_new.h 2009-03-11 03:07:08 UTC (rev 86) @@ -0,0 +1,233 @@ +#ifndef IDE_H +#define IDE_H + +#include <arch/io.h> +#include "hdreg.h" + +/* + * legacy ide ports + */ +#define IDEREG_DATA 0x00 +#define IDEREG_ERROR 0x01 +#define IDEREG_FEATURE IDEREG_ERROR +#define IDEREG_NSECTOR 0x02 +#define IDEREG_SECTOR 0x03 +#define IDEREG_LCYL 0x04 +#define IDEREG_HCYL 0x05 +#define IDEREG_CURRENT 0x06 +#define IDEREG_STATUS 0x07 +#define IDEREG_COMMAND IDEREG_STATUS +#define IDEREG_CONTROL 0x08 +#define IDEREG_ASTATUS IDEREG_CONTROL + +/* + * device control bits + */ +#define IDECON_NIEN 0x02 +#define IDECON_SRST 0x04 + +/* + * device head bits + */ +#define IDEHEAD_LBA 0x40 +#define IDEHEAD_DEV0 0x00 +#define IDEHEAD_DEV1 0x10 + +/* + * status bytes + */ +#define ERR_STAT 0x01 +#define DRQ_STAT 0x08 +#define SEEK_STAT 0x10 +#define WRERR_STAT 0x20 +#define READY_STAT 0x40 +#define BUSY_STAT 0x80 + +#define IREASON_CD 0x01 +#define IREASON_IO 0x02 + +/* + * ATA opcodes + */ +#define WIN_READ 0x20 +#define WIN_READ_EXT 0x24 +#define WIN_IDENTIFY 0xEC +#define WIN_PACKET 0xA0 +#define WIN_IDENTIFY_PACKET 0xA1 + +/* + * ATAPI opcodes + */ +#define ATAPI_TUR 0x00 +#define ATAPI_READ_10 0x28 +#define ATAPI_REQ_SENSE 0x03 +#define ATAPI_START_STOP_UNIT 0x1b +#define ATAPI_READ_CAPACITY 0x25 + +/* + * atapi sense keys + */ +#define ATAPI_SENSE_NOT_READY 0x02 + +/* + * supported device types + */ +enum { + ide_type_unknown, + ide_type_ata, + ide_type_atapi, +}; + +enum { + ide_media_floppy = 0x00, + ide_media_cdrom = 0x05, + ide_media_optical = 0x07, + ide_media_disk = 0x20, +}; + +/* + * drive addressing + */ +enum { + ide_chs = 1, + ide_lba28, + ide_lba48, +}; + +/* + * simple ata command that works for everything (except 48-bit lba commands) + */ +struct ata_command { + char *buffer; + unsigned int buflen; + + /* + * data register + */ + unsigned char data; + unsigned char feature; + unsigned char nsector; + unsigned char sector; + unsigned char lcyl; + unsigned char hcyl; + unsigned char device_head; + unsigned char command; + unsigned char control; + + /* + * or tasklet, just for lba48 for now (above could be scrapped) + */ + unsigned char task[10]; + + /* + * output + */ + unsigned char stat; + unsigned int bytes; +}; + +struct atapi_command { + unsigned char cdb[12]; + unsigned char *buffer; + unsigned int buflen; + unsigned char data_direction; + + unsigned char stat; + unsigned char sense_valid; + struct request_sense sense; + unsigned char old_cdb; +}; + +struct ide_channel; + +struct ide_drive { + char unit; /* 0: master, 1: slave */ + char present; /* there or not */ + char type; /* ata or atapi */ + char media; /* disk, cdrom, etc */ + char addressing; /* chs/lba28/lba48 */ + + char model[40]; /* name */ + int nr; + + unsigned long sectors; + + unsigned int max_sectors; + + /* + * for legacy chs crap + */ + unsigned int cyl; + unsigned int head; + unsigned int sect; + + unsigned int bs; /* block size */ + + struct ide_channel *channel; +}; + +struct ide_channel { + /* + * either mmio or io_regs is set to indicate mmio or not + */ + int mmio; + int io_regs[10]; + + /* + * can be set to a mmio hook, default it legacy outb/inb + */ + void (*obide_outb)(unsigned char addr, unsigned long port); + unsigned char (*obide_inb)(unsigned long port); + void (*obide_insw)(unsigned long port, unsigned char *addr, unsigned int count); + void (*obide_outsw)(unsigned long port, unsigned char *addr, unsigned int count); + + struct ide_drive drives[2]; + char selected; + char present; + + /* + * only one can be busy per channel + */ + struct ata_command ata_cmd; + struct atapi_command atapi_cmd; + +}; + +enum { + atapi_ddir_none, + atapi_ddir_read, + atapi_ddir_write, +}; + +struct ata_sector { + u16 all; + union { +#ifdef CONFIG_BIG_ENDIAN + u8 high; + u8 low; +#endif +#ifdef CONFIG_LITTLE_ENDIAN + u8 low; + u8 high; +#endif + }; +}; + +int +ob_ide_read_blocks(struct ide_drive *drive, int n, u32 blk, char* dest); +static int +ob_ide_atapi_request_sense(struct ide_drive *drive); +//int ob_ide_init(int (*func)(struct ide_drive*)); +int ob_ide_init(void); + + +/* FILO compat */ +#define CONFIG_LITTLE_ENDIAN +#define CONFIG_IDE_LBA48 + +#define __be32_to_cpu be32_to_cpu +#define __be16_to_cpu be16_to_cpu +#define __le32_to_cpu le32_to_cpu +#define __le16_to_cpu le16_to_cpu + +#endif
Modified: trunk/filo/drivers/intel.c =================================================================== --- trunk/filo/drivers/intel.c 2008-12-20 11:23:33 UTC (rev 85) +++ trunk/filo/drivers/intel.c 2009-03-11 03:07:08 UTC (rev 86) @@ -1,7 +1,7 @@ /* * This file is part of FILO. * - * Copyright (C) 2008 coresystems GmbH + * Copyright (C) 2008-2009 coresystems GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,8 +30,10 @@
void platform_poweroff(void) { - int pmbase = 0x800; + int pmbase;
+ pmbase = pci_read_config16(PCI_DEV(0,0x1f, 0), 0x40) & 0xfffe; + /* XXX The sequence is correct; It works fine under Linux. * Yet, it does not power off the system in FILO. * Some initialization is missing
Modified: trunk/filo/fs/blockdev.c =================================================================== --- trunk/filo/fs/blockdev.c 2008-12-20 11:23:33 UTC (rev 85) +++ trunk/filo/fs/blockdev.c 2009-03-11 03:07:08 UTC (rev 86) @@ -247,7 +247,7 @@ }
switch (type) { -#ifdef CONFIG_IDE_DISK +#if defined(CONFIG_IDE_DISK) || defined(CONFIG_IDE_NEW_DISK) case DISK_IDE: if (ide_probe(drive) != 0) { debug("Failed to open IDE.\n"); @@ -384,12 +384,24 @@ if (cache_sect[hash] != sector) { cache_sect[hash] = (unsigned long) -1; switch (dev_type) { -#ifdef CONFIG_IDE_DISK +#if defined(CONFIG_IDE_DISK) case DISK_IDE: if (ide_read(dev_drive, sector, buf) != 0) goto readerr; break; #endif +#if defined(CONFIG_IDE_NEW_DISK) + case DISK_IDE: + { + int count = (NUM_CACHE-hash>8)?8:(NUM_CACHE-hash); + if (ide_read_blocks(dev_drive, sector, count, buf) != 0) + goto readerr; + while (--count>0) { + cache_sect[hash+count] = sector + count; + } + break; + } +#endif #if defined(CONFIG_USB_NEW_DISK) && defined(CONFIG_USB) case DISK_NEW_USB: {
Modified: trunk/filo/include/fs.h =================================================================== --- trunk/filo/include/fs.h 2008-12-20 11:23:33 UTC (rev 85) +++ trunk/filo/include/fs.h 2009-03-11 03:07:08 UTC (rev 86) @@ -27,11 +27,17 @@ #define DEV_SECTOR_SIZE (1<<9) #define DEV_SECTOR_MASK (DEV_SECTOR_SIZE-1)
-#ifdef CONFIG_IDE_DISK +#if defined(CONFIG_IDE_DISK) int ide_probe(int drive); int ide_read(int drive, sector_t sector, void *buffer); #endif
+#if defined(CONFIG_IDE_NEW_DISK) +int ide_probe(int drive); +int ide_probe_verbose(int drive); +int ide_read_blocks(const int drive, const sector_t sector, const int size, void *buffer); +#endif + #ifdef CONFIG_USB_DISK int usb_probe(int drive); int usb_read(int drive, sector_t sector, void *buffer);
Modified: trunk/filo/main/Makefile.inc =================================================================== --- trunk/filo/main/Makefile.inc 2008-12-20 11:23:33 UTC (rev 85) +++ trunk/filo/main/Makefile.inc 2009-03-11 03:07:08 UTC (rev 86) @@ -16,6 +16,7 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #
-TARGETS-y += main/filo.o main/elfload.o main/elfnote.o main/ipchecksum.o main/strtox.o +TARGETS-y += main/filo.o main/strtox.o +TARGETS-y += main/elfload.o main/elfnote.o main/ipchecksum.o TARGETS-$(CONFIG_SUPPORT_SOUND) += main/sound.o
Modified: trunk/filo/main/grub/builtins.c =================================================================== --- trunk/filo/main/grub/builtins.c 2008-12-20 11:23:33 UTC (rev 85) +++ trunk/filo/main/grub/builtins.c 2009-03-11 03:07:08 UTC (rev 86) @@ -319,6 +319,56 @@ #endif };
+#if CONFIG_DEVELOPER_TOOLS +/* default */ +static int dumppm_func(char *arg, int flags) +{ + u16 pmbase; + + pmbase = pci_read_config16(PCI_DEV(0,0x1f, 0), 0x40) & 0xfffe; + + grub_printf("pmbase+0x0000: 0x%04x (PM1_STS)\n", inw(pmbase+0x0000)); + grub_printf("pmbase+0x0002: 0x%04x (PM1_EN)\n", inw(pmbase+0x0002)); + grub_printf("pmbase+0x0004: 0x%08x (PM1_CNT)\n", inl(pmbase+0x0004)); + grub_printf("pmbase+0x0008: 0x%08x (PM1_TMR)\n", inl(pmbase+0x0008)); + grub_printf("pmbase+0x0010: 0x%08x (PROC_CNT)\n", inl(pmbase+0x0010)); + grub_printf("pmbase+0x0020: 0x%08x (PM2_CNT)\n", inl(pmbase+0x0020)); + grub_printf("pmbase+0x0028: 0x%08x (GPE0_STS)\n", inl(pmbase+0x0028)); + grub_printf("pmbase+0x002c: 0x%08x (GPE0_EN)\n", inl(pmbase+0x002c)); + grub_printf("pmbase+0x0030: 0x%08x (SMI_EN)\n", inl(pmbase+0x0030)); + grub_printf("pmbase+0x0034: 0x%08x (SMI_STS)\n", inl(pmbase+0x0034)); + grub_printf("pmbase+0x0038: 0x%04x (ALT_GP_SMI_EN)\n", inw(pmbase+0x0038)); + grub_printf("pmbase+0x003a: 0x%04x (ALT_GP_SMI_STS)\n", inw(pmbase+0x003a)); + grub_printf("pmbase+0x0042: 0x%02x (GPE_CNTL)\n", inb(pmbase+0x0042)); + grub_printf("pmbase+0x0044: 0x%04x (DEVACT_STS)\n", inw(pmbase+0x0044)); + grub_printf("pmbase+0x0050: 0x%02x (SS_CNT)\n", inb(pmbase+0x0050)); + grub_printf("pmbase+0x0054: 0x%08x (C3_RES)\n", inl(pmbase+0x0054)); +#if 0 + // TCO + grub_printf("pmbase+0x0060: 0x%04x (TCO_RLD)\n", inw(pmbase+0x0060)); + grub_printf("pmbase+0x0062: 0x%02x (TCO_DAT_IN)\n", inb(pmbase+0x0062)); + grub_printf("pmbase+0x0063: 0x%02x (TCO_DAT_OUT)\n", inb(pmbase+0x0063)); + grub_printf("pmbase+0x0064: 0x%04x (TCO1_STS)\n", inw(pmbase+0x0064)); + grub_printf("pmbase+0x0066: 0x%04x (TCO2_STS)\n", inw(pmbase+0x0066)); + grub_printf("pmbase+0x0068: 0x%04x (TCO1_CNT)\n", inw(pmbase+0x0068)); + grub_printf("pmbase+0x006a: 0x%04x (TCO2_CNT)\n", inw(pmbase+0x006a)); + grub_printf("pmbase+0x006c: 0x%04x (TCO_MESSAGE)\n", inw(pmbase+0x006c)); + grub_printf("pmbase+0x006e: 0x%02x (TCO_WDCNT)\n", inb(pmbase+0x006e)); + grub_printf("pmbase+0x0070: 0x%02x (TCO_SW_IRQ_GEN)\n", inb(pmbase+0x0070)); + grub_printf("pmbase+0x0072: 0x%04x (TCO_TMR)\n", inw(pmbase+0x0072)); +#endif + return 0; +} + +static struct builtin builtin_dumppm = { + "dumppm", + dumppm_func, + BUILTIN_CMDLINE | BUILTIN_HELP_LIST, + "dumppm", + "Dump Powermanagement registers" +}; +#endif + #if CONFIG_EXPERIMENTAL #warning "FIND not implemented yet." /* find */ @@ -1059,11 +1109,16 @@ #ifdef CONFIG_DEVELOPER_TOOLS static int probe_func(char *arg, int flags) { -#if CONFIG_IDE_DISK +#if CONFIG_IDE_DISK int i;
for (i=0; i<8; i++) ide_probe(i); +#elif CONFIG_IDE_NEW_DISK + int i; + + for (i=0; i<8; i++) + ide_probe_verbose(i); #else grub_printf("No IDE driver.\n"); #endif @@ -1566,14 +1621,52 @@ #endif };
+static int cat_func(char *arg, int flags) +{ + char buf[4096]; + int len;
+ temp_space[0]=0; + copy_path_to_filo_bootline(arg, temp_space, 1); + if (temp_space[0]==0) { + return help_func("cat",0); + } + if (!file_open(temp_space)) { + errnum = ERR_FILE_NOT_FOUND; + return 1; + } + + while ((len = file_read(buf, sizeof(buf))) != 0) { + int cnt; + for (cnt = 0; cnt < len; cnt++) { + grub_putchar(buf[cnt]); + } + } + + file_close(); + + return 0; +} + +static struct builtin builtin_cat = { + "cat", + cat_func, + BUILTIN_CMDLINE | BUILTIN_HELP_LIST, + "cat FILENAME", + "Print the content of FILENAME to the terminal." +}; + /* README !!! XXX !!! This list has to be alphabetically ordered !!! */
struct builtin *builtin_table[] = { &builtin_boot, + &builtin_cat, &builtin_color, &builtin_configfile, &builtin_default, +#ifdef CONFIG_DEVELOPER_TOOLS + &builtin_dumppm, +#endif #ifdef CONFIG_EXPERIMENTAL &builtin_find, #endif