我是靠谱客的博主 热情海燕,最近开发中收集的这篇文章主要介绍linux下pm子系统,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

工作中分配了低功耗相关方面的内容,查了相关资料,linux更是有一套pm相关的子系统。

首先需要说明的是既然是低功耗,那么SoC就要支持相关低功耗。

如果要进入低功耗模式,有一种方式可以在用户空间通知内核空间,echo “stanby" > /sys/power/state至于字符串的内容,需要在/kernel/power/suspend.c去查看或者修改。

linux中/sys/中是内核空间数据结构的体现,功能和/proc是相同的,/sys/下文件夹的创建,

sysfs_create_group(power_kobj, &attr_group); 后面的那个参数就是,是之前注册的回调函数。

#define power_attr(_name) 
  static struct kobj_attribute _name##_attr = {   
      .attr   = {             
          .name = __stringify(_name), 
          .mode = 0644,           
      },                  
      .show   = _name##_show,         
      .store  = _name##_store,        
  }

最后还是会调用到state_store.

既然进入低功耗模式,控制台端口就需要关闭。调试的时候没有任何打印,当然就不知道现在的状态的,所以现在需要在.config中的CONFIG_CMDLINE后面加上no_console_suspend。这样就会有内核打印了。

各硬件外设在注册设备的时候如果有低功耗的功能的时候,会注册相应的suspend和resume的回调函数,但是仔细看一下,发现有好几个结构体的都有这两种回调函数,所以肯定会有调用的先后顺序,先把相应的结构体列举出来。

struct platform_driver { 
     int (*probe)(struct platform_device *); 
     int (*remove)(struct platform_device *); 
     void (*shutdown)(struct platform_device *); 
     int (*suspend)(struct platform_device *, pm_message_t state); 
     int (*resume)(struct platform_device *); 
     struct device_driver driver; 
     const struct platform_device_id *id_table; 
 };

struct device_driver {
     const char      *name;
     struct bus_type     *bus;
 
     struct module       *owner;
     const char      *mod_name;  /* used for built-in modules */
                      
     bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */
 
     const struct of_device_id   *of_match_table;
 
     int (*probe) (struct device *dev);
     int (*remove) (struct device *dev);
     void (*shutdown) (struct device *dev);
     int (*suspend) (struct device *dev, pm_message_t state);
     int (*resume) (struct device *dev);
     const struct attribute_group **groups;
     
     const struct dev_pm_ops *pm;
     
     struct driver_private *p;
 };    

struct dev_pm_ops {
     int (*prepare)(struct device *dev);
     void (*complete)(struct device *dev);
     int (*suspend)(struct device *dev);
     int (*resume)(struct device *dev);
     int (*freeze)(struct device *dev);
     int (*thaw)(struct device *dev);
     int (*poweroff)(struct device *dev);
     int (*restore)(struct device *dev);
     int (*suspend_noirq)(struct device *dev);
     int (*resume_noirq)(struct device *dev);
     int (*freeze_noirq)(struct device *dev);
     int (*thaw_noirq)(struct device *dev);
     int (*poweroff_noirq)(struct device *dev);
     int (*restore_noirq)(struct device *dev);
     int (*runtime_suspend)(struct device *dev);
     int (*runtime_resume)(struct device *dev);
     int (*runtime_idle)(struct device *dev);
 };
第三个回调函数是后来细化了低功耗相关的动作。

真正去调用这些回调函数是在该函数中:

 /**
  * dpm_suspend_start - Prepare devices for PM transition and suspend them.
  * @state: PM transition of the system being carried out.
  *
  * Prepare all non-sysdev devices for system PM transition and execute "suspend"
  * callbacks for them.
  */    
 int dpm_suspend_start(pm_message_t state)
 {          
     int error;
 
     error = dpm_prepare(state);
     if (!error)
         error = dpm_suspend(state);
     return error;
 }      
 EXPORT_SYMBOL_GPL(dpm_suspend_start);
这两个分别去调用回调prepare和suspend回调函数,里面的优先级就有很明确的定义。

    if (dev->pwr_domain) {
          pm_dev_dbg(dev, state, "power domain ");
          error = pm_op(dev, &dev->pwr_domain->ops, state);
          goto End;
      }
  
      if (dev->type && dev->type->pm) {
          pm_dev_dbg(dev, state, "type ");
          error = pm_op(dev, dev->type->pm, state);
          goto End;
      }
  
      if (dev->class) {
          if (dev->class->pm) {
              pm_dev_dbg(dev, state, "class ");
              error = pm_op(dev, dev->class->pm, state);
              goto End;
          } else if (dev->class->suspend) {
              pm_dev_dbg(dev, state, "legacy class ");
              error = legacy_suspend(dev, state, dev->class->suspend);
              goto End;
          }
      }
  
      if (dev->bus) {
          if (dev->bus->pm) {
              pm_dev_dbg(dev, state, "");
              error = pm_op(dev, dev->bus->pm, state);
          } else if (dev->bus->suspend) {
              pm_dev_dbg(dev, state, "legacy ");
              error = legacy_suspend(dev, state, dev->bus->suspend);
          }
      }
可见顺序是从小到大的,device->class->bus。

硬件设备进入休眠状态之后,需要让CPU进入休眠状态。

int suspend_enter(suspend_state_t state)
该函数的核心是

error = syscore_suspend();
     if (!error) {
         if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) {
             error = suspend_ops->enter(state);
             events_check_enabled = false;
         }
         syscore_resume();
     }
而suspend_ops的回调函数,是各SoC厂商在arch/cpuxxx/SoCxxx/pm.c中调用函数

static const struct platform_suspend_ops xxx_pm_ops =
 {
     .valid      = xxx_valid,
     .enter      = xxx_enter,
 };
 
 static int __init xxx_pm_init(void)
 {
     suspend_set_ops(&xxx_pm_ops);
     return 0;

 }
而在xxx_enter中主要是对SoC中power manager unit寄存器设置,包括设置中断唤醒源,是进入cpuoff模式还是plloff模式。

在完成该低功耗任务时,遇到的最大问题就是,板子上的wifi模块,每次suspend之后,被唤醒源唤醒之后,各个设备能够resume成功,但是唯独wifi模块6212,因为wifi模块是在sdio总线上的,在resume过程中,sdio出现了timeout的情况,跟了很久,发现是在wifi驱动在suspend的时候,注释了相关代码导致的,而实际上最后只需要在sd/mmc驱动中,增加一个mmc标志位就可以了,该情况充分证明了总线和挂载在总线上的紧密联系。

因为之前也弄了实时操作系统的低功耗,通过这两次,发现了,自己并没有对当时的现场保存下来,而我也不可能将全部的现场保存下来,我发现时,当我设置PMU单元进入低功耗模式之后,sdram的内容会被定格下来,也正是这个原因,所以不需要人工手动去保存现场,不然将又是一场浩大的工程。

最后

以上就是热情海燕为你收集整理的linux下pm子系统的全部内容,希望文章能够帮你解决linux下pm子系统所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部