enable_irq_wake是如何起作用的

来源:互联网 发布:淘宝推广平台哪个好 编辑:程序博客网 时间:2024/06/05 07:36

enable_irq_wake是如何起作用的

在linux kernel中,调用enable_irq_wake函数,可以将一个irq具有唤醒系统的功能,即把系统从低功耗模式中唤醒,如从suspend to RAM中唤醒。
enable_irq_wake具体如何起作用的呢,今天来学习学习。

先从函数enable_irq_wake开始,实现很简单:

[objc] view plain copy
  1. static inline int enable_irq_wake(unsigned int irq)  
  2. {  
  3.  return irq_set_irq_wake(irq, 1);  
  4. }  


 

函数irq_set_irq_wake的实现也不是很复杂,并且有注释说明,容易理解:

[objc] view plain copy
  1. /** 
  2.  * irq_set_irq_wake - control irq power management wakeup 
  3.  * @irq: interrupt to control 
  4.  * @on: enable/disable power management wakeup 
  5.  * 
  6.  * Enable/disable power management wakeup mode, which is 
  7.  * disabled by default.  Enables and disables must match, 
  8.  * just as they match for non-wakeup mode support. 
  9.  * 
  10.  * Wakeup mode lets this IRQ wake the system from sleep 
  11.  * states like "suspend to RAM". 
  12.  */  
  13. int irq_set_irq_wake(unsigned int irq, unsigned int on)  
  14. {  
  15.  unsigned long flags;  
  16.  struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);  
  17.  int ret = 0;  
  18.   
  19.  if (!desc)  
  20.   return -EINVAL;  
  21.   
  22.  /* wakeup-capable irqs can be shared between drivers that 
  23.   * don't need to have the same sleep mode behaviors. 
  24.   */  
  25.  if (on) {  
  26.   if (desc->wake_depth++ == 0) {  
  27.    ret = set_irq_wake_real(irq, on);  
  28.    if (ret)  
  29.     desc->wake_depth = 0;  
  30.    else  
  31.     irqd_set(&desc->irq_data, IRQD_WAKEUP_STATE);  
  32.   }  
  33.  } else {  
  34.   if (desc->wake_depth == 0) {  
  35.    WARN(1"Unbalanced IRQ %d wake disable\n", irq);  
  36.   } else if (--desc->wake_depth == 0) {  
  37.    ret = set_irq_wake_real(irq, on);  
  38.    if (ret)  
  39.     desc->wake_depth = 1;  
  40.    else  
  41.     irqd_clear(&desc->irq_data, IRQD_WAKEUP_STATE);  
  42.   }  
  43.  }  
  44.  irq_put_desc_busunlock(desc, flags);  
  45.  return ret;  
  46. }  



其中关键的代码是计数:desc->wake_depth++, 以及调用函数set_irq_wake_real。
set_irq_wake_real函数调用到了具体cpu相关的代码:

[objc] view plain copy
  1. static int set_irq_wake_real(unsigned int irq, unsigned int on)  
  2. {  
  3.  struct irq_desc *desc = irq_to_desc(irq);  
  4.  int ret = -ENXIO;  
  5.   
  6.  if (desc->irq_data.chip->irq_set_wake)  
  7.   ret = desc->irq_data.chip->irq_set_wake(&desc->irq_data, on);  
  8.   
  9.  return ret;  
  10. }  



irq_set_wake为cpu相关代码。
例如,xxx cpu对应的arch\arm\mach-xxx\Irq.c文件中,xxx_init_irq函数中有如下语句:

[objc] view plain copy
  1. desc->irq_data.chip->irq_set_wake = xxx_gic_irq_set_wake;  



xxx_gic_irq_set_wake的实现将irq mask,结果保存在一个数组中gpc_wake_irq。

cpu在做power on/off时会使用到该数组。
如mach-xxx\system.c中的函数xxx_cpu_lp_set中有如下代码:

[objc] view plain copy
  1. gpc_set_wakeup(gpc_wake_irq);  


 

函数gpc_set_wakeup的实现:

[objc] view plain copy
  1. void gpc_set_wakeup(unsigned int irq[4])  
  2. {  
  3.  /* Mask all wake up source */  
  4.  __raw_writel(~irq[0], gpc_base + 0x8);  
  5.  __raw_writel(~irq[1], gpc_base + 0xc);  
  6.  __raw_writel(~irq[2], gpc_base + 0x10);  
  7.  __raw_writel(~irq[3], gpc_base + 0x14);  
  8.   
  9.  return;  
  10. }  



将irq mask设置到了cpu中。

中cpu的suspend enter函数中,会调用xxx_cpu_lp_set函数。
如xxx_suspend_enter函数中有如下代码:

[objc] view plain copy
  1. switch (state) {  
  2. case PM_SUSPEND_MEM:  
  3.  ...  
  4.  mxc_cpu_lp_set(ARM_POWER_OFF);  
  5.  arm_pg = true;  
  6.  break;  
  7. ...  
  8. }  



 
至此,流程基本清晰了。
enable_irq_wake函数会将irq mask到一个数组。
在进入suspend时,会将irq mask写入到cpu。
也就是告诉cpu哪些irq可以将其从睡眠中唤醒。

4
原创粉丝点击