Failed to wakeup Linux with GPIO keys

230 views Asked by At

I am working on an embedded Linux kernel v5.10.24, where power management is enabled, and there is a GPIO to wake system up when it is in standby/SDR mode.

But with my testing, the GPIO cannot wake up the system.

Here is the DTS of the GPIO definition.

    gpio_keys: gpio_keys {
           compatible = "gpio-keys";
           gpioa {
               label = "gpioa";
               linux,code = <KEY_WAKEUP>;
               gpios = <&gpa 3 GPIO_ACTIVE_HIGH CHRP_GPIO_NOBIAS>;
               gpio-key,wakeup;
               wakeup-source;
           };
    };

I wrote a kernel module for this GPIO and set it up to wake system up as follows,

#include <linux/init.h>
.....
#include <linux/interrupt.h>

static int gpio_wake = 0;
static int irq = 0;

struct gpio_keys_platform_data {
    const char *label;
};

static irqreturn_t gpio_keys_irq(int irq, void *dev_id)
{
    return IRQ_HANDLED;
}

static int gpio_keys_probe(struct platform_device *pdev)
{
    struct device_node *node = pdev->dev.of_node;
    struct gpio_keys_platform_data pdata;
    enum of_gpio_flags flags;
    int ret;
    struct device_node *child = of_get_child_by_name(node, "gpioa");

    if (!child) {
        dev_err(&pdev->dev, "Failed to find gpioa node\n");
        return -ENOENT;
    }

    ret = of_property_read_string(child, "label", &pdata.label);
    if (ret) {
        dev_err(&pdev->dev, "Failed to read gpioa label property\n");
        return ret;
    }

    gpio_wake = of_get_named_gpio_flags(child, "gpios", 0, &flags);
    if (!gpio_is_valid(gpio_wake))
        gpio_wake = -1;

    if (gpio_request(gpio_wake, "gpio-wake") < 0)
        return -ENODEV;

    if (gpio_direction_input(gpio_wake) < 0)
        return -ENODEV;

    irq = gpio_to_irq(gpio_wake);
    ret = request_irq(irq, gpio_keys_irq, IRQF_TRIGGER_HIGH, "GPIOWAKE", NULL);
    if (ret < 0)
        return -ENODEV;

    device_init_wakeup(&pdev->dev, 1);

    return 0;
}

static int gpio_keys_remove(struct platform_device *pdev)
{
...
    device_init_wakeup(&pdev->dev, 0);
    dev_info(&pdev->dev, "GPIO keys driver removed\n");
...
    return 0;
}

static int gpio_suspend(struct platform_device *pdev, pm_message_t state)
{
    if (gpio_wake >= 0)
        enable_irq_wake(irq);

    return 0;
}

static int gpio_resume(struct platform_device *pdev)
{
    if (gpio_wake >= 0)
        disable_irq_wake(irq);

    return 0;
}

static const struct of_device_id gpio_keys_of_match[] = {
    { .compatible = "gpio-keys" },
    {},
};
MODULE_DEVICE_TABLE(of, gpio_keys_of_match);

static struct platform_driver gpio_keys_driver = {
    .suspend = gpio_suspend,
    .resume = gpio_resume,
    .probe = gpio_keys_probe,
    .remove = gpio_keys_remove,
    .driver = {
        .name = "gpio-keys",
        .of_match_table = gpio_keys_of_match,
        .owner = THIS_MODULE,
    },
};
module_platform_driver(gpio_keys_driver);

MODULE_AUTHOR("MinoltaFan");
MODULE_DESCRIPTION("TestGPIO Driver");
MODULE_LICENSE("GPL");

The kernel module can be inserted without error, and /proc/interrupts will show GPIOWAKE item.

I pulled the GPIO to high, and cat /proc/interrupts showed that number is increased, so the interrupt is triggered successfully with the GPIO pin.

Then I echo standby > /sys/power/state to enter 'STANDBY' mode. I can see the kernel log to say system is pm enter and the CPUs are frozen, there is no further output in serial console.

Then I pulled the GPIO pin to high (to trigger the interrupt), but the system is NOT woken up. I tried several times, but no success.

So I am wondering what is the necessary steps to configure a GPIO to wake Linux up when it is in power management mode, is there anything missing or wrong in my code?

BTW, I did NOT use Linux input system to do the wakeup, I am trying to do a minimal/basic setting to achieve the system wakeup by GPIO.

Updated.

I changed the call as follows,

ret = request_irq(irq, gpio_keys_irq, IRQF_TRIGGER_RISING, "GPIOWAKE", NULL);

and got the same result, the system cannot be waken up by this GPIO.

1

There are 1 answers

0
wangt13 On

This issue finally is root caused and fixed.

Firstly, GPIO to wake system up is supported in this system.
My test did NOT work since there is a loop in kernel when it went into suspend mode, which prevented the system from entering suspend mode.

So the GPIO interrupt cannot wake up system which is NOT really in suspend mode.