summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2024-04-11 11:54:02 -0400
committerSolomon Peachy <pizza@shaftnet.org>2024-04-22 15:46:30 -0400
commit886060475e25d04b9eb1753dbbaea0db8b78a0d4 (patch)
tree472b29d2dbb994cbfa1addac945f9043796614c6
parent6cbcde13b9eb10b50724f956ba5696fc82ed7cf4 (diff)
downloadrockbox-886060475e25d04b9eb1753dbbaea0db8b78a0d4.tar.gz
rockbox-886060475e25d04b9eb1753dbbaea0db8b78a0d4.zip
ata: Heavily rework sleep and poweroff logic
* Use of ata_disk_can_poweroff() was inverted, resulting in SATA SSDs getting powered off but leaving _everything_ else on, including spinning rust! * Replace the can_poweroff() heuristic with a test for the mandatory ATA power mgmt feature flag. Notably, the CF->SD adapters don't claim to support this! * Eliminate duplicated tests in sleep code * Wrap all poweroff-related code with HAVE_ATA_POWER_OFF * Don't ever use SLEEP command, only STANDBY_IMMEDIATE * Gate call to STANDBY_IMMEDIATE behind a can_poweroff() test * Prefer FLUSH_CACHE_EXT to FLUSH_CACHE where available. * Improve SSD detection heuristics to any of these: * Explicltly identifies as SSD (covers newer CF and SATA) * TRIM support * CFA compliant AND (CF level 0 OR high speed support) * Report SSD detection in debug menu Change-Id: I7fcb83b6d6eabddc11c64326a573b08ab85412b5
-rw-r--r--apps/debug_menu.c4
-rw-r--r--firmware/drivers/ata.c111
-rw-r--r--firmware/export/ata.h52
3 files changed, 87 insertions, 80 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 245cd38627..07640c3937 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -1074,7 +1074,7 @@ static bool view_battery(void)
1074 1074
1075 lcd_putsf(0, line++, "State: %s", chrgstate_strings[y]); 1075 lcd_putsf(0, line++, "State: %s", chrgstate_strings[y]);
1076 1076
1077 lcd_putsf(0, line++, "%s Switch: %s", "Battery", 1077 lcd_putsf(0, line++, "%s Switch: %s", "Battery",
1078 (st & POWER_INPUT_BATTERY) ? "On" : "Off"); 1078 (st & POWER_INPUT_BATTERY) ? "On" : "Off");
1079 1079
1080 y = chrgraw_adc_voltage(); 1080 y = chrgraw_adc_voltage();
@@ -1405,6 +1405,7 @@ static int disk_callback(int btn, struct gui_synclist *lists)
1405 volume_size( IF_MV(0,) NULL, &free ); 1405 volume_size( IF_MV(0,) NULL, &free );
1406 simplelist_addline( 1406 simplelist_addline(
1407 "Free: %ld MB", free / 1024); 1407 "Free: %ld MB", free / 1024);
1408 simplelist_addline("SSD detected: %s", ata_disk_isssd() ? "yes" : "no");
1408 simplelist_addline( 1409 simplelist_addline(
1409 "Spinup time: %d ms", storage_spinup_time() * (1000/HZ)); 1410 "Spinup time: %d ms", storage_spinup_time() * (1000/HZ));
1410 i = identify_info[82] & (1<<3); 1411 i = identify_info[82] & (1<<3);
@@ -1512,6 +1513,7 @@ static int disk_callback(int btn, struct gui_synclist *lists)
1512 i = identify_info[0] & (1 << 7); 1513 i = identify_info[0] & (1 << 7);
1513 simplelist_addline( 1514 simplelist_addline(
1514 "Removeable media: %s", i ? "yes" : "no"); 1515 "Removeable media: %s", i ? "yes" : "no");
1516
1515 return btn; 1517 return btn;
1516} 1518}
1517 1519
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
106static long last_disk_activity = -1; 107static long last_disk_activity = -1;
107#ifdef HAVE_ATA_POWER_OFF 108#ifdef HAVE_ATA_POWER_OFF
108static long power_off_tick; 109static long power_off_tick = 0;
109#endif 110#endif
110 111
111static unsigned long total_sectors; 112static 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
155static 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
163static inline bool ata_power_off_timed_out(void) 156static 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
231static int ata_perform_sleep(void) 224static 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
268static int ata_perform_flush_cache(void) 257static 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
390int 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
418int 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
431static int ata_transfer_sectors(unsigned long start, 385static 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 }
diff --git a/firmware/export/ata.h b/firmware/export/ata.h
index 7c7c60e898..62c9467643 100644
--- a/firmware/export/ata.h
+++ b/firmware/export/ata.h
@@ -166,10 +166,58 @@ long ata_last_disk_activity(void);
166int ata_spinup_time(void); /* ticks */ 166int ata_spinup_time(void); /* ticks */
167 167
168/* Returns 1 if drive is solid-state */ 168/* Returns 1 if drive is solid-state */
169int ata_disk_isssd(void); 169static inline int ata_disk_isssd(void)
170{
171 unsigned short *identify_info = ata_get_identify();
172 /*
173 Offset 217 is "Nominal Rotation rate"
174 0x0000 == Not reported
175 0x0001 == Solid State
176 0x0401 -> 0xffe == RPM
177 All others reserved
178
179 Some CF cards return 0x0100 (ie byteswapped 0x0001) so accept either.
180 However, this is a relatively recent change, and we can't rely on it,
181 especially for the FC1307A CF->SD adapters!
182
183 Offset 168 is "Nominal Form Factor"
184 all values >= 0x06 are guaranteed to be Solid State (mSATA, m.2, etc)
185
186 Offset 169 b0 is set to show device implements TRIM.
187
188 Unreliable mechanisms:
189
190 Offset 83 b2 shows device implements CFA commands.
191 However microdrives pose a problem as they support CFA but are not
192 SSD.
193
194 Offset 160 b15 indicates support for CF+ power level 1, if not set
195 then device is standard flash CF. However this is not foolproof
196 as newer CF cards may support it for extra performance.
197
198 Offset 163 shows CF Advanced timing modes; microdrive seems to
199 report 0, but all others (including iFlash) report higher!
200
201 So if device support CFA _AND_ reports higher speeds modes, it is SSD.
202
203 */
204 return ( (identify_info[217] == 0x0001 || identify_info[217] == 0x0100) /* "Solid state" rotational rate */
205 || ((identify_info[168] & 0x0f) >= 0x06) /* Explicit SSD form factors */
206 || (identify_info[169] & (1<<0)) /* TRIM supported */
207 || ((identify_info[83] & (1<<2)) && /* CFA compliant */
208 (((identify_info[160] & (1<<15)) == 0) || /* CF level 0 */
209 (identify_info[163] > 0))) /* Advanced timing modes */
210 );
211}
170 212
171/* Returns 1 if the drive can be powered off safely */ 213/* Returns 1 if the drive can be powered off safely */
172int ata_disk_can_poweroff(void); 214static inline int ata_disk_can_poweroff(void)
215{
216 unsigned short *identify_info = ata_get_identify();
217 /* Only devices that claim to support PM can be safely powered off.
218 This notably excludes the various SD adapters! */
219 return (identify_info[82] & (1<<3) && identify_info[85] & (1<<3));
220}
173 221
174#ifdef HAVE_ATA_DMA 222#ifdef HAVE_ATA_DMA
175/* Returns current DMA mode */ 223/* Returns current DMA mode */