目前共有11篇帖子。 字體大小:較小 - 100% (默認)▼  內容轉換:不轉換▼
 
點擊 回復
35 10
Linux内核 Runtime PM
一派掌門 二十級
1樓 發表于:2025-12-5 15:14
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>

struct data
{
    struct platform_device *pdev;
    struct miscdevice miscdevice;
    bool miscdevice_registered;
};

static int hello_remove(struct platform_device *pdev);

static int hello_open(struct inode *inode, struct file *file)
{
    int ret;
    struct miscdevice *miscdevice = file->private_data;
    struct data *data = container_of(miscdevice, struct data, miscdevice);
    
    printk("%s\n", __func__);
    ret = pm_runtime_get_sync(&data->pdev->dev);
    if (ret != 0) {
        dev_err(&data->pdev->dev, "pm_runtime_get_sync() failed: ret=%d\n", ret);
        return ret;
    }
    return 0;
}

static ssize_t hello_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    printk("%s\n", __func__);
    return count;
}

static ssize_t hello_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    printk("%s\n", __func__);
    return count;
}

static int hello_release(struct inode *inode, struct file *file)
{
    struct miscdevice *miscdevice = file->private_data;
    struct data *data = container_of(miscdevice, struct data, miscdevice);
    
    printk("%s\n", __func__);
    
    pm_runtime_put_sync(&data->pdev->dev);
    return 0;
}

static const struct file_operations hello_fops = {
    .owner = THIS_MODULE,
    .open = hello_open,
    .read = hello_read,
    .write = hello_write,
    .release = hello_release
};

static int hello_probe(struct platform_device *pdev)
{
    int ret;
    struct data *data;
    
    printk("%s\n", __func__);
    
    data = devm_kzalloc(&pdev->dev, sizeof(struct data), GFP_KERNEL);
    if (data == NULL)
        return -ENOMEM;
    data->pdev = pdev;
    platform_set_drvdata(pdev, data);
    
    data->miscdevice.minor = MISC_DYNAMIC_MINOR;
    data->miscdevice.name = "hello_file";
    data->miscdevice.fops = &hello_fops;
    data->miscdevice.mode = 0666;
    ret = misc_register(&data->miscdevice);
    if (ret != 0) {
        dev_err(&pdev->dev, "failed to register miscdevice\n");
        hello_remove(pdev);
        return ret;
    }
    data->miscdevice_registered = true;
    
    //pm_runtime_set_active(&pdev->dev);
    pm_runtime_enable(&pdev->dev);
    return 0;
}

static int hello_remove(struct platform_device *pdev)
{
    struct data *data = platform_get_drvdata(pdev);
    
    printk("%s\n", __func__);
    
    if (data->miscdevice_registered) {
        misc_deregister(&data->miscdevice);
        data->miscdevice_registered = false;
    }
    
    printk("---disable begin---\n");
    pm_runtime_disable(&pdev->dev);
    printk("---disable end---\n");
    return 0;
}

static int hello_suspend(struct device *dev)
{
    printk("%s\n", __func__);
    return 0;
}

static int hello_resume(struct device *dev)
{
    printk("%s\n", __func__);
    return 0;
}

static UNIVERSAL_DEV_PM_OPS(hello_pm, hello_suspend, hello_resume, NULL);

static struct platform_driver hello_driver = {
    .probe = hello_probe,
    .remove = hello_remove,
    .driver = {
        .name = "my_hello",
        .pm    = &hello_pm
    }
};

static void hello_device_release(struct device *dev)
{
    printk("%s\n", __func__);
}

static struct platform_device hello_device = {
    .name = "my_hello",
    .id = -1,
    .dev = {
        .release = hello_device_release
    }
};

static int __init hello_init(void)
{
    printk("%s\n", __func__);
    platform_driver_register(&hello_driver);
    platform_device_register(&hello_device);
    return 0;
}

static void __exit hello_exit(void)
{
    printk("%s\n", __func__);
    platform_device_unregister(&hello_device);
    platform_driver_unregister(&hello_driver);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_AUTHOR("Oct1158");
MODULE_LICENSE("GPL");

一派掌門 二十級
2樓 發表于:2025-12-5 15:14

root@rk3308b-buildroot:/root# insmod platform_test.ko

[23420.584047] hello_init

[23420.585266] hello_probe

root@rk3308b-buildroot:/root# rmmod platform_test.ko

[23423.727269] hello_exit

[23423.727619] hello_resume

[23423.727689] hello_suspend

[23423.727727] hello_remove

[23423.728393] ---disable begin---

[23423.728455] ---disable end---

[23423.728656] hello_device_release

root@rk3308b-buildroot:/root#

 
一派掌門 二十級
3樓 發表于:2025-12-5 15:15

root@rk3308b-buildroot:/root# insmod platform_test.ko

[23438.316540] hello_init

[23438.317773] hello_probe

root@rk3308b-buildroot:/root# echo "shujen" > /dev/hello_file

[23459.941964] hello_open

[23459.942049] hello_resume

[23459.942181] hello_write

[23459.942238] hello_release

root@rk3308b-buildroot:/root# [23459.942273] hello_suspend


root@rk3308b-buildroot:/root# rmmod platform_test.ko

[23464.348851] hello_exit

[23464.349162] hello_resume

[23464.349233] hello_suspend

[23464.349269] hello_remove

[23464.349984] ---disable begin---

[23464.350030] ---disable end---

[23464.350216] hello_device_release

root@rk3308b-buildroot:/root#

 
一派掌門 二十級
4樓 發表于:2025-12-5 15:16
参考驱动程序:Luckfox_Nova_SDK_250430/kernel/drivers/input/misc/bma150.c
 
巨大八爪鱼:电源管理入门:驱动Runtime PM管理

https://www.elecfans.com/d/2327034.html

  2025-12-5 16:43 回復
巨大八爪鱼

如果不想让设备频繁地开、关,可以使用autosuspend功能

驱动里: 执行pm_runtime_use_autosuspend来设置启动autosuspend功能,
put设备时, 执行这2个函数:
pm_runtime_mark_last_busy(&lcd_dev.dev);(更新power.last_busy的状态)
pm_runtime_put_sync_autosuspend(&lcd_dev.dev);(根据power.last_busy的状态启动定时器,一段时间后如果没有再次打开动作才休眠)

(pm_runtime_get_sync不需要改,open的时候应该马上上电运行)

https://www.cnblogs.com/liusiluandzhangkun/p/8977059.html

  2025-12-5 17:19 回復
一派掌門 二十級
5樓 發表于:2025-12-5 17:34

ret = pm_runtime_get_sync(&data->pdev->dev);

if (ret != 0) {应该改成if (ret < 0) {才对。

只有<0才是出错,>=0都是正常状态。

 
一派掌門 二十級
8樓 發表于:2025-12-5 17:57

【autosuspend功能的使用】

static int hello_open(struct inode *inode, struct file *file)

{

    ...

    ret = pm_runtime_get_sync(&data->pdev->dev);

    if (ret < 0) { // 这里应该是<0才对

        dev_err(&data->pdev->dev, "pm_runtime_get_sync() failed: ret=%d\n", ret);

        return ret;

    }

    return 0;

}


static int hello_release(struct inode *inode, struct file *file)

{

    ...

    pm_runtime_mark_last_busy(&data->pdev->dev);

    pm_runtime_put_sync_autosuspend(&data->pdev->dev);

    return 0;

}


static int hello_probe(struct platform_device *pdev)

{

    ...

    pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); // 设置超时时间为2秒

    pm_runtime_use_autosuspend(&pdev->dev);

    pm_runtime_enable(&pdev->dev);

    return 0;

}


实测发现pm_runtime_mark_last_busy函数是必须要手动调用的,否则设备关闭的时间是以第一次关闭设备文件的时间为准,而不是最后一次关闭设备文件的时间。

pm_runtime_put_sync_autosuspend函数也可以换成pm_runtime_put_autosuspend函数。


以下是这三个函数的介绍:

https://docs.kernel.org/power/runtime_pm.html

int pm_runtime_put_autosuspend(struct device *dev);

set the power.last_busy field to the current time and decrement the device’s usage counter; if the result is 0 then run pm_request_autosuspend(dev) and return its result


int pm_runtime_put_sync_autosuspend(struct device *dev);

set the power.last_busy field to the current time and decrement the device’s usage counter; if the result is 0 then run pm_runtime_autosuspend(dev) and return its result


void pm_runtime_mark_last_busy(struct device *dev);

set the power.last_busy field to the current time

 
一派掌門 二十級
9樓 發表于:2025-12-5 19:12

在remove函数中,pm_runtime_disable前必须还要调用pm_runtime_dont_use_autosuspend,否则rmmod的时候有可能不会suspend设备。

static int hello_remove(struct platform_device *pdev)

{

    struct data *data = platform_get_drvdata(pdev);

    

    printk("%s\n", __func__);

    

    if (data->miscdevice_registered) {

        misc_deregister(&data->miscdevice);

        data->miscdevice_registered = false;

    }

    

    printk("---disable begin---\n");

    pm_runtime_dont_use_autosuspend(&pdev->dev);

    pm_runtime_disable(&pdev->dev);

    printk("---disable end---\n");

    return 0;

}


root@rk3308b-buildroot:/root# echo "xxx" > /dev/hello_file

[ 1852.244258] hello_open

[ 1852.244513] hello_write

[ 1852.244578] hello_release

root@rk3308b-buildroot:/root# rmmod platform_test

[ 1852.618912] hello_exit

[ 1852.619244] hello_remove

[ 1852.620159] ---disable begin---

[ 1852.620229] hello_suspend

[ 1852.620265] ---disable end---

[ 1852.620552] hello_device_release

root@rk3308b-buildroot:/root#

 
巨大八爪鱼

suspend后再rmmod的打印:会先resume再suspend

[ 2098.009571] hello_release

root@rk3308b-buildroot:/root# echo "xxx" > /dev/hello_file

[ 2098.397681] hello_open

[ 2098.397854] hello_write

[ 2098.397914] hello_release

root@rk3308b-buildroot:/root# [ 2100.898057] hello_suspend


root@rk3308b-buildroot:/root#

root@rk3308b-buildroot:/root# rmmod platform_test

[ 2107.616929] hello_exit

[ 2107.617241] hello_resume

[ 2107.617319] hello_remove

[ 2107.617991] ---disable begin---

[ 2107.618048] hello_suspend

[ 2107.618083] ---disable end---

[ 2107.618313] hello_device_release

root@rk3308b-buildroot:/root#

  2025-12-5 19:16 回復
巨大八爪鱼:有了pm_runtime_dont_use_autosuspend函数,只要rmmod就一定会suspend。

如果之前suspend了才rmmod,那么先resume再suspend。

如果之前没有suspend就rmmod,那么直接suspend。

  2025-12-5 19:17 回復

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
(快捷鍵:Ctrl+Enter)
 

本帖信息

點擊數:35 回複數:10
評論數: ?
作者:巨大八爪鱼
最後回復:巨大八爪鱼
最後回復時間:2025-12-5 19:17
 
©2010-2025 Purasbar Ver2.0
除非另有聲明,本站採用創用CC姓名標示-相同方式分享 3.0 Unported許可協議進行許可。