diff options
author | Solomon Peachy <pizza@shaftnet.org> | 2021-03-04 17:47:01 -0500 |
---|---|---|
committer | Solomon Peachy <pizza@shaftnet.org> | 2021-03-11 19:28:52 +0000 |
commit | bd507fc7b4e3c6145e3d4e17747f6d77a34427f4 (patch) | |
tree | 33f3b30792b01912b2b017a02ae7c50e8f59df15 /firmware/drivers/ata.c | |
parent | eb9f05f835dab48f8d5de4ac364c6a46670cc61c (diff) | |
download | rockbox-bd507fc7b4e3c6145e3d4e17747f6d77a34427f4.tar.gz rockbox-bd507fc7b4e3c6145e3d4e17747f6d77a34427f4.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
Diffstat (limited to 'firmware/drivers/ata.c')
-rw-r--r-- | firmware/drivers/ata.c | 84 |
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) | |||
164 | static ICODE_ATTR int wait_for_bsy(void) | 164 | static ICODE_ATTR int wait_for_bsy(void) |
165 | { | 165 | { |
166 | long timeout = current_tick + HZ*30; | 166 | long timeout = current_tick + HZ*30; |
167 | 167 | ||
168 | do | 168 | do |
169 | { | 169 | { |
170 | if (!(ATA_IN8(ATA_STATUS) & STATUS_BSY)) | 170 | if (!(ATA_IN8(ATA_STATUS) & STATUS_BSY)) |
171 | return 1; | 171 | return 1; |
@@ -184,8 +184,8 @@ static ICODE_ATTR int wait_for_rdy(void) | |||
184 | return 0; | 184 | return 0; |
185 | 185 | ||
186 | timeout = current_tick + HZ*10; | 186 | timeout = current_tick + HZ*10; |
187 | 187 | ||
188 | do | 188 | do |
189 | { | 189 | { |
190 | if (ATA_IN8(ATA_ALT_STATUS) & STATUS_RDY) | 190 | if (ATA_IN8(ATA_ALT_STATUS) & STATUS_RDY) |
191 | return 1; | 191 | return 1; |
@@ -220,6 +220,15 @@ static int ata_perform_wakeup(int state) | |||
220 | 220 | ||
221 | static int ata_perform_sleep(void) | 221 | static int ata_perform_sleep(void) |
222 | { | 222 | { |
223 | /* Don't issue the sleep command if the device | ||
224 | doesn't support (mandatory!) ATA power management commands! | ||
225 | |||
226 | The FC1307A ATA->SD chipset (used by the common iFlash adapters) | ||
227 | is the only known offender, and will eat your data if told to sleep. | ||
228 | */ | ||
229 | if (!(identify_info[82] & (1 << 3))) | ||
230 | return 0; | ||
231 | |||
223 | ATA_OUT8(ATA_SELECT, ata_device); | 232 | ATA_OUT8(ATA_SELECT, ata_device); |
224 | 233 | ||
225 | if(!wait_for_rdy()) { | 234 | if(!wait_for_rdy()) { |
@@ -250,15 +259,15 @@ static ICODE_ATTR int wait_for_end_of_transfer(void) | |||
250 | { | 259 | { |
251 | if (!wait_for_bsy()) | 260 | if (!wait_for_bsy()) |
252 | return 0; | 261 | return 0; |
253 | return (ATA_IN8(ATA_ALT_STATUS) & | 262 | return (ATA_IN8(ATA_ALT_STATUS) & |
254 | (STATUS_BSY|STATUS_RDY|STATUS_DF|STATUS_DRQ|STATUS_ERR)) | 263 | (STATUS_BSY|STATUS_RDY|STATUS_DF|STATUS_DRQ|STATUS_ERR)) |
255 | == STATUS_RDY; | 264 | == STATUS_RDY; |
256 | } | 265 | } |
257 | 266 | ||
258 | #if (CONFIG_LED == LED_REAL) | 267 | #if (CONFIG_LED == LED_REAL) |
259 | /* Conditionally block LED access for the ATA driver, so the LED can be | 268 | /* Conditionally block LED access for the ATA driver, so the LED can be |
260 | * (mis)used for other purposes */ | 269 | * (mis)used for other purposes */ |
261 | static void ata_led(bool on) | 270 | static void ata_led(bool on) |
262 | { | 271 | { |
263 | ata_led_on = on; | 272 | ata_led_on = on; |
264 | if (ata_led_enabled) | 273 | if (ata_led_enabled) |
@@ -591,7 +600,7 @@ static int cache_sector(unsigned long sector) | |||
591 | { | 600 | { |
592 | int rc; | 601 | int rc; |
593 | 602 | ||
594 | sector &= ~(phys_sector_mult - 1); | 603 | sector &= ~(phys_sector_mult - 1); |
595 | /* round down to physical sector boundary */ | 604 | /* round down to physical sector boundary */ |
596 | 605 | ||
597 | /* check whether the sector is already cached */ | 606 | /* check whether the sector is already cached */ |
@@ -602,7 +611,7 @@ static int cache_sector(unsigned long sector) | |||
602 | sector_cache.inuse = false; | 611 | sector_cache.inuse = false; |
603 | rc = ata_transfer_sectors(sector, phys_sector_mult, sector_cache.data, false); | 612 | rc = ata_transfer_sectors(sector, phys_sector_mult, sector_cache.data, false); |
604 | if (!rc) | 613 | if (!rc) |
605 | { | 614 | { |
606 | sector_cache.sectornum = sector; | 615 | sector_cache.sectornum = sector; |
607 | sector_cache.inuse = true; | 616 | sector_cache.inuse = true; |
608 | } | 617 | } |
@@ -627,19 +636,19 @@ int ata_read_sectors(IF_MD(int drive,) | |||
627 | (void)drive; /* unused for now */ | 636 | (void)drive; /* unused for now */ |
628 | #endif | 637 | #endif |
629 | mutex_lock(&ata_mtx); | 638 | mutex_lock(&ata_mtx); |
630 | 639 | ||
631 | offset = start & (phys_sector_mult - 1); | 640 | offset = start & (phys_sector_mult - 1); |
632 | 641 | ||
633 | if (offset) /* first partial sector */ | 642 | if (offset) /* first partial sector */ |
634 | { | 643 | { |
635 | int partcount = MIN(incount, phys_sector_mult - offset); | 644 | int partcount = MIN(incount, phys_sector_mult - offset); |
636 | 645 | ||
637 | rc = cache_sector(start); | 646 | rc = cache_sector(start); |
638 | if (rc) | 647 | if (rc) |
639 | { | 648 | { |
640 | rc = rc * 10 - 1; | 649 | rc = rc * 10 - 1; |
641 | goto error; | 650 | goto error; |
642 | } | 651 | } |
643 | memcpy(inbuf, sector_cache.data + offset * SECTOR_SIZE, | 652 | memcpy(inbuf, sector_cache.data + offset * SECTOR_SIZE, |
644 | partcount * SECTOR_SIZE); | 653 | partcount * SECTOR_SIZE); |
645 | 654 | ||
@@ -651,7 +660,7 @@ int ata_read_sectors(IF_MD(int drive,) | |||
651 | { | 660 | { |
652 | offset = incount & (phys_sector_mult - 1); | 661 | offset = incount & (phys_sector_mult - 1); |
653 | incount -= offset; | 662 | incount -= offset; |
654 | 663 | ||
655 | if (incount) | 664 | if (incount) |
656 | { | 665 | { |
657 | rc = ata_transfer_sectors(start, incount, inbuf, false); | 666 | rc = ata_transfer_sectors(start, incount, inbuf, false); |
@@ -693,9 +702,9 @@ int ata_write_sectors(IF_MD(int drive,) | |||
693 | (void)drive; /* unused for now */ | 702 | (void)drive; /* unused for now */ |
694 | #endif | 703 | #endif |
695 | mutex_lock(&ata_mtx); | 704 | mutex_lock(&ata_mtx); |
696 | 705 | ||
697 | offset = start & (phys_sector_mult - 1); | 706 | offset = start & (phys_sector_mult - 1); |
698 | 707 | ||
699 | if (offset) /* first partial sector */ | 708 | if (offset) /* first partial sector */ |
700 | { | 709 | { |
701 | int partcount = MIN(count, phys_sector_mult - offset); | 710 | int partcount = MIN(count, phys_sector_mult - offset); |
@@ -705,7 +714,7 @@ int ata_write_sectors(IF_MD(int drive,) | |||
705 | { | 714 | { |
706 | rc = rc * 10 - 1; | 715 | rc = rc * 10 - 1; |
707 | goto error; | 716 | goto error; |
708 | } | 717 | } |
709 | memcpy(sector_cache.data + offset * SECTOR_SIZE, buf, | 718 | memcpy(sector_cache.data + offset * SECTOR_SIZE, buf, |
710 | partcount * SECTOR_SIZE); | 719 | partcount * SECTOR_SIZE); |
711 | rc = flush_current_sector(); | 720 | rc = flush_current_sector(); |
@@ -713,7 +722,7 @@ int ata_write_sectors(IF_MD(int drive,) | |||
713 | { | 722 | { |
714 | rc = rc * 10 - 2; | 723 | rc = rc * 10 - 2; |
715 | goto error; | 724 | goto error; |
716 | } | 725 | } |
717 | start += partcount; | 726 | start += partcount; |
718 | buf += partcount * SECTOR_SIZE; | 727 | buf += partcount * SECTOR_SIZE; |
719 | count -= partcount; | 728 | count -= partcount; |
@@ -722,7 +731,7 @@ int ata_write_sectors(IF_MD(int drive,) | |||
722 | { | 731 | { |
723 | offset = count & (phys_sector_mult - 1); | 732 | offset = count & (phys_sector_mult - 1); |
724 | count -= offset; | 733 | count -= offset; |
725 | 734 | ||
726 | if (count) | 735 | if (count) |
727 | { | 736 | { |
728 | rc = ata_transfer_sectors(start, count, (void*)buf, true); | 737 | rc = ata_transfer_sectors(start, count, (void*)buf, true); |
@@ -742,7 +751,7 @@ int ata_write_sectors(IF_MD(int drive,) | |||
742 | rc = rc * 10 - 4; | 751 | rc = rc * 10 - 4; |
743 | goto error; | 752 | goto error; |
744 | } | 753 | } |
745 | memcpy(sector_cache.data, buf, offset * SECTOR_SIZE); | 754 | memcpy(sector_cache.data, buf, offset * SECTOR_SIZE); |
746 | rc = flush_current_sector(); | 755 | rc = flush_current_sector(); |
747 | if (rc) | 756 | if (rc) |
748 | { | 757 | { |
@@ -809,30 +818,11 @@ void ata_spindown(int seconds) | |||
809 | 818 | ||
810 | bool ata_disk_is_active(void) | 819 | bool ata_disk_is_active(void) |
811 | { | 820 | { |
812 | /* "active" here means "spinning / not sleeping" | ||
813 | we normally leave active state by putting the device to | ||
814 | sleep (using ATA powersave commands) which flushes all writes | ||
815 | and puts the device into an inactive/quiescent state. | ||
816 | |||
817 | Unfortuantely the CF->SD chipset used by the common iFlash | ||
818 | adapters does not support ATA powersave, which makes the | ||
819 | "active/not" distinction irrelevant, so insead we just mirror | ||
820 | the sd/mmc/flash storage drivers and claim that we're always | ||
821 | inactive. | ||
822 | */ | ||
823 | if (!(identify_info[82] & (1 << 3))) | ||
824 | return false; | ||
825 | |||
826 | return ata_state >= ATA_SPINUP; | 821 | return ata_state >= ATA_SPINUP; |
827 | } | 822 | } |
828 | 823 | ||
829 | void ata_sleepnow(void) | 824 | void ata_sleepnow(void) |
830 | { | 825 | { |
831 | /* Don't enter sleep if the device doesn't support | ||
832 | power management. See comment in ata_disk_is_active() */ | ||
833 | if (!(identify_info[82] & (1 << 3))) | ||
834 | return; | ||
835 | |||
836 | if (ata_state >= ATA_SPINUP) { | 826 | if (ata_state >= ATA_SPINUP) { |
837 | mutex_lock(&ata_mtx); | 827 | mutex_lock(&ata_mtx); |
838 | if (ata_state == ATA_ON) { | 828 | if (ata_state == ATA_ON) { |
@@ -954,7 +944,7 @@ static int perform_soft_reset(void) | |||
954 | int ata_soft_reset(void) | 944 | int ata_soft_reset(void) |
955 | { | 945 | { |
956 | int ret = -6; | 946 | int ret = -6; |
957 | 947 | ||
958 | mutex_lock(&ata_mtx); | 948 | mutex_lock(&ata_mtx); |
959 | 949 | ||
960 | if (ata_state > ATA_OFF) { | 950 | if (ata_state > ATA_OFF) { |
@@ -969,7 +959,7 @@ int ata_soft_reset(void) | |||
969 | static int ata_power_on(void) | 959 | static int ata_power_on(void) |
970 | { | 960 | { |
971 | int rc; | 961 | int rc; |
972 | 962 | ||
973 | ide_power_enable(true); | 963 | ide_power_enable(true); |
974 | sleep(HZ/4); /* allow voltage to build up */ | 964 | sleep(HZ/4); /* allow voltage to build up */ |
975 | 965 | ||
@@ -1100,7 +1090,7 @@ static int set_features(void) | |||
1100 | } | 1090 | } |
1101 | else | 1091 | else |
1102 | features[4].id_word = 88; | 1092 | features[4].id_word = 88; |
1103 | 1093 | ||
1104 | features[4].id_bit = dma_mode & 7; | 1094 | features[4].id_bit = dma_mode & 7; |
1105 | features[4].parameter = dma_mode; | 1095 | features[4].parameter = dma_mode; |
1106 | #endif /* HAVE_ATA_DMA */ | 1096 | #endif /* HAVE_ATA_DMA */ |
@@ -1172,7 +1162,7 @@ static int STORAGE_INIT_ATTR init_and_check(bool hard_reset) | |||
1172 | rc = check_registers(); | 1162 | rc = check_registers(); |
1173 | if (rc) | 1163 | if (rc) |
1174 | return -30 + rc; | 1164 | return -30 + rc; |
1175 | 1165 | ||
1176 | return 0; | 1166 | return 0; |
1177 | } | 1167 | } |
1178 | 1168 | ||
@@ -1243,7 +1233,7 @@ int STORAGE_INIT_ATTR ata_init(void) | |||
1243 | { /* (needs BigLBA addressing) */ | 1233 | { /* (needs BigLBA addressing) */ |
1244 | if (identify_info[102] || identify_info[103]) | 1234 | if (identify_info[102] || identify_info[103]) |
1245 | panicf("Unsupported disk size: >= 2^32 sectors"); | 1235 | panicf("Unsupported disk size: >= 2^32 sectors"); |
1246 | 1236 | ||
1247 | total_sectors = identify_info[100] | (identify_info[101] << 16); | 1237 | total_sectors = identify_info[100] | (identify_info[101] << 16); |
1248 | lba48 = true; /* use BigLBA */ | 1238 | lba48 = true; /* use BigLBA */ |
1249 | } | 1239 | } |
@@ -1282,7 +1272,7 @@ int STORAGE_INIT_ATTR ata_init(void) | |||
1282 | if (rc == 0) | 1272 | if (rc == 0) |
1283 | phys_sector_mult = 1; | 1273 | phys_sector_mult = 1; |
1284 | } | 1274 | } |
1285 | 1275 | ||
1286 | if (phys_sector_mult > (MAX_PHYS_SECTOR_SIZE/SECTOR_SIZE)) | 1276 | if (phys_sector_mult > (MAX_PHYS_SECTOR_SIZE/SECTOR_SIZE)) |
1287 | panicf("Unsupported physical sector size: %d", | 1277 | panicf("Unsupported physical sector size: %d", |
1288 | phys_sector_mult * SECTOR_SIZE); | 1278 | phys_sector_mult * SECTOR_SIZE); |
@@ -1301,7 +1291,7 @@ error: | |||
1301 | } | 1291 | } |
1302 | 1292 | ||
1303 | #if (CONFIG_LED == LED_REAL) | 1293 | #if (CONFIG_LED == LED_REAL) |
1304 | void ata_set_led_enabled(bool enabled) | 1294 | void ata_set_led_enabled(bool enabled) |
1305 | { | 1295 | { |
1306 | ata_led_enabled = enabled; | 1296 | ata_led_enabled = enabled; |
1307 | if (ata_led_enabled) | 1297 | if (ata_led_enabled) |
@@ -1372,7 +1362,7 @@ int ata_num_drives(int first_drive) | |||
1372 | { | 1362 | { |
1373 | /* We don't care which logical drive number(s) we have been assigned */ | 1363 | /* We don't care which logical drive number(s) we have been assigned */ |
1374 | (void)first_drive; | 1364 | (void)first_drive; |
1375 | 1365 | ||
1376 | return 1; | 1366 | return 1; |
1377 | } | 1367 | } |
1378 | #endif | 1368 | #endif |