diff options
Diffstat (limited to 'firmware/drivers')
-rw-r--r-- | firmware/drivers/ata.c | 107 |
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) | |||
160 | static inline void schedule_ata_power_off(void) | 152 | static 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 | ||
240 | static int ata_perform_sleep(void) | 227 | static 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 | ||
264 | static 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 | |||
268 | static ICODE_ATTR int wait_for_start_of_transfer(void) | 292 | static 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 | ||
362 | int ata_disk_isssd(void) | 386 | int 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 | ||
375 | static int ata_transfer_sectors(unsigned long start, | 411 | static int ata_transfer_sectors(unsigned long start, |
@@ -836,29 +872,16 @@ void ata_spindown(int seconds) | |||
836 | 872 | ||
837 | bool ata_disk_is_active(void) | 873 | bool 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 | ||
849 | void ata_sleepnow(void) | 878 | void 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 |