diff options
author | Solomon Peachy <pizza@shaftnet.org> | 2021-03-12 09:37:36 -0500 |
---|---|---|
committer | Solomon Peachy <pizza@shaftnet.org> | 2021-03-12 10:04:52 -0500 |
commit | 2743bde09b970e44af1c55a0750790146697e28a (patch) | |
tree | b6335031d965d1971c118fb39e8c7f10172dc0f9 /firmware/drivers/ata.c | |
parent | 04c29984cecd4e971f7d60226b96b83872488849 (diff) |
ATA: Increase delay to power-off when device doesn't support ATA SLEEP
The storage subsystem aggressively issues SLEEPNOW events when idle
and power off happened a fixed 2s later. This turns out to not be
enough time for FC1307A (eg iFlash) adapters to flush outstanding writes.
So, when we detect a lack of PM support, increase the poweroff delay to
5 seconds to compensate for not being able to issue the ATA SLEEP command.
Hopefully this is enough time. If not, we will have to re-disable PM
entirely when we detect these popular adapters. Thankfully that is now
just an #ifdef away.
Change-Id: I4112b9acb965973d81f70483bd9d595461c7301c
Diffstat (limited to 'firmware/drivers/ata.c')
-rw-r--r-- | firmware/drivers/ata.c | 46 |
1 files changed, 42 insertions, 4 deletions
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c index d2e1ad4a22..3514511270 100644 --- a/firmware/drivers/ata.c +++ b/firmware/drivers/ata.c @@ -18,6 +18,9 @@ * KIND, either express or implied. * ****************************************************************************/ + +//#define LOGF_ENABLE + #include <stdbool.h> #include <inttypes.h> #include "led.h" @@ -31,6 +34,15 @@ #include "ata-defines.h" #include "fs_defines.h" #include "storage.h" +#include "logf.h" + +/* The FC1307A ATA->SD chipset (used by the common iFlash adapters) + doesn't support mandatory ATA power management commands. Unfortunately + simply gating off the SLEEP command isn't sufficient; we need to + disable advanced powersaving entirely because otherwise we might + kill power before the device has finished flusing writes. +*/ +//#define FC1307A_WORKAROUND #define SELECT_DEVICE1 0x10 #define SELECT_LBA 0x40 @@ -63,6 +75,7 @@ #ifdef HAVE_ATA_POWER_OFF #define ATA_POWER_OFF_TIMEOUT 2*HZ +#define ATA_POWER_OFF_TIMEOUT_NOPM 5*HZ #endif #if defined(HAVE_USBSTACK) @@ -147,7 +160,12 @@ static inline bool ata_sleep_timed_out(void) static inline void schedule_ata_power_off(void) { #ifdef HAVE_ATA_POWER_OFF - power_off_tick = current_tick + ATA_POWER_OFF_TIMEOUT; + power_off_tick = current_tick; + /* If our device doesn't support SLEEP give a bit more time to flush */ + if (!(identify_info[82] & (1 << 3))) + power_off_tick += ATA_POWER_OFF_TIMEOUT_NOPM; + else + power_off_tick += ATA_POWER_OFF_TIMEOUT; #endif } @@ -202,6 +220,7 @@ static ICODE_ATTR int wait_for_rdy(void) static int ata_perform_wakeup(int state) { + logf("ata WAKE %ld", current_tick); if (state > ATA_OFF) { if (perform_soft_reset()) { return -1; @@ -222,13 +241,12 @@ static int ata_perform_sleep(void) { /* Don't issue the sleep command if the device doesn't support (mandatory!) ATA power management commands! - - The FC1307A ATA->SD chipset (used by the common iFlash adapters) - is the only known offender, and will eat your data if told to sleep. */ if (!(identify_info[82] & (1 << 3))) return 0; + logf("ata SLEEP %ld", current_tick); + ATA_OUT8(ATA_SELECT, ata_device); if(!wait_for_rdy()) { @@ -818,12 +836,26 @@ void ata_spindown(int seconds) bool ata_disk_is_active(void) { +#ifdef FC1307A_WORKAROUND + /* "active" == "spinning" in this context. + without power management this becomes moot */ + if (!(identify_info[82] & (1 << 3))) + return false; +#endif + return ata_state >= ATA_SPINUP; } void ata_sleepnow(void) { +#ifdef FC1307A_WORKAROUND + /* Completely disable all power management */ + if (!(identify_info[82] & (1 << 3))) + return; +#endif + if (ata_state >= ATA_SPINUP) { + logf("ata SLEEPNOW %ld", current_tick); mutex_lock(&ata_mtx); if (ata_state == ATA_ON) { if (!ata_perform_sleep()) { @@ -904,6 +936,8 @@ static int perform_soft_reset(void) int ret; int retry_count; + logf("ata SOFT RESET %ld", current_tick); + ATA_OUT8(ATA_SELECT, SELECT_LBA | ata_device ); ATA_OUT8(ATA_CONTROL, CONTROL_nIEN|CONTROL_SRST ); sleep(1); /* >= 5us */ @@ -960,6 +994,8 @@ static int ata_power_on(void) { int rc; + logf("ata ON %ld", current_tick); + ide_power_enable(true); sleep(HZ/4); /* allow voltage to build up */ @@ -1381,6 +1417,7 @@ int ata_event(long id, intptr_t data) if (state == ATA_SLEEPING && ata_power_off_timed_out()) { mutex_lock(&ata_mtx); if (ata_state == ATA_SLEEPING) { + logf("ata OFF %ld", current_tick); ide_power_enable(false); ata_state = ATA_OFF; } @@ -1397,6 +1434,7 @@ int ata_event(long id, intptr_t data) } #ifndef USB_NONE else if (id == SYS_USB_CONNECTED) { + logf("deq USB %ld", current_tick); if (ATA_ACTIVE_IN_USB) { /* There is no need to force ATA power on */ STG_EVENT_ASSERT_ACTIVE(STORAGE_ATA); |