You are on page 1of 71

Android Jelly Bean Power Management Hacks

: suspend/resume, early_suspend/late_resume, wakelocks, runtime PM, clock/regulator framework, battery/charger

chunghan.yi@gmail.com, slowboot

Revision 0.1 0.6 0.7 0.8


0.9

12/20/2011 12/21/2011 (1 ) 12/22/2011 (wakelock ) 12/26/2011 (2 )


03/05/2012 (3 )

0.91

12/24/2012 (JB review)

1. Linux Power Management


PM Core, Suspend/Resume

2. Android Power Management


PowerManager/PowerManagerService, EarlySuspend/LateResume

3. Wakelocks
application wakelock, driver wakelock

4. Runtime Power Management


runtime PM, callback, API

5. CPUFreq and CPUIdle


CPUFreq, CPUIdle,

6. Power Management Subsystems


clock framework, regulator framework

7. Battery & Charger


BatteryService, charger & fuel gauge driver
Ram Console

References

1. Linux Power Management

1. Linux Power Management(1) - Overview

System running

suspend

resume

sleep System suspended

wakeup

(*) suspend/resume . 1) kernel/power/main.c 2) kernel/power/suspend.c 3) arch/arm/mach-xxx/pm..c 4) drivers/base/power/*

1. Linux Power Management(2) PM Core


Power Management Core
Kernel suspend/resume kernel/power/main.c Application suspend
# echo mem > /sys/power/state main.c state_store() request_suspend_state( )( enter_state( )) !!!

struct platform_suspend_ops
) struct platform_suspend_ops msm_pm_ops = { .enter = msm_pm_enter, .valid = suspend_valid_only_mem,
};

arch/arm/mach-xxx/pm.c

struct platform_suspend_ops { int (*valid)(suspend_state_t state); int (*begin)(suspend_state_t state); int (*prepare)(void); int (*prepare_late)(void); int (*enter)(suspend_state_t state); void (*wake)(void); void (*finish)(void); void (*end)(void); void (*recover)(void); }; struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); int (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, ); int (*suspend_late)(struct platform_device *, ); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *); };

platform_driver or device_driver

1. Linux Power Management(3) Power States


Power States(/sys/power/state) 1) on

2) standby(Standby) power saving (standby) ON , 1-2 3) mem(Suspend-to-RAM) power saving (Suspend-to-RAM) ON , 3-5 4) disk(Suspend-to-Disk) power saving (Suspend-to-Disk) ON , 30

Power State # cat /sys/power/state Power State # echo mem > /sys/power/state # echo standby > /sys/power/state

1. Linux Power Management(4) Suspend/Resume

Suspend : *) application echo mem > /sys/power/state suspend


1) task freezing , 2) device driver suspend callback 3) CPU core device suspend

Resume : *) interrupt (: power key , ). wakeup . 1) System (/sys/devices/system) , 2) IRQ , CPU 3) (resume callback ), freezing task .

Suspend Sequence

Resume Sequence

2. Android Power Management

2. Android Power Management - Overview

(*) Android 1) PowerManager 2) PowerManagerService 3) JNI 4) HAL(power.c) 5) Kernel - Early suspend/late resume - wakelock

2. Android Power Management Early Suspend/Late Resume


Early Suspend: google linux kernel , linux original suspend LCD screen off . LCD LCD back-light, Gsensor, touch screen .

System Running

Late Resume: Early Suspend , google linux kernel . Linux resume , early suspend resume .

early_suspend

late_resume

suspend

resume

sleep System Suspended


(*) kernel/power/earlysuspend.c !

wakeup

2. Android Power Management Early Suspend/1


(1) goToSleep
Power Manager Service
setScreenStateLocked(false) setScreenState set_screen_state echo mem > /sys/power/state

application application application

(2) sleep Userspace kernel


(3)

acquire wakelock (A) or release wakelock

queue_work

early_suspend
queue_work

Unlock main_wake_lock check

suspend

(4) check

wake locks

driver driver driver

(5)

If there is no wakelock, go to the suspended state.

(B)

wake_lock or wake_unlock

2. Android Power Management Early Suspend/2


<Early Suspend & Suspend Step>
(A) Android application application , wakelock . (B) Linux device driver driver , wakelock . (1) (2) Sleep (goToSleep) . PowerManager -> PowerManagerService -> HAL(Power.c) kernel suspend(=sleep) . off , Power Manager Service early suspend . PARITAL_WAKE_LOCK wake lock early suspend

. mem /sys/power/state . (3) early_suspend work(early_suspend_work) <suspend> work queue . <work queue > early_suspend( ) early_suspend callback . main_wake_lock .
(4) wake lock ( ), suspend work(suspend_work) <suspend> work queue .

(5) suspend .

wake lock timer , wake_lock()/wake_unlock() . Wakelock , suspend work <suspend> work queue ^^. Wake lock application linux device driver . , suspend/resume main_wake_lock .

2. Android Power Management Late Resume/1


system_server WindowManager
(5) request for resume PowerManager Service

(6)

(4)

InputManager
(7)

EventHub echo on > /sys/power/state (3) Power key


resume

Userspace

(8) Lock the main_wake_lock queue_work late_resume (2) (9)

kernel

(1) Power key pressed (interrupt)

Kernel wakeup

Touch

2. Android Power Management Late Resume/2 <Resume & Late Resume Step>
(1) power key .

System resume (ex: ).


(2) (interrupt ) kernel resume .

^^
(3) Power key input driver , android framework . (4) Android framework power key (system_server )EventHub -> Input Manager -> Window Manager . (5) Window Manager power key , PowerManager -> PowerManagerService .

Power key .
(6) PowerManagerService early suspend kernel late resume . (7) on /sys/power/state . (8) late_resume main_wake_lock enable.

main_wake_lock early_suspend( ) disable .


(9) Late resume work(late_resume_work) <suspend> work queue . , early_suspend suspend, .

2. Android Power Management suspend work queue/1

early_suspend_work
wakelock

1) Timer( ) 2) wake_lock() 3) wake_unlock() kernel/power/wakelock.c


queue_work( ) work queue

<suspend thread>

suspend_work

func

late_resume_work
(*) , early_suspend late_resume <suspend> work queue ^^ (*) , early_suspend , suspend work queue . (*) Android patch kernel suspend . a) timer , wake_lock b) wake_lock(), wake_unlock() , wake_lock (*) kernel/power/earlysuspend.c, wakelock.c !!!

2. Android Power Management suspend work queue/2

<suspend sys_sync> work queue thread

<suspend> work queue thread

2. Android Power Management earlysuspend/suspend code /1


Request Suspend State

EarlySuspend State

Early Suspend Callbacks

drivers

< action> cat /proc/wakelocks active wakelock

No wakelocks

(A)
if wakelocks exist

if wakelocks dont exist

Suspend Callbacks
(A) : earlysuspend drivers active wakelock , suspend . (B) : suspend , suspend callback , resume .

NG

(B)

Resume Callbacks

OK
Suspend State

2. Android Power Management earlysuspend/suspend code /2

<suspend flow 1 earlysuspend >


a) state_store() .............................................................................................................................. [kernel/power/main.c] b) request_suspend_state() ................................................................................... [kernel/power/earlysuspend.c] c) queue_work(early_suspend_work_queue, &early_suspend_work); early_suspend() call d) pos->suspend(pos) .......... in early_suspend() early suspend callback !(return ) e) suspend_sys_sync_queue() ...... in early_suspend() .............................................[kernel/power/wakelock.c] queue_work(suspend_sys_sync_work_queue, &suspend_sys_sync_work); suspend_sys_sync() ..........................................................[kernel/power/wakelock.c]
f) sys_sync() . in suspend_sys_sync() system call(= sys_sync() )

--------------------------------> early_suspend work !!!

2. Android Power Management earlysuspend/suspend code /3


<suspend flow 2 suspend_work_queue & suspend > a) DECLARE_WORK(suspend_work, suspend); ..[kernel/power/wakelock.c] b) suspend_work_queue = create_singlethread_workqueue("suspend"); c) queue_work(suspend_work_queue, &suspend_work); 3 queue_work() . , c-1) expire_wake_locks() c-2) wake_lock_internal() c-3) wake_unlock()

(*) suspend() early_suspend wake lock , . , wakelock , ^^.

d) suspend() .........................................................................................................[kernel/power/wakelock.c] - suspend_sys_sync_queue(); queue_work(suspend_sys_sync_work_queue, &suspend_sys_sync_work); suspend_sys_sync() system call(= sys_sync() ) !

- pm_suspend(requested_suspend_state); .....................................[kernel/power/suspend.c] enter_state() (*) original linux suspend routine .

2. Android Power Management earlysuspend/suspend code /4


<suspend flow 3 enter_state() suspend callback >
*) enter_state( ) .. [kernel/power/suspend.c] a) suspend_sys_sync_queue() sys_sync( ) ( earlysuspend, suspend ) b) suspend_prepare() sleep - pm_prepare_console()/pm_notifier_call_chain()/suspend_freeze_processes() c) suspend_devices_and_enter() sleep - suspend_console(); - c-1) dpm_suspend_start(PMSG_SUSPEND); drivers power management - dpm_prepare() - dpm_suspend() ................................................................................................. [drivers/base/power/ main.c] Execute "suspend" callbacks for all non-sysdev devices. - device_suspend(dev); __device_suspend(dev, pm_transition, false); Execute "suspend" callbacks for given device - callback = pm_op() - dpm_run_callback(callback, ) - error = cb(dev, state); (A) callback !!! - legacy_suspend() . drivers/base/power/main.c Execute a legacy (bus or class) suspend callback for device. - error = cb(dev, state); (B) callback !!!

=============<callback >===============

void __suspend_report_result(const char *function, void *fn, int ret) { if (ret) printk(KERN_ERR, %s(): %pF returns %d\n, function, fn, ret); }

. [base/power/main.c]

====================================>===============

2. Android Power Management earlysuspend/suspend code /5


<suspend flow 3 enter_state() suspend callback >
c) suspend_devices_and_enter() sleep - c-2) suspend_enter(state); enter the desired system sleep state. - suspend_ops->prepare(); - dpm_suspend_end(PMSG_SUSPEND);

late suspend callback !!!

Execute late and noirq device suspend callbacks - dpm_suspend_late( ) - device_suspend_late( ) - Execute a late suspend callback - callback = pm_late_early_op( ) - dpm_run_callback(callback, ) (C) callback !!!

- dpm_suspend_noriq( ) - device_suspend_noirq( ) - Execute a late suspend callback


- callback = pm_late_early_op( ) - dpm_run_callback(callback, ) (D) callback !!!

- suspend_ops->prepare_late(); - disable_nonboot_cpus(); - arch_suspend_disable_irqs(); - syscore_suspend(); .......................................................................................................... [drivers/base/ syscore.c] Execute all the registered system core suspend callbacks.
- ret = ops->suspend( ) (E) callback !!!

(*) suspend callback (A) ~ (E) .

2. Android Power Management earlysuspend/suspend code /6


<wakelock debugging point>

1) script , active_since(=now last_time) field 0 wakelock . active_since wakelock .


#!/system/bin/sh while [ true ]; do export PATH=/data/test; cat /proc/wakelocks | awk '{print $1 $5}' echo "------------------------------------------------------------------------------" echo -n sleep 2 done script busybox .

<suspend callback debugging point>


1) (A) ~ (D) drivers/base/power/main.c callback . .

void __suspend_report_result(const char *function, void *fn, int ret)


2) (E) drivers/base/syscore.c callback . .

int syscore_suspend(void) { } wakelock issue debugging point .

3. Wakelocks
(suspend_blocker)

3. Wakelock(1)
goToSleep

Power Manager Service

application application application

sleep Userspace kernel

acquire wakelock

early_suspend
Unlock main wakelock check Ignored. No

suspend
Yes

check

wake wake locks locks

driver driver driver

B
wake_lock

If wakelock doesnt exist, go to the suspended state.

(*) android appl linux device driver wakelock . (*) PowerManager suspend wakelock , wakelock , system suspend ^^.

3. Wakelock(2) application wakelock

(*) android appl wakelock

PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, My Tag); wl.acquire(); /* screeen will stay on during this section */ wl.release();

3. Wakelock(2) application wakelock: 4 Wakelock flag

Wakelock flag 1) PARTIAL_WAKE_LOCK 2) SCREEN_DIM_WAKE_LOCK 3) SCREEN_BRIGHT_WAKE_LOCK 4) FULL_WAKE_LOCK

(application code )
CPU , screen on . Screen on , keyboard backlight

Screen . Keyboard backlight . Screen keyoard .

3. Wakelock(3) Acquiring wakelock by application


(*) android appl wakelock acquire

3. Wakelock(4) Releasing wakelock by application


(*) android appl wakelock release

3. Wakelock(5) Kernel wakelock


(*) wakelock: android , suspend low power state (google ). (*) Smart Phone , sleep mode . (*) wake_lock_init , name /proc/wakelocks .

Declare and init wakelock (wake )


<wakeup routine> wake_lock()

wake_lock_init()

<sleep routine> Release wakelock (wake )


sleep

Start wakelock (wake )


wake_unlock()

Destroy wakelock (wake driver )

wake_lock_destroy()

3. Wakelock(6) Kernel wakelock


<Kernel Wakelock API > [ ] struct wakelock mywakelock; [] wake_lock_init(&mywakelock, int type, wakelock_name);
type :
- WAKE_LOCK_SUSPEND: suspend(full system suspend) - WAKE_LOCK_IDLE: low-power idle .

[To hold(wake )] wake_lock(&mywakelock);

[To release(sleep )] wake_unlock(&mywakelock);

[To release( , sleep )] wake_lock_timeout(&mywakelock, HZ);

[] wake_lock_destroy (&mywakelock);
[] long has_wake_lock(int type); wake lock non-zero return .

3. Wakelock(7) Kernel wakelock internal/1


(*) wakelock , , active_wake_locks list , suspend . (*) , wake_lock flags expires (kernel/power/wakelock.c ).

Go to suspend

timer

wake_lock() wake_unlock()

main_wake _lock

appl appl appl appl

Go to suspend

wakelock wakelock wakelock wakelock

active_wake_locks (linked list)

wakelock

appl appl appl driver struct wake_lock { struct list_head link; int flags; const char *name; unsigned long expires; };

suspended state

3. Wakelock(7) Kernel wakelock internal/2 wakelocks_init ( ) [ ]


(kernel/power/wakelock.c)

1) main_wake_lock, unknown_wakeup wakelock 2) platform_device(power_device), platform_driver(power_driver) struct platform_driver power_driver = { .driver.name = power, .driver.pm = &power_driver_pm_ops, }; struct platform_device power_device = { .name = power, }; 3) suspend_sys_sync suspend work queue sys_sync early_suspend/suspend/late_resume work queue sys_sync process freeze , check 4) suspend_sys_sync_comp completion kernel/power/process.c freeze_processes( ) suspend_sys_sync_wait( ) , wait_for_completion , suspend_sys_sync_timer handler complete . 5) /proc/wakelocks

3. Wakelock(8) wakelock status


cat /proc/wakelocks
(*) active_since .

(*) : Suspend(Sleep) , ?
0) , .
suspend

1) active wakelocks , 2) early suspend callback suspend callback (?) , 3) runtime pm (?) , suspend .

1) /proc/wakelocks active_since 0 .

(USB )USB PowerManagerService wakelock active_since 0 , . (USB ) active_since 0 , , ram console . active_since , sleep mode , suspend callback , runtime pm , runtime suspend callback .

2) earlysuspend suspend callback , fail .

Early suspend , suspend .

3) runtime suspend callback , fail .

( ) suspend mode , suspend ( !)

4) USB ram console .

, 1), 2), 3) kernel log , system (power off , reboot ) , (cat /proc/last_kmsg) .

4. Runtime Power Management

Page

Page

Page

4. Linux Runtime PM -
System sleep is not enough to decrease runtime energy consumption System sleep . Devices may depend on another device Device . Be helpful to figure out 'idle' condition Idle . Not doable to do I/O runtime PM in CPUIdle CPU PM I/O device PM . - devices may be idle but cpu is not idle - task schedule may be caused during device suspend - some devices' suspend is moved from cpuidle platform driver to runtime PM (such as, uart suspend on omap3/4) - long latency is involved when returning from cpuidle
(*) runtime PM system suspend( ) , System suspend device suspend , system suspend .

4. Linux Runtime PM - Runtime PM driver


Usb subsystem HUB, usb mass storage, UVC, HID, CDC, serial, usb net, ... PCI subsystem e1000e, rtl8169, ehci-pci, uhci-pci, ... SCSI subsystem sd I2C subsystem MMC subsystem Serial devices Misc device(gpio, key,......) ARCHs(RPM via dev_pm_domain)

(*) Runtime PM CPU , I/O device .

4. Linux Runtime PM - States

Active

resuming

suspending

Suspended

1) Bus port halting 2) clock, regulator disable 3) GPIO pin high impedance

4. Linux Runtime PM - callbacks

(*) I/O , run-time low-power state , wakeup run-time power management . (*) PM core callback () . (*) include/linux/pm.h

struct dev_pm_ops { int (*runtime_suspend)(struct device *dev); int (*runtime_resume)(struct device *dev); int (*runtime_idle)(struct device *dev); };

(*) Runtime PM CONFIG_PM_RUNTIME=y . (*) Documentation/power/runtime_pm.tx !

4. Linux Runtime PM - API


4) Runtime callback a) pm_runtime_suspend(dev), pm_schedule_suspend(dev, delay) device can suspend subsys: ->runtime_suspend() driver: ->runtime_suspend() b) pm_runtime_resume(dev), pm_request_resume(dev) subsys: ->runtime_resume() subsys: ->runtime_idle() driver: ->runtime_resume()
c) pm_runtime_idle(dev), pm_request_idle(dev) driver: ->runtime_idle()

1) Probe pm_runtime_enable() /* probe/configure hardware .. */ pm_runtime_suspend() (*) remove( ) pm_runtime_disalbe() .


2) Activity ( ) pm_runtime_get() /* do work */ pm_runtime_put()

5) Device

3) Suspend pm_runtime_suspend()

a)

pm_runtime_get(), _sync(), _noresume() Increment use count, pm_runtime_resume() device resume . b) pm_runtime_put(), _sync, _noidle() Decrement use count, pm_runtime_idle() device idle .

5. CPUFreq & CPUIdle

5.1 CPUFreq

5.2 CPUIdle

6. Power Management Subsystem

6.1 Clock Framework(1)

6.1 Clock Framework(2)

6.1 Clock Framework(3)

6.2 Regulator Framework(1)

6.2 Regulator Framework(2)

6.2 Regulator Framework(3)

6.2 Regulator Framework(4)

7. Battery & Charger

7. Battery & Charger: Battery Service Overview


sendIntent(battery )

1) Linux kernel power supply class /sys/class/power_supply 2) Battery uevent . SUBSYSTEM=power_supply


3) BatteryService battery . (intent ).

<linux kernel battery driver> (*) drivers/power/power_supply*.c power supply class (*) drivers/power/ds2784_battery.c battery driver . power supply code

7. Battery & Charger : Battery Service uevent


base/core/java/android/os/UEventObserver.java base/core/jni/android_os_UEventObserver.cpp

0) init , ueventd.{hardware}.rc 1) Socket , (recv) 2) Event , sysfs device (hotplug)

UEventObserver

To BatteryService

Device

ueventd

<HAL: hardware/libhardware_legacy/uevent/uevent.c> uevent_next_event( ) recv( )

netlink socket

Userspace kernel

kobject_uevent(struct kobject *kobj, enum kobject_action action); action event socket buffer(sk_buffer)

charger device driver


battery (alarm work queue )

7. Battery & Charger : Basic Charging Sequence/1


1) Trickle charging battery , fast charging fast charging battery , , trickle charging . 2) Constant current charging(fast charging) 3.2V ~ 4.1V CC -> CV voltage detector . Signal state machine 512min

3) Constant voltage charging 4.1V ~ 90


*) Vbat , battery current ( ). SM(State Machine) .

7. Battery & Charger : Basic Charging Sequence/2

Start charging

Trickle charging

Constant current charging (fast charging)

Constant voltage charging


90 min

work

veoc_begin_work

done
16 , __pm8058_start_charging()

7. Battery & Charger : Charger Driver

App Processor

PMIC chip

Userspace Charging done interrupt kernel event

interrupt

irq hdler

uevent

pm8058 charger

irq hdler

msm-charger

irq hdler

battery (fuel gauge) driver

Get battery info

7. Battery & Charger : msm_charger work queues


<schedule > 1) Register 2) suspend/resume 3) Recursive call
update_heartbeat_work update_heartbeat( ) work queue

<schedule > 1) handle_charging_done( ) 2) handle_battery_inserted() 90 3) handle_battery_removed() cancel 4) handle_charger_ready() 5) handle_charger_removed()

teoc_work teoc( )

queue_work( )

queue_work process_events( )

work handle_event( ) Event power_supply_changed( ) (framework battery )

msm_charger_notify_event( ) in pm8058_pmic_charger.c

msm_charger_eventd thread

7. Battery & Charger : pm8058 charger probe


pm8058_charger_probe( )
1) pm8058_chg variable 2) irq handler pm8058_chg_chgval_handler(CHGVAL_IRQ) pm8058_chg_chginval_handler(CHGINVAL_IRQ) pm8058_chg_auto_chgdone_handler(AUTO_CHGDONE_IRQ) pm8058_chg_auto_chgfail_handler(AUTO_CHGFAIL_IRQ) pm8058_chg_chgstate_handler(CHGSTATE_IRQ) pm8058_chg_fastchg_handler(FASTCHG_IRQ) pm8058_chg_battemp_handler(BATTTEMP_IRQ) pm8058_chg_batt_replace_handler(BATT_REPLACE_IRQ) pm8058_chg_battconnect_handler(BATTCONNECT_IRQ) pm8058_chg_vbatdet_handler(VBATDET_IRQ) 3) msm_charger_register(&usb_hw_chg); power supply class 4) msm_battery_gauge_register(&pm8058_batt_gauge); power supply class 5) work queue veoc_begin_work( ) vbat_low_work( ) chg_done_check_work( ) charging_check_work( ) chg_end_workqueue_handler( ) 6) pm8xxx_batt_alarm_threshold_set( ) PM8XXX_BATT_ALARM_UPPER_COMPARATOR/LOWER_COMPARATOR 7) pm8xxx_batt_alarm_register_notifier( ) 6) threshold event .

7. Battery & Charger : fuel gauge driver

Get temperature value


Get voltage value

msm_charger

Get soc value

Get current value

Fuel gauge

Appendix

Ram Console(1)
<Ram Console >
This allows saving the kernel printk messages to a buffer in RAM, so that after a kernel panic they can be viewed in the next kernel invocation, by accessing /proc/last_kmsg. kernel printk message RAM buffer , kernel panic , ( booting ) /proc/last_kmsg !!!

Ram Console(2)
virtual address
printk
Kernel messages through ram console

tty

size = 256K mapping

virtual memory

start = 0x78e00000

ram_console platform driver

size = 256K

Contiguous EBI memory (physical memory)

/proc/last_kmsg

ram_console platform device

Ram Console(3)

(*) # cat /proc/last_kmsg , kernel message ^^. kernel panic ^^

/proc/last_kmsg

old new

ram console buffer (persistent ram buffer)

References
1) Android-Power Management.pdf [Renaldo Noma 2010] 2) Suspend-to-RAM in Linux . [Proceedings of the Linux Symposium July 23 rd-26th, 2008] 3) Power Management & Battery Life [The 7th Korea Android Conference, Kwangwoo LEE, kwangwoo.lee@gmail.com] 4) Power Saving in Linux devices [Chris Simmonds, 2net Limited] 5) Linux clock management framework [Siarhei Yermalayeu ] 6) Runtime Power Management [Kevin Hilman] 7) Runtime PM(upstream I/O Device Power Management) [Magnus Damm] 8) Runtime Power Management Framework for I/O Devices in the Linux Kernel [Rafael J. Wysocki] 9) Linux Power Management Architecture(An updated review on Linux PM frameworks [Patrick Bellasi] 10) Power Consumption(kernel level) [Jan Slupski ] 11) Power management [Michael Opdenacker] 12) A Dynamic Voltage and Current Regulator Control Interface for the Linux Kernel [Liam Girdwood] 13) Powerdebugging inside Linaro [Amit Kucheria] 14) Some Internet Articles

SlowBoot

You might also like