linux 低功耗模式中的中断链
来源:互联网 发布:淘宝高品质鹿皮绒女装 编辑:程序博客网 时间:2024/05/14 19:36
linux 系统中,针对低功耗模式
在驱动代码中所需要做出的实现
Low-Power-Consumption/
├── resume
└── suspend
在底层的驱动程序中,需要实现supend和resume函数.注册一个中断唤醒源,一般是GPIO,RTC中断 .然后调用echo mem > state将系统挂起到内存中,这时候SDRAM在进行self-refresh的动作.基本电源的消耗在这里了
目前2.6.kernel已经为你作好了。你只需要在你每个驱动里按照接口函数suspend(…),和resume()将这个驱动的suspend 和resume完成.因为调用apm命令后,kernel会依次调用你注册驱动里的suspend函数,将各种外设都进入节电模式.最后CPU进入power down 模式 相同的,当用RTC或者GPIO中的一个将cpu从power down 模式唤醒.依次也会调用各个驱动里的resume函数将外设唤醒,进入正常工作状态. 当然这个中断唤醒源你必须自己定义enable_irq_wake(irq);
另外,在linux 2.6里
- 1.要对驱动的suspend里面添加相应设备进入节电状态的代码,
- 2.在resume里添加相应设备从低功耗返回正常工作模式的代码.
键盘驱动初始化时加入enable_irq_wake,当按键中断来时,就可以唤醒贪睡的内核
在编写驱动时有关中断的操作是经常要用到的,内核驱动中已经编写了一些中断配置函数可以方便的供使用,其与gpio操作配置函数配合使用效果更好。
void disable_irq(unsigned int); //关闭相应中断号中断
void enable_irq(unsigned int);//开启相应中断号中断
int set_irq_type(unsigned int irq, unsigned int type);//设置中断为何种触发模式
void disable_irq_wake(unsigned int irq); //禁止中断唤醒功能
void enable_irq_wake(unsigned int irq);//使能中断唤醒功能
外部中断的驱动初始化时应该完成以下几个步骤:
1.把对应的引脚设置成中断功能
2.设置中断类型(IRQ或者FIQ)
3.设置触发方式
4.使能此中断
设置触发方式的函数接口
int set_irq_type(unsigned int irq, unsigned int type);
中断pending寄存器,主要是用来标识哪个中断产生了.
实例:
这是bcmdhd中的关于suspend和resume的封装。
bcmsdh_suspend
int bcmsdh_suspend(bcmsdh_info_t *bcmsdh){ bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt; if (drvinfo.suspend && drvinfo.suspend(bcmsdh_osinfo->context)) return -EBUSY; return 0;}
bcmsdh_sdmmc_suspend
static int bcmsdh_sdmmc_suspend(struct device *pdev){ int err; sdioh_info_t *sdioh; struct sdio_func *func = dev_to_sdio_func(pdev); mmc_pm_flag_t sdio_flags; printf("%s Enter func->num=%d\n", __FUNCTION__, func->num); if (func->num != 2) return 0; sdioh = sdio_get_drvdata(func); err = bcmsdh_suspend(sdioh->bcmsdh); if (err) { printf("%s bcmsdh_suspend err=%d\n", __FUNCTION__, err); return err; } sdio_flags = sdio_get_host_pm_caps(func); if (!(sdio_flags & MMC_PM_KEEP_POWER)) { sd_err(("%s: can't keep power while host is suspended\n", __FUNCTION__)); return -EINVAL; } /* keep power while host suspended */ err = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); if (err) { sd_err(("%s: error while trying to keep power\n", __FUNCTION__)); return err; }#if defined(OOB_INTR_ONLY) bcmsdh_oob_intr_set(sdioh->bcmsdh, FALSE);#endif dhd_mmc_suspend = TRUE; smp_mb(); printf("%s Exit\n", __FUNCTION__); return 0;}
bcmsdh_resume
int bcmsdh_resume(bcmsdh_info_t *bcmsdh){ bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt; if (drvinfo.resume) return drvinfo.resume(bcmsdh_osinfo->context); return 0;}
bcmsdh_sdmmc_resume
static int bcmsdh_sdmmc_resume(struct device *pdev){#if defined(OOB_INTR_ONLY) sdioh_info_t *sdioh;#endif struct sdio_func *func = dev_to_sdio_func(pdev); printf("%s Enter func->num=%d\n", __FUNCTION__, func->num); if (func->num != 2) return 0; dhd_mmc_suspend = FALSE;#if defined(OOB_INTR_ONLY) sdioh = sdio_get_drvdata(func); bcmsdh_resume(sdioh->bcmsdh);#endif smp_mb(); printf("%s Exit\n", __FUNCTION__); return 0;}
bcmsdh_dev_pm_stay_awake
void bcmsdh_dev_pm_stay_awake(bcmsdh_info_t *bcmsdh){#if !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt; pm_stay_awake(bcmsdh_osinfo->dev);#endif /* !defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) */}
bcmsdh_oob_intr_register
int bcmsdh_oob_intr_register(bcmsdh_info_t *bcmsdh, bcmsdh_cb_fn_t oob_irq_handler, void* oob_irq_handler_context){ int err = 0; bcmsdh_os_info_t *bcmsdh_osinfo = bcmsdh->os_cxt; SDLX_MSG(("%s: Enter\n", __FUNCTION__)); if (bcmsdh_osinfo->oob_irq_registered) { SDLX_MSG(("%s: irq is already registered\n", __FUNCTION__)); return -EBUSY; }#ifdef HW_OOB printf("%s: HW_OOB enabled\n", __FUNCTION__);#else printf("%s: SW_OOB enabled\n", __FUNCTION__);#endif SDLX_MSG(("%s OOB irq=%d flags=%X\n", __FUNCTION__, (int)bcmsdh_osinfo->oob_irq_num, (int)bcmsdh_osinfo->oob_irq_flags)); bcmsdh_osinfo->oob_irq_handler = oob_irq_handler; bcmsdh_osinfo->oob_irq_handler_context = oob_irq_handler_context; bcmsdh_osinfo->oob_irq_enabled = TRUE; bcmsdh_osinfo->oob_irq_registered = TRUE;#if defined(CONFIG_ARCH_ODIN) err = odin_gpio_sms_request_irq(bcmsdh_osinfo->oob_irq_num, wlan_oob_irq, bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh);#else err = request_irq(bcmsdh_osinfo->oob_irq_num, wlan_oob_irq, bcmsdh_osinfo->oob_irq_flags, "bcmsdh_sdmmc", bcmsdh);#endif /* defined(CONFIG_ARCH_ODIN) */ if (err) { bcmsdh_osinfo->oob_irq_enabled = FALSE; bcmsdh_osinfo->oob_irq_registered = FALSE; SDLX_MSG(("%s: request_irq failed with %d\n", __FUNCTION__, err)); return err; }#if defined(DISABLE_WOWLAN) SDLX_MSG(("%s: disable_irq_wake\n", __FUNCTION__)); bcmsdh_osinfo->oob_irq_wake_enabled = FALSE;#else SDLX_MSG(("%s: enable_irq_wake\n", __FUNCTION__)); err = enable_irq_wake(bcmsdh_osinfo->oob_irq_num); if (err) SDLX_MSG(("%s: enable_irq_wake failed with %d\n", __FUNCTION__, err)); else bcmsdh_osinfo->oob_irq_wake_enabled = TRUE;#endif return 0;}
此外,以下是对disable_irq() 和 disable_irq_nosync()探讨
disable_irq关闭中断并等待中断处理完后返回, 而disable_irq_nosync立即返回. 那么在中断处理程序中应该使用哪一个函数来关闭中断呢?
在”linux设备驱动开发详解”中的按键驱动中, 使用disable_irq来关闭中断, 但是我在测试时进入中断后系统会死在中断处理程序, 而改为disable_irq_nosync则能正常退出中断处理程序.下面从内核代码来找一下原因:
先看一下disable_irq_nosync,内核代码中是这样解释的:
disable_irq_nosync
/** * disable_irq_nosync - disable an irq without waiting * @irq: Interrupt to disable * * Disable the selected interrupt line. Disables and Enables are * nested. * Unlike disable_irq(), this function does not ensure existing * instances of the IRQ handler have completed before returning. * * This function may be called from IRQ context. */void disable_irq_nosync(unsigned int irq){ struct irq_desc *desc = irq_to_desc(irq); unsigned long flags; if (!desc) return; chip_bus_lock(irq, desc); spin_lock_irqsave(&desc->lock, flags); __disable_irq(desc, irq, false); spin_unlock_irqrestore(&desc->lock, flags); chip_bus_sync_unlock(irq, desc);}
关闭中断后程序返回, 如果在中断处理程序中, 那么会继续将中断处理程序执行完.
disable_irq
/** * disable_irq - disable an irq and wait for completion * @irq: Interrupt to disable * * Disable the selected interrupt line. Enables and Disables are * nested. * This function waits for any pending IRQ handlers for this interrupt * to complete before returning. If you use this function while * holding a resource the IRQ handler may need you will deadlock. * * This function may be called - with care - from IRQ context. */void disable_irq(unsigned int irq){ struct irq_desc *desc = irq_desc + irq; if (irq >= NR_IRQS) return; disable_irq_nosync(irq); if (desc->action) synchronize_irq(irq);}
关闭中断并等待中断处理完后返回.从代码中可以看到, disable_irq先是调用了disable_irq_nosync, 然后检测desc->action是否为1. 在中断处理程序中, action是置1的, 所以进入synchronize_irq函数中.
synchronize_irq
/** * synchronize_irq - wait for pending IRQ handlers (on other CPUs) * @irq: interrupt number to wait for * * This function waits for any pending IRQ handlers for this interrupt * to complete before returning. If you use this function while * holding a resource the IRQ handler may need you will deadlock. * * This function may be called - with care - from IRQ context. */void synchronize_irq(unsigned int irq){ struct irq_desc *desc = irq_to_desc(irq); unsigned int status; if (!desc) return; do { unsigned long flags; /* * Wait until we're out of the critical section. This might * give the wrong answer due to the lack of memory barriers. */ while (desc->status & IRQ_INPROGRESS) cpu_relax(); /* Ok, that indicated we're done: double-check carefully. */ spin_lock_irqsave(&desc->lock, flags); status = desc->status; spin_unlock_irqrestore(&desc->lock, flags); /* Oops, that failed? */ } while (status & IRQ_INPROGRESS); /* * We made sure that no hardirq handler is running. Now verify * that no threaded handlers are active. */ wait_event(desc->wait_for_threads, !atomic_read(&desc->threads_active));}
注释中说明该函数是在等待中断处理程序的结束, 这也是disable_irq与disable_irq_nosync不同的主要所在. 但是在中断处理函数中调用会发生什么情况呢? 进入中断处理函数前IRQ_INPROGRESS会被__setup_irq设置, 所以程序会一直陷在while循环中, 而此时内核以经被独占, 这就导致系统死掉.
总结:
由于在disable_irq中会调用synchronize_irq函数等待中断返回, 所以在中断处理程序中不能使用disable_irq, 否则会导致cpu被synchronize_irq独占而发生系统崩溃.
- linux 低功耗模式中的中断链
- STM32--低功耗模式
- NFC低功耗模式
- MSP430低功耗模式
- MSP430--低功耗模式
- stm32低功耗模式
- 低功耗模式操作
- halt低功耗模式
- 低功耗运行模式
- 低功耗等待模式
- 影响低功耗模式功耗的因素
- STM32F103C8T6 Standby低功耗模式
- ZIGBEE--CC2430低功耗模式
- stm8l 低功耗 halt_active模式
- WIFI低功耗模式说明
- STM32低功耗模式解读
- STM323中低功耗模式
- STM32低功耗模式实践
- oracle导入excel表格数据
- ubuntu下JDK8的安装与配置
- 简单说下拦截器的实现
- Atitit 数据存储视图的最佳实际best practice attilax总结
- [AND最大生成树 分治 Trie || Kruskal] UOJ Goodbye Yiwei C #176. 新年的繁荣
- linux 低功耗模式中的中断链
- Swift基础之UIPickerView和小animate的使用
- 共享单车火了!看上去美丽的背后潜藏着怎样的问题与挑战?
- 仿淘宝、京东拖拽上拉下拉商品详情(可嵌套ViewPager、ListView、WebView、FragmentTabhost)
- Python学习笔记,6,函数
- ionic使用cordovaMedia、cordovaFile和cordovaFileTransfer播放在线音乐
- 继续复习倒数第二天
- JSP 页面对时间格式化
- PE文件结构详解--延迟导入表