您的位置:首页 > 产品设计 > 产品经理

关于s5pc110系统休眠的情景分析(PMIC为MAX8698)

2012-08-24 16:06 351 查看
我们来分析enter_state这个函数,这个函数应该是一个重量级的函数,首先再次通过valid_state检测state的有效性,前面我们已经分析过

 这个检测方法通过全局的变量suspend_ops是否为空,其成员函数valid是否为空,以及调用成员函数valid的返回值来判断,s5pc110的平台 只接受PM_SUSPEND_MEM的suspend,获取互斥锁,同步文件系统之后, 调用suspend_prepare()函数,这个函数也定义在suspend.c中,做一些 进入suspend状态时的准备工作,我们分析这个函数

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

 static int suspend_prepare(void)

 {

  int error;

  if (!suspend_ops || !suspend_ops->enter)

   return -EPERM;

  pm_prepare_console();

  error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);

  if (error)

   goto Finish;

  error = usermodehelper_disable();

  if (error)

   goto Finish;

  error = suspend_freeze_processes();

  if (!error)

   return 0;

  suspend_thaw_processes();

  usermodehelper_enable();

  Finish:

  pm_notifier_call_chain(PM_POST_SUSPEND);

  pm_restore_console();

  return error;

 }

 我们看pm_notifier_call_chain(PM_SUSPEND_PREPARE),这里涉及到PM notifier的一个机制,关于这个机制,我们简单介绍一下

 pm在power/main.c中定义了一个pm_chain_head,用于pm在转换过程中的通知路径

 static BLOCKING_NOTIFIER_HEAD(pm_chain_head);

 pm在休眠和唤醒的过程中会发出一些EVENTS,这些事件定义在notifier.h中

 /* Hibernation and suspend events */

 #define PM_HIBERNATION_PREPARE 0x0001 /* Going to hibernate */

 #define PM_POST_HIBERNATION 0x0002 /* Hibernation finished */

 #define PM_SUSPEND_PREPARE 0x0003 /* Going to suspend the system */

 #define PM_POST_SUSPEND  0x0004 /* Suspend finished */

 #define PM_RESTORE_PREPARE 0x0005 /* Going to restore a saved image */

 #define PM_POST_RESTORE  0x0006 /* Restore failed */

 

 如果哪个内核模块想得到这些EVENTS,就需要定义一个notifier_block, 并把它挂载到pm_chain_head上。为此,PM定义了挂载和卸载函数

 int register_pm_notifier(struct notifier_block *nb)

 {

  return blocking_notifier_chain_register(&pm_chain_head, nb);

 }

 EXPORT_SYMBOL_GPL(register_pm_notifier);

 int unregister_pm_notifier(struct notifier_block *nb)

 {

  return blocking_notifier_chain_unregister(&pm_chain_head, nb);

 }

 EXPORT_SYMBOL_GPL(unregister_pm_notifier);

 在PM休眠或者唤醒进行到某个特定阶段时,PM就会使用下面这个函数向链上所有的内核模块发送一个EVENT

 int pm_notifier_call_chain(unsigned long val)

 {

  return (blocking_notifier_call_chain(&pm_chain_head, val, NULL)

   == NOTIFY_BAD) ? -EINVAL : 0;

 }

 我们看到就是在suspend_prepare中调用了pm_notifier_call_chain(PM_SUSPEND_PREPARE)

 我们以s5pc110平台为例,看看触发了那些事件

 在文件drivers/char/apm-emulation.c中定义了apm_suspend_notifier

 因此pm_notifier_call_chain会触发apm_suspend_notifier的相应事件,这里执行apm_suspend_notifier的PM_SUSPEND_PREPARE分支

 下面执行函数usermodehelper_disable(),这个函数在kernel/kmod.c中

 int usermodehelper_disable(void)

 {

  long retval;

  usermodehelper_disabled = 1;

  smp_mb();

  /*

   * From now on call_usermodehelper_exec() won't start any new

   * helpers, so it is sufficient if running_helpers turns out to

   * be zero at one point (it may be increased later, but that

   * doesn't matter).

   */

  retval = wait_event_timeout(running_helpers_waitq,

     atomic_read(&running_helpers) == 0,

     RUNNING_HELPERS_TIMEOUT);

  if (retval)

   return 0;

  usermodehelper_disabled = 0;

  return -EAGAIN;

 }

 这个函数中主要是调用等待事件wait_event_timeout(queue, condition, timeout);

 如果condition为真,则立即返回值RUNNING_HELPERS_TIMEOUT,如果调用call_usermodehelper_exec中的

 helper_lock()会增加计数running_helpers,使得条件为假,当调用helper_unlock时会减小计数running_helpers,并判断引用计数   running_helpers是否为0,如果为零则调用wake_up(&running_helpers_waitq), 唤醒以running_helpers_waitq作为等待队列头的所有等待  队列对应的进程。

 因此这个等待事件用于等待以running_helpers_waitq作为等待队列头的进程被唤醒,如果条件为真则立即返回,如果条件为假则阻塞,如果  超出等待时间RUNNING_HELPERS_TIMEOUT返回-EAGAIN。

 

 下面调用函数suspend_freeze_processes()

  调用power/process.c文件中的函数freeze_processes(void)

  

        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

 做完以上的工作之后,调用suspend_devices_and_enter

 这是一个重量级的函数,我们分析一下

 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 /**

  * suspend_devices_and_enter - suspend devices and enter the desired system

  *        sleep state.

  * @state:    state to enter

  */

 int suspend_devices_and_enter(suspend_state_t state)

 {

  int error;

  if (!suspend_ops)

   return -ENOSYS;

  if (suspend_ops->begin) {

   error = suspend_ops->begin(state);

   if (error)

    goto Close;

  }

  suspend_console();

  suspend_test_start();

  error = dpm_suspend_start(PMSG_SUSPEND);

  if (error) {

   printk(KERN_ERR "PM: Some devices failed to suspend/n");

   goto Recover_platform;

  }

  suspend_test_finish("suspend devices");

  if (suspend_test(TEST_DEVICES))

   goto Recover_platform;

  suspend_enter(state);

  Resume_devices:

  suspend_test_start();

  dpm_resume_end(PMSG_RESUME);

  suspend_test_finish("resume devices");

  resume_console();

  Close:

  if (suspend_ops->end)

   suspend_ops->end();

  return error;

  Recover_platform:

  if (suspend_ops->recover)

   suspend_ops->recover();

  goto Resume_devices;

 }

 首先我们看suspend_ops->begin,根据以前的分析这个函数指针指向s3c_pm_ops的begin成员,即s3c_pm_begin,这里需要在kernel中配置

 CONFIG_REGULATOR,

 s3c_pm_begin函数会调用函数regulator_suspend_prepare,这个函数在/drivers/regulator/core.c中

  int regulator_suspend_prepare(suspend_state_t state)

  {

   struct regulator_dev *rdev;

   int ret = 0;

   /* ON is handled by regulator active state */

   printk("++ %s, state:%d/n", __func__, state);

   if (state == PM_SUSPEND_ON)

    return -EINVAL;

   mutex_lock(®ulator_list_mutex);

   list_for_each_entry(rdev, ®ulator_list, list) {

    printk("rdev :%p/n", rdev);

    mutex_lock(&rdev->mutex);

    ret = suspend_prepare(rdev, state);

    mutex_unlock(&rdev->mutex);

    if (ret < 0) {

     printk(KERN_ERR "%s: failed to prepare %s/n",

      __func__, rdev->desc->name);

     goto out;

    }

   }

  out:

   mutex_unlock(®ulator_list_mutex);

   return ret;

  }

 在这个函数中,首先判断参数state。接下来遍历链表regulator_list,这个链表将结构体struct regulator_dev通过成员list链接在一起

 这个链表是在max8698_pmic_probe中的regulator_register中建立的,在regulator_register函数中有一句

 list_add(&rdev->list, ®ulator_list);

 遍历每一个rdev,调用函数suspend_prepare

   static int suspend_prepare(struct regulator_dev *rdev, suspend_state_t state)

   {

    if (!rdev->constraints)

     return -EINVAL;

    printk("%s, rdev->constraints:%p, state:%d/n", __func__,rdev->constraints, state );

    switch (state) {

     case PM_SUSPEND_STANDBY:

      return suspend_set_state(rdev,

       &rdev->constraints->state_standby);

     case PM_SUSPEND_MEM:

      return suspend_set_state(rdev,

       &rdev->constraints->state_mem);

     case PM_SUSPEND_MAX:

      return suspend_set_state(rdev,

       &rdev->constraints->state_disk);

     default:

      return -EINVAL;

    }

   }

 这个函数中根据相应的state调用函数suspend_set_state,这里我们肯定state参数为PM_SUSPEND_MEM,于是调用

 suspend_set_state(rdev,&rdev->constraints->state_mem);

 

   static int suspend_set_state(struct regulator_dev *rdev,

     struct regulator_state *rstate)

   {

    int ret = 0;

    /* enable & disable are mandatory for suspend control */

    if (!rdev->desc->ops->set_suspend_enable ||

      !rdev->desc->ops->set_suspend_disable) {

     printk(KERN_ERR "%s: no way to set suspend state/n",

         __func__);

      return -EINVAL;

    }

    printk("rstate->enabled:%d/n", rstate->enabled);

    if (rstate->enabled)

     ret = rdev->desc->ops->set_suspend_enable(rdev);

    else

     ret = rdev->desc->ops->set_suspend_disable(rdev);

    if (ret < 0) {

     printk(KERN_ERR "%s: failed to enabled/disable/n", __func__);

     return ret;

    }

    if (rdev->desc->ops->set_suspend_voltage && rstate->uV > 0) {

     ret = rdev->desc->ops->set_suspend_voltage(rdev, rstate->uV);

     if (ret < 0) {

      printk(KERN_ERR "%s: failed to set voltage/n",

        _func__);

      return ret;

     }

    }

    if (rdev->desc->ops->set_suspend_mode && rstate->mode > 0) {

     ret = rdev->desc->ops->set_suspend_mode(rdev, rstate->mode);

     if (ret < 0) {

      printk(KERN_ERR "%s: failed to set mode/n", __func__);

      return ret;

     }

    }

    return ret;

   }

 这个函数中,根据传递的rdev,rstate进行相应的操作,首先做一些简单的判断,确认rdev->desc->ops->set_suspend_enable和

 rdev->desc->ops->set_suspend_disable两个函数非空。这两个函数在max8698_pmic_probe时被初始化,这里分别初始化为函数

 max8698_ldo_enable和max8698_ldo_disable,根据rstate->enabled的值确定是调用max8698_ldo_enable和max8698_ldo_disable哪一个函数

 然后判断是否要调整电压,执行函数rdev->desc->ops->set_suspend_voltage,这个函数指针指向max8698_set_suspend_voltage;

 最后如果rdev->desc->ops->set_suspend_mode非空并且rstate->mode为正,则执行rdev->desc->ops->set_suspend_mode指向的函数

 我们不妨看看在S5PC110平台下定义的相关结构体,在文件arch/arm/mach-s5pv210/mach-smdkc110.c中

  static struct regulator_consumer_supply buck1_consumers[] = {

   {

    .supply  = "vddarm",

   },

  };

  static struct regulator_init_data max8698_buck1_data = {

   .constraints = {

    .name  = "VCC_ARM",

    .min_uV  =  750000,

    .max_uV  = 1500000,

    .always_on = 1,

    .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,

    .state_mem = {

     .uV = 1250000,

     .mode = REGULATOR_MODE_STANDBY,

     .enabled = 0,

    },

   },

   .num_consumer_supplies = ARRAY_SIZE(buck1_consumers),

   .consumer_supplies = buck1_consumers,

  };

  static struct regulator_consumer_supply buck2_consumers[] = {

   {

    .supply  = "vddint",

   },

  };

  static struct regulator_init_data max8698_buck2_data = {

   .constraints = {

    .name  = "VCC_INTERNAL",

    .min_uV  = 950000,

    .max_uV  = 1200000,

    .always_on = 1,

    .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,

    .state_mem = {

     .uV = 1100000,

     .mode = REGULATOR_MODE_NORMAL,

     .enabled = 0,

     },

   },

   .num_consumer_supplies = ARRAY_SIZE(buck2_consumers),

   .consumer_supplies = buck2_consumers,

  };

  static struct regulator_init_data max8698_buck3_data = {

   .constraints = {

    .name  = "VCC_MEM",

    .min_uV  = 1800000,

    .max_uV  = 1800000,

    .apply_uV = 1,

    .state_mem = {

     .uV = 1800000,

     .mode = REGULATOR_MODE_NORMAL,

     .enabled = 1,

    },

    /* .initial_state = PM_SUSPEND_MEM, */

   },

  };

  static struct regulator_init_data max8698_ldo2_data = {

   .constraints = {

    .name  = "VALIVE_1.1V",

    .min_uV  = 1100000,

    .max_uV  = 1100000,

    .apply_uV = 1,

    .always_on = 1,

    .state_mem = {

     .uV = 1100000,

     .mode = REGULATOR_MODE_NORMAL,

     .enabled = 1,

    },

   },

  };

  .......

  .......

  static struct max8698_subdev_data smdkc110_regulators[] = {

    { MAX8698_LDO2, &max8698_ldo2_data },

    { MAX8698_LDO3, &max8698_ldo3_data },

    { MAX8698_LDO4, &max8698_ldo4_data },

    { MAX8698_LDO5, &max8698_ldo5_data },

    { MAX8698_LDO6, &max8698_ldo6_data },

    { MAX8698_LDO7, &max8698_ldo7_data },

    { MAX8698_LDO8, &max8698_ldo8_data },

    { MAX8698_LDO9, &max8698_ldo9_data },

    { MAX8698_BUCK1, &max8698_buck1_data },

    { MAX8698_BUCK2, &max8698_buck2_data },

    { MAX8698_BUCK3, &max8698_buck3_data },

  };

  /* 1Ghz default voltage */

  static struct max8698_platform_data max8698_platform_data = {

    .num_regulators = ARRAY_SIZE(smdkc110_regulators),

    .regulators     = smdkc110_regulators,

    .set1           = S5PV210_GPH1(6),

    .set2           = S5PV210_GPH1(7),

    .set3           = S5PV210_GPH0(4),

    .dvsarm1        = 0xa,  // 1.25v

    .dvsarm2        = 0x9,  // 1.20V

    .dvsarm3        = 0x6,  // 1.05V

    .dvsarm4        = 0x4,  // 0.95V

    .dvsint1        = 0x7,  // 1.10v

    .dvsint2        = 0x5,  // 1.00V

  };

 

 在这个文件一共定义了11个regulator_init_data结构,这些平台初始化数据都会在max8698_pmic_probe时被初始化到相应的结构中

 这里我们逐个分析下这11个LDO做了什么事情

 对于max8698_buck3_data,这个肯定是在链表regulator_list的表头,这个显然是给内存供电的,其.constraints.state_mem.enabled = 1

 那么调用max8698_ldo_enable函数

  static int max8698_ldo_enable(struct regulator_dev *rdev)

  {

   struct max8698_data *max8698 = rdev_get_drvdata(rdev);

   int reg, shift = 8, value, ret;

 

   ret = max8698_get_register(rdev, ®, &shift);

   if (ret)

    return ret;

   value = max8698_read_reg(max8698, reg);

   if (!(value & (1 << shift))) {

    value |= (1 << shift);

    ret = max8698_write_reg(max8698, reg, value);

   }

   printk("%s[%d] ldo %d/n", __func__, __LINE__, max8698_get_ldo(rdev));

   return ret;

  }

 在这个函数中首先根据参数rdev,获得相应的驱动数据,即成员reg_data,这是指向max8698的;然后通过函数max8698_get_register获取

 相应的reg和shift,这里max8698_buck3_data对应的ldo是MAX8698_BUCK3,所以reg为MAX8698_REG_ONOFF1,shift为相应的位

 通过max8698_read_reg读取数组max8698_cache_regs[16] 中的相应寄存器对应的值;然后判断如果这个值的相应shift位为0的话,那么将其

 改为1,更新max8698_cache_regs数组元素,并通过i2c_smbus_write_byte_data写相应的设备寄存器。

   

 我们看max8698_buck2_data,这里其state_mem.enabled=0,因此调用max8698_ldo_disable,这里max8698_ldo_disable执行的是  max8698_ldo_enable的反操作,如果从cache数组中读取的相关值的shift位为1的话,则更新这一位为0,并写设备寄存器

 以上两个regulator中,其state_mem.uV不为零,因此都需要调用函数max8698_set_suspend_voltage

  static int max8698_set_suspend_voltage(struct regulator_dev *rdev, int uV)

  {

   int ldo = max8698_get_ldo(rdev);

   switch (ldo) {

 

    case MAX8698_BUCK1 ... MAX8698_BUCK3:

     return max8698_buck_set_voltage(rdev, uV, uV);

    case MAX8698_LDO1 ... MAX8698_LDO9:

     return max8698_set_voltage(rdev, uV, uV);

    default:

     return -EINVAL;

   }

  }

 根据参数rdev调用max8698_buck_set_voltage或者max8698_set_voltage

 至此,s3c_pm_begin函数就结束了,回到函数suspend_devices_and_enter,下面会调用函数suspend_console(),

 这个函数定义在kernel/printk.c,这里修改全局变量console_suspended为1,此时控制台挂起无输出

 然后调用函数suspend_test_start(),如果kernel配置CONFIG_PM_TEST_SUSPEND,那么就调用

  void suspend_test_start(void)

  {

   /* FIXME Use better timebase than "jiffies", ideally a clocksource.

    * What we want is a hardware counter that will work correctly even

    * during the irqs-are-off stages of the suspend/resume cycle...

    */

   suspend_test_start_time = jiffies;

  }

 然后执行

   error = dpm_suspend_start(PMSG_SUSPEND);

 

 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 ******************************************************************************************

 if (debug_mask & DEBUG_EXIT_SUSPEND) {

  struct timespec ts;

  struct rtc_time tm;

  getnstimeofday(&ts);

  rtc_time_to_tm(ts.tv_sec, &tm);

  pr_info("suspend: exit suspend, ret = %d "

   "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)/n", ret,

   tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,

   tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);

 }

register_pm_notifier

 if (current_event_num == entry_event_num) {

  if (debug_mask & DEBUG_SUSPEND)

   pr_info("suspend: pm_suspend returned with no event/n");

  wake_lock_timeout(&unknown_wakeup, HZ / 2);

 }

}

static DECLARE_WORK(suspend_work, suspend);

需要时通过调用queue_work(suspend_work_queue, &suspend_work)去执行suspend_work上的成员函数suspend

suspend_work_queue是一个全局的struct workqueue_struct *类型结构

在系统初始化时通过static int __init wakelocks_init(void)创建

suspend_work_queue = create_singlethread_workqueue("suspend");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息