runtime pm的实例分析

概念

运行时的PM与前文描述的系统级挂起到RAM时候的PM不太一样,它是针对单个设备,指系统在非睡眠状态的情况下,某个设备在空闲时可以进入运行时挂起状态,而在不是空闲时执行运行时恢复使得设备进入正常工作状态,如此,这个设备在运行时会省电

struct dev_pm_ops {
...
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
...
};

基本框架简化如下,就是驱动通过runtime pm core提供的接口,再调用到驱动本身的实现

 

spi_pump_messages

通过之前的文章简述spi内核框架,我们可以知道,这个文件drivers/spi/spi.c里的spi_pump_messages就是一次传输行为,总线空闲就会调用unprepare_transfer_hardware,总线忙就会调用prepare_transfer_hardware;soc根据这两个行为就可以去实现pm操作来省电

static void spi_pump_messages(struct kthread_work *work)
{
        struct spi_master *master =
                container_of(work, struct spi_master, pump_messages);
        unsigned long flags;
        bool was_busy = false;
        int ret;

        /* Lock queue and check for queue work */
        spin_lock_irqsave(&master->queue_lock, flags);
        if (list_empty(&master->queue) || !master->running) {
                if (!master->busy) {
                        spin_unlock_irqrestore(&master->queue_lock, flags);
                        return;
                }
                master->busy = false;
                spin_unlock_irqrestore(&master->queue_lock, flags);
                if (master->unprepare_transfer_hardware &&
                    master->unprepare_transfer_hardware(master))
                        dev_err(&master->dev,
                                "failed to unprepare transfer hardware\n");
                return;
        }

        /* Make sure we are not already running a message */
        if (master->cur_msg) {
                spin_unlock_irqrestore(&master->queue_lock, flags);
                return;
        }
        /* Extract head of queue */
        master->cur_msg =
            list_entry(master->queue.next, struct spi_message, queue);

        list_del_init(&master->cur_msg->queue);
        if (master->busy)
                was_busy = true;
        else
                master->busy = true;
        spin_unlock_irqrestore(&master->queue_lock, flags);

        if (!was_busy && master->prepare_transfer_hardware) {
                ret = master->prepare_transfer_hardware(master);
                if (ret) {
                        dev_err(&master->dev,
                                "failed to prepare transfer hardware\n");
                        return;
                }
        }

        ret = master->transfer_one_message(master, master->cur_msg);
        if (ret) {
                dev_err(&master->dev,
                        "failed to transfer one message from queue\n");
                return;
        }
}

unprepare_transfer_hardware

比如在drivers/spi/spi_qsd.c中,unprepare_transfer_hardware接口会去通过pm_runtime_put_autosuspend,去调用pm_dev_ops的runtime_suspend成员

pm_runtime_put_autosuspend
        __pm_runtime_suspend
                rpm_suspend
                        callback = RPM_GET_CALLBACK(dev, runtime_suspend);
static int msm_spi_unprepare_transfer_hardware(struct spi_master *master)
{
        struct msm_spi  *dd = spi_master_get_devdata(master);

        pm_runtime_mark_last_busy(dd->dev);
        pm_runtime_put_autosuspend(dd->dev);
        return 0;
}

prepare_transfer_hardware

比如在drivers/spi/spi_qsd.c中,prepare_transfer_hardware接口会去通过pm_runtime_get_sync,去调用pm_dev_ops的runtime_resume成员

pm_runtime_get_sync
        __pm_runtime_resume
                rpm_resume
                        callback = RPM_GET_CALLBACK(dev, runtime_resume);
                        rpm_callback(callback, dev);
static int msm_spi_prepare_transfer_hardware(struct spi_master *master)
{
        struct msm_spi  *dd = spi_master_get_devdata(master);
        int resume_state = 0;

        resume_state = pm_runtime_get_sync(dd->dev);
        if (resume_state < 0)
                return resume_state;
        /*
         * Counter-part of system-suspend when runtime-pm is not enabled.
         * This way, resume can be left empty and device will be put in
         * active mode only if client requests anything on the bus
         */
        if (!pm_runtime_enabled(dd->dev))
                resume_state = msm_spi_pm_resume_runtime(dd->dev);
        if (resume_state < 0)
                return resume_state;
        if (dd->suspended)
                return -EBUSY;

        return 0;
}

runtime_suspend

runtime_suspend成员的实现如下:主要是去关闭spi控制器的时钟,达到省电的目的

static int msm_spi_pm_suspend_runtime(struct device *device)
{
        struct platform_device *pdev = to_platform_device(device);
        struct spi_master *master = platform_get_drvdata(pdev);
        struct msm_spi    *dd;
        unsigned long      flags;

        dev_err(device, "pm_runtime: suspending...\n");
        if (!master)
                goto suspend_exit;
        dd = spi_master_get_devdata(master);
        if (!dd)
                goto suspend_exit;

        if (dd->suspended)
                return 0;

        /*
         * Make sure nothing is added to the queue while we're
         * suspending
         */
        spin_lock_irqsave(&dd->queue_lock, flags);
        dd->suspended = 1;
        spin_unlock_irqrestore(&dd->queue_lock, flags);

        /* Wait for transactions to end, or time out */
        wait_event_interruptible(dd->continue_suspend,
                !dd->transfer_pending);

        if (dd->pdata && !dd->pdata->active_only)
                msm_spi_clk_path_unvote(dd);

suspend_exit:
        return 0;
}

runtime_resume

runtime_resume成员的实现如下,主要是去开启spi控制的时钟

static int msm_spi_pm_resume_runtime(struct device *device)
{
        struct platform_device *pdev = to_platform_device(device);
        struct spi_master *master = platform_get_drvdata(pdev);
        struct msm_spi    *dd;
        int               ret = 0;

        dev_err(device, "pm_runtime: resuming...\n");
        if (!master)
                goto resume_exit;
        dd = spi_master_get_devdata(master);
        if (!dd)
                goto resume_exit;

        if (!dd->suspended)
                return 0;
        if (!dd->is_init_complete) {
                ret = init_resources(pdev);
                if (ret != 0)
                        return ret;
                else
                        dd->is_init_complete = true;
        }
        msm_spi_clk_path_init(dd);
        if (!dd->pdata->active_only)
                msm_spi_clk_path_vote(dd);
        dd->suspended = 0;

resume_exit:
        return 0;
}

dev_pm_ops

将实现赋值给dev_pm_ops结构里的runtime_suspend和runtime_resume成员

static const struct dev_pm_ops msm_spi_dev_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(msm_spi_suspend, msm_spi_resume)
        SET_RUNTIME_PM_OPS(msm_spi_pm_suspend_runtime,
                        msm_spi_pm_resume_runtime, NULL)
};

static struct platform_driver msm_spi_driver = {
        .driver         = {
                .name   = SPI_DRV_NAME,
                .owner  = THIS_MODULE,
                .pm             = &msm_spi_dev_pm_ops,
                .of_match_table = msm_spi_dt_match,
        },
        .remove         = msm_spi_remove,
        .probe          = msm_spi_probe,
};

pm_runtime_get/put

在/drivers/base/power/runtime.c中,设备驱动调用的pm_runtime_get/put 会先原子操作usage,然后判断是否调用rpm_idle/rpm_suspend/rpm_resume;他们会来执行设备驱动实现的对应操作;suspend_timer用定时器来延时调用rpm_suspend

void pm_runtime_init(struct device *dev)
{
	dev->power.runtime_status = RPM_SUSPENDED;
	dev->power.last_status = RPM_INVALID;
	dev->power.idle_notification = false;

	dev->power.disable_depth = 1;
	atomic_set(&dev->power.usage_count, 0);

	dev->power.runtime_error = 0;

	atomic_set(&dev->power.child_count, 0);
	pm_suspend_ignore_children(dev, false);
	dev->power.runtime_auto = true;

	dev->power.request_pending = false;
	dev->power.request = RPM_REQ_NONE;
	dev->power.deferred_resume = false;
	dev->power.needs_force_resume = 0;
	INIT_WORK(&dev->power.work, pm_runtime_work);

	dev->power.timer_expires = 0;
	hrtimer_init(&dev->power.suspend_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
	dev->power.suspend_timer.function = pm_suspend_timer_fn;

	init_waitqueue_head(&dev->power.wait_queue);
}

static void pm_runtime_work(struct work_struct *work)
{
	struct device *dev = container_of(work, struct device, power.work);
	enum rpm_request req;

	spin_lock_irq(&dev->power.lock);

	if (!dev->power.request_pending)
		goto out;

	req = dev->power.request;
	dev->power.request = RPM_REQ_NONE;
	dev->power.request_pending = false;

	switch (req) {
	case RPM_REQ_NONE:
		break;
	case RPM_REQ_IDLE:
		rpm_idle(dev, RPM_NOWAIT);
		break;
	case RPM_REQ_SUSPEND:
		rpm_suspend(dev, RPM_NOWAIT);
		break;
	case RPM_REQ_AUTOSUSPEND:
		rpm_suspend(dev, RPM_NOWAIT | RPM_AUTO);
		break;
	case RPM_REQ_RESUME:
		rpm_resume(dev, RPM_NOWAIT);
		break;
	}

 out:
	spin_unlock_irq(&dev->power.lock);
}

  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值