diff options
-rw-r--r-- | c/src/lib/libbsp/i386/pc386/ChangeLog | 8 | ||||
-rw-r--r-- | c/src/lib/libbsp/i386/pc386/ide/ide.c | 376 |
2 files changed, 287 insertions, 97 deletions
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 <chrisj@rtems.org> + + * 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 <ralf.corsepius@rtems.org> * 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 <libchip/ide_ctrl_cfg.h> #include <libchip/ide_ctrl_io.h> +#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 | <none> | \*=========================================================================*/ { - 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 | <none> | \*=========================================================================*/ { - 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; } } } |