Skip to content

Instantly share code, notes, and snippets.

@pamolloy
Last active October 17, 2018 15:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pamolloy/8640710fec0291943a257e180a66c8ca to your computer and use it in GitHub Desktop.
Save pamolloy/8640710fec0291943a257e180a66c8ca to your computer and use it in GitHub Desktop.
Disabling runtime power management does not disable autosuspend
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#define MODULE_NAME "device"
#define COMPATIBLE_STRING "company,device"
#define DEVICE_AUTOSUSPEND_TIMEOUT 4000 // milliseconds
struct device_drvdata {
struct gpio_desc *power_gpio;
};
static int device_runtime_suspend(struct device *dev)
{
struct device_drvdata *drvdata = dev_get_drvdata(dev);
gpiod_set_value(drvdata->power_gpio, 0);
return 0;
}
static int device_runtime_resume(struct device *dev)
{
struct device_drvdata *drvdata = dev_get_drvdata(dev);
gpiod_set_value(drvdata->power_gpio, 1);
return 0;
}
static const struct dev_pm_ops device_pm_ops = {
SET_RUNTIME_PM_OPS(device_runtime_suspend, device_runtime_resume, NULL)
};
static int device_probe(struct platform_device *pdev)
{
int status;
struct device_drvdata *drvdata;
struct device *dev = &pdev->dev;
drvdata = devm_kzalloc(dev, sizeof(struct device_drvdata), GFP_KERNEL);
if (!drvdata) {
err("failed to allocate memory for device driver data");
status = -ENOMEM;
goto error_alloc_drvdata;
}
drvdata->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW);
if (IS_ERR(drvdata->power_gpio)) {
err("failed to get power GPIO from device tree");
status = PTR_ERR(drvdata->power_gpio);
goto error_gpiod_get;
}
dev_set_drvdata(dev, drvdata);
pm_runtime_set_autosuspend_delay(dev, DEVICE_AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(dev);
pm_runtime_enable(dev);
pm_runtime_mark_last_busy(dev);
if (pm_runtime_put_autosuspend(dev) != 0) {
err("failed to autosuspend device");
goto error_pm_runtime_autosuspend;
}
return 0;
error_pm_runtime_autosuspend:
error_gpiod_get:
error_alloc_drvdata:
devm_kfree(dev, drvdata);
return status;
}
static int device_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_drvdata *drvdata = platform_get_drvdata(pdev);
pm_runtime_autosuspend(dev);
pm_runtime_disable(dev);
return 0;
}
static const struct of_device_id device_of_match[] = {
{ .compatible = COMPATIBLE_STRING },
{},
};
struct platform_driver device_platform_driver = {
.probe = device_probe,
.remove = device_remove,
.driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
.of_match_table = device_of_match,
.pm = &device_pm_ops,
},
};
module_platform_driver(device_platform_driver);
$ cd /sys/devices/platform/device\@0/power/
$ grep "" *
autosuspend_delay_ms:4000
control:auto
runtime_active_time:0
runtime_status:suspended
runtime_suspended_time:122968
$ echo on > control
$ grep "" *
autosuspend_delay_ms:4000
control:on
runtime_active_time:16856
runtime_status:active
runtime_suspended_time:1552912
$ grep "" *
autosuspend_delay_ms:4000
control:on
runtime_active_time:16928
runtime_status:suspended
runtime_suspended_time:1555212
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment