如何延迟Linux中断处理程序(我知道通常不会睡觉)

Ste*_*eve 7 c linux delay interrupt-handling

我正在开发一个嵌入式Linux ARM系统,它需要通过按特定顺序关闭一些电源(通过GPIO控制)来对电源故障信号作出反应.这个过程需要尽快启动,所以我安装了一个中断处理程序来检测这个电源故障.

问题是我们需要在关闭每个电源之间引入一点延迟.我知道在中断处理程序中通常不允许延迟,但如果这个处理程序永远不会返回(电源失败!),那就完全可以了.

我试图通过使用所描述的方法来引入延迟这篇文章,但我不能为我的生活其实导致可测量的延迟(在示波器上观察到的).

我做错了什么,我该怎么做?

以下是相关代码.

/* This function sets en_gpio low, then waits until pg_gpio goes low. */
static inline void powerdown(int en_gpio, int pg_gpio)
{
    /* Bring the enable line low. */
    gpio_set_value(en_gpio, 0);
    /* Loop until power good goes low. */
    while (gpio_get_value(pg_gpio) != 0);
}

/* This is my attempt at a delay function. */
#define DELAY_COUNT 1000000000
static void delay(void)
{
    volatile u_int32_t random;
    volatile u_int32_t accum;
    volatile u_int32_t i;

    get_random_bytes((void*)&random, 4);
    accum = 0;
    for (i = 0; i < DELAY_COUNT; i++)
        accum = accum * random;
}

/* This is the interrupt handler. */
static irqreturn_t power_fail_interrupt(int irq, void *dev_id)
{
    powerdown(VCC0V75_EN, VCC0V75_PG);
    delay();
    powerdown(DVDD15_EN, DVDD15_PG);
    delay();
    powerdown(DVDD18_EN, DVDD18_PG);
    delay();
    powerdown(CVDD1_EN, CVDD1_PG);
    delay();
    powerdown(CVDD_EN, CVDD_PG);
    /* It doesn't matter if we get past this point. Power is failing. */
    /* I'm amazed this printk() sometimes gets the message out before power drops! */
    printk(KERN_ALERT "egon_power_fail driver: Power failure detected!\n");
    return IRQ_HANDLED;
}
Run Code Online (Sandbox Code Playgroud)

Sam*_*nko 14

delay在硬IRQ处理程序中使用函数通常是个坏主意,因为在硬IRQ处理程序中禁用了中断,系统将挂起,直到您的硬IRQ函数完成.另一方面,您不能sleep在硬IRQ处理程序中使用函数,因为硬IRQ是原子上下文.

将所有这些都纳入帐户,您可能想要使用线程IRQ.这种方式硬IRQ处理程序只唤醒下半部分 IRQ处理程序(在内核线程中执行).在此线程处理程序中,您可以使用常规sleep函数.

要实现线程IRQ而不是常规IRQ,只需用request_irq()函数替换request_threaded_irq()函数即可.例如,如果你有这样的IRQ请求:

ret = request_irq(irq, your_irq_handler, IRQF_SHARED,
                  dev_name(&dev->dev), chip);
Run Code Online (Sandbox Code Playgroud)

你可以用这样的东西替换它:

ret = request_threaded_irq(irq, NULL, your_irq_handler,
                           IRQF_ONESHOT | IRQF_SHARED,
                           dev_name(&dev->dev), chip);
Run Code Online (Sandbox Code Playgroud)

NULL意味着将使用标准的硬IRQ处理程序(它只唤醒线程IRQ处理程序),并且your_irq_handler()函数将在内核线程中执行(您可以在其中调用sleep函数).IRQF_ONESHOT请求线程IRQ时也应使用标志.

还应该提到的是有一个托管版本的request_threaded_irq()函数,叫做devm_request_threaded_irq().使用它(而不是常规request_threaded_irq())允许您省略free_irq()驱动程序退出功能(以及错误路径)中的功能.我建议你使用devm_*函数(如果你的内核版本已经有它).但是free_irq()如果您决定使用,请不要忘记删除驱动程序中的所有调用devm_*.

TL; DR

替换你request_irq()request_threaded_irq()(如上所示),你将能够sleep在你的IRQ处理程序中使用.