From b896e86842d68920d53ceddb5f68ee5a8c4a4985 Mon Sep 17 00:00:00 2001 From: Torne Wuff Date: Tue, 24 Nov 2009 18:03:57 +0000 Subject: FS#10798 - unify ata_read_sectors and ata_write_sectors Writes will now be retried if they fail, and will be done using multisector transfers which should be faster. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23740 a1c6a512-1295-4272-9138-f99709370657 --- firmware/drivers/ata.c | 269 ++++++++++++++----------------------------------- 1 file changed, 74 insertions(+), 195 deletions(-) (limited to 'firmware') diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c index 3c37077fd3..b00f67f5ec 100644 --- a/firmware/drivers/ata.c +++ b/firmware/drivers/ata.c @@ -71,7 +71,7 @@ #define Q_SLEEP 0 #define Q_CLOSE 1 -#define READ_TIMEOUT 5*HZ +#define READWRITE_TIMEOUT 5*HZ #ifdef HAVE_ATA_POWER_OFF #define ATA_POWER_OFF_TIMEOUT 2*HZ @@ -302,16 +302,47 @@ STATICIRAM ICODE_ATTR void copy_read_sectors(unsigned char* buf, int wordcount) } #endif /* !ATA_OPTIMIZED_READING */ -#ifdef MAX_PHYS_SECTOR_SIZE -static int _read_sectors(unsigned long start, - int incount, - void* inbuf) +#ifndef ATA_OPTIMIZED_WRITING +STATICIRAM ICODE_ATTR 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 + { +#if defined(SWAP_WORDS) || defined(ROCKBOX_LITTLE_ENDIAN) + tmp = (unsigned short) *buf++; + tmp |= (unsigned short) *buf++ << 8; + SET_16BITREG(ATA_DATA, tmp); #else -int ata_read_sectors(IF_MD2(int drive,) - unsigned long start, - int incount, - void* inbuf) + tmp = (unsigned short) *buf++ << 8; + tmp |= (unsigned short) *buf++; + SET_16BITREG(ATA_DATA, 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 + SET_16BITREG(ATA_DATA, swap16(*wbuf)); +#else + SET_16BITREG(ATA_DATA, *wbuf); +#endif + } while (++wbuf < wbufend); /* tail loop is faster */ + } +} +#endif /* !ATA_OPTIMIZED_WRITING */ + +static int ata_transfer_sectors(unsigned long start, + int incount, + void* inbuf, + int write) { int ret = 0; long timeout; @@ -323,9 +354,6 @@ int ata_read_sectors(IF_MD2(int drive,) #endif #ifndef MAX_PHYS_SECTOR_SIZE -#ifdef HAVE_MULTIDRIVE - (void)drive; /* unused for now */ -#endif mutex_lock(&ata_mtx); #endif @@ -355,7 +383,7 @@ int ata_read_sectors(IF_MD2(int drive,) } } - timeout = current_tick + READ_TIMEOUT; + timeout = current_tick + READWRITE_TIMEOUT; SET_REG(ATA_SELECT, ata_device); if (!wait_for_rdy()) @@ -373,7 +401,7 @@ int ata_read_sectors(IF_MD2(int drive,) #ifdef HAVE_ATA_DMA /* If DMA is supported and parameters are ok for DMA, use it */ - if (dma_mode && ata_dma_setup(inbuf, incount * SECTOR_SIZE, false)) + if (dma_mode && ata_dma_setup(inbuf, incount * SECTOR_SIZE, write)) usedma = true; #endif @@ -390,9 +418,12 @@ int ata_read_sectors(IF_MD2(int drive,) SET_REG(ATA_HCYL, (start >> 16) & 0xff); /* 23:16 */ SET_REG(ATA_SELECT, SELECT_LBA | ata_device); #ifdef HAVE_ATA_DMA - SET_REG(ATA_COMMAND, usedma ? CMD_READ_DMA_EXT : CMD_READ_MULTIPLE_EXT); + if (write) + SET_REG(ATA_COMMAND, usedma ? CMD_WRITE_DMA_EXT : CMD_WRITE_MULTIPLE_EXT); + else + SET_REG(ATA_COMMAND, usedma ? CMD_READ_DMA_EXT : CMD_READ_MULTIPLE_EXT); #else - SET_REG(ATA_COMMAND, CMD_READ_MULTIPLE_EXT); + SET_REG(ATA_COMMAND, write ? CMD_WRITE_MULTIPLE_EXT : CMD_READ_MULTIPLE_EXT); #endif } else @@ -404,9 +435,12 @@ int ata_read_sectors(IF_MD2(int drive,) SET_REG(ATA_HCYL, (start >> 16) & 0xff); SET_REG(ATA_SELECT, ((start >> 24) & 0xf) | SELECT_LBA | ata_device); #ifdef HAVE_ATA_DMA - SET_REG(ATA_COMMAND, usedma ? CMD_READ_DMA : CMD_READ_MULTIPLE); + if (write) + SET_REG(ATA_COMMAND, usedma ? CMD_WRITE_DMA : CMD_WRITE_MULTIPLE); + else + SET_REG(ATA_COMMAND, usedma ? CMD_READ_DMA : CMD_READ_MULTIPLE); #else - SET_REG(ATA_COMMAND, CMD_READ_MULTIPLE); + SET_REG(ATA_COMMAND, write ? CMD_WRITE_MULTIPLE : CMD_READ_MULTIPLE); #endif } @@ -473,7 +507,10 @@ int ata_read_sectors(IF_MD2(int drive,) wordcount = sectors * SECTOR_SIZE / 2; - copy_read_sectors(buf, wordcount); + if (write) + copy_write_sectors(buf, wordcount); + else + copy_read_sectors(buf, wordcount); /* "Device errors encountered during READ MULTIPLE commands @@ -513,191 +550,33 @@ int ata_read_sectors(IF_MD2(int drive,) return ret; } -#ifndef ATA_OPTIMIZED_WRITING -STATICIRAM ICODE_ATTR void copy_write_sectors(const unsigned char* buf, - int wordcount) +#ifndef MAX_PHYS_SECTOR_SIZE +int ata_read_sectors(IF_MD2(int drive,) + unsigned long start, + int incount, + void* inbuf) { - 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 - { -#if defined(SWAP_WORDS) || defined(ROCKBOX_LITTLE_ENDIAN) - tmp = (unsigned short) *buf++; - tmp |= (unsigned short) *buf++ << 8; - SET_16BITREG(ATA_DATA, tmp); -#else - tmp = (unsigned short) *buf++ << 8; - tmp |= (unsigned short) *buf++; - SET_16BITREG(ATA_DATA, 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 - SET_16BITREG(ATA_DATA, swap16(*wbuf)); -#else - SET_16BITREG(ATA_DATA, *wbuf); +#ifdef HAVE_MULTIDRIVE + (void)drive; /* unused for now */ #endif - } while (++wbuf < wbufend); /* tail loop is faster */ - } + + return ata_transfer_sectors(start, incount, inbuf, false); } -#endif /* !ATA_OPTIMIZED_WRITING */ +#endif -#ifdef MAX_PHYS_SECTOR_SIZE -static int _write_sectors(unsigned long start, - int count, - const void* buf) -#else +#ifndef MAX_PHYS_SECTOR_SIZE int ata_write_sectors(IF_MD2(int drive,) unsigned long start, int count, const void* buf) -#endif { - int i; - int ret = 0; - long spinup_start; -#ifdef HAVE_ATA_DMA - bool usedma = false; -#endif - -#ifndef MAX_PHYS_SECTOR_SIZE #ifdef HAVE_MULTIDRIVE (void)drive; /* unused for now */ #endif - mutex_lock(&ata_mtx); -#endif - - if (start + count > total_sectors) - panicf("Writing past end of disk"); - last_disk_activity = current_tick; - spinup_start = current_tick; - - ata_led(true); - - if ( sleeping ) { - spinup = true; - if (poweroff) { - if (ata_power_on()) { - ret = -1; - goto error; - } - } - else { - if (perform_soft_reset()) { - ret = -1; - goto error; - } - } - } - - SET_REG(ATA_SELECT, ata_device); - if (!wait_for_rdy()) - { - ret = -2; - goto error; - } - -#ifdef HAVE_ATA_DMA - /* If DMA is supported and parameters are ok for DMA, use it */ - if (dma_mode && ata_dma_setup((void *)buf, count * SECTOR_SIZE, true)) - usedma = true; -#endif - -#ifdef HAVE_LBA48 - if (lba48) - { - SET_REG(ATA_NSECTOR, count >> 8); - SET_REG(ATA_NSECTOR, count & 0xff); - SET_REG(ATA_SECTOR, (start >> 24) & 0xff); /* 31:24 */ - SET_REG(ATA_SECTOR, start & 0xff); /* 7:0 */ - SET_REG(ATA_LCYL, 0); /* 39:32 */ - SET_REG(ATA_LCYL, (start >> 8) & 0xff); /* 15:8 */ - SET_REG(ATA_HCYL, 0); /* 47:40 */ - SET_REG(ATA_HCYL, (start >> 16) & 0xff); /* 23:16 */ - SET_REG(ATA_SELECT, SELECT_LBA | ata_device); -#ifdef HAVE_ATA_DMA - SET_REG(ATA_COMMAND, usedma ? CMD_WRITE_DMA_EXT : CMD_WRITE_SECTORS_EXT); -#else - SET_REG(ATA_COMMAND, CMD_WRITE_SECTORS_EXT); -#endif - } - else -#endif - { - SET_REG(ATA_NSECTOR, count & 0xff); /* 0 means 256 sectors */ - SET_REG(ATA_SECTOR, start & 0xff); - SET_REG(ATA_LCYL, (start >> 8) & 0xff); - SET_REG(ATA_HCYL, (start >> 16) & 0xff); - SET_REG(ATA_SELECT, ((start >> 24) & 0xf) | SELECT_LBA | ata_device); -#ifdef HAVE_ATA_DMA - SET_REG(ATA_COMMAND, usedma ? CMD_WRITE_DMA : CMD_WRITE_SECTORS); -#else - SET_REG(ATA_COMMAND, CMD_WRITE_SECTORS); -#endif - } - -#ifdef HAVE_ATA_DMA - if (usedma) { - if (!ata_dma_finish()) - ret = -7; - else if (spinup) { - spinup_time = current_tick - spinup_start; - spinup = false; - sleeping = false; - poweroff = false; - } - } - else -#endif /* HAVE_ATA_DMA */ - { - for (i=0; i