📄 ata.c
字号:
int ata_init(void)
{
int rc;
bool coldstart = false;
if ( !initialized )
{
// first try
rc = init_and_check( coldstart );
if (rc)
{ /* failed? -> second try, always with hard reset */
DEBUGF("ata: init failed, retrying...\n");
rc = init_and_check( true );
if (rc)
return rc;
}
rc = identify();
if (rc)
return -40 + rc;
multisectors = identify_info[47] & 0xff;
DEBUGF("ata: %d sectors per ata request\n",multisectors);
rc = freeze_lock();
if (rc)
return -50 + rc;
rc = set_features();
if (rc)
return -60 + rc;
last_disk_activity = alt_nticks();
initialized = true;
}
rc = set_multiple_mode(multisectors);
if (rc)
return -70 + rc;
return 0;
}
static int perform_soft_reset(void)
{
int ret;
int retry_count;
IOWR_ALTERA_AVALON_CF_IDE_DEVICE_HEAD( ide_base, SELECT_LBA | ata_device );
IOWR_ALTERA_AVALON_CF_CTL_CONTROL( ctl_base , CONTROL_nIEN|CONTROL_SRST);
usleep(10);
IOWR_ALTERA_AVALON_CF_CTL_CONTROL( ctl_base , CONTROL_nIEN );
usleep(10000);
/* This little sucker can take up to 30 seconds */
retry_count = 8;
do
{
ret = wait_for_rdy();
} while(!ret && retry_count--);
/* Massage the return code so it is 0 on success and -1 on failure */
ret = ret?0:-1;
return ret;
}
static void copy_read_sectors(unsigned char* buf, int wordcount)
{
unsigned short tmp = 0;
if ( (unsigned long)buf & 1)
{ /* not 16-bit aligned, copy byte by byte */
unsigned char* bufend = buf + wordcount*2;
do
{ /* loop compiles to 9 assembler instructions */
/* takes 14 clock cycles (2 pipeline stalls, 1 wait) */
tmp = ATA_DATA;
#ifdef SWAP_WORDS
*buf++ = tmp & 0xff; /* I assume big endian */
*buf++ = tmp >> 8; /* and don't use the SWAB16 macro */
#else
*buf++ = tmp >> 8;
*buf++ = tmp & 0xff;
#endif
} while (buf < bufend); /* tail loop is faster */
}
else
{ /* 16-bit aligned, can do faster copy */
unsigned short* wbuf = (unsigned short*)buf;
unsigned short* wbufend = wbuf + wordcount;
do
{ /* loop compiles to 7 assembler instructions */
/* takes 12 clock cycles (2 pipeline stalls, 1 wait) */
#ifdef SWAP_WORDS
*wbuf = SWAB16(ATA_DATA);
#else
*wbuf = ATA_DATA;
#endif
} while (++wbuf < wbufend); /* tail loop is faster */
}
}
static int wait_for_end_of_transfer(void)
{
if (!wait_for_bsy())
return 0;
return (ATA_ALT_STATUS & (STATUS_RDY|STATUS_DRQ)) == STATUS_RDY;
}
int ata_read_sectors(IF_MV2(int drive,)
unsigned long start,
int incount,
void* inbuf)
{
int ret = 0;
int i;
long timeout;
int count;
void* buf;
long spinup_start;
#ifdef HAVE_MULTIVOLUME
(void)drive; /* unused for now */
#endif
last_disk_activity = alt_nticks();
spinup_start = alt_nticks();
timeout = alt_nticks() + ( 5 * alt_ticks_per_second() );
IOWR_ALTERA_AVALON_CF_IDE_DEVICE_HEAD( ide_base, ata_device );
if (!wait_for_rdy())
{
return -2;
}
retry:
buf = inbuf;
count = incount;
while (TIME_BEFORE(alt_nticks(), timeout)) {
ret = 0;
last_disk_activity = alt_nticks();
if ( count == 256 )
IOWR_ALTERA_AVALON_CF_IDE_SECTOR_COUNT( ide_base, 0 );
else
IOWR_ALTERA_AVALON_CF_IDE_SECTOR_COUNT( ide_base, (unsigned char)count );
IOWR_ALTERA_AVALON_CF_IDE_SECTOR_NUMBER( ide_base, start & 0xff );
IOWR_ALTERA_AVALON_CF_IDE_CYLINDER_LOW(ide_base, (start >> 8) & 0xff );
IOWR_ALTERA_AVALON_CF_IDE_CYLINDER_HIGH( ide_base, (start >> 16) & 0xff );
IOWR_ALTERA_AVALON_CF_IDE_DEVICE_HEAD(ide_base, ((start >> 24) & 0xf) | SELECT_LBA | ata_device );
IOWR_ALTERA_AVALON_CF_IDE_COMMAND( ide_base, CMD_READ_MULTIPLE );
/* wait at least 500ns between writing command and reading status */
for( i = 0; i < ((ALT_CPU_FREQ / 1000000) / 1); i++ )
{
__asm__ volatile ("nop");
}
while (count) {
int sectors;
int wordcount;
int status;
if (!wait_for_start_of_transfer()) {
/* We have timed out waiting for RDY and/or DRQ, possibly
because the hard drive is shaking and has problems reading
the data. We have two options:
1) Wait some more
2) Perform a soft reset and try again.
We choose alternative 2.
*/
perform_soft_reset();
ret = -4;
goto retry;
}
/* read the status register exactly once per loop */
status = ATA_STATUS;
/* if destination address is odd, use byte copying,
otherwise use word copying */
if (count >= multisectors )
sectors = multisectors;
else
sectors = count;
wordcount = sectors * SECTOR_SIZE / 2;
copy_read_sectors(buf, wordcount);
/*
"Device errors encountered during READ MULTIPLE commands are
posted at the beginning of the block or partial block transfer,
but the DRQ bit is still set to one and the data transfer shall
take place, including transfer of corrupted data, if any."
-- ATA specification
*/
if ( status & (STATUS_BSY | STATUS_ERR | STATUS_DF) ) {
perform_soft_reset();
ret = -5;
goto retry;
}
buf += sectors * SECTOR_SIZE; /* Advance one chunk of sectors */
count -= sectors;
last_disk_activity = alt_nticks();
}
if(!ret && !wait_for_end_of_transfer()) {
perform_soft_reset();
ret = -3;
goto retry;
}
break;
}
return ret;
}
static void copy_write_sectors(const unsigned char* buf, int wordcount)
{
if ( (unsigned long)buf & 1)
{ /* not 16-bit aligned, copy byte by byte */
unsigned short tmp = 0;
const unsigned char* bufend = buf + wordcount*2;
do
{
#ifdef SWAP_WORDS
/* SH1: loop compiles to 9 assembler instructions */
/* takes 13 clock cycles (2 pipeline stalls) */
tmp = (unsigned short) *buf++;
tmp |= (unsigned short) *buf++ << 8; /* I assume big endian */
IOWR_ALTERA_AVALON_CF_IDE_DATA( ide_base, tmp );
#else
tmp = (unsigned short) *buf++ << 8;
tmp |= (unsigned short) *buf++;
IOWR_ALTERA_AVALON_CF_IDE_DATA( ide_base, tmp );
#endif
} while (buf < bufend); /* tail loop is faster */
}
else
{ /* 16-bit aligned, can do faster copy */
unsigned short* wbuf = (unsigned short*)buf;
unsigned short* wbufend = wbuf + wordcount;
do
{
#ifdef SWAP_WORDS
/* loop compiles to 6 assembler instructions */
/* takes 10 clock cycles (2 pipeline stalls) */
IOWR_ALTERA_AVALON_CF_IDE_DATA( ide_base, SWAB16(*wbuf) );
#else
IOWR_ALTERA_AVALON_CF_IDE_DATA( ide_base, *wbuf );
#endif
} while (++wbuf < wbufend); /* tail loop is faster */
}
}
int ata_write_sectors(IF_MV2(int drive,)
unsigned long start,
int count,
const void* buf)
{
int i;
volatile short j;
int ret = 0;
long spinup_start;
#ifdef HAVE_MULTIVOLUME
(void)drive; /* unused for now */
#endif
if (start == 0)
panicf("Writing on sector 0\n");
last_disk_activity = alt_nticks();
spinup_start = alt_nticks();
IOWR_ALTERA_AVALON_CF_IDE_DEVICE_HEAD( ide_base, ata_device );
if (!wait_for_rdy())
{
return -2;
}
if ( count == 256 )
IOWR_ALTERA_AVALON_CF_IDE_SECTOR_COUNT( ide_base, 0 );
else
IOWR_ALTERA_AVALON_CF_IDE_SECTOR_COUNT( ide_base, (unsigned char)count );
IOWR_ALTERA_AVALON_CF_IDE_SECTOR_NUMBER( ide_base, start & 0xff );
IOWR_ALTERA_AVALON_CF_IDE_CYLINDER_LOW(ide_base, (start >> 8) & 0xff );
IOWR_ALTERA_AVALON_CF_IDE_CYLINDER_HIGH( ide_base, (start >> 16) & 0xff );
IOWR_ALTERA_AVALON_CF_IDE_DEVICE_HEAD(ide_base, ((start >> 24) & 0xf) | SELECT_LBA | ata_device );
IOWR_ALTERA_AVALON_CF_IDE_COMMAND( ide_base, CMD_WRITE_SECTORS );
for (i=0; i<count; i++) {
if (!wait_for_start_of_transfer()) {
ret = -3;
break;
}
copy_write_sectors(buf, SECTOR_SIZE/2);
#ifdef USE_INTERRUPT
/* reading the status register clears the interrupt */
j = ATA_STATUS;
#endif
buf += SECTOR_SIZE;
last_disk_activity = alt_nticks();
}
if(!ret && !wait_for_end_of_transfer()) {
DEBUGF("End on transfer failed. -- jyp");
ret = -4;
}
return ret;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -