summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2021-04-22 17:20:00 -0400
committerSolomon Peachy <pizza@shaftnet.org>2021-04-23 13:07:45 +0000
commitaab72f969f10367d9e74864a6f6b8635d7ebaa1f (patch)
tree714bc2653574509a81c91c4e8ccc9e76349b8286
parent0271c0ed36791df28d793a27926d83a72610f5ef (diff)
downloadrockbox-aab72f969f10367d9e74864a6f6b8635d7ebaa1f.tar.gz
rockbox-aab72f969f10367d9e74864a6f6b8635d7ebaa1f.zip
ata: Rework power management behavior a bit
After continued reports of corruption using iFlash adapters, I went digging for more clues, and this combination of changes seemed to solve data corruption with the iFlash adapters on the ipod video: 1) Instead of SLEEP, use STANDBY_IMMEDIATE when we detect drive as an SSD or CFA-compliant device. The latter is technically higher power than the former, but what this means in practice is unclear. 2) Don't check ATA powermanagement flag prior to issuing powermgmt commands. This reverts the previous "workaround" for the FC1307A -- and PM is a mandatory part of the ATA spec for any CFA device. 3) Prior to issuing SLEEP/STANDBY_IMMEDIATE, issue FLUSH CACHE. The ATA spec says this is redundant for the latter, but says nothing about the former. Either way it is always safe to call first. 4) Delete all other FC1307A_WORKAROUND code related to powermgmt flags. Change-Id: I492d06664c097d9bbd5cccfb9f5b516da165b1ee
-rw-r--r--firmware/drivers/ata.c107
1 files changed, 65 insertions, 42 deletions
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c
index 3514511270..a02539c896 100644
--- a/firmware/drivers/ata.c
+++ b/firmware/drivers/ata.c
@@ -36,14 +36,6 @@
36#include "storage.h" 36#include "storage.h"
37#include "logf.h" 37#include "logf.h"
38 38
39/* The FC1307A ATA->SD chipset (used by the common iFlash adapters)
40 doesn't support mandatory ATA power management commands. Unfortunately
41 simply gating off the SLEEP command isn't sufficient; we need to
42 disable advanced powersaving entirely because otherwise we might
43 kill power before the device has finished flusing writes.
44*/
45//#define FC1307A_WORKAROUND
46
47#define SELECT_DEVICE1 0x10 39#define SELECT_DEVICE1 0x10
48#define SELECT_LBA 0x40 40#define SELECT_LBA 0x40
49 41
@@ -62,6 +54,7 @@
62#define CMD_STANDBY 0xE2 54#define CMD_STANDBY 0xE2
63#define CMD_IDENTIFY 0xEC 55#define CMD_IDENTIFY 0xEC
64#define CMD_SLEEP 0xE6 56#define CMD_SLEEP 0xE6
57#define CMD_FLUSH_CACHE 0xE7
65#define CMD_SET_FEATURES 0xEF 58#define CMD_SET_FEATURES 0xEF
66#define CMD_SECURITY_FREEZE_LOCK 0xF5 59#define CMD_SECURITY_FREEZE_LOCK 0xF5
67#ifdef HAVE_ATA_DMA 60#ifdef HAVE_ATA_DMA
@@ -75,7 +68,6 @@
75 68
76#ifdef HAVE_ATA_POWER_OFF 69#ifdef HAVE_ATA_POWER_OFF
77#define ATA_POWER_OFF_TIMEOUT 2*HZ 70#define ATA_POWER_OFF_TIMEOUT 2*HZ
78#define ATA_POWER_OFF_TIMEOUT_NOPM 5*HZ
79#endif 71#endif
80 72
81#if defined(HAVE_USBSTACK) 73#if defined(HAVE_USBSTACK)
@@ -160,12 +152,7 @@ static inline bool ata_sleep_timed_out(void)
160static inline void schedule_ata_power_off(void) 152static inline void schedule_ata_power_off(void)
161{ 153{
162#ifdef HAVE_ATA_POWER_OFF 154#ifdef HAVE_ATA_POWER_OFF
163 power_off_tick = current_tick; 155 power_off_tick = current_tick + ATA_POWER_OFF_TIMEOUT;
164 /* If our device doesn't support SLEEP give a bit more time to flush */
165 if (!(identify_info[82] & (1 << 3)))
166 power_off_tick += ATA_POWER_OFF_TIMEOUT_NOPM;
167 else
168 power_off_tick += ATA_POWER_OFF_TIMEOUT;
169#endif 156#endif
170} 157}
171 158
@@ -239,12 +226,6 @@ static int ata_perform_wakeup(int state)
239 226
240static int ata_perform_sleep(void) 227static int ata_perform_sleep(void)
241{ 228{
242 /* Don't issue the sleep command if the device
243 doesn't support (mandatory!) ATA power management commands!
244 */
245 if (!(identify_info[82] & (1 << 3)))
246 return 0;
247
248 logf("ata SLEEP %ld", current_tick); 229 logf("ata SLEEP %ld", current_tick);
249 230
250 ATA_OUT8(ATA_SELECT, ata_device); 231 ATA_OUT8(ATA_SELECT, ata_device);
@@ -254,10 +235,25 @@ static int ata_perform_sleep(void)
254 return -1; 235 return -1;
255 } 236 }
256 237
257 ATA_OUT8(ATA_COMMAND, CMD_SLEEP); 238 /* STANDBY IMMEDIATE
239 - writes all cached data
240 - transitions to PM2:Standby
241 - enters Standby_z power condition
258 242
259 if (!wait_for_rdy()) 243 This places the device into a state where power-off is safe, but
260 { 244 it is not the lowest-theoretical power state -- that is SLEEP, but
245 that is bugged on some SSDs (FC1307A-based).
246
247 TODO: Is there a practical downside to using STANDBY_IMMEDIATE instead
248 of SLEEP, assuming the former spins down the drive?
249 */
250 if (ata_disk_isssd()) {
251 ATA_OUT8(ATA_COMMAND, CMD_STANDBY_IMMEDIATE);
252 } else {
253 ATA_OUT8(ATA_COMMAND, CMD_SLEEP);
254 }
255
256 if (!wait_for_rdy()) {
261 DEBUGF("ata_perform_sleep() - CMD failed\n"); 257 DEBUGF("ata_perform_sleep() - CMD failed\n");
262 return -2; 258 return -2;
263 } 259 }
@@ -265,6 +261,34 @@ static int ata_perform_sleep(void)
265 return 0; 261 return 0;
266} 262}
267 263
264static int ata_perform_flush_cache(void)
265{
266 /* Don't issue the flush cache command if the device
267 doesn't support it, even though it's mandatory.
268 */
269 if (!(identify_info[83] & (1 << 12)))
270 return 0;
271
272 logf("ata FLUSH CACHE %ld", current_tick);
273
274 ATA_OUT8(ATA_SELECT, ata_device);
275
276 if(!wait_for_rdy()) {
277 DEBUGF("ata_perform_flush_cache() - not RDY\n");
278 return -1;
279 }
280
281 ATA_OUT8(ATA_COMMAND, CMD_FLUSH_CACHE);
282
283 if (!wait_for_rdy()) {
284 DEBUGF("ata_perform_flush_cache() - CMD failed\n");
285 return -2;
286 }
287
288 return 0;
289}
290
291
268static ICODE_ATTR int wait_for_start_of_transfer(void) 292static ICODE_ATTR int wait_for_start_of_transfer(void)
269{ 293{
270 if (!wait_for_bsy()) 294 if (!wait_for_bsy())
@@ -361,15 +385,27 @@ static ICODE_ATTR void copy_write_sectors(const unsigned char* buf,
361 385
362int ata_disk_isssd(void) 386int ata_disk_isssd(void)
363{ 387{
364 /* offset 217 is "Nominal Rotation rate" 388 /*
389 offset 217 is "Nominal Rotation rate"
365 0x0000 == Not reported 390 0x0000 == Not reported
366 0x0001 == Solid State 391 0x0001 == Solid State
367 0x0401 -> 0xffe == RPM 392 0x0401 -> 0xffe == RPM
368 All others reserved 393 All others reserved
369 394
370 Some CF cards return 0x0100 (ie byteswapped 0x0001) so accept either 395 Some CF cards return 0x0100 (ie byteswapped 0x0001) so accept either.
396
397 However, this is a very recent change, and we can't rely on it,
398 especially for the FC1307A CF->SD adapters.
399
400 So we have to resort to other heuristics.
401
402 offset 83 b2 is set to show device implementes CFA commands
403 offset 0 is 0x848a for CF, but that's not guaranteed, because reasons.
404
405 These don't guarantee this is an SSD but it's better than nothing.
371 */ 406 */
372 return (identify_info[217] == 0x0001 || identify_info[217] == 0x0100); 407 return (identify_info[83] & (1<<2) ||
408 identify_info[217] == 0x0001 || identify_info[217] == 0x0100);
373} 409}
374 410
375static int ata_transfer_sectors(unsigned long start, 411static int ata_transfer_sectors(unsigned long start,
@@ -836,29 +872,16 @@ void ata_spindown(int seconds)
836 872
837bool ata_disk_is_active(void) 873bool ata_disk_is_active(void)
838{ 874{
839#ifdef FC1307A_WORKAROUND
840 /* "active" == "spinning" in this context.
841 without power management this becomes moot */
842 if (!(identify_info[82] & (1 << 3)))
843 return false;
844#endif
845
846 return ata_state >= ATA_SPINUP; 875 return ata_state >= ATA_SPINUP;
847} 876}
848 877
849void ata_sleepnow(void) 878void ata_sleepnow(void)
850{ 879{
851#ifdef FC1307A_WORKAROUND
852 /* Completely disable all power management */
853 if (!(identify_info[82] & (1 << 3)))
854 return;
855#endif
856
857 if (ata_state >= ATA_SPINUP) { 880 if (ata_state >= ATA_SPINUP) {
858 logf("ata SLEEPNOW %ld", current_tick); 881 logf("ata SLEEPNOW %ld", current_tick);
859 mutex_lock(&ata_mtx); 882 mutex_lock(&ata_mtx);
860 if (ata_state == ATA_ON) { 883 if (ata_state == ATA_ON) {
861 if (!ata_perform_sleep()) { 884 if (!ata_perform_flush_cache() && !ata_perform_sleep()) {
862 ata_state = ATA_SLEEPING; 885 ata_state = ATA_SLEEPING;
863 schedule_ata_power_off(); 886 schedule_ata_power_off();
864 } 887 }
@@ -1093,7 +1116,7 @@ static int set_features(void)
1093 unsigned char parameter; 1116 unsigned char parameter;
1094 } features[] = { 1117 } features[] = {
1095 { 83, 14, 0x03, 0 }, /* force PIO mode */ 1118 { 83, 14, 0x03, 0 }, /* force PIO mode */
1096 { 83, 3, 0x05, 0x80 }, /* adv. power management: lowest w/o standby */ 1119 { 83, 3, 0x05, 0x80 }, /* adv. power management: lowest w/o standby */ // TODO: What about FC1307A that doesn't advertise this properly?
1097 { 83, 9, 0x42, 0x80 }, /* acoustic management: lowest noise */ 1120 { 83, 9, 0x42, 0x80 }, /* acoustic management: lowest noise */
1098 { 82, 6, 0xaa, 0 }, /* enable read look-ahead */ 1121 { 82, 6, 0xaa, 0 }, /* enable read look-ahead */
1099#ifdef HAVE_ATA_DMA 1122#ifdef HAVE_ATA_DMA