3

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.

0andriy
  • 4,183
  • 1
  • 24
  • 37
wangt13
  • 959
  • 7
  • 17
  • 1
    I ask out of curiousity: to support wake-on-gpio, don't you need some sort of support in the firmware, since the kernel isn't running when the system is suspended? – larsks Jul 10 '23 at 14:02
  • This is an embedded system on SoC, which has SRAM on chip to support the suspend/resume, so I don't think it involves the support in firmware. And the RTC wakeup is working in this system, its DS also mentioned wakeing up by GPIO is supported. – wangt13 Jul 10 '23 at 23:36
  • First of all, it’s unclear what GPIO controller is in use (which driver). Second, does simply `gpiomon` tool work for you? It makes your entire driver not needed for the testing purposes. – 0andriy Jul 11 '23 at 12:25
  • I have not tried `gpiomon` (I have not heard about this tool :-( ). The GPIO driver is the default one in Linux (I did not change the kernel configuration on this) and GPIO in sysfs is working, I can export a GPIO, change the direction, edge mode, etc. I tested my kernel module, I can see the new stuffs in `sysfs` and `/proc/interrupts`, the interrupt count is increased when I pressed button. I wrote this module since I don't know if it needs kernel codes as `device_init_wakeup` and `enable_irq_wake`, to support GPIO wakeup (The cost of my ignorance :-( ) – wangt13 Jul 11 '23 at 12:53
  • 1
    Sorry, there are dozens of default drivers in Linux. If you’re going to keep secrecy about the hardware details, it would be almost impossible to give you any advice. – 0andriy Jul 14 '23 at 06:29

1 Answers1

0

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.

wangt13
  • 959
  • 7
  • 17