summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2021-03-04 17:47:01 -0500
committerSolomon Peachy <pizza@shaftnet.org>2021-03-11 19:28:52 +0000
commitbd507fc7b4e3c6145e3d4e17747f6d77a34427f4 (patch)
tree33f3b30792b01912b2b017a02ae7c50e8f59df15
parenteb9f05f835dab48f8d5de4ac364c6a46670cc61c (diff)
downloadrockbox-bd507fc7b4.tar.gz
rockbox-bd507fc7b4.zip
ATA: When device doesn't support powermgmt, only gate ata sleep command.
The FC1307A ATA->SD chipset (used by the common iFlash adapters) doesn't support mandatory ATA power management commands, leading to massive data corruption if they were issued. A workaround was identified (54629073ae) that basically disabled all of rockbox's power management code for these adapters, which extends well beyond the specific ATA commands issued. This patch moves the gating test to the issuance of the actual SLEEP, so that the rest of rockbox's PM code can function as intended. This allows the device to get powered down when idle, yielding potentially significant improvements in battery life. Change-Id: Ia13e2405243fe5efe6f68c3a549ab4933567790b
-rw-r--r--firmware/drivers/ata.c84
1 files changed, 37 insertions, 47 deletions
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c
index 7d306084e4..d2e1ad4a22 100644
--- a/firmware/drivers/ata.c
+++ b/firmware/drivers/ata.c
@@ -164,8 +164,8 @@ static inline bool ata_power_off_timed_out(void)
static ICODE_ATTR int wait_for_bsy(void)
{
long timeout = current_tick + HZ*30;
-
- do
+
+ do
{
if (!(ATA_IN8(ATA_STATUS) & STATUS_BSY))
return 1;
@@ -184,8 +184,8 @@ static ICODE_ATTR int wait_for_rdy(void)
return 0;
timeout = current_tick + HZ*10;
-
- do
+
+ do
{
if (ATA_IN8(ATA_ALT_STATUS) & STATUS_RDY)
return 1;
@@ -220,6 +220,15 @@ static int ata_perform_wakeup(int state)
static int ata_perform_sleep(void)
{
+ /* Don't issue the sleep command if the device
+ doesn't support (mandatory!) ATA power management commands!
+
+ The FC1307A ATA->SD chipset (used by the common iFlash adapters)
+ is the only known offender, and will eat your data if told to sleep.
+ */
+ if (!(identify_info[82] & (1 << 3)))
+ return 0;
+
ATA_OUT8(ATA_SELECT, ata_device);
if(!wait_for_rdy()) {
@@ -250,15 +259,15 @@ static ICODE_ATTR int wait_for_end_of_transfer(void)
{
if (!wait_for_bsy())
return 0;
- return (ATA_IN8(ATA_ALT_STATUS) &
+ return (ATA_IN8(ATA_ALT_STATUS) &
(STATUS_BSY|STATUS_RDY|STATUS_DF|STATUS_DRQ|STATUS_ERR))
== STATUS_RDY;
-}
+}
#if (CONFIG_LED == LED_REAL)
/* Conditionally block LED access for the ATA driver, so the LED can be
* (mis)used for other purposes */
-static void ata_led(bool on)
+static void ata_led(bool on)
{
ata_led_on = on;
if (ata_led_enabled)
@@ -591,7 +600,7 @@ static int cache_sector(unsigned long sector)
{
int rc;
- sector &= ~(phys_sector_mult - 1);
+ sector &= ~(phys_sector_mult - 1);
/* round down to physical sector boundary */
/* check whether the sector is already cached */
@@ -602,7 +611,7 @@ static int cache_sector(unsigned long sector)
sector_cache.inuse = false;
rc = ata_transfer_sectors(sector, phys_sector_mult, sector_cache.data, false);
if (!rc)
- {
+ {
sector_cache.sectornum = sector;
sector_cache.inuse = true;
}
@@ -627,19 +636,19 @@ int ata_read_sectors(IF_MD(int drive,)
(void)drive; /* unused for now */
#endif
mutex_lock(&ata_mtx);
-
+
offset = start & (phys_sector_mult - 1);
-
+
if (offset) /* first partial sector */
{
int partcount = MIN(incount, phys_sector_mult - offset);
-
+
rc = cache_sector(start);
if (rc)
{
rc = rc * 10 - 1;
goto error;
- }
+ }
memcpy(inbuf, sector_cache.data + offset * SECTOR_SIZE,
partcount * SECTOR_SIZE);
@@ -651,7 +660,7 @@ int ata_read_sectors(IF_MD(int drive,)
{
offset = incount & (phys_sector_mult - 1);
incount -= offset;
-
+
if (incount)
{
rc = ata_transfer_sectors(start, incount, inbuf, false);
@@ -693,9 +702,9 @@ int ata_write_sectors(IF_MD(int drive,)
(void)drive; /* unused for now */
#endif
mutex_lock(&ata_mtx);
-
+
offset = start & (phys_sector_mult - 1);
-
+
if (offset) /* first partial sector */
{
int partcount = MIN(count, phys_sector_mult - offset);
@@ -705,7 +714,7 @@ int ata_write_sectors(IF_MD(int drive,)
{
rc = rc * 10 - 1;
goto error;
- }
+ }
memcpy(sector_cache.data + offset * SECTOR_SIZE, buf,
partcount * SECTOR_SIZE);
rc = flush_current_sector();
@@ -713,7 +722,7 @@ int ata_write_sectors(IF_MD(int drive,)
{
rc = rc * 10 - 2;
goto error;
- }
+ }
start += partcount;
buf += partcount * SECTOR_SIZE;
count -= partcount;
@@ -722,7 +731,7 @@ int ata_write_sectors(IF_MD(int drive,)
{
offset = count & (phys_sector_mult - 1);
count -= offset;
-
+
if (count)
{
rc = ata_transfer_sectors(start, count, (void*)buf, true);
@@ -742,7 +751,7 @@ int ata_write_sectors(IF_MD(int drive,)
rc = rc * 10 - 4;
goto error;
}
- memcpy(sector_cache.data, buf, offset * SECTOR_SIZE);
+ memcpy(sector_cache.data, buf, offset * SECTOR_SIZE);
rc = flush_current_sector();
if (rc)
{
@@ -809,30 +818,11 @@ void ata_spindown(int seconds)
bool ata_disk_is_active(void)
{
- /* "active" here means "spinning / not sleeping"
- we normally leave active state by putting the device to
- sleep (using ATA powersave commands) which flushes all writes
- and puts the device into an inactive/quiescent state.
-
- Unfortuantely the CF->SD chipset used by the common iFlash
- adapters does not support ATA powersave, which makes the
- "active/not" distinction irrelevant, so insead we just mirror
- the sd/mmc/flash storage drivers and claim that we're always
- inactive.
- */
- if (!(identify_info[82] & (1 << 3)))
- return false;
-
return ata_state >= ATA_SPINUP;
}
void ata_sleepnow(void)
{
- /* Don't enter sleep if the device doesn't support
- power management. See comment in ata_disk_is_active() */
- if (!(identify_info[82] & (1 << 3)))
- return;
-
if (ata_state >= ATA_SPINUP) {
mutex_lock(&ata_mtx);
if (ata_state == ATA_ON) {
@@ -954,7 +944,7 @@ static int perform_soft_reset(void)
int ata_soft_reset(void)
{
int ret = -6;
-
+
mutex_lock(&ata_mtx);
if (ata_state > ATA_OFF) {
@@ -969,7 +959,7 @@ int ata_soft_reset(void)
static int ata_power_on(void)
{
int rc;
-
+
ide_power_enable(true);
sleep(HZ/4); /* allow voltage to build up */
@@ -1100,7 +1090,7 @@ static int set_features(void)
}
else
features[4].id_word = 88;
-
+
features[4].id_bit = dma_mode & 7;
features[4].parameter = dma_mode;
#endif /* HAVE_ATA_DMA */
@@ -1172,7 +1162,7 @@ static int STORAGE_INIT_ATTR init_and_check(bool hard_reset)
rc = check_registers();
if (rc)
return -30 + rc;
-
+
return 0;
}
@@ -1243,7 +1233,7 @@ int STORAGE_INIT_ATTR ata_init(void)
{ /* (needs BigLBA addressing) */
if (identify_info[102] || identify_info[103])
panicf("Unsupported disk size: >= 2^32 sectors");
-
+
total_sectors = identify_info[100] | (identify_info[101] << 16);
lba48 = true; /* use BigLBA */
}
@@ -1282,7 +1272,7 @@ int STORAGE_INIT_ATTR ata_init(void)
if (rc == 0)
phys_sector_mult = 1;
}
-
+
if (phys_sector_mult > (MAX_PHYS_SECTOR_SIZE/SECTOR_SIZE))
panicf("Unsupported physical sector size: %d",
phys_sector_mult * SECTOR_SIZE);
@@ -1301,7 +1291,7 @@ error:
}
#if (CONFIG_LED == LED_REAL)
-void ata_set_led_enabled(bool enabled)
+void ata_set_led_enabled(bool enabled)
{
ata_led_enabled = enabled;
if (ata_led_enabled)
@@ -1372,7 +1362,7 @@ int ata_num_drives(int first_drive)
{
/* We don't care which logical drive number(s) we have been assigned */
(void)first_drive;
-
+
return 1;
}
#endif