赞
踩
Linux进入休眠后,会进入dpm_suspend_noirq函数。该函数会遍历所有的中断,进行关闭。
- int dpm_suspend_noirq(pm_message_t state)
- {
- device_wakeup_arm_wake_irqs();
- suspend_device_irqs();
- }
-
- static inline bool irqd_is_wakeup_set(struct irq_data *d)
- {
- return __irqd_to_state(d) & IRQD_WAKEUP_STATE;
- }
-
- void suspend_device_irqs(void)
- {
- struct irq_desc *desc;
- for_each_irq_desc(irq, desc) {
- raw_spin_lock_irqsave(&desc->lock, flags);
- sync = suspend_device_irq(desc);
- raw_spin_unlock_irqrestore(&desc->lock, flags);
- }
- }
-
- static bool suspend_device_irq(struct irq_desc *desc)
- {
- if (!desc->action || irq_desc_is_chained(desc) ||
- desc->no_suspend_depth)
- return false;
-
- if (irqd_is_wakeup_set(&desc->irq_data)) {
- irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
- /*
- * We return true here to force the caller to issue
- * synchronize_irq(). We need to make sure that the
- * IRQD_WAKEUP_ARMED is visible before we return from
- * suspend_device_irqs().
- */
- return true;
- }
-
- desc->istate |= IRQS_SUSPENDED;
- __disable_irq(desc);
-
- /*
- * Hardware which has no wakeup source configuration facility
- * requires that the non wakeup interrupts are masked at the
- * chip level. The chip implementation indicates that with
- * IRQCHIP_MASK_ON_SUSPEND.
- */
- if (irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND)
- mask_irq(desc);
- return true;
- }
从上面的代码可以看出,如果no_suspend_depth计数不为0或者有设置IRQD_WAKEUP_STATE标志,就不会关闭该中断。
方法1:可以在申请中断时,加入参数IRQF_NO_SUSPEND,no_suspend_depth计数不为0,其原理如下
- static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
- const char *name, void *dev)
- {
- return request_threaded_irq(irq, handler, NULL, flags, name, dev);
- }
-
- int request_threaded_irq(unsigned int irq, irq_handler_t handler,irq_handler_t thread_fn, unsigned long irqflags,const char *devname, void *dev_id)
- {
- desc = irq_to_desc(irq);
- if (!desc)
- return -EINVAL;
- action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
- if (!action)
- return -ENOMEM;
-
- action->handler = handler;
- action->thread_fn = thread_fn;
- action->flags = irqflags;
- action->name = devname;
- action->dev_id = dev_id;
-
- retval = __setup_irq(irq, desc, action);
- }
-
- static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
- {
- irq_pm_install_action(desc, new);
- }
-
- void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action)
- {
- desc->nr_actions++;
-
- if (action->flags & IRQF_FORCE_RESUME)
- desc->force_resume_depth++;
-
- WARN_ON_ONCE(desc->force_resume_depth &&
- desc->force_resume_depth != desc->nr_actions);
-
- if (action->flags & IRQF_NO_SUSPEND)
- desc->no_suspend_depth++;
- else if (action->flags & IRQF_COND_SUSPEND)
- desc->cond_suspend_depth++;
-
- WARN_ON_ONCE(desc->no_suspend_depth &&
- (desc->no_suspend_depth +
方法2:在进入suspend函数中加入irq_set_irq_wake(irq,1),该方法会设置IRQD_WAKEUP_STATE标志,resume函数中加入irq_set_irq_wake(irq,0)其原理如下
- int irq_set_irq_wake(unsigned int irq, unsigned int on)
- {
- unsigned long flags;
- struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
- int ret = 0;
-
- if (on) {
- if (desc->wake_depth++ == 0) {
- ret = set_irq_wake_real(irq, on);
- if (ret)
- desc->wake_depth = 0;
- else
- irqd_set(&desc->irq_data, IRQD_WAKEUP_STATE);
- }
- } else {
- if (desc->wake_depth == 0) {
- WARN(1, "Unbalanced IRQ %d wake disable\n", irq);
- } else if (--desc->wake_depth == 0) {
- ret = set_irq_wake_real(irq, on);
- if (ret)
- desc->wake_depth = 1;
- else
- irqd_clear(&desc->irq_data, IRQD_WAKEUP_STATE);
- }
- }
-
- return ret;
- }
-
-
- static int set_irq_wake_real(unsigned int irq, unsigned int on)
- {
- struct irq_desc *desc = irq_to_desc(irq);
- int ret = -ENXIO;
-
- if (irq_desc_get_chip(desc)->flags & IRQCHIP_SKIP_SET_WAKE)
- return 0;
-
- if (desc->irq_data.chip->irq_set_wake)
- ret = desc->irq_data.chip->irq_set_wake(&desc->irq_data, on);
-
- return ret;
- }
另外,休眠和唤醒流程中有如下函数
- int dpm_suspend_noirq(pm_message_t state)
- {
- device_wakeup_arm_wake_irqs();
- }
-
- void dpm_resume_noirq(pm_message_t state)
- {
- device_wakeup_disarm_wake_irqs();
- }
- void dev_pm_arm_wake_irq(struct wake_irq *wirq)
- {
- if (!wirq)
- return;
-
- if (device_may_wakeup(wirq->dev))
- enable_irq_wake(wirq->irq);
- }
-
- void device_wakeup_arm_wake_irqs(void)
- {
- struct wakeup_source *ws;
- int srcuidx;
-
- srcuidx = srcu_read_lock(&wakeup_srcu);
- list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
- if (ws->wakeirq)
- dev_pm_arm_wake_irq(ws->wakeirq);
- }
- srcu_read_unlock(&wakeup_srcu, srcuidx);
- }
-
- void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
- {
- if (!wirq)
- return;
-
- if (device_may_wakeup(wirq->dev))
- disable_irq_wake(wirq->irq);
- }
- void device_wakeup_disarm_wake_irqs(void)
- {
- struct wakeup_source *ws;
- int srcuidx;
-
- srcuidx = srcu_read_lock(&wakeup_srcu);
- list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
- if (ws->wakeirq)
- dev_pm_disarm_wake_irq(ws->wakeirq);
- }
- srcu_read_unlock(&wakeup_srcu, srcuidx);
- }
-
- int device_wakeup_attach_irq(struct device *dev,
- struct wake_irq *wakeirq)
- {
- struct wakeup_source *ws;
-
- ws = dev->power.wakeup;
- if (!ws) {
- dev_err(dev, "forgot to call call device_init_wakeup?\n");
- return -EINVAL;
- }
-
- if (ws->wakeirq)
- return -EEXIST;
-
- ws->wakeirq = wakeirq;
- return 0;
- }
-
- static int dev_pm_attach_wake_irq(struct device *dev, int irq,struct wake_irq *wirq)
- {
- err = device_wakeup_attach_irq(dev, wirq);
- }
-
- int dev_pm_set_wake_irq(struct device *dev, int irq)
- {
- struct wake_irq *wirq;
- int err;
-
- if (irq < 0)
- return -EINVAL;
-
- wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
- if (!wirq)
- return -ENOMEM;
-
- wirq->dev = dev;
- wirq->irq = irq;
-
- err = dev_pm_attach_wake_irq(dev, irq, wirq);
- if (err)
- kfree(wirq);
-
- return err;
- }
-
- static inline int device_init_wakeup(struct device *dev, bool val)
- {
- device_set_wakeup_capable(dev, val);
- device_set_wakeup_enable(dev, val);
- return 0;
- }
在初始化中加入如下函数,系统默认会自动调用irq_set_irq_wake开启和关闭中断唤醒。
- device_init_wakeup(struct device *dev, bool val)
- dev_pm_set_wake_irq(struct device *dev, int irq)
总结
1.申请中断时加入IRQF_NO_SUSPEND标志。
中断唤醒cpu后,如果中断处理里有调用某些资源(如i2c),内核会报i2c处于suspend状态,这是因为i2c还没唤醒,需要进行一定的延时,建议使用irq_set_irq_wake函数,因为按照休眠唤醒逻辑,i2c已经唤醒了。其深度休眠,唤醒流程跟非深度休眠流程一致。
2.休眠时开中断唤醒,唤醒时关闭中断唤醒。
- irq_set_irq_wake(irq,1)
- irq_set_irq_wake(irq,0)
对应某些场景,我们需要中断总是能唤醒cpu(而不是由用户设置),我们可以在probe函数里直接调用irq_set_irq_wake(irq,1)即可(不需要关闭)。
3.初始化调用如下函数,其原理是自动调用irq_set_irq_wake函数。
- device_init_wakeup(struct device *dev, bool val)
- dev_pm_set_wake_irq(struct device *dev, int irq)
具体的唤醒流程参考http://www.wowotech.net/irq_subsystem/irq_handle_procedure.html
唤醒后,通过irq_set_irq_wake(irq,1)设置的中断,处理流程如下
- bool irq_pm_check_wakeup(struct irq_desc *desc)
- {
- if (irqd_is_wakeup_armed(&desc->irq_data)) {
- irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED);
- desc->istate |= IRQS_SUSPENDED | IRQS_PENDING;
- desc->depth++;
- irq_disable(desc);-----------------------------先关闭irq
- pm_system_irq_wakeup(irq_desc_get_irq(desc));
- return true;
- }
- return false;
- }
-
- static bool irq_may_run(struct irq_desc *desc)
- {
- unsigned int mask = IRQD_IRQ_INPROGRESS | IRQD_WAKEUP_ARMED;
-
- /*
- * If the interrupt is not in progress and is not an armed
- * wakeup interrupt, proceed.
- */
- if (!irqd_has_set(&desc->irq_data, mask))
- return true;
-
- /*
- * If the interrupt is an armed wakeup source, mark it pending
- * and suspended, disable it and notify the pm core about the
- * event.
- */
- if (irq_pm_check_wakeup(desc))
- return false;
-
- /*
- * Handle a potential concurrent poll on a different core.
- */
- return irq_check_poll(desc);
- }
-
- static inline void mask_ack_irq(struct irq_desc *desc)
- {
- if (desc->irq_data.chip->irq_mask_ack)
- desc->irq_data.chip->irq_mask_ack(&desc->irq_data);
- else {
- desc->irq_data.chip->irq_mask(&desc->irq_data);
- if (desc->irq_data.chip->irq_ack)
- desc->irq_data.chip->irq_ack(&desc->irq_data);
- }
- irq_state_set_masked(desc);
- }
-
- void __enable_irq(struct irq_desc *desc)
- {
- switch (desc->depth) {
- case 0:
- err_out:
- WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n",
- irq_desc_get_irq(desc));
- break;
- case 1: {
- if (desc->istate & IRQS_SUSPENDED)
- goto err_out;
- /* Prevent probing on this irq: */
- irq_settings_set_noprobe(desc);
- irq_enable(desc);-----------------------------再开启irq
- check_irq_resend(desc);
- /* fall-through */
- }
- default:
- desc->depth--;
- }
- }
-
- void irq_enable(struct irq_desc *desc)
- {
- irq_state_clr_disabled(desc);
- if (desc->irq_data.chip->irq_enable)
- desc->irq_data.chip->irq_enable(&desc->irq_data);
- else
- desc->irq_data.chip->irq_unmask(&desc->irq_data);
- irq_state_clr_masked(desc);
- }
-
- void check_irq_resend(struct irq_desc *desc)
- {
- /*
- * We do not resend level type interrupts. Level type
- * interrupts are resent by hardware when they are still
- * active. Clear the pending bit so suspend/resume does not
- * get confused.
- */
- if (irq_settings_is_level(desc)) {
- desc->istate &= ~IRQS_PENDING;
- return;
- }
- if (desc->istate & IRQS_REPLAY)
- return;
- if (desc->istate & IRQS_PENDING) {
- desc->istate &= ~IRQS_PENDING;
- desc->istate |= IRQS_REPLAY;
-
- if (!desc->irq_data.chip->irq_retrigger ||
- !desc->irq_data.chip->irq_retrigger(&desc->irq_data)) {
- #ifdef CONFIG_HARDIRQS_SW_RESEND
- unsigned int irq = irq_desc_get_irq(desc);
-
- /*
- * If the interrupt is running in the thread
- * context of the parent irq we need to be
- * careful, because we cannot trigger it
- * directly.
- */
- if (irq_settings_is_nested_thread(desc)) {
- /*
- * If the parent_irq is valid, we
- * retrigger the parent, otherwise we
- * do nothing.
- */
- if (!desc->parent_irq)
- return;
- irq = desc->parent_irq;
- }
- /* Set it pending and activate the softirq: */
- set_bit(irq, irqs_resend);
- tasklet_schedule(&resend_tasklet);
- #endif
- }
- }
- }
-
- void handle_level_irq(struct irq_desc *desc)
- {
- raw_spin_lock(&desc->lock);
- mask_ack_irq(desc);
-
- if (!irq_may_run(desc))
- goto out_unlock;
-
- desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
- kstat_incr_irqs_this_cpu(desc);
-
- /*
- * If its disabled or no action available
- * keep it masked and get out of here
- */
- if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
- desc->istate |= IRQS_PENDING;
- goto out_unlock;
- }
-
- handle_irq_event(desc);
-
- cond_unmask_irq(desc);
-
- out_unlock:
- raw_spin_unlock(&desc->lock);
- }
-
- void handle_edge_irq(struct irq_desc *desc)
- {
- raw_spin_lock(&desc->lock);
-
- desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
-
- if (!irq_may_run(desc)) {
- desc->istate |= IRQS_PENDING;
- mask_ack_irq(desc);
- goto out_unlock;
- }
-
- /*
- * If its disabled or no action available then mask it and get
- * out of here.
- */
- if (irqd_irq_disabled(&desc->irq_data) || !desc->action) {
- desc->istate |= IRQS_PENDING;
- mask_ack_irq(desc);
- goto out_unlock;
- }
-
- kstat_incr_irqs_this_cpu(desc);
-
- /* Start handling the irq */
- desc->irq_data.chip->irq_ack(&desc->irq_data);
-
- do {
- if (unlikely(!desc->action)) {
- mask_irq(desc);
- goto out_unlock;
- }
-
- /*
- * When another irq arrived while we were handling
- * one, we could have masked the irq.
- * Renable it, if it was not disabled in meantime.
- */
- if (unlikely(desc->istate & IRQS_PENDING)) {
- if (!irqd_irq_disabled(&desc->irq_data) &&
- irqd_irq_masked(&desc->irq_data))
- unmask_irq(desc);
- }
-
- handle_irq_event(desc);
-
- } while ((desc->istate & IRQS_PENDING) &&
- !irqd_irq_disabled(&desc->irq_data));
-
- out_unlock:
- raw_spin_unlock(&desc->lock);
- }
check_irq_resend直译过来就是检查irq并重发(软件模拟)
对于电平中断来说,handle_level_irq关掉中断后,再次开启irq,中断信号还在,会重新调用handle_level_irq
对应边沿中断来说,只会触发一次,handle_edge_irq已经关掉了一次,再次开启irq时,由于没信号过来了,需要软件模拟中断过来,即重新调用一遍desc->handle_irq()/handle_edge_irq
- static DECLARE_TASKLET(resend_tasklet, resend_irqs, 0);
- static void resend_irqs(unsigned long arg)
- {
- struct irq_desc *desc;
- int irq;
-
- while (!bitmap_empty(irqs_resend, nr_irqs)) {
- irq = find_first_bit(irqs_resend, nr_irqs);
- clear_bit(irq, irqs_resend);
- desc = irq_to_desc(irq);
- local_irq_disable();
- desc->handle_irq(desc);
- local_irq_enable();
- }
- }
电平中断和边沿中断的处理流程有差异,见下图
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。