==== LOG FROM CONSOLE ====
[root@OMAP3EVM /]# echo 1 > /sys/power/enable_off_mode
[root@OMAP3EVM /]#
[root@OMAP3EVM /]#
[root@OMAP3EVM /]# echo mem > /sys/power/state
PM: Syncing filesystems ... done.
Freezing user space processes ... (elapsed 0.00 seconds) done.
Freezing remaining freezable tasks ... (elapsed 0.05 seconds) done.
Suspending console(s) (use no_console_suspend to debug)
twl4030_keypad twl4030_keypad: suspending...
Successfully put all powerdomains to target state
twl4030_keypad twl4030_keypad: resuming...
------------[ cut here ]------------
WARNING: at kernel/irq/manage.c:356 set_irq_wake+0x78/0xdc()
Unbalanced IRQ 369 wake disable
Modules linked in:
[<c002f408>] (unwind_backtrace+0x0/0xdc) from [<c0053f6c>] (warn_slowpath_common+0x48/0x60)
[<c0053f6c>] (warn_slowpath_common+0x48/0x60) from [<c0053fbc>] (warn_slowpath_fmt+0x24/0x30)
[<c0053fbc>] (warn_slowpath_fmt+0x24/0x30) from [<c00798a8>] (set_irq_wake+0x78/0xdc)
[<c00798a8>] (set_irq_wake+0x78/0xdc) from [<c01f1ef4>] (twl4030_kp_resume+0x44/0x98)
[<c01f1ef4>] (twl4030_kp_resume+0x44/0x98) from [<c01bb474>] (platform_pm_resume+0x48/0x54)
[<c01bb474>] (platform_pm_resume+0x48/0x54) from [<c01bd07c>] (pm_op+0x4c/0x74)
[<c01bd07c>] (pm_op+0x4c/0x74) from [<c01bd6f0>] (dpm_resume_end+0x80/0x328)
[<c01bd6f0>] (dpm_resume_end+0x80/0x328) from [<c0077ca8>] (suspend_devices_and_enter+0x150/0x18c)
[<c0077ca8>] (suspend_devices_and_enter+0x150/0x18c) from [<c0077d90>] (enter_state+0xac/0xec)
[<c0077d90>] (enter_state+0xac/0xec) from [<c0077518>] (state_store+0x94/0xbc)
[<c0077518>] (state_store+0x94/0xbc) from [<c0176050>] (kobj_attr_store+0x18/0x1c)
[<c0176050>] (kobj_attr_store+0x18/0x1c) from [<c00e5124>] (sysfs_write_file+0x108/0x13c)
[<c00e5124>] (sysfs_write_file+0x108/0x13c) from [<c00a2468>] (vfs_write+0xac/0x154)
[<c00a2468>] (vfs_write+0xac/0x154) from [<c00a25bc>] (sys_write+0x3c/0x68)
[<c00a25bc>] (sys_write+0x3c/0x68) from [<c0029dc0>] (ret_fast_syscall+0x0/0x2c)
---[ end trace 54f593fa7172d35d ]---
Restarting tasks ... done.
[root@OMAP3EVM /]#
[root@OMAP3EVM /]#
[root@OMAP3EVM /]#
==== PATCH ====
@@ -55,7 +55,9 @@
#include "sdram-micron-mt46h32m32lf-6.h"
#include "mmc-twl4030.h"
+#include "prm.h"
#include "pm.h"
+#include "prm-regbits-34xx.h"
#include "omap3-opp.h"
#include "board-omap3evm-camera.h"
@@ -742,6 +744,13 @@ static void __init omap3_evm_init(void)
omap35x_pmic_init();
+#ifdef CONFIG_PM
+ omap3evm_kp_data.wk_st = OMAP34XX_PRM_REGADDR(WKUP_MOD, PM_WKEN1);
+ omap3evm_kp_data.wk_en = OMAP34XX_PRM_REGADDR(WKUP_MOD, PM_WKST1);
+ omap3evm_kp_data.wk_mask = (1 << 3);
+ omap3evm_kp_data.padconf = 0x1e0 ;
+#endif
+
omap3_evm_i2c_init();
platform_add_devices(omap3_evm_devices, ARRAY_SIZE(omap3_evm_devices));
@@ -32,7 +32,9 @@
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl4030.h>
+#include <linux/io.h>
#include <mach/keypad.h>
+#include <mach/control.h>
/*
* The TWL4030 family chips include a keypad controller that supports
@@ -315,6 +317,7 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
if (!kp)
return -ENOMEM;
+ device_init_wakeup(&pdev->dev, 1);
platform_set_drvdata(pdev, kp);
/* Get the debug Device */
@@ -458,6 +461,68 @@ static int __devexit twl4030_kp_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int twl4030_kp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct twl4030_keypad *kp = platform_get_drvdata(pdev);
+ struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
+
+ /* JUST FOR DEBUG */
+ dev_err(&pdev->dev, "suspending...");
+
+ /* Set wake-enable bit */
+ if (pdata->wk_en && pdata->wk_mask) {
+ u32 v = __raw_readl(pdata->wk_en);
+ v |= pdata->wk_mask;
+ __raw_writel(v, pdata->wk_en);
+ }
+
+ /* Ensure IOPAD wake-enables are set */
+ if (cpu_is_omap34xx() && pdata->padconf) {
+ u16 v = omap_ctrl_readw(pdata->padconf);
+ v |= OMAP3_PADCONF_WAKEUPENABLE0;
+ omap_ctrl_writew(v, pdata->padconf);
+ }
+
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(kp->irq);
+
+ return 0;
+}
+
+static int twl4030_kp_resume(struct platform_device *pdev)
+{
+ struct twl4030_keypad *kp = platform_get_drvdata(pdev);
+ struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
+
+ /* JUST FOR DEBUG */
+ dev_err(&pdev->dev, "resuming...");
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(kp->irq);
+
+ /* Clear wake-enable bit */
+ if (pdata->wk_en && pdata->wk_mask) {
+ u32 v = __raw_readl(pdata->wk_en);
+ v &= ~pdata->wk_mask;
+ __raw_writel(v, pdata->wk_en);
+ }
+
+ /* Ensure IOPAD wake-enables are cleared */
+ if (cpu_is_omap34xx() && pdata->padconf) {
+ u16 v = omap_ctrl_readw(pdata->padconf);
+ v &= ~OMAP3_PADCONF_WAKEUPENABLE0;
+ omap_ctrl_writew(v, pdata->padconf);
+ }
+
+ return 0;
+}
+#else
+#define twl4030_kp_suspend NULL
+#define twl4030_kp_resume NULL
+#endif
+
+
/*
* NOTE: twl4030 are multi-function devices connected via I2C.
* So this device is a child of an I2C parent, thus it needs to
@@ -469,6 +534,8 @@ MODULE_ALIAS("platform:twl4030_keypad");
static struct platform_driver twl4030_kp_driver = {
.probe = twl4030_kp_probe,
.remove = __devexit_p(twl4030_kp_remove),
+ .suspend = twl4030_kp_suspend,
+ .resume = twl4030_kp_resume,
.driver = {
.name = "twl4030_keypad",
.owner = THIS_MODULE,
@@ -352,6 +352,12 @@ struct twl4030_keypad_data {
int irq;
unsigned int keymapsize;
unsigned int rep:1;
+#ifdef CONFIG_PM
+ void __iomem *wk_st;
+ void __iomem *wk_en;
+ u32 wk_mask;
+ u32 padconf;
+#endif
};
enum twl4030_usb_mode {