So, I have spent a lot of time recently thinking about how best to implement IDLE2.
I decided to do something which hadn’t been done before, and implement both IDLE states so they are available concurrently without having to ‘switch’ between them, as was being done previously.
I implemented 2 idle states, IDLE (which was already implemented) and IDLE2, which is better described as DEEP IDLE TOP OFF, referred to as IDLE2 mode from hereon in.
I spent a lot of time making the hot paths as cheap as possible, wherever possible using variables for storage and checking them instead of polling (expensive) functions. This has paid off with a doubling of the number of times the CPU enters IDLE2 mode.
I also implemented PM notifiers to disable IDLE2 mode when suspend is active instead of hooking into the suspend sequence. However, due to the implementation, IDLE2 mode should never be enabled when the device is suspending, so it is just a failsafe.
Bluetooth caused me a massive headache, and although I have managed to partially solve the issue, I’m not entirely happy with the fix. Due to the way bluetooth works in ICS/JB, it is permanently enabled. This causes an issue, as IDLE2 mode is incompatible with bluetooth, enabling IDLE2 mode whilst the bluetooth is actively working breaks the link. The fix for this is a bit of a fudge. I added a hook to herring-rfkill to call a function when the GPIO_BT_HOST_WAKE goes high. This sets the external_active variable to true in cpuidle.c, which tells cpuidle to ignore the IDLE2 mode. When the gpio goes low again, a function is called which sets external_active to false, but it queues it for 60 seconds time. If another interrupt occurs, the queued inactive work will be cancelled and so on. This should work for the majority of bluetooth use cases. If the devices don’t ping each other every minute, I would be surprised. But, if this doesn’t work for you, IDLE2 mode can be disabled with a switch.
I implemented a hook into the audio subsystem so IDLE2 mode will not be activated unless audio playback has been active for 30 seconds. This choice was made to stop spurious activations of IDLE2 mode when there are system notifications, for example.
IDLE2 mode will now be activated under the following circumstances:
- The disabled switch is not set
- Audio playback has been active for 30 seconds
- USB cable is disconnected
- Bluetooth is inactive (see above)
- Earlysuspend is active (screen is turned off)
- Various clocks are gated and various subsystems are power gated
- No interrupts pending
Added code to disable cpufreq and clamp the CPU at 800MHz when the conditions are right for IDLE2 mode to be entered. This appears to save significantly more power.
Codewise, many of the IDLE2 specific functions have been moved into idle2.h, to keep them separate from the standard cpufreq code. The asm has also been further cleaned up, with calls to the generic flush_cache_all() and cpu_do_idle() functions instead of using asm calls. The remaining asm is involved in saving and restoring registers for IDLE2 mode. Exit latency has been set to 400, which is what is stated in the S5PC100 TRM. Residency time has been reduced to 2000, as even going into IDLE2 for short periods is more efficient than using regular idle or not idling at all.
IDLE2 mode now has a disable switch, which can be activated with the following command:
echo 1 > /sys/module/cpuidle/parameters/idle2_disabled
As the IDLE2 state is now implemented concurrently with IDLE, info can be obtained from the standard cpuidle interfaces.
For normal IDLE these can be found here:
And IDLE2 can be found here:
I think that is all the improvements.
Patches v0.200 and v0.201 are available on my github which will need to be applied on top of all the rest of the IDLE2 patches.
My kernel, implementing IDLE2 v0.201, is available here.
I will squash them and release a standalone patch at some point in the near future.
Update: Standalone patch is available here.