diff options
Diffstat (limited to 'firmware/drivers/ata.c')
-rw-r--r-- | firmware/drivers/ata.c | 111 |
1 files changed, 34 insertions, 77 deletions
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c index 1c85b7bd5f..9896fa87e0 100644 --- a/firmware/drivers/ata.c +++ b/firmware/drivers/ata.c | |||
@@ -58,6 +58,7 @@ | |||
58 | #define CMD_IDENTIFY 0xEC | 58 | #define CMD_IDENTIFY 0xEC |
59 | #define CMD_SLEEP 0xE6 | 59 | #define CMD_SLEEP 0xE6 |
60 | #define CMD_FLUSH_CACHE 0xE7 | 60 | #define CMD_FLUSH_CACHE 0xE7 |
61 | #define CMD_FLUSH_CACHE_EXT 0xEA | ||
61 | #define CMD_SET_FEATURES 0xEF | 62 | #define CMD_SET_FEATURES 0xEF |
62 | #define CMD_SECURITY_FREEZE_LOCK 0xF5 | 63 | #define CMD_SECURITY_FREEZE_LOCK 0xF5 |
63 | #ifdef HAVE_ATA_DMA | 64 | #ifdef HAVE_ATA_DMA |
@@ -105,7 +106,7 @@ static bool lba48 = false; /* set for 48 bit addressing */ | |||
105 | 106 | ||
106 | static long last_disk_activity = -1; | 107 | static long last_disk_activity = -1; |
107 | #ifdef HAVE_ATA_POWER_OFF | 108 | #ifdef HAVE_ATA_POWER_OFF |
108 | static long power_off_tick; | 109 | static long power_off_tick = 0; |
109 | #endif | 110 | #endif |
110 | 111 | ||
111 | static unsigned long total_sectors; | 112 | static unsigned long total_sectors; |
@@ -152,18 +153,10 @@ static inline bool ata_sleep_timed_out(void) | |||
152 | TIME_AFTER(current_tick, last_disk_activity + sleep_timeout); | 153 | TIME_AFTER(current_tick, last_disk_activity + sleep_timeout); |
153 | } | 154 | } |
154 | 155 | ||
155 | static inline void schedule_ata_power_off(void) | ||
156 | { | ||
157 | #ifdef HAVE_ATA_POWER_OFF | ||
158 | if (!ata_disk_can_poweroff()) | ||
159 | power_off_tick = current_tick + ATA_POWER_OFF_TIMEOUT; | ||
160 | #endif | ||
161 | } | ||
162 | |||
163 | static inline bool ata_power_off_timed_out(void) | 156 | static inline bool ata_power_off_timed_out(void) |
164 | { | 157 | { |
165 | #ifdef HAVE_ATA_POWER_OFF | 158 | #ifdef HAVE_ATA_POWER_OFF |
166 | return TIME_AFTER(current_tick, power_off_tick); | 159 | return power_off_tick && TIME_AFTER(current_tick, power_off_tick); |
167 | #else | 160 | #else |
168 | return false; | 161 | return false; |
169 | #endif | 162 | #endif |
@@ -230,6 +223,10 @@ static int ata_perform_wakeup(int state) | |||
230 | 223 | ||
231 | static int ata_perform_sleep(void) | 224 | static int ata_perform_sleep(void) |
232 | { | 225 | { |
226 | /* If device doesn't support PM features, don't try to sleep. */ | ||
227 | if (!ata_disk_can_poweroff()) | ||
228 | return 0; // XXX or return a failure? | ||
229 | |||
233 | logf("ata SLEEP %ld", current_tick); | 230 | logf("ata SLEEP %ld", current_tick); |
234 | 231 | ||
235 | ATA_OUT8(ATA_SELECT, ata_device); | 232 | ATA_OUT8(ATA_SELECT, ata_device); |
@@ -244,18 +241,10 @@ static int ata_perform_sleep(void) | |||
244 | - transitions to PM2:Standby | 241 | - transitions to PM2:Standby |
245 | - enters Standby_z power condition | 242 | - enters Standby_z power condition |
246 | 243 | ||
247 | This places the device into a state where power-off is safe, but | 244 | This places the device into a state where power-off is safe. We |
248 | it is not the lowest-theoretical power state -- that is SLEEP, but | 245 | will cut power at a later time. |
249 | that is bugged on some SSDs (FC1307A-based). | ||
250 | |||
251 | TODO: Is there a practical downside to using STANDBY_IMMEDIATE instead | ||
252 | of SLEEP, assuming the former spins down the drive? | ||
253 | */ | 246 | */ |
254 | if (ata_disk_isssd()) { | 247 | ATA_OUT8(ATA_COMMAND, CMD_STANDBY_IMMEDIATE); |
255 | ATA_OUT8(ATA_COMMAND, CMD_STANDBY_IMMEDIATE); | ||
256 | } else { | ||
257 | ATA_OUT8(ATA_COMMAND, CMD_SLEEP); | ||
258 | } | ||
259 | 248 | ||
260 | if (!wait_for_rdy()) { | 249 | if (!wait_for_rdy()) { |
261 | DEBUGF("ata_perform_sleep() - CMD failed\n"); | 250 | DEBUGF("ata_perform_sleep() - CMD failed\n"); |
@@ -267,11 +256,17 @@ static int ata_perform_sleep(void) | |||
267 | 256 | ||
268 | static int ata_perform_flush_cache(void) | 257 | static int ata_perform_flush_cache(void) |
269 | { | 258 | { |
270 | /* Don't issue the flush cache command if the device | 259 | uint8_t cmd; |
271 | doesn't support it, even though it's mandatory. | 260 | |
272 | */ | 261 | if (identify_info[83] & (1 << 13)) { |
273 | if (!(identify_info[83] & (1 << 12))) | 262 | cmd = CMD_FLUSH_CACHE_EXT; |
263 | } else if (identify_info[83] & (1 << 12)) { | ||
264 | cmd = CMD_FLUSH_CACHE; | ||
265 | } else { | ||
266 | /* If neither (mandatory!) command is supported | ||
267 | then don't issue it. */ | ||
274 | return 0; | 268 | return 0; |
269 | } | ||
275 | 270 | ||
276 | logf("ata FLUSH CACHE %ld", current_tick); | 271 | logf("ata FLUSH CACHE %ld", current_tick); |
277 | 272 | ||
@@ -282,7 +277,7 @@ static int ata_perform_flush_cache(void) | |||
282 | return -1; | 277 | return -1; |
283 | } | 278 | } |
284 | 279 | ||
285 | ATA_OUT8(ATA_COMMAND, CMD_FLUSH_CACHE); | 280 | ATA_OUT8(ATA_COMMAND, cmd); |
286 | 281 | ||
287 | if (!wait_for_rdy()) { | 282 | if (!wait_for_rdy()) { |
288 | DEBUGF("ata_perform_flush_cache() - CMD failed\n"); | 283 | DEBUGF("ata_perform_flush_cache() - CMD failed\n"); |
@@ -387,47 +382,6 @@ static ICODE_ATTR void copy_write_sectors(const unsigned char* buf, | |||
387 | } | 382 | } |
388 | #endif /* !ATA_OPTIMIZED_WRITING */ | 383 | #endif /* !ATA_OPTIMIZED_WRITING */ |
389 | 384 | ||
390 | int ata_disk_isssd(void) | ||
391 | { | ||
392 | /* | ||
393 | Offset 217 is "Nominal Rotation rate" | ||
394 | 0x0000 == Not reported | ||
395 | 0x0001 == Solid State | ||
396 | 0x0401 -> 0xffe == RPM | ||
397 | All others reserved | ||
398 | |||
399 | Some CF cards return 0x0100 (ie byteswapped 0x0001) so accept either. | ||
400 | However, this is a relatively recent change, and we can't rely on it, | ||
401 | especially for the FC1307A CF->SD adapters! | ||
402 | |||
403 | Offset 168 is "Nominal Form Factor" | ||
404 | all values >= 0x06 are guaranteed to be Solid State (mSATA, m.2, etc) | ||
405 | |||
406 | Offset 83 b2 and/or 86 b2 is set to show device implementes CFA commands | ||
407 | |||
408 | Offset 169 b0 is set to show device implements TRIM. | ||
409 | |||
410 | Offset 0 is 0x848a for CF, but that's not guaranteed, because reasons. | ||
411 | */ | ||
412 | return (identify_info[83] & (1<<2) || identify_info[86] & (1<<2) || | ||
413 | (identify_info[168] & 0x0f) >= 0x06 || | ||
414 | identify_info[169] & (1<<0) || | ||
415 | identify_info[217] == 0x0001 || identify_info[217] == 0x0100); | ||
416 | } | ||
417 | |||
418 | int ata_disk_can_poweroff(void) | ||
419 | { | ||
420 | /* Some SSDs don't like getting powered off, presumably because | ||
421 | in the real world they're not in removable form factors and | ||
422 | don't expect to have power removed. | ||
423 | |||
424 | In particular, mSATA, m.2, and MicroSSD are suspect. | ||
425 | */ | ||
426 | |||
427 | return ((identify_info[168] & 0x0f) < 0x06 || | ||
428 | (identify_info[168] & 0x0f) > 0x08); | ||
429 | } | ||
430 | |||
431 | static int ata_transfer_sectors(unsigned long start, | 385 | static int ata_transfer_sectors(unsigned long start, |
432 | int incount, | 386 | int incount, |
433 | void* inbuf, | 387 | void* inbuf, |
@@ -903,7 +857,10 @@ void ata_sleepnow(void) | |||
903 | if (ata_state == ATA_ON) { | 857 | if (ata_state == ATA_ON) { |
904 | if (!ata_perform_flush_cache() && !ata_perform_sleep()) { | 858 | if (!ata_perform_flush_cache() && !ata_perform_sleep()) { |
905 | ata_state = ATA_SLEEPING; | 859 | ata_state = ATA_SLEEPING; |
906 | schedule_ata_power_off(); | 860 | #ifdef HAVE_ATA_POWER_OFF |
861 | if (ata_disk_can_poweroff()) | ||
862 | power_off_tick = current_tick + ATA_POWER_OFF_TIMEOUT; | ||
863 | #endif | ||
907 | } | 864 | } |
908 | } | 865 | } |
909 | mutex_unlock(&ata_mtx); | 866 | mutex_unlock(&ata_mtx); |
@@ -1136,7 +1093,7 @@ static int set_features(void) | |||
1136 | unsigned char parameter; | 1093 | unsigned char parameter; |
1137 | } features[] = { | 1094 | } features[] = { |
1138 | { 83, 14, 0x03, 0 }, /* force PIO mode */ | 1095 | { 83, 14, 0x03, 0 }, /* force PIO mode */ |
1139 | { 83, 3, 0x05, 0x80 }, /* adv. power management: lowest w/o standby */ // TODO: What about FC1307A that doesn't advertise this properly? | 1096 | { 83, 3, 0x05, 0x80 }, /* adv. power management: lowest w/o standby */ |
1140 | { 83, 9, 0x42, 0x80 }, /* acoustic management: lowest noise */ | 1097 | { 83, 9, 0x42, 0x80 }, /* acoustic management: lowest noise */ |
1141 | { 82, 6, 0xaa, 0 }, /* enable read look-ahead */ | 1098 | { 82, 6, 0xaa, 0 }, /* enable read look-ahead */ |
1142 | #ifdef HAVE_ATA_DMA | 1099 | #ifdef HAVE_ATA_DMA |
@@ -1455,17 +1412,17 @@ int ata_event(long id, intptr_t data) | |||
1455 | the first case is frequently hit anyway. */ | 1412 | the first case is frequently hit anyway. */ |
1456 | if (LIKELY(id == Q_STORAGE_TICK)) { | 1413 | if (LIKELY(id == Q_STORAGE_TICK)) { |
1457 | /* won't see ATA_BOOT in here */ | 1414 | /* won't see ATA_BOOT in here */ |
1458 | int state = ata_state; | 1415 | if (ata_state != ATA_ON || !ata_sleep_timed_out()) { |
1459 | if (state != ATA_ON || !ata_sleep_timed_out()) { | 1416 | #ifdef HAVE_ATA_POWER_OFF |
1460 | if (state == ATA_SLEEPING && ata_power_off_timed_out()) { | 1417 | if (ata_state == ATA_SLEEPING && ata_power_off_timed_out()) { |
1418 | power_off_tick = 0; | ||
1461 | mutex_lock(&ata_mtx); | 1419 | mutex_lock(&ata_mtx); |
1462 | if (ata_state == ATA_SLEEPING) { | 1420 | logf("ata OFF %ld", current_tick); |
1463 | logf("ata OFF %ld", current_tick); | 1421 | ide_power_enable(false); |
1464 | ide_power_enable(false); | 1422 | ata_state = ATA_OFF; |
1465 | ata_state = ATA_OFF; | ||
1466 | } | ||
1467 | mutex_unlock(&ata_mtx); | 1423 | mutex_unlock(&ata_mtx); |
1468 | } | 1424 | } |
1425 | #endif | ||
1469 | STG_EVENT_ASSERT_ACTIVE(STORAGE_ATA); | 1426 | STG_EVENT_ASSERT_ACTIVE(STORAGE_ATA); |
1470 | } | 1427 | } |
1471 | } | 1428 | } |