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.