Thursday, November 10, 2011

Wake-up enable for UART

Requirement
=========
Suspend the device to RAM. It should wake up on any key press from UART.

First Method
=============
This can be done by just adding  "no_console_suspend" to bootargs.
By doing this during suspends to RAM, Uart clk src will be kept alive.

Second Method
==============
For this method, hardware should have the capability to wake-up from UART interrupt.

All devices in the device model has two flags to control the wake-up events. They are "can_wakeup" and "wakeup" flags in

struct device {
....
struct dev_pm_info power;
....
}

struct dev_pm_info {
.......
unsigned int can_wakeup:1;
struct wakeup_source *wakeup;
........
}

Flag can_wakeup is initialized by the device driver code by using device_set_wakeup_capable().
And, device_set_wakeup_enable(&dev, 1) will initiallize the struct wakeup_source and add in the list of wake-able sources.

can_wakeup is used to mark whether the hardware can support the wake-up. For most of the devices wake-up will be NULL by default. But used by drivers like power buttons, keyboards and Ethernet.

I want to configure UART as my wake-up source, this patch does it on kernel-3.0.1

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index ab2e6e7..2bae1aa 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2401,7 +2401,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);
if (likely(!IS_ERR(tty_dev))) {
device_init_wakeup(tty_dev, 1);
- device_set_wakeup_enable(tty_dev, 0);
+ device_set_wakeup_enable(tty_dev, 1);
} else
printk(KERN_ERR "Cannot register tty device on line %d\n",
uport->line);

Then I tested it with echo mem > /sys/power/state

The device is able to wake-up from my UART1 interrupt which is also my console.

Now my board has many UART. I want to configure the UART2 as wake-able source, but my console is on UART1.

From the command prompt,
cat /dev/ttyS1 &

this done to initialize the UART2 port

And opened UART2 serial port on second instance of minicom. Then entering any character on second minicom wake-up my device.

4 comments:

Alok said...

That is really good.

In the first method:

the first method causes console_suspend_enabled to be ramained 1. which in turn causes suspend_console() method to return immidiately as soon as it enters the function.

Alok said...

Sorry, I forgot o ask my doubt :)

How this console_suspend_enabled is helping uart clk not to be turned off?

Arun KS said...

Hi Alok,

Below is the code snippet from kernel/printk.c

int console_suspend_enabled = 1;
EXPORT_SYMBOL(console_suspend_enabled);

static int __init console_suspend_disable(char *str)
{
console_suspend_enabled = 0;
return 1;
}
__setup("no_console_suspend", console_suspend_disable);

So by default console_suspend_enabled = 1, when you pass no_console_suspend as boot argument, kernel will make it 0.


Global variable console_suspend_enabled is used in the serial drivers to see whether it has to suspend or not that uart port which is used as a console during a Suspend to Ram (STR).


A dummy code as below,

8250_suspend()
{
/* Donot suspend the UART port if it is a console */
If(console_suspend_enabled || !uart_console(uport) )
Stop_tx();
Stop_rx();
}

Hope this helps. Sorry for the very late reply.

PREM said...

Hello,

i have customized board based on Beale-Bone Black. i am trying to disable waking up the System from UART0.

echo disabled > /sys/devices/ocp.2/44e09000.serial/tty/ttyO0/power/wakeup
echo disabled > /sys/devices/ocp.2/44e09000.serial/power/wakeup

this is the setting of the currnt wakeup sources:
cat /sys/kernel/debug/wakeup_sources
--------------------------------------------------
name ................
wakeup.gpiokey ................
44e3e00.rtc ................
alarmtimer ................


As you see there is no UART to see!! but i am still able to wake the system up from UART ??

root@milo:~# echo mem > /sys/power/state
[ 5309.439410] PM: Syncing filesystems ... done.
[ 5309.448122] Freezing user space processes ... (elapsed 0.004 seconds) done.
[ 5309.460957] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done.
[ 5309.470180] Suspending console(s) (use no_console_suspend to debug)
[ 5309.531079] PM: suspend of devices complete after 53.350 msecs
[ 5309.532112] PM: late suspend of devices complete after 1.017 msecs
[ 5309.533249] PM: noirq suspend of devices complete after 1.119 msecs
[ 5309.533326] PM: Successfully put all powerdomains to target state
[ 5309.533326] PM: Wakeup source UART
[ 5309.547912] PM: noirq resume of devices complete after 14.450 msecs
[ 5309.548769] PM: early resume of devices complete after 0.708 msecs
[ 5309.549692] c_can_platform 481d0000.d_can can0: setting BTR=2a01 BRPE=0000
[ 5309.549843] net eth0: initializing cpsw version 1.12 (0)
[ 5309.552140] net eth0: phy found : id is : 0x7c0f1
[ 5309.552202] libphy: PHY 4a101000.mdio:01 not found
[ 5309.552209] net eth0: phy 4a101000.mdio:01 not found on slave 1
[ 5309.774800] PM: resume of devices complete after 226.004 msecs
[ 5309.853396] Restarting tasks ... [ 5309.857207] usb 2-1: USB disconnect, device number 9

As you see in line 10, the system is still waking up from UART0 !!!any idea what is going wrong???