Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2088639
  • 博文数量: 288
  • 博客积分: 10594
  • 博客等级: 上将
  • 技术积分: 3469
  • 用 户 组: 普通用户
  • 注册时间: 2006-10-27 19:27
文章分类

全部博文(288)

文章存档

2012年(4)

2011年(30)

2010年(40)

2009年(32)

2008年(71)

2007年(79)

2006年(32)

分类: LINUX

2011-04-23 15:31:35

我们来分析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");

阅读(3327) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~