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.
This commit is contained in:
Chris Johns
2009-10-08 07:15:08 +00:00
parent eb649786c8
commit c1fc5d4709
2 changed files with 288 additions and 98 deletions

View File

@@ -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

View File

@@ -29,51 +29,125 @@
#include <libchip/ide_ctrl_cfg.h>
#include <libchip/ide_ctrl_io.h>
bool pc386_ide_show;
#define ATA_SECTOR_SIZE (512)
/* #define DEBUG_OUT */
/*
* 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 ();
}
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)
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)
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)
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))
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,6 +219,11 @@ 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];
@@ -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,23 +255,10 @@ 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);
@@ -210,10 +271,10 @@ void pc386_ide_initialize
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;
@@ -247,6 +318,18 @@ void pc386_ide_initialize
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);
@@ -344,7 +497,7 @@ void pc386_ide_read_block
| Input Parameters: |
\*-------------------------------------------------------------------------*/
int minor,
uint16_t block_size,
uint32_t block_size,
rtems_blkdev_sg_buffer *bufs,
uint32_t *cbuf,
uint32_t *pos
@@ -355,46 +508,58 @@ 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 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);
return;
}
lbuf = (uint16_t*)((uint8_t*)(bufs[(*cbuf)].buffer) + (*pos));
for (b = 0; b < (ATA_SECTOR_SIZE / 2); b++)
{
inport_word(port+IDE_REGISTER_DATA,*lbuf);
#ifdef DEBUG_OUT
printk("%04x ",*lbuf);
#if PC386_IDE_DEBUG_OUT
pc386_ide_printk("%04x ",*lbuf);
i32++;
if (i32 >= 16)
{
printk("\n");
pc386_ide_printk("\n");
i32 = 0;
}
#endif
lbuf++;
cnt += sizeof(*lbuf);
(*pos) += sizeof(*lbuf);
if ((*pos) == llength) {
}
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
@@ -422,40 +587,57 @@ 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 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)
{
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) {
}
cnt += ATA_SECTOR_SIZE;
(*pos) += ATA_SECTOR_SIZE;
if ((*pos) == bufs[(*cbuf)].length) {
(*pos) = 0;
(*cbuf)++;
lbuf = bufs[(*cbuf)].buffer;
llength = bufs[(*cbuf)].length;
}
}
}