Commit the common portion of FS#9708: ATA (IDE) DMA by Boris Gjenero with a couple cosmetic tweaks and without the inclusion of 'FS#9721: No error check after writes in ata.c'changes (which can be done separately). No code is changed for targets without HAVE_ATA_DMA defined other than to not display DMA modes in the View Disk Info debug screen if not using DMA (Gigabeat F/X/S were). No target uses the code yet but Gigabeat S use will follow shortly.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@20298 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
01c390d5c5
commit
bc8cab4c24
3 changed files with 229 additions and 59 deletions
|
@ -2029,7 +2029,7 @@ static int disk_callback(int btn, struct gui_synclist *lists)
|
|||
simplelist_addline(SIMPLELIST_ADD_LINE,
|
||||
"No timing info");
|
||||
}
|
||||
#if defined (TOSHIBA_GIGABEAT_F) || defined (TOSHIBA_GIGABEAT_S)
|
||||
#ifdef HAVE_ATA_DMA
|
||||
if (identify_info[63] & (1<<0)) {
|
||||
char mdma0[2], mdma1[2], mdma2[2];
|
||||
mdma0[1] = mdma1[1] = mdma2[1] = 0;
|
||||
|
@ -2047,24 +2047,25 @@ static int disk_callback(int btn, struct gui_synclist *lists)
|
|||
simplelist_addline(SIMPLELIST_ADD_LINE,
|
||||
"No MDMA mode info");
|
||||
}
|
||||
if (identify_info[88] & (1<<0)) {
|
||||
char udma0[2], udma1[2], udma2[2], udma3[2], udma4[2], udma5[2];
|
||||
udma0[1] = udma1[1] = udma2[1] = udma3[1] = udma4[1] = udma5[1] = 0;
|
||||
if (identify_info[53] & (1<<2)) {
|
||||
char udma0[2], udma1[2], udma2[2], udma3[2], udma4[2], udma5[2], udma6[2];
|
||||
udma0[1] = udma1[1] = udma2[1] = udma3[1] = udma4[1] = udma5[1] = udma6[1] = 0;
|
||||
udma0[0] = (identify_info[88] & (1<<0)) ? '0' : 0;
|
||||
udma1[0] = (identify_info[88] & (1<<1)) ? '1' : 0;
|
||||
udma2[0] = (identify_info[88] & (1<<2)) ? '2' : 0;
|
||||
udma3[0] = (identify_info[88] & (1<<3)) ? '3' : 0;
|
||||
udma4[0] = (identify_info[88] & (1<<4)) ? '4' : 0;
|
||||
udma5[0] = (identify_info[88] & (1<<5)) ? '5' : 0;
|
||||
udma6[0] = (identify_info[88] & (1<<6)) ? '6' : 0;
|
||||
simplelist_addline(SIMPLELIST_ADD_LINE,
|
||||
"UDMA modes: %s %s %s %s %s %s", udma0, udma1, udma2,
|
||||
udma3, udma4, udma5);
|
||||
"UDMA modes: %s %s %s %s %s %s %s", udma0, udma1, udma2,
|
||||
udma3, udma4, udma5, udma6);
|
||||
}
|
||||
else {
|
||||
simplelist_addline(SIMPLELIST_ADD_LINE,
|
||||
"No UDMA mode info");
|
||||
}
|
||||
#endif /* defined (TOSHIBA_GIGABEAT_F) || defined (TOSHIBA_GIGABEAT_S) */
|
||||
#endif /* HAVE_ATA_DMA */
|
||||
timing_info_present = identify_info[53] & (1<<1);
|
||||
if(timing_info_present) {
|
||||
i = identify_info[49] & (1<<11);
|
||||
|
@ -2079,6 +2080,18 @@ static int disk_callback(int btn, struct gui_synclist *lists)
|
|||
}
|
||||
simplelist_addline(SIMPLELIST_ADD_LINE,
|
||||
"Cluster size: %d bytes", fat_get_cluster_size(IF_MV(0)));
|
||||
#ifdef HAVE_ATA_DMA
|
||||
i = ata_get_dma_mode();
|
||||
if (i == 0) {
|
||||
simplelist_addline(SIMPLELIST_ADD_LINE,
|
||||
"DMA not enabled");
|
||||
} else {
|
||||
simplelist_addline(SIMPLELIST_ADD_LINE,
|
||||
"DMA mode: %s %c",
|
||||
(i & 0x40) ? "UDMA" : "MDMA",
|
||||
'0' + (i & 7));
|
||||
}
|
||||
#endif /* HAVE_ATA_DMA */
|
||||
return btn;
|
||||
}
|
||||
#else /* No SD, MMC or ATA */
|
||||
|
|
|
@ -60,6 +60,12 @@
|
|||
#define CMD_SLEEP 0xE6
|
||||
#define CMD_SET_FEATURES 0xEF
|
||||
#define CMD_SECURITY_FREEZE_LOCK 0xF5
|
||||
#ifdef HAVE_ATA_DMA
|
||||
#define CMD_READ_DMA 0xC8
|
||||
#define CMD_READ_DMA_EXT 0x25
|
||||
#define CMD_WRITE_DMA 0xCA
|
||||
#define CMD_WRITE_DMA_EXT 0x35
|
||||
#endif
|
||||
|
||||
/* Should all be < 0x100 (which are reserved for control messages) */
|
||||
#define Q_SLEEP 0
|
||||
|
@ -188,6 +194,10 @@ static struct sector_cache_entry sector_cache;
|
|||
static int phys_sector_mult = 1;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ATA_DMA
|
||||
static int dma_mode = 0;
|
||||
#endif
|
||||
|
||||
static int ata_power_on(void);
|
||||
static int perform_soft_reset(void);
|
||||
static int set_multiple_mode(int sectors);
|
||||
|
@ -308,6 +318,9 @@ int ata_read_sectors(IF_MV2(int drive,)
|
|||
int count;
|
||||
void* buf;
|
||||
long spinup_start;
|
||||
#ifdef HAVE_ATA_DMA
|
||||
bool usedma = false;
|
||||
#endif
|
||||
|
||||
#ifndef MAX_PHYS_SECTOR_SIZE
|
||||
#ifdef HAVE_MULTIVOLUME
|
||||
|
@ -358,6 +371,12 @@ int ata_read_sectors(IF_MV2(int drive,)
|
|||
ret = 0;
|
||||
last_disk_activity = current_tick;
|
||||
|
||||
#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))
|
||||
usedma = true;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LBA48
|
||||
if (lba48)
|
||||
{
|
||||
|
@ -370,7 +389,11 @@ int ata_read_sectors(IF_MV2(int drive,)
|
|||
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_READ_DMA_EXT : CMD_READ_MULTIPLE_EXT);
|
||||
#else
|
||||
SET_REG(ATA_COMMAND, CMD_READ_MULTIPLE_EXT);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
@ -380,7 +403,11 @@ int ata_read_sectors(IF_MV2(int drive,)
|
|||
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_READ_DMA : CMD_READ_MULTIPLE);
|
||||
#else
|
||||
SET_REG(ATA_COMMAND, CMD_READ_MULTIPLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* wait at least 400ns between writing command and reading status */
|
||||
|
@ -390,22 +417,13 @@ int ata_read_sectors(IF_MV2(int drive,)
|
|||
__asm__ volatile ("nop");
|
||||
__asm__ volatile ("nop");
|
||||
|
||||
while (count) {
|
||||
int sectors;
|
||||
int wordcount;
|
||||
int status;
|
||||
#ifdef HAVE_ATA_DMA
|
||||
if (usedma) {
|
||||
if (!ata_dma_finish())
|
||||
ret = -7;
|
||||
|
||||
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.
|
||||
*/
|
||||
if (ret != 0) {
|
||||
perform_soft_reset();
|
||||
ret = -5;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
@ -415,36 +433,67 @@ int ata_read_sectors(IF_MV2(int drive,)
|
|||
sleeping = false;
|
||||
poweroff = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif /* HAVE_ATA_DMA */
|
||||
{
|
||||
while (count) {
|
||||
int sectors;
|
||||
int wordcount;
|
||||
int status;
|
||||
|
||||
/* read the status register exactly once per loop */
|
||||
status = ATA_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.
|
||||
|
||||
if (count >= multisectors )
|
||||
sectors = multisectors;
|
||||
else
|
||||
sectors = count;
|
||||
We choose alternative 2.
|
||||
*/
|
||||
perform_soft_reset();
|
||||
ret = -5;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
wordcount = sectors * SECTOR_SIZE / 2;
|
||||
if (spinup) {
|
||||
spinup_time = current_tick - spinup_start;
|
||||
spinup = false;
|
||||
sleeping = false;
|
||||
poweroff = false;
|
||||
}
|
||||
|
||||
copy_read_sectors(buf, wordcount);
|
||||
/* read the status register exactly once per loop */
|
||||
status = ATA_STATUS;
|
||||
|
||||
/*
|
||||
"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 = -6;
|
||||
goto retry;
|
||||
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 = -6;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
buf += sectors * SECTOR_SIZE; /* Advance one chunk of sectors */
|
||||
count -= sectors;
|
||||
|
||||
last_disk_activity = current_tick;
|
||||
}
|
||||
|
||||
buf += sectors * SECTOR_SIZE; /* Advance one chunk of sectors */
|
||||
count -= sectors;
|
||||
|
||||
last_disk_activity = current_tick;
|
||||
}
|
||||
|
||||
if(!ret && !wait_for_end_of_transfer()) {
|
||||
|
@ -515,6 +564,9 @@ int ata_write_sectors(IF_MV2(int drive,)
|
|||
int i;
|
||||
int ret = 0;
|
||||
long spinup_start;
|
||||
#ifdef HAVE_ATA_DMA
|
||||
bool usedma = false;
|
||||
#endif
|
||||
|
||||
#ifndef MAX_PHYS_SECTOR_SIZE
|
||||
#ifdef HAVE_MULTIVOLUME
|
||||
|
@ -554,6 +606,12 @@ int ata_write_sectors(IF_MV2(int drive,)
|
|||
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)
|
||||
{
|
||||
|
@ -566,7 +624,11 @@ int ata_write_sectors(IF_MV2(int drive,)
|
|||
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
|
||||
|
@ -576,32 +638,51 @@ int ata_write_sectors(IF_MV2(int drive,)
|
|||
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
|
||||
}
|
||||
|
||||
for (i=0; i<count; i++) {
|
||||
|
||||
if (!wait_for_start_of_transfer()) {
|
||||
ret = -3;
|
||||
break;
|
||||
}
|
||||
|
||||
if (spinup) {
|
||||
#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<count; i++) {
|
||||
|
||||
copy_write_sectors(buf, SECTOR_SIZE/2);
|
||||
if (!wait_for_start_of_transfer()) {
|
||||
ret = -3;
|
||||
break;
|
||||
}
|
||||
|
||||
if (spinup) {
|
||||
spinup_time = current_tick - spinup_start;
|
||||
spinup = false;
|
||||
sleeping = false;
|
||||
poweroff = false;
|
||||
}
|
||||
|
||||
copy_write_sectors(buf, SECTOR_SIZE/2);
|
||||
|
||||
#ifdef USE_INTERRUPT
|
||||
/* reading the status register clears the interrupt */
|
||||
j = ATA_STATUS;
|
||||
/* reading the status register clears the interrupt */
|
||||
j = ATA_STATUS;
|
||||
#endif
|
||||
buf += SECTOR_SIZE;
|
||||
buf += SECTOR_SIZE;
|
||||
|
||||
last_disk_activity = current_tick;
|
||||
last_disk_activity = current_tick;
|
||||
}
|
||||
}
|
||||
|
||||
if(!ret && !wait_for_end_of_transfer()) {
|
||||
|
@ -1039,7 +1120,12 @@ static int perform_soft_reset(void)
|
|||
SET_REG(ATA_CONTROL, CONTROL_nIEN|CONTROL_SRST );
|
||||
sleep(1); /* >= 5us */
|
||||
|
||||
#ifdef HAVE_ATA_DMA
|
||||
/* DMA requires INTRQ be enabled */
|
||||
SET_REG(ATA_CONTROL, 0);
|
||||
#else
|
||||
SET_REG(ATA_CONTROL, CONTROL_nIEN);
|
||||
#endif
|
||||
sleep(1); /* >2ms */
|
||||
|
||||
/* This little sucker can take up to 30 seconds */
|
||||
|
@ -1179,6 +1265,22 @@ static int set_multiple_mode(int sectors)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ATA_DMA
|
||||
static int get_best_mode(unsigned short identword, int max, int modetype)
|
||||
{
|
||||
unsigned short testbit = 1u << max;
|
||||
|
||||
while (1) {
|
||||
if (identword & testbit)
|
||||
return max | modetype;
|
||||
testbit >>= 1;
|
||||
if (!testbit)
|
||||
return 0;
|
||||
max--;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static int set_features(void)
|
||||
{
|
||||
static struct {
|
||||
|
@ -1191,6 +1293,9 @@ static int set_features(void)
|
|||
{ 83, 3, 0x05, 0x80 }, /* adv. power management: lowest w/o standby */
|
||||
{ 83, 9, 0x42, 0x80 }, /* acoustic management: lowest noise */
|
||||
{ 82, 6, 0xaa, 0 }, /* enable read look-ahead */
|
||||
#ifdef HAVE_ATA_DMA
|
||||
{ 0, 0, 0x03, 0 }, /* DMA mode */
|
||||
#endif
|
||||
};
|
||||
int i;
|
||||
int pio_mode = 2;
|
||||
|
@ -1204,6 +1309,23 @@ static int set_features(void)
|
|||
|
||||
/* Update the table: set highest supported pio mode that we also support */
|
||||
features[0].parameter = 8 + pio_mode;
|
||||
|
||||
#ifdef HAVE_ATA_DMA
|
||||
if (identify_info[53] & (1<<2))
|
||||
/* Ultra DMA mode info present, find a mode */
|
||||
dma_mode = get_best_mode(identify_info[88], ATA_MAX_UDMA, 0x40);
|
||||
|
||||
if (!dma_mode) {
|
||||
/* No UDMA mode found, try to find a multi-word DMA mode */
|
||||
dma_mode = get_best_mode(identify_info[63], ATA_MAX_MWDMA, 0x20);
|
||||
features[4].id_word = 63;
|
||||
}
|
||||
else
|
||||
features[4].id_word = 88;
|
||||
|
||||
features[4].id_bit = dma_mode & 7;
|
||||
features[4].parameter = dma_mode;
|
||||
#endif /* HAVE_ATA_DMA */
|
||||
|
||||
SET_REG(ATA_SELECT, ata_device);
|
||||
|
||||
|
@ -1237,6 +1359,10 @@ static int set_features(void)
|
|||
ata_set_pio_timings(pio_mode);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ATA_DMA
|
||||
ata_dma_set_mode(dma_mode);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1305,6 +1431,11 @@ int ata_init(void)
|
|||
sleep(HZ/4); /* allow voltage to build up */
|
||||
}
|
||||
|
||||
#ifdef HAVE_ATA_DMA
|
||||
/* DMA requires INTRQ be enabled */
|
||||
SET_REG(ATA_CONTROL, 0);
|
||||
#endif
|
||||
|
||||
/* first try, hard reset at cold start only */
|
||||
rc = init_and_check(coldstart);
|
||||
|
||||
|
@ -1450,3 +1581,17 @@ void ata_get_info(struct storage_info *info)
|
|||
info->revision=revision;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ATA_DMA
|
||||
/* Returns last DMA mode as set by set_features() */
|
||||
int ata_get_dma_mode(void)
|
||||
{
|
||||
return dma_mode;
|
||||
}
|
||||
|
||||
/* Needed to allow updating while waiting for DMA to complete */
|
||||
void ata_keep_active(void)
|
||||
{
|
||||
last_disk_activity = current_tick;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -60,5 +60,17 @@ bool ata_present(IF_MV_NONVOID(int drive));
|
|||
long ata_last_disk_activity(void);
|
||||
int ata_spinup_time(void); /* ticks */
|
||||
|
||||
#ifdef HAVE_ATA_DMA
|
||||
/* Needed to allow updating while waiting for DMA to complete */
|
||||
void ata_keep_active(void);
|
||||
/* Returns current DMA mode */
|
||||
int ata_get_dma_mode(void);
|
||||
/* Set DMA mode for ATA interface */
|
||||
void ata_dma_set_mode(unsigned char mode);
|
||||
/* Sets up DMA transfer */
|
||||
bool ata_dma_setup(void *addr, unsigned long bytes, bool write);
|
||||
/* Waits for DMA transfer completion */
|
||||
bool ata_dma_finish(void);
|
||||
#endif /* HAVE_ATA_DMA */
|
||||
|
||||
#endif
|
||||
#endif /* __ATA_H__ */
|
||||
|
|
Loading…
Reference in a new issue