From c1fc5d470913c6824f2ab673c9d69e7d3920a4df Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Thu, 8 Oct 2009 07:15:08 +0000 Subject: 2009-10-08 Chris Johns * ide/ide.c: Fixed more IDE driver bugs. The driver can transfer 64K in a single write on my test PC and qemu with this driver. The driver only checks the data ready bit at the start of each 512 block being transfered. This has speed the transfer loop up. Fixed the timing so the BSP timer is used until RTEMS has started. --- c/src/lib/libbsp/i386/pc386/ChangeLog | 8 + c/src/lib/libbsp/i386/pc386/ide/ide.c | 376 +++++++++++++++++++++++++--------- 2 files changed, 287 insertions(+), 97 deletions(-) (limited to 'c') diff --git a/c/src/lib/libbsp/i386/pc386/ChangeLog b/c/src/lib/libbsp/i386/pc386/ChangeLog index 941fcb9c0f..a02aa5908a 100644 --- a/c/src/lib/libbsp/i386/pc386/ChangeLog +++ b/c/src/lib/libbsp/i386/pc386/ChangeLog @@ -1,3 +1,11 @@ +2009-10-08 Chris Johns + + * ide/ide.c: Fixed more IDE driver bugs. The driver can transfer + 64K in a single write on my test PC and qemu with this driver. The + driver only checks the data ready bit at the start of each 512 + block being transfered. This has speed the transfer loop up. Fixed + the timing so the BSP timer is used until RTEMS has started. + 2009-09-30 Ralf Corsépius * console/console.c, console/serial_mouse.c: Reflect diff --git a/c/src/lib/libbsp/i386/pc386/ide/ide.c b/c/src/lib/libbsp/i386/pc386/ide/ide.c index a43f796d11..b7850fab10 100644 --- a/c/src/lib/libbsp/i386/pc386/ide/ide.c +++ b/c/src/lib/libbsp/i386/pc386/ide/ide.c @@ -29,51 +29,125 @@ #include #include +#define ATA_SECTOR_SIZE (512) + +/* + * Use during initialisation. + */ +extern void Wait_X_ms(unsigned int msecs); + bool pc386_ide_show; +uint32_t pc386_ide_timeout; + +#define PC386_IDE_DEBUG_OUT 0 + +#if PC386_IDE_DEBUG_OUT +bool pc386_ide_trace; +#define pc386_ide_printk if (pc386_ide_trace) printk +#endif + +#define PC386_IDE_PROBE_TIMEOUT (500) +#define PC386_IDE_PRESTART_TIMEOUT (1000) +#define PC386_IDE_TASKING_TIMEOUT (2000) + +/* + * Prestart sleep using a calibrated timing loop. + */ +static void pc386_ide_prestart_sleep (void) +{ + Wait_X_ms (10); +} + +/* + * Once tasking has started we use a task sleep. + */ +static void pc386_ide_tasking_sleep (void) +{ + rtems_task_wake_after (TOD_MICROSECONDS_TO_TICKS (10000) ? + TOD_MICROSECONDS_TO_TICKS (10000) : 1); +} + +typedef void (*pc386_ide_sleeper)(void); + +static void pc386_ide_sleep (pc386_ide_sleeper sleeper) +{ + sleeper (); +} -/* #define DEBUG_OUT */ +static void wait(volatile uint32_t loops) +{ + while (loops) + loops--; +} -static bool pc386_ide_status_busy (uint32_t port, - uint32_t timeout, - uint8_t* status_val) +static bool pc386_ide_status_busy (uint32_t port, + volatile uint32_t timeout, + uint8_t* status_val, + pc386_ide_sleeper sleeper) { + volatile uint8_t status; + int polls; + do { - inport_byte (port + IDE_REGISTER_STATUS, *status_val); - if ((*status_val & IDE_REGISTER_STATUS_BSY) == 0) - return true; + polls = 500; + while (polls) + { + inport_byte (port + IDE_REGISTER_STATUS, status); + if ((status & IDE_REGISTER_STATUS_BSY) == 0) + { + *status_val = status; + return true; + } + polls--; + } if (timeout) { timeout--; - rtems_task_wake_after (TOD_MICROSECONDS_TO_TICKS (1000)); + pc386_ide_sleep (sleeper); } } while (timeout); + *status_val = status; return false; } -static bool pc386_ide_status_data_ready (uint32_t port, - uint32_t timeout, - uint8_t* status_val) +static bool pc386_ide_status_data_ready (uint32_t port, + volatile uint32_t timeout, + uint8_t* status_val, + pc386_ide_sleeper sleeper) { + volatile uint8_t status; + int polls; + do { - inport_byte (port + IDE_REGISTER_STATUS, *status_val); + polls = 1000; + while (polls) + { + inport_byte (port + IDE_REGISTER_STATUS, status); - if (((*status_val & IDE_REGISTER_STATUS_BSY) == 0) && - (*status_val & IDE_REGISTER_STATUS_DRQ)) - return true; + if (((status & IDE_REGISTER_STATUS_BSY) == 0) && + (status & IDE_REGISTER_STATUS_DRQ)) + { + *status_val = status; + return true; + } + + polls--; + } if (timeout) { timeout--; - rtems_task_wake_after (TOD_MICROSECONDS_TO_TICKS (1000)); + pc386_ide_sleep (sleeper); } } while (timeout); + *status_val = status; return false; } @@ -103,12 +177,6 @@ bool pc386_ide_probe return ide_card_plugged; } -static void wait(volatile uint32_t loops) -{ - while (loops) - loops--; -} - /*=========================================================================*\ | Function: | \*-------------------------------------------------------------------------*/ @@ -151,9 +219,14 @@ void pc386_ide_initialize uint8_t cyllsb; uint8_t cylmsb; const char* label = dev ? " slave" : "master"; + int max_multiple_sectors = 0; + int cur_multiple_sectors = 0; + uint32_t cylinders = 0; + uint32_t heads = 0; + uint32_t sectors = 0; char model_number[41]; char* p = &model_number[0]; - + memset(model_number, 0, sizeof(model_number)); outport_byte(port+IDE_REGISTER_DEVICE_HEAD, @@ -165,7 +238,8 @@ void pc386_ide_initialize outport_byte(port+IDE_REGISTER_COMMAND, 0x00); - if (!pc386_ide_status_busy (port, 6000, &status)) + if (!pc386_ide_status_busy (port, PC386_IDE_PROBE_TIMEOUT, + &status, pc386_ide_prestart_sleep)) continue; inport_byte(port+IDE_REGISTER_STATUS, status); @@ -181,39 +255,26 @@ void pc386_ide_initialize printk("IDE%d:%s: cylinder-high=%02x\n", minor, label, cylmsb); } -#if 0 - /* - * Filter based on the cylinder values and the status. - * Taken from grub's ata.c. - */ - if (cyllsb != 0x14 || cylmsb != 0xeb) - if (status == 0 || (cyllsb != 0 && cylmsb != 0 && - cyllsb != 0xc3 && cylmsb != 0x3c)) - { - if (pc386_ide_show) - printk("IDE%d:%s: bad device\n", minor, label); - } -#endif - outport_byte(port+IDE_REGISTER_COMMAND, 0xec); - if (!pc386_ide_status_busy (port, 6000, &status)) + if (!pc386_ide_status_busy (port, PC386_IDE_PRESTART_TIMEOUT, + &status, pc386_ide_prestart_sleep)) { if (pc386_ide_show) printk("IDE%d:%s: device busy: %02x\n", minor, label, status); continue; } - + byte = 0; while (byte < 512) { uint16_t word; bool data_ready; - if (pc386_ide_show && ((byte % 16) == 0)) - printk("\n %04x : ", byte); - - data_ready = pc386_ide_status_data_ready (port, 100, &status); + data_ready = pc386_ide_status_data_ready (port, + 250, + &status, + pc386_ide_prestart_sleep); if (status & IDE_REGISTER_STATUS_ERR) { @@ -234,11 +295,21 @@ void pc386_ide_initialize if (!data_ready) break; + if (pc386_ide_show && ((byte % 16) == 0)) + printk("\n %04x : ", byte); + inport_word(port+IDE_REGISTER_DATA, word); if (pc386_ide_show) printk ("%04x ", word); + if (byte == 2) + cylinders = word; + if (byte == 6) + heads = word; + if (byte == 12) + sectors = word; + if (byte >= 54 && byte < (54 + 40)) { *p = word >> 8; @@ -246,7 +317,19 @@ void pc386_ide_initialize *p = word; p++; } - + + if (byte == 94) + max_multiple_sectors = word & 0xff; + + if (byte == (47 * 2)) + max_multiple_sectors = word & 0xff; + + if (byte == (59 * 2)) + { + if (word & (1 << 8)) + cur_multiple_sectors = word & 0xff; + } + byte += 2; } @@ -254,8 +337,78 @@ void pc386_ide_initialize printk("\nbytes read = %d\n", byte); if (p != &model_number[0]) - printk("IDE%d:%s: %s\n", minor, label, model_number); + { + uint32_t size; + uint32_t left; + uint32_t right; + char units; + + size = ((((uint64_t) cylinders) * heads) * sectors * 512) / 1024; + + if (size > (1024 * 1024)) + { + size = (size * 10) / (1000 * 1000); + units = 'G'; + } + else if (size > 1024) + { + size = (size * 10) / 1000; + units = 'M'; + } + else + { + size = size * 10; + units = 'K'; + } + + left = size / 10; + right = size % 10; + + p--; + while (*p == ' ') + { + *p = '\0'; + p--; + } + + printk("IDE%d:%s:%s, %u.%u%c (%u/%u/%u), max blk size:%d\n", + minor, label, model_number, left, right, units, + heads, cylinders, sectors, max_multiple_sectors * 512); + } + +#if IDE_CLEAR_MULTI_SECTOR_COUNT + if (max_multiple_sectors) + { + outport_byte(port+IDE_REGISTER_SECTOR_COUNT, 0); + outport_byte(port+IDE_REGISTER_COMMAND, 0xc6); + + if (!pc386_ide_status_busy (port, PC386_IDE_PRESTART_TIMEOUT, + &status, pc386_ide_prestart_sleep)) + { + if (pc386_ide_show) + printk("IDE%d:%s: device busy: %02x\n", minor, label, status); + continue; + } + + inport_byte(port+IDE_REGISTER_STATUS, status); + if (status & IDE_REGISTER_STATUS_ERR) + { + inport_byte(port+IDE_REGISTER_ERROR, error); + if (error & IDE_REGISTER_ERROR_ABRT) + printk("IDE%d:%s: disable multiple failed\n", minor, label); + else + printk("IDE%d:%s: unknown error on disable multiple: %02x\n", + minor, label, error); + } + } +#endif + + outport_byte(port+IDE_REGISTER_DEVICE_CONTROL, + IDE_REGISTER_DEVICE_CONTROL_nIEN); + wait(10000); } + + pc386_ide_timeout = PC386_IDE_TASKING_TIMEOUT; /* * FIXME: enable interrupts, if needed @@ -294,8 +447,8 @@ void pc386_ide_read_reg inport_byte(port+reg, bval1); *value = bval1; } -#ifdef DEBUG_OUT - printk("pc386_ide_read_reg (0x%x)=0x%x\r\n",reg,*value & 0xff); +#if PC386_IDE_DEBUG_OUT + pc386_ide_printk("pc386_ide_read_reg (0x%x)=0x%x\r\n",reg,*value & 0xff); #endif } @@ -321,8 +474,8 @@ void pc386_ide_write_reg { uint32_t port = IDE_Controller_Table[minor].port1; -#ifdef DEBUG_OUT - printk("pc386_ide_write_reg(0x%x,0x%x)\r\n",reg,value & 0xff); +#if PC386_IDE_DEBUG_OUT + pc386_ide_printk("pc386_ide_write_reg(0x%x,0x%x)\r\n",reg,value & 0xff); #endif if (reg == IDE_REGISTER_DATA_WORD) { outport_word(port+reg,value); @@ -343,8 +496,8 @@ void pc386_ide_read_block +---------------------------------------------------------------------------+ | Input Parameters: | \*-------------------------------------------------------------------------*/ - int minor, - uint16_t block_size, + int minor, + uint32_t block_size, rtems_blkdev_sg_buffer *bufs, uint32_t *cbuf, uint32_t *pos @@ -354,47 +507,59 @@ void pc386_ide_read_block | | \*=========================================================================*/ { - uint32_t port = IDE_Controller_Table[minor].port1; - uint16_t cnt = 0; - uint32_t llength = bufs[(*cbuf)].length; - uint8_t status_val; - uint16_t *lbuf = (uint16_t*) - ((uint8_t*)(bufs[(*cbuf)].buffer) + (*pos)); -#ifdef DEBUG_OUT + uint32_t port = IDE_Controller_Table[minor].port1; + uint32_t cnt = 0; +#if PC386_IDE_DEBUG_OUT int i32 = 0; + pc386_ide_printk("pc386_ide_read_block(bs=%u,bn=%u,bl=%u,cb=%d,p=%d)\n", + block_size, bufs[(*cbuf)].block, llength, *cbuf, *pos); #endif + while (cnt < block_size) { - if (!pc386_ide_status_data_ready (port, 100, &status_val)) + uint16_t *lbuf; + uint8_t status_val; + int b; + + if (!pc386_ide_status_data_ready (port, pc386_ide_timeout, + &status_val, pc386_ide_tasking_sleep)) { - printk ("pc386_ide_read_block: status=%02x, cnt=%d bs=%d\n", status_val, cnt, block_size); + printk ("pc386_ide_read_block: block=%u cbuf=%u status=%02x, cnt=%d bs=%d\n", + bufs[*cbuf].block, *cbuf, status_val, cnt, block_size); /* FIXME: add an error here. */ return; } if (status_val & IDE_REGISTER_STATUS_ERR) + { + inport_byte(port+IDE_REGISTER_ERROR, status_val); printk("pc386_ide_read_block: error: %02x\n", status_val); - - inport_word(port+IDE_REGISTER_DATA,*lbuf); + return; + } -#ifdef DEBUG_OUT - printk("%04x ",*lbuf); - i32++; - if (i32 >= 16) + lbuf = (uint16_t*)((uint8_t*)(bufs[(*cbuf)].buffer) + (*pos)); + + for (b = 0; b < (ATA_SECTOR_SIZE / 2); b++) { - printk("\n"); - i32 = 0; - } + inport_word(port+IDE_REGISTER_DATA,*lbuf); + +#if PC386_IDE_DEBUG_OUT + pc386_ide_printk("%04x ",*lbuf); + i32++; + if (i32 >= 16) + { + pc386_ide_printk("\n"); + i32 = 0; + } #endif - - lbuf++; - cnt += sizeof(*lbuf); - (*pos) += sizeof(*lbuf); - if ((*pos) == llength) { + lbuf++; + } + cnt += ATA_SECTOR_SIZE; + (*pos) += ATA_SECTOR_SIZE; + if ((*pos) == bufs[(*cbuf)].length) { (*pos) = 0; (*cbuf)++; lbuf = bufs[(*cbuf)].buffer; - llength = bufs[(*cbuf)].length; } } } @@ -411,7 +576,7 @@ void pc386_ide_write_block | Input Parameters: | \*-------------------------------------------------------------------------*/ int minor, - uint16_t block_size, + uint32_t block_size, rtems_blkdev_sg_buffer *bufs, uint32_t *cbuf, uint32_t *pos @@ -421,41 +586,58 @@ void pc386_ide_write_block | | \*=========================================================================*/ { - uint32_t port = IDE_Controller_Table[minor].port1; - uint16_t cnt = 0; - uint32_t llength = bufs[(*cbuf)].length; - uint8_t status_val; - uint16_t *lbuf = (uint16_t*) - ((uint8_t*)(bufs[(*cbuf)].buffer) + (*pos)); - -#ifdef DEBUG_OUT - printk("pc386_ide_write_block()\n"); + uint32_t port = IDE_Controller_Table[minor].port1; + uint32_t cnt = 0; +#if PC386_IDE_DEBUG_OUT + int i32 = 0; + pc386_ide_printk("pc386_ide_write_block(bs=%u,bn=%u,bl=%u,cb=%d,p=%d)\n", + block_size, bufs[(*cbuf)].block, llength, *cbuf, *pos); #endif while (cnt < block_size) { - if (!pc386_ide_status_data_ready (port, 100, &status_val)) + uint16_t *lbuf; + uint8_t status_val; + int b; + + if (!pc386_ide_status_data_ready (port, pc386_ide_timeout, + &status_val, pc386_ide_tasking_sleep)) { - printk ("pc386_ide_write_block: status=%02x, cnt=%d bs=%d\n", status_val, cnt, block_size); + printk ("pc386_ide_write_block: block=%u status=%02x, cnt=%d bs=%d\n", + bufs[*cbuf].block, status_val, cnt, block_size); /* FIXME: add an error here. */ return; } if (status_val & IDE_REGISTER_STATUS_ERR) - printk("pc386_ide_write_block: error: %02x\n", status_val); + { + inport_byte(port+IDE_REGISTER_ERROR, status_val); + printk ("pc386_ide_write_block: error: %02x\n", status_val); + return; + } -#ifdef DEBUG_OUT - printk("0x%x ",*lbuf); + lbuf = (uint16_t*)(((uint8_t*)bufs[*cbuf].buffer) + (*pos)); + + for (b = 0; b < (ATA_SECTOR_SIZE / 2); b++) + { +#if PC386_IDE_DEBUG_OUT + pc386_ide_printk("%04x ",*lbuf); + i32++; + if (i32 >= 16) + { + pc386_ide_printk("\n"); + i32 = 0; + } #endif - outport_word(port+IDE_REGISTER_DATA,*lbuf); - lbuf++; - cnt += sizeof(*lbuf); - (*pos) += sizeof(*lbuf); - if ((*pos) == llength) { + outport_word(port+IDE_REGISTER_DATA,*lbuf); + lbuf++; + } + cnt += ATA_SECTOR_SIZE; + (*pos) += ATA_SECTOR_SIZE; + if ((*pos) == bufs[(*cbuf)].length) { (*pos) = 0; (*cbuf)++; lbuf = bufs[(*cbuf)].buffer; - llength = bufs[(*cbuf)].length; } } } -- cgit v1.2.3