linux下pm子系统

来源:互联网 发布:mac怎么玩dota2国服 编辑:程序博客网 时间:2024/06/16 00:34

工作中分配了低功耗相关方面的内容,查了相关资料,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的内容会被定格下来,也正是这个原因,所以不需要人工手动去保存现场,不然将又是一场浩大的工程。

0 0
原创粉丝点击