Suspending with keypad bug (RFC)

andrzej zaborowski
2006-08-04T23:33:57Z

[-- Attachment #1: Type: text/plain, Size: 1138 bytes --]

Hi
When the keypad driver is compiled in, the kernel won't resume from
a sleep state if any key on the keypad was pressed during suspending.
This is because the keypad driver masks the keyboard interrupt when
the irq is received and doesn't unmask it until after keys are
released. I don't know if it's true for all CPUs, it happens on my
board which is 15xx.

This may often be a problem when one of the keys is a power key (like
on PDAs) and you're in a console. If your device doesn't have UART2 it
effectively means a lock-up.

I unmasked the keyboard interrupt just before suspending, in pm.c and
this prevents the lock-ups but I don't know if it's a correct fix.
This results in that the device immediately resumes if any key is
still pressed after entering idle.

I came up with a different fix, in the keypad driver but it's a bit
more intrusive. With this the device sleeps peacefully until another
key-press is detected. What should be the correct behavior?
I'm attaching both changes.

Another approach could be to mask the irq in the MPU interrupt handler
registers instead of with keypad registers, maybe.
Regards,
Andrzej

[-- Attachment #2: linux-keypad-suspend0.patch --]
[-- Type: application/octet-stream, Size: 827 bytes --]

--- linux-omap-2.6-git/arch/arm/mach-omap1/pm.c 2006-07-06 02:47:56.000000000 +0000
+++ linux-omap-2.6-newbuild/arch/arm/mach-omap1/pm.c 2006-08-05 00:16:38.000000000 +0000
@@ -60,6 +60,7 @@
#include <asm/arch/dma.h>
#include <asm/arch/dsp_common.h>
#include <asm/arch/dmtimer.h>
+#include <asm/arch/gpio.h>

static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE];
static unsigned short dsp_sleep_save[DSP_SLEEP_SAVE_SIZE];
@@ -222,6 +221,9 @@ static void omap_pm_wakeup_setup(void)
OMAP_IRQ_BIT(INT_730_MPUIO_KEYPAD)),
OMAP_IH2_1_MIR);
} else if (cpu_is_omap15xx()) {
+ /* Make sure INT_KEYBOARD is enabled in MPUIO */
+ omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
level2_wake |= OMAP_IRQ_BIT(INT_KEYBOARD);
omap_writel(~level2_wake, OMAP_IH2_MIR);
} else if (cpu_is_omap16xx()) {

[-- Attachment #3: linux-keypad-suspend1.patch --]
[-- Type: application/octet-stream, Size: 1540 bytes --]

--- linux-omap-2.6-git/drivers/input/keyboard/omap-keypad.c 2006-07-06 02:48:09.000000000 +0000
+++ linux-omap-2.6-newbuild/drivers/input/keyboard/omap-keypad.c 2006-08-05 00:40:54.000000000 +0000
@@ -59,6 +59,7 @@ struct omap_kp {
int irq;
unsigned int rows;
unsigned int cols;
+ int suspended;
};

DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
@@ -100,6 +101,9 @@ static irqreturn_t omap_kp_interrupt(int
{
struct omap_kp *omap_kp = dev_id;

+ if (omap_kp->suspended)
+ return IRQ_HANDLED;
+
/* disable keyboard interrupt and schedule for handling */
if (cpu_is_omap24xx()) {
int i;
@@ -269,15 +273,24 @@ static DEVICE_ATTR(enable, S_IRUGO | S_I
#ifdef CONFIG_PM
static int omap_kp_suspend(struct platform_device *dev, pm_message_t state)
{
- /* Nothing yet */
+ struct omap_kp *omap_kp = platform_get_drvdata(dev);
+
+ omap_kp->suspended = 1;
+ /*
+ * Re-enable the interrupt in case it has been masked by the
+ * handler and a key is still pressed. We need the interrupt
+ * to wake us up from suspended.
+ */
+ omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);

return 0;
}

static int omap_kp_resume(struct platform_device *dev)
{
- /* Nothing yet */
+ struct omap_kp *omap_kp = platform_get_drvdata(dev);

+ omap_kp->suspended = 0;
return 0;
}
#else
@@ -349,6 +362,8 @@ static int __init omap_kp_probe(struct p
}
}

+ omap_kp->suspended = 0;
+
init_timer(&omap_kp->timer);
omap_kp->timer.function = omap_kp_timer;
omap_kp->timer.data = (unsigned long) omap_kp;

[-- Attachment #4: Type: text/plain, Size: 0 bytes --]