Difference between revisions of "OMAP Power Management"

From eLinux.org
Jump to: navigation, search
(Released: 16 dec 2009)
m (add wiki link for nokia n900)
 
(43 intermediate revisions by 6 users not shown)
Line 1: Line 1:
 
== PM branch ==
 
== PM branch ==
The PM branch is a developement branch of the linux-omap kernel for the purposes of developing and stabilizing the PM infrastructure
+
The PM branch is a developement branch of the linux-omap kernel for the purposes of developing and stabilizing the PM infrastructure for OMAP and submitting it upstream.
for OMAP and submitting it upstream.
 
  
 
The maintainer of the PM branch is Kevin Hilman.
 
The maintainer of the PM branch is Kevin Hilman.
Line 9: Line 8:
 
* full-chip retention in idle and suspend
 
* full-chip retention in idle and suspend
 
* full-chip OFF in idle and suspend
 
* full-chip OFF in idle and suspend
* CPUidle
+
* idle PM via CPUidle  
* DVFS using CPUfreq
+
* support for multiple OMAP3/4 boards
* support for multiple OMAP3 boards
 
  
 
The latest, tested PM branch is available as a branch named [http://git.kernel.org/?p=linux/kernel/git/khilman/linux-omap-pm.git;a=shortlog;h=pm 'pm'] from the [http://git.kernel.org/?p=linux/kernel/git/khilman/linux-omap-pm.git linux-omap-pm repository].  This branch is also sync'd daily as the 'pm' branch of the [http://git.kernel.org/?p=linux/kernel/git/tmlind/linux-omap-2.6.git main linux-omap repository].
 
The latest, tested PM branch is available as a branch named [http://git.kernel.org/?p=linux/kernel/git/khilman/linux-omap-pm.git;a=shortlog;h=pm 'pm'] from the [http://git.kernel.org/?p=linux/kernel/git/khilman/linux-omap-pm.git linux-omap-pm repository].  This branch is also sync'd daily as the 'pm' branch of the [http://git.kernel.org/?p=linux/kernel/git/tmlind/linux-omap-2.6.git main linux-omap repository].
  
 +
<!--
 +
[[File:OMAP_PM_today.png|320px|thumb|right]]
 +
-->
  
[[File:OMAP_PM_today.png|320px|thumb|right]]
 
 
=== Current version ===  
 
=== Current version ===  
==== Released:  17 dec 2009 ====
 
 
==== Important recent changes ====
 
 
* rebased to latest omap/master
 
* dropped removed /sys/power/vdd*
 
* dropped OPP API from OMAP PM layer
 
* omap3_pm_defconfig: now works an all supported boards. I moved to the new DEBUG_LL_NONE so all boards should work out of the box.
 
* voltage_off_while_idle: only modifies the VOLTCTRL reg during OFF transitions instead of when debugfs file is changed
 
  
 
==== Supported platforms (OMAP3 only) ====
 
==== Supported platforms (OMAP3 only) ====
Line 35: Line 26:
 
* Beagle
 
* Beagle
 
* Overo (Water + Tobi)
 
* Overo (Water + Tobi)
* RX51
+
* [[N900|Nokia N900]] (a.k.a RX51)
 
* Zoom2
 
* Zoom2
* [http://www.kwikbyte.com/KBOC.html KwikByte KBOC] (under testing now)
+
* [http://www.kwikbyte.com/KBOC.html KwikByte KBOC]
  
=== Using the PM branch ===
+
=== Using OMAP PM ===
  
 
==== Features ====
 
==== Features ====
Line 50: Line 41:
  
 
Serial console activity or other configured wakeup sources (keypad, touchscreen) will trigger resume.
 
Serial console activity or other configured wakeup sources (keypad, touchscreen) will trigger resume.
 
Optionally you can use a wake-up timer:
 
# echo '15' > /debug/pm_debug/wakeup_timer_seconds
 
  
 
Upon resume, you can use the powerdomain state statistics to check whether all states hit the desired state, cf. 'Debug info'
 
Upon resume, you can use the powerdomain state statistics to check whether all states hit the desired state, cf. 'Debug info'
Line 58: Line 46:
 
  # cat /debug/pm_debug/count
 
  # cat /debug/pm_debug/count
  
In addition, if any powerdomains did not hit the desired state, you will see a message on the console.
+
In addition, if any power domains did not hit the desired state, you will see a message on the console.
  
 
===== Enabling system for hitting retention during idle =====
 
===== Enabling system for hitting retention during idle =====
  
By default, the kernel will not try to hit retention or off while idle.  To enable idle path to attempt
+
By default, the UARTs will not automatically idle when unused so they will prevent low-power states during idle.  To enable UART idle timeouts with a 5 second timeout:
deeper sleep states:
+
 
 +
# echo 5 > /sys/devices/platform/omap/omap_uart.0/sleep_timeout             
 +
# echo 5 > /sys/devices/platform/omap/omap_uart.1/sleep_timeout             
 +
# echo 5 > /sys/devices/platform/omap/omap_uart.2/sleep_timeout             
 +
# echo 5 > /sys/devices/platform/omap/omap_uart.3/sleep_timeout             
  
# echo 1 > /debug/pm_debug/sleep_while_idle
+
NOTE: the 4th UART is only present on 3630 and OMAP4.
  
 
Then, wait for any inactivity timers to expire (such as the 5 second UART timer) and check the powerdomain transition statistics
 
Then, wait for any inactivity timers to expire (such as the 5 second UART timer) and check the powerdomain transition statistics
Line 74: Line 66:
 
===== Enabling system for hitting OFF =====
 
===== Enabling system for hitting OFF =====
  
By default, only retention is the deepest sleep state attempted.  To enable powerdomain transitions to off mode
+
By default, retention is the deepest sleep state attempted.  To enable power domain transitions to off mode
  
 
  # echo 1 > /debug/pm_debug/enable_off_mode
 
  # echo 1 > /debug/pm_debug/enable_off_mode
  
In addition, to enable VDD1 and VDD2 to hit 0V
+
Once again, after a suspend or after some idle time, use the power domain transition stats to check that
 
 
# echo 1 > /debug/pm_debug/voltage_off_while_idle
 
 
 
Once again, after a suspend or after some idle time, use the powerdomain transition stats to check that
 
 
transitions to off-mode are happening
 
transitions to off-mode are happening
  
 
   # cat /debug/pm_debug/count
 
   # cat /debug/pm_debug/count
  
 +
<!--
 
==== DVFS: Dynamic Voltage and Frequency Scaling ====
 
==== DVFS: Dynamic Voltage and Frequency Scaling ====
  
Line 105: Line 94:
 
  # cpufreq-set -f 550000
 
  # cpufreq-set -f 550000
 
The frequency is in KHz, as shown by cpufreq-info
 
The frequency is in KHz, as shown by cpufreq-info
 +
-->
  
 
=== Known Problems ===
 
=== Known Problems ===
  
* Zoom2: serial console wakeups not working
+
* Zoom2/3: serial console wakeups not working
 
** Problem: on suspend, by default the serial driver will disable serial interrupts, thus disabling the GPIO IRQ needed for wakeup.
 
** Problem: on suspend, by default the serial driver will disable serial interrupts, thus disabling the GPIO IRQ needed for wakeup.
 
** Fix: enable the wakeup feature for the tty used as console:
 
** Fix: enable the wakeup feature for the tty used as console:
 
+
   # echo enabled > /sys/devices/platform/serial8250.0/tty/ttyS0/power/wakeup  
   # echo enabled > /sys/devices/platform/serial8250.3/tty/ttyS3/power/wakeup  
 
 
 
* Root filesystem on MMC leads to crash when using off-mode.
 
** There is currently no support for off-mode in the MMC driver.
 
 
* UART doesn't come back after off-while-idle
 
** Known issue on only some OMAP3 silicon.  Under investigation at TI.
 
  
 
* GPIO module-level wakeups not always working
 
* GPIO module-level wakeups not always working
Line 144: Line 127:
 
  # cat /debug/pm_debug/count
 
  # cat /debug/pm_debug/count
  
 +
This will look something like this on OMAP3:
 +
 +
  # cat /debug/pm_debug/count
 +
  cefuse_pwrdm (OFF),OFF:1,RET:0,INA:0,ON:0,RET-LOGIC-OFF:0
 +
  always_on_core_pwrdm (OFF),OFF:1,RET:0,INA:0,ON:0,RET-LOGIC-OFF:0
 +
  l4per_pwrdm (ON),OFF:0,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0
 +
  l3init_pwrdm (RET),OFF:0,RET:1,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0
 +
  cam_pwrdm (OFF),OFF:1,RET:0,INA:0,ON:0,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0
 +
  ivahd_pwrdm (RET),OFF:1,RET:1,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0,RET-MEMBANK3-OFF:0,RET-MEMBANK4-OFF:0
 +
  mpu_pwrdm (ON),OFF:0,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0
 +
  cpu1_pwrdm (ON),OFF:0,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0
 +
  cpu0_pwrdm (ON),OFF:0,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0
 +
  tesla_pwrdm (RET),OFF:1,RET:1,INA:0,ON:0,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0,RET-MEMBANK3-OFF:0
 +
  dss_pwrdm (RET),OFF:0,RET:1,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0
 +
  abe_pwrdm (ON),OFF:1,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0
 +
  gfx_pwrdm (OFF),OFF:2,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0
 +
  core_pwrdm (ON),OFF:0,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0,RET-MEMBANK3-OFF:0,RET-MEMBANK4-OFF:0,RET-MEMBANK5-OFF:0
 +
 +
If you see each power domain has counters specified. OFF, RET, INA and so on...The count basically keeps incrementing every time
 +
it hits low power state. In the above example, cam_pwrdm (camera power domain) has hit OFF state once. GFX power domain has hit OFF state twice and like wise.
 +
 +
<!--
 
Dump current PRCM registers
 
Dump current PRCM registers
  
 
  # cat /debug/pm_debug/registers/current
 
  # cat /debug/pm_debug/registers/current
  
Dump PRCM registers at last suspend
+
Dump PRCM register snapshot taken just before suspend (just before jump into SRAM idle code)
  
 
  # cat /debug/pm_debug/registers/1
 
  # cat /debug/pm_debug/registers/1
 +
 +
Dump PRCM register snapshot taken immediately after resume
 +
 +
# cat /debug/pm_debug/registers/2
 +
-->
  
 
==== UART wakeup and timeout options ====
 
==== UART wakeup and timeout options ====
Line 156: Line 166:
 
By default, each of the on-chip OMAP UARTs are enabled as wakeup sources.  In addition, they are configured with a configurable inactivity timer (default 5 seconds) after which the UART clocks are allowed to be gated during idle or suspend.
 
By default, each of the on-chip OMAP UARTs are enabled as wakeup sources.  In addition, they are configured with a configurable inactivity timer (default 5 seconds) after which the UART clocks are allowed to be gated during idle or suspend.
  
For example, to disable the wakeup capability of a UART1 (a.k.a ttyS0)
+
For example, to disable the wakeup capability of a UART1 (a.k.a ttyO0)
 
+
   # echo disabled > /sys/devices/platform/omap/omap-hsuart.0/power/wakeup
   # echo disabled > /sys/devices/platform/serial8250.0/power/wakeup
 
 
 
 
And to change the inactivity timer to 10 seconds, instead of the default 5:
 
And to change the inactivity timer to 10 seconds, instead of the default 5:
 
+
   # echo 10 > /sys/devices/platform/omap/omap-hsuart.0/sleep_timeout
   # echo 10 > /sys/devices/platform/serial8250.0/sleep_timeout  
 
  
 
Note that you can <tt>cat</tt> these files under <tt>/sys</tt> as well to see the current values.
 
Note that you can <tt>cat</tt> these files under <tt>/sys</tt> as well to see the current values.
Line 169: Line 176:
 
Debugging problems with the OMAP UART driver wakeup and data transfer when Power Management is enabled can be quite tedious, if one does not have a proper HW setup. An example of a setup (including both HW and SW changes) can be found in the [[OMAP_UART_pm_debugging]] page.  
 
Debugging problems with the OMAP UART driver wakeup and data transfer when Power Management is enabled can be quite tedious, if one does not have a proper HW setup. An example of a setup (including both HW and SW changes) can be found in the [[OMAP_UART_pm_debugging]] page.  
  
==== Misc. options ====
 
===== OPPs control =====
 
 
'''NOTE: OPP control via sysfs has been removed.  Please use CPUfreq interfaces for DVFS. '''
 
 
===== SmartReflex control =====
 
NOTE: detailed information on SmartReflex can be found [[OMAP_Power_Management/SmartReflex|HERE]]
 
 
Enables SmartReflex autocompensation on VDD1 (Note: This feature can only be tested on a ES3.1 silicon)
 
# echo 1 > /sys/power/sr_vdd1_autocomp
 
Disables SmartReflex? autocompensation on VDD1
 
# echo 0 > /sys/power/sr_vdd1_autocomp
 
 
Enables SmartReflex autocompensation on VDD2 (Note: This feature can only be tested on a ES3.1 silicon)
 
# echo 1 > /sys/power/sr_vdd2_autocomp
 
Disables SmartReflex? autocompensation on VDD2
 
# echo 0 > /sys/power/sr_vdd2_autocomp
 
  
 +
<!--
 
===== CPUfreq kernel interface =====
 
===== CPUfreq kernel interface =====
  
Line 207: Line 198:
 
  # cat /sys/devices/system/cpu/cpu0/cpufreq/stats/total_trans
 
  # cat /sys/devices/system/cpu/cpu0/cpufreq/stats/total_trans
 
  # cat /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state
 
  # cat /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state
 +
-->
  
=== PM code in Mainline ===
+
<!--
 
 
Here's a very crude outline of my plans for getting PM branch code to mainline.  Unless otherwise stated, this has only been tested on OMAP3, although some care has been taken to not break OMAP2 in the process.
 
 
 
==== Currently in mainline (2.6.31) ====
 
 
 
* clock framework and infrastructure
 
* clockdomain and powerdomain core
 
* full-chip retention in suspend
 
* full-chip retention in idle
 
 
 
==== merged in 2.6.32 ====
 
 
 
* misc. PM driver updates
 
** SPI
 
* PM debug infrastructure
 
* OMAP PM layer
 
* omap_hwmod/omap_device
 
* twl4030 power support
 
 
 
==== merged in 2.6.33 ====
 
 
 
* off-mode support
 
** context save/restore support
 
 
 
* CPUidle support
 
** including off-mode C-states
 
 
 
* Drivers
 
** I2C driver off-mode support: re-init every transaction
 
 
 
==== Planned for next merge window (2.6.34) ====
 
* Large set of fixes from Nokia and others
 
* GPIO off-mode support
 
* debug observability
 
* Misc. fixes
 
 
 
==== What's left in PM branch ====
 
The remaining parts in the PM branch are not ready for upstream in their current form.  This is either due to code quality, basic design problems or not ready to scale for OMAP3430 and OMAP4.
 
 
 
* OPP layer
 
* SRF
 
* DVFS, CPUfreq
 
* [[OMAP_Power_Management/SmartReflex|SmartReflex]] driver core (deps: OMAP PM layer, OPP layer)
 
 
 
 
[[File:OMAP_PM_future.png|320px|thumb|right]]
 
[[File:OMAP_PM_future.png|320px|thumb|right]]
  
Line 270: Line 218:
 
===== omap_hwmod, omap_device conversion =====
 
===== omap_hwmod, omap_device conversion =====
  
* HS-MMC example (06-05-2009, Paul Walmsley): needs update against current PM branch, but shows an example conversion
+
An important buidling block to converting to a common framework (runtime PM) for device PM is a common framework for all on-chip hardware blocks. This is available as the omap_device and omap_hwmod layers.  These layers provide an abstraction so that all hardware IP blocks can be controlled using the same API. The runtime PM layer is then implemented as a think layer on top of the omap_device API.
** [http://marc.info/?l=linux-omap&m=124419788924563&w=2 PATCH 0/3: omap_device implementation, and HSMMC example]
+
 
** [http://marc.info/?l=linux-omap&m=124419789124567&w=2 PATCH 1/3: OMAP2/3/4 core: create omap_device layer]
+
This implies that in order to have runtime PM support for a device, the underlying HW IP must be represented by an omap_hwmod and have a correspdonding omap_device built for it. Then, using the runtime PM API from the driver will result in omap_device API calls to control the IP.
** [http://marc.info/?l=linux-omap&m=124419789024565&w=2 PATCH 2/3: OMAP: MMC (core): split device registration by OMAP]
 
** [http://marc.info/?l=linux-omap&m=124419789124570&w=2 PATCH 3/3: OMAP2/3 MMC: initial conversion to omap_device]
 
  
 +
* Current Status: http://omappedia.org/wiki/HWMOD
 
===== Run-time PM =====
 
===== Run-time PM =====
  
Line 281: Line 228:
  
 
* LWN article: http://lwn.net/Articles/347573/
 
* LWN article: http://lwn.net/Articles/347573/
 +
* Kevin Hilman's talk from ELC 2010 in San Francisco: http://elinux.org/images/0/08/ELC-2010-Hilman-Runtime-PM.pdf
  
 
* Key features
 
* Key features
Line 290: Line 238:
 
** implement platform specific runtime PM hooks for OMAP  
 
** implement platform specific runtime PM hooks for OMAP  
 
** runtime PM API used by '''all''' OMAP drivers
 
** runtime PM API used by '''all''' OMAP drivers
** Explore platform_bus notifiers for automatic init/idle/suspend of devices
 
  
* TODO
+
* Current status
** (add overview of API from driver perspective)
+
** Propsed runtime PM implementation for OMAP available in <tt>pm-wip/runtime</tt> branch of Kevin's [http://git.kernel.org/?p=linux/kernel/git/khilman/linux-omap-pm.git linux-omap-pm repository].
 +
-->
 +
 
 +
=== Public Power management test framework ===
 +
Some commonly used power management utilities are listed here which make sense from an OMAP perspective
 +
 
 +
==== Cpufreq utils ====
 +
[http://www.kernel.org/pub/linux/utils/kernel/cpufreq/cpufrequtils.html cpufreq utils] for testing dynamic voltage and frequency scaling.
 +
 
 +
==== Maemo pm_test ====
 +
[https://garage.maemo.org/projects/pm-test/ pm-test] plugin for Maemo [https://garage.maemo.org/plugins/wiki/index.php?id=778&type=g says]
 +
  utility which tests that kernel and kernel modules works power management wise
 +
This utility could be used to sanity test the powermanagement impact to a system for suspend/restore and basic power features.
 +
==== Quick verification of suspend-idle functionality ====
 +
the following script may be used with userspace supporting something simple as busybox:
 +
#!/bin/ash
 +
# Quick script to verify SUSPEND Resume behavior without human intervention
 +
# Refer: http://elinux.org/OMAP_Power_Management for details
 +
 +
# Some params that might change based on the environment
 +
SYS=/sys
 +
DEBUG=$SYS/kernel/debug
 +
PROC=/proc
 +
 +
PMDEBUG=$DEBUG/pm_debug
 +
VOLTAGE_OFF=$PMDEBUG/voltage_off_mode
 +
kver=`uname -r`
 +
if [ $kver > "2.6.36" ]; then
 +
UART="$SYS/devices/platform/omap/omap-hsuart"
 +
else
 +
UART="$SYS/devices/platform/serial8250"
 +
fi
 +
UART1=$UART.0/sleep_timeout
 +
UART2=$UART.1/sleep_timeout
 +
UART3=$UART.2/sleep_timeout
 +
 +
# Setup cpu idle
 +
cpu_idle(){
 +
echo -n "$1" > $PMDEBUG/sleep_while_idle
 +
}
 +
 +
# setup off mode
 +
off_mode(){
 +
echo -n "$1" > $PMDEBUG/enable_off_mode
 +
}
 +
 +
# Do a suspend
 +
suspend_me(){
 +
echo -n "mem" > $SYS/power/state
 +
}
 +
 +
# get my core data (This is the last domain to hit lowest power state)
 +
core_count(){
 +
cat $PMDEBUG/count |grep "^core_pwrdm"
 +
}
 +
 +
# get my retention counter
 +
core_ret_count(){
 +
core_count|cut -d ',' -f3|cut -d ':' -f2
 +
}
 +
 +
# get my off counter
 +
core_off_count(){
 +
core_count|cut -d ',' -f2|cut -d ':' -f2
 +
}
 +
 +
# setup wakeup timer - automated testing
 +
wakeup_timer(){
 +
echo -n "$1" > $PMDEBUG/wakeup_timer_seconds
 +
echo -n "$2" > $PMDEBUG/wakeup_timer_milliseconds
 +
}
 +
 +
# Setup our uart to be inactivity timer
 +
setup_tty_sleep_timeout() {
 +
if [ -f $UART1 ]; then
 +
echo -n "$1" > $UART1
 +
fi
 +
if [ -f $UART2 ]; then
 +
echo -n "$1" > $UART1
 +
fi
 +
if [ -f $UART3 ]; then
 +
echo -n "$1" > $UART3
 +
fi
 +
 +
}
 +
 +
# Measurement Start
 +
measure_start(){
 +
OFF_START=`core_off_count`
 +
RET_START=`core_ret_count`
 +
TIME_START=`date "+%s"`
 +
}
 +
# Measurement End
 +
measure_end(){
 +
OFF_END=`core_off_count`
 +
RET_END=`core_ret_count`
 +
TIME_END=`date "+%s"`
 +
}
 +
# Common formatted print
 +
measure_print(){
 +
DUR=`expr $TIME_END - $TIME_START`
 +
echo "$1 | $2 | OFF: $OFF_START->$OFF_END| RET:$RET_START->$RET_END ($DUR sec)"
 +
}
 +
 +
# verify function
 +
check_core_off(){
 +
RESULT=FAIL
 +
if [ $OFF_START -lt $OFF_END ]; then
 +
RESULT=PASS
 +
fi
 +
}
 +
check_core_ret(){
 +
RESULT=FAIL
 +
if [ $RET_START -lt $RET_END ]; then
 +
RESULT=PASS
 +
fi
 +
}
 +
 +
# Disable everything
 +
disable_all(){
 +
# disable voltage off
 +
if [ -f $VOLTAGE_OFF ]; then
 +
echo -n "0" >$VOLTAGE_OFF
 +
fi
 +
setup_tty_sleep_timeout 0
 +
wakeup_timer 0 0
 +
off_mode 0
 +
cpu_idle 0
 +
}
 +
 +
# test idle - core ret
 +
test_idle_ret() {
 +
disable_all
 +
measure_start
 +
setup_tty_sleep_timeout 5
 +
cpu_idle 1
 +
sleep 20
 +
disable_all
 +
sleep 1;sync
 +
measure_end
 +
check_core_ret
 +
measure_print "IDLE:RET test" $RESULT
 +
}
 +
 +
# test idle - core off
 +
test_idle_off() {
 +
disable_all
 +
measure_start
 +
setup_tty_sleep_timeout 5
 +
off_mode 1
 +
cpu_idle 1
 +
sleep 20
 +
disable_all
 +
sleep 1;sync
 +
measure_end
 +
check_core_off
 +
measure_print "IDLE:OFF test" $RESULT
 +
}
 +
 +
# test suspend - core ret
 +
test_suspend_ret() {
 +
disable_all
 +
measure_start
 +
wakeup_timer 5 0
 +
suspend_me
 +
disable_all
 +
sleep 1;sync
 +
measure_end
 +
check_core_ret
 +
measure_print "SUSPEND:RET test" $RESULT
 +
}
 +
 +
# test suspend - core off
 +
test_suspend_off() {
 +
disable_all
 +
measure_start
 +
off_mode 1
 +
wakeup_timer 5 0
 +
suspend_me
 +
disable_all
 +
sleep 1;sync
 +
measure_end
 +
check_core_off
 +
measure_print "SUSPEND:OFF test" $RESULT
 +
}
 +
 +
# mount up the basic fs
 +
already_mntd=`mount|grep $PROC`
 +
if [ x == x"$already_mntd" ]; then
 +
mount -t proc none $PROC
 +
fi
 +
 +
already_mntd=`mount|grep $SYS`
 +
if [ x == x"$already_mntd" ]; then
 +
mount -t sysfs none $SYS
 +
fi
 +
already_mntd=`mount|grep $DEBUG`
 +
if [ x == x"$already_mntd" ]; then
 +
mount -t debugfs none $DEBUG
 +
fi
 +
# Lets run the tests one by one..
 +
NR=""
 +
R=`test_suspend_off`
 +
echo $R
 +
NR="$NR\n$R"
 +
R=`test_suspend_ret`
 +
echo $R
 +
NR="$NR\n$R"
 +
R=`test_idle_off`
 +
echo $R
 +
NR="$NR\n$R"
 +
R=`test_idle_ret`
 +
echo $R
 +
NR="$NR\n$R"
 +
# Print End result summary
 +
cat $PMDEBUG/count
 +
 +
# Print test summary
 +
echo -e "$NR"
 +
 
 +
[[Category:OMAP]]
 +
[[Category:Power Management]]

Latest revision as of 07:33, 18 March 2014

PM branch

The PM branch is a developement branch of the linux-omap kernel for the purposes of developing and stabilizing the PM infrastructure for OMAP and submitting it upstream.

The maintainer of the PM branch is Kevin Hilman.

Features

  • full-chip retention in idle and suspend
  • full-chip OFF in idle and suspend
  • idle PM via CPUidle
  • support for multiple OMAP3/4 boards

The latest, tested PM branch is available as a branch named 'pm' from the linux-omap-pm repository. This branch is also sync'd daily as the 'pm' branch of the main linux-omap repository.


Current version

Supported platforms (OMAP3 only)

Tested on the following platforms using omap3_pm_defconfig with busybox-based initramfs, and tested full-chip RET and OFF in idle and suspend:

Using OMAP PM

Features

By default, the OMAP is configured to hit full-chip retention in suspend.

Suspend/Resume
# echo mem > /sys/power/state

Serial console activity or other configured wakeup sources (keypad, touchscreen) will trigger resume.

Upon resume, you can use the powerdomain state statistics to check whether all states hit the desired state, cf. 'Debug info'

# cat /debug/pm_debug/count

In addition, if any power domains did not hit the desired state, you will see a message on the console.

Enabling system for hitting retention during idle

By default, the UARTs will not automatically idle when unused so they will prevent low-power states during idle. To enable UART idle timeouts with a 5 second timeout:

# echo 5 > /sys/devices/platform/omap/omap_uart.0/sleep_timeout               
# echo 5 > /sys/devices/platform/omap/omap_uart.1/sleep_timeout               
# echo 5 > /sys/devices/platform/omap/omap_uart.2/sleep_timeout               
# echo 5 > /sys/devices/platform/omap/omap_uart.3/sleep_timeout               

NOTE: the 4th UART is only present on 3630 and OMAP4.

Then, wait for any inactivity timers to expire (such as the 5 second UART timer) and check the powerdomain transition statistics to see that transitions are happening

 # cat /debug/pm_debug/count
Enabling system for hitting OFF

By default, retention is the deepest sleep state attempted. To enable power domain transitions to off mode

# echo 1 > /debug/pm_debug/enable_off_mode

Once again, after a suspend or after some idle time, use the power domain transition stats to check that transitions to off-mode are happening

 # cat /debug/pm_debug/count


Known Problems

  • Zoom2/3: serial console wakeups not working
    • Problem: on suspend, by default the serial driver will disable serial interrupts, thus disabling the GPIO IRQ needed for wakeup.
    • Fix: enable the wakeup feature for the tty used as console:
 # echo enabled > /sys/devices/platform/serial8250.0/tty/ttyS0/power/wakeup 
  • GPIO module-level wakeups not always working
    • Background: GPIO wakeups can happen either via the GPIO module itself (module-level wakeups) or via IO pad wakeups if the CORE powerdomain is inactive, in retention or off.
    • If the IO pad wakeups are not enabled (either because CORE remains on, or because IO pad is not armed) GPIO wakeups may not happen unless the GPIO module-level wakeups are programmed correctly.
    • To ensure GPIO module wakeups are programmed correctly:
      • Enable GPIO IRQ for wakeup GPIO, including ISR. Use request_irq()
      • Ensure GPIO is edge-triggered. Only edge triggered GPIOs are wakeup capable (c.f. omap34xx TRM Sec. 25.5.3.1)
        • the flags argument of request_irq() should have either IRQF_TRIGGER_FALLING, IRQF_TRIGGER_RISING or both.
      • Enable GPIO IRQ as wakeup source using enable_irq_wake(gpio_to_irq(<gpio>))
    • NOTE: It is very important that an interrupt handler be configured for the GPIO IRQ, even if it does nothing but return IRQ_HANDLED. This is because without an interrupt handler, the GPIO IRQ event will never be properly cleared and this can prevent the GPIO module from hitting retention or off on the next idle request (c.f. omap34xx TRM Sec. 25.5.3.1).
  • GPIO wakeup works once, but prevents future retention
    • See NOTE just above

Advanced features for PM developers and power users

Debug info

First, mount the debug filesystem (debugfs)

# mount -t debugfs debugfs /debug

Show powerdomain state statistics and clockdomain active clocks

# cat /debug/pm_debug/count

This will look something like this on OMAP3:

 # cat /debug/pm_debug/count
 cefuse_pwrdm (OFF),OFF:1,RET:0,INA:0,ON:0,RET-LOGIC-OFF:0
 always_on_core_pwrdm (OFF),OFF:1,RET:0,INA:0,ON:0,RET-LOGIC-OFF:0
 l4per_pwrdm (ON),OFF:0,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0
 l3init_pwrdm (RET),OFF:0,RET:1,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0
 cam_pwrdm (OFF),OFF:1,RET:0,INA:0,ON:0,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0
 ivahd_pwrdm (RET),OFF:1,RET:1,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0,RET-MEMBANK3-OFF:0,RET-MEMBANK4-OFF:0
 mpu_pwrdm (ON),OFF:0,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0
 cpu1_pwrdm (ON),OFF:0,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0
 cpu0_pwrdm (ON),OFF:0,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0
 tesla_pwrdm (RET),OFF:1,RET:1,INA:0,ON:0,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0,RET-MEMBANK3-OFF:0
 dss_pwrdm (RET),OFF:0,RET:1,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0
 abe_pwrdm (ON),OFF:1,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0
 gfx_pwrdm (OFF),OFF:2,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0
 core_pwrdm (ON),OFF:0,RET:0,INA:0,ON:1,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0,RET-MEMBANK3-OFF:0,RET-MEMBANK4-OFF:0,RET-MEMBANK5-OFF:0

If you see each power domain has counters specified. OFF, RET, INA and so on...The count basically keeps incrementing every time it hits low power state. In the above example, cam_pwrdm (camera power domain) has hit OFF state once. GFX power domain has hit OFF state twice and like wise.


UART wakeup and timeout options

By default, each of the on-chip OMAP UARTs are enabled as wakeup sources. In addition, they are configured with a configurable inactivity timer (default 5 seconds) after which the UART clocks are allowed to be gated during idle or suspend.

For example, to disable the wakeup capability of a UART1 (a.k.a ttyO0)

 # echo disabled > /sys/devices/platform/omap/omap-hsuart.0/power/wakeup

And to change the inactivity timer to 10 seconds, instead of the default 5:

 # echo 10 > /sys/devices/platform/omap/omap-hsuart.0/sleep_timeout

Note that you can cat these files under /sys as well to see the current values.

UART PM Debugging Techniques

Debugging problems with the OMAP UART driver wakeup and data transfer when Power Management is enabled can be quite tedious, if one does not have a proper HW setup. An example of a setup (including both HW and SW changes) can be found in the OMAP_UART_pm_debugging page.



Public Power management test framework

Some commonly used power management utilities are listed here which make sense from an OMAP perspective

Cpufreq utils

cpufreq utils for testing dynamic voltage and frequency scaling.

Maemo pm_test

pm-test plugin for Maemo says

 utility which tests that kernel and kernel modules works power management wise

This utility could be used to sanity test the powermanagement impact to a system for suspend/restore and basic power features.

Quick verification of suspend-idle functionality

the following script may be used with userspace supporting something simple as busybox:

#!/bin/ash
# Quick script to verify SUSPEND Resume behavior without human intervention
# Refer: http://elinux.org/OMAP_Power_Management for details

# Some params that might change based on the environment
SYS=/sys
DEBUG=$SYS/kernel/debug
PROC=/proc

PMDEBUG=$DEBUG/pm_debug
VOLTAGE_OFF=$PMDEBUG/voltage_off_mode
kver=`uname -r`
if [ $kver > "2.6.36" ]; then
	UART="$SYS/devices/platform/omap/omap-hsuart"
else
	UART="$SYS/devices/platform/serial8250"
fi
UART1=$UART.0/sleep_timeout
UART2=$UART.1/sleep_timeout
UART3=$UART.2/sleep_timeout

# Setup cpu idle
cpu_idle(){
	echo -n "$1" > $PMDEBUG/sleep_while_idle
}

# setup off mode
off_mode(){
	echo -n "$1" > $PMDEBUG/enable_off_mode
}

# Do a suspend
suspend_me(){
	echo -n "mem" > $SYS/power/state
}

# get my core data (This is the last domain to hit lowest power state)
core_count(){
	cat $PMDEBUG/count |grep "^core_pwrdm"
}

# get my retention counter
core_ret_count(){
	core_count|cut -d ',' -f3|cut -d ':' -f2
}

# get my off counter
core_off_count(){
	core_count|cut -d ',' -f2|cut -d ':' -f2
}

# setup wakeup timer - automated testing
wakeup_timer(){
	echo -n "$1" > $PMDEBUG/wakeup_timer_seconds
	echo -n "$2" > $PMDEBUG/wakeup_timer_milliseconds
}

# Setup our uart to be inactivity timer
setup_tty_sleep_timeout() {
	if [ -f $UART1 ]; then
		echo -n "$1" > $UART1
	fi
	if [ -f $UART2 ]; then
		echo -n "$1" > $UART1
	fi
	if [ -f $UART3 ]; then
		echo -n "$1" > $UART3
	fi

}

# Measurement Start
measure_start(){
	OFF_START=`core_off_count`
	RET_START=`core_ret_count`
	TIME_START=`date "+%s"`
}
# Measurement End
measure_end(){
	OFF_END=`core_off_count`
	RET_END=`core_ret_count`
	TIME_END=`date "+%s"`
}
# Common formatted print
measure_print(){
	DUR=`expr $TIME_END - $TIME_START`
	echo "$1 | $2 | OFF: $OFF_START->$OFF_END| RET:$RET_START->$RET_END ($DUR sec)"
}

# verify function
check_core_off(){
	RESULT=FAIL
	if [ $OFF_START -lt $OFF_END ]; then
		RESULT=PASS
	fi
}
check_core_ret(){
	RESULT=FAIL
	if [ $RET_START -lt $RET_END ]; then
		RESULT=PASS
	fi
}

# Disable everything
disable_all(){
	# disable voltage off
	if [ -f $VOLTAGE_OFF ]; then
		echo -n "0" >$VOLTAGE_OFF
	fi
	setup_tty_sleep_timeout 0
	wakeup_timer 0 0
	off_mode 0
	cpu_idle 0
}

# test idle - core ret
test_idle_ret() {
	disable_all
	measure_start
	setup_tty_sleep_timeout 5
	cpu_idle 1
	sleep 20
	disable_all
	sleep 1;sync
	measure_end
	check_core_ret
	measure_print "IDLE:RET test" $RESULT
}

# test idle - core off
test_idle_off() {
	disable_all
	measure_start
	setup_tty_sleep_timeout 5
	off_mode 1
	cpu_idle 1
	sleep 20
	disable_all
	sleep 1;sync
	measure_end
	check_core_off
	measure_print "IDLE:OFF test" $RESULT
}

# test suspend - core ret
test_suspend_ret() {
	disable_all
	measure_start
	wakeup_timer 5 0
	suspend_me
	disable_all
	sleep 1;sync
	measure_end
	check_core_ret
	measure_print "SUSPEND:RET test" $RESULT
}

# test suspend - core off
test_suspend_off() {
	disable_all
	measure_start
	off_mode 1
	wakeup_timer 5 0
	suspend_me
	disable_all
	sleep 1;sync
	measure_end
	check_core_off
	measure_print "SUSPEND:OFF test" $RESULT
}

# mount up the basic fs
already_mntd=`mount|grep $PROC`
if [ x == x"$already_mntd" ]; then
	mount -t proc none $PROC
fi

already_mntd=`mount|grep $SYS`
if [ x == x"$already_mntd" ]; then
	mount -t sysfs none $SYS
fi
already_mntd=`mount|grep $DEBUG`
if [ x == x"$already_mntd" ]; then
	mount -t debugfs none $DEBUG
fi
# Lets run the tests one by one..
NR=""
R=`test_suspend_off`
echo $R
NR="$NR\n$R"
R=`test_suspend_ret`
echo $R
NR="$NR\n$R"
R=`test_idle_off`
echo $R
NR="$NR\n$R"
R=`test_idle_ret`
echo $R
NR="$NR\n$R"
# Print End result summary
cat $PMDEBUG/count

# Print test summary
echo -e "$NR"