3

我在运行带有 CONFIG_PREEMPT_RT 补丁的 linux 内核 (3.8.13) 的飞思卡尔 imx.233 上遇到了一些不一致的 IRQ/ISR 性能。我有点惊讶为什么这个处理器(ARM9,454mhz)即使有 74kHz 的 IRQ 请求也无法跟上..?

在我的内核配置中,我设置了以下标志:

CONFIG_TINY_PREEMPT_RCU=y
CONFIG_PREEMPT_RCU=y
CONFIG_PREEMPT=y
CONFIG_PREEMPT_RT_BASE=y
CONFIG_HAVE_PREEMPT_LAZY=y
CONFIG_PREEMPT_LAZY=y
CONFIG_PREEMPT_RT_FULL=y
CONFIG_PREEMPT_COUNT=y
CONFIG_DEBUG_PREEMPT=y

在系统上基本上没有运行(由 bu​​ildroot 创建),我将 PWM 设置为生成 74kHz 的脉冲,用作中断。然后在 ISR 中,我只是触发另一个 GPIO 输出引脚,然后检查输出。我发现有时我会错过中断 - 您可以在此处查看错过的中断:

错过中断

而且输出引脚的触发似乎有点不一致,输出引脚通常在“5%窗口”内触发,这可能仍然可以接受。但我担心,当我开始实现数据传输逻辑时,而不是仅仅触发引脚,我可能会遇到更多问题......

我的简单驱动程序代码如下所示:

#needed includes

uint16_t INPUT_IRQ = 39;
uint16_t OUTPUT_GPIO = 38;

struct test_device *device;

//Prototypes
void irqtest_exit(void);
int irqtest_init(void);
void free_device(void);

//Default functions
module_init(irqtest_init);
module_exit(irqtest_exit);

//triggering flag
uint16_t pulse = 0x1;

irqreturn_t irq_handle_function(int irq, void *device_id)
{
pulse = !pulse;
gpio_set_value(OUTPUT_GPIO, pulse);

return IRQ_HANDLED;
}

struct test_device { 
    int huuhaa;
};

void free_device() {
if (device)
    kfree(device);
}   

int irqtest_init(void) {
    int result = 0;

    device = kmalloc(sizeof *device, GFP_KERNEL);
    device->huuhaa = 10;

    printk("IRB/irqtest_init: Inserting IRQ module\n"); 

    printk("IRB/irqtest_init: Requesting GPIO (%d)\n", INPUT_IRQ); 
    result = gpio_request_one(INPUT_IRQ, GPIOF_IN, "PWM input");

    if (result != 0) {
        free_device();
        printk("IRB/irqtest_init: Failed to set GPIO (%d) as input.. exiting\n", INPUT_IRQ);
        return -EINVAL;
    } 
    result = gpio_request_one(OUTPUT_GPIO, GPIOF_OUT_INIT_LOW , "IR OUTPUT");
    if (result != 0) {
        free_device();
        printk("IRB/irqtest_init: Failed to set GPIO (%d) as output.. exiting\n", OUTPUT_GPIO);
        return -EINVAL;
    } 

    //Set our desired interrupt line as input
    result = gpio_direction_input(INPUT_IRQ);

    if (result != 0) {
        printk("IRB/irqtest_init: Failed to set IRQ as input.. exiting\n");
        free_device();
        return -EINVAL;
    }   

    //Set flags for our interrupt, guessing here..
    irq_flags |=  IRQF_NO_THREAD;
    irq_flags |=  IRQF_NOBALANCING;
    irq_flags |=  IRQF_TRIGGER_RISING;
    irq_flags |=  IRQF_NO_SOFTIRQ_CALL;

    //register interrupt
    result = request_irq(gpio_to_irq(INPUT_IRQ), irq_handle_function, irq_flags, "irq testing", device);

    if (result != 0) {
        printk("IRB/irqtest_init: Failed to reserve GPIO 38\n");
        return -EINVAL;
    } 
    printk("IRB/irqtest_init: insert success\n"); 
    return 0;
}

void irqtest_exit(void) {
    if (device)
        kfree(device);
    gpio_free(INPUT_IRQ);
    gpio_free(OUTPUT_GPIO);

    printk("IRB/irqtest_exit: Removing irqtest module\n");
}


int irqtest_open(struct inode *inode, struct file *filp) {return 0;}
int irqtest_release(struct inode *inode, struct file *filp) {return 0;}

在系统中,加载驱动程序后,我注册了以下中断:

# cat /proc/interrupts 
       CPU0       
 16:      36379         -  MXS Timer Tick
 17:          0         -  mxs-spi
 18:       2103         -  mxs-dma
 60:          0  gpio-mxs  irq testing
118:          0         -  mxs-spi
119:          0         -  mxs-dma
120:          0         -  RTC alarm
124:          0         -  8006c000.serial
127:      68050         -  uart-pl011
128:        151         -  ci13xxx_imx
Err:          0

我想知道我向 IRQ 声明的标志是否正确?我注意到使用这种配置,我无法再访问控制台,因此内核现在似乎完全被服务于这个 74kHz 触发器所消耗。这不可能吗?我想这对我来说没什么大不了的,因为这只是在数据传输期间,但我仍然觉得我做错了什么..

另外,我想知道使用 ioremap 映射寄存器并使用直接内存写入触发输出是否更有效?

有什么方法可以提高中断的优先级吗?或者我可以在数据传输期间(约 400 毫秒)以某种方式锁定内核,并以其他方式生成我的输出时间吗?

编辑:忘记在问题中添加 /proc/interrupts 输出...

4

3 回答 3

4

您在这里遇到的是中断抖动。这在 Linux 上是可以预料的,因为内核会定期禁用各种任务的中断(进入自旋锁、处理中断等)。

无论您是否有 PREEMPT_RT,这都会发生,因此期望通过常规中断生成 74kHz 信号几乎是不现实的。

现在,ARM 有更高优先级的中断,称为 FIQ,永远不会被屏蔽或禁用。

Linux 不使用 FIQ,也不是为处理可以使用 FIQ 的事实而构建的,因此您将无法使用通用内核框架。

然而,从 Linux 驱动程序开发的角度来看,只要您牢记这一点,它并没有真正的不同:您必须编写一个处理程序,并将其关联到一个 IRQ。您还必须插入中断控制器以使其为您要使用的中断生成 FIQ(有关如何更改它的详细信息取决于平台。某些平台具有执行此操作的功能(如 imx25 和 mxc_set_irq_fiq) , 有些没有。imx23/28 没有,所以你必须手动完成)。

唯一设置 fiq 处理程序的函数只能与汇编编写的处理程序一起使用,因此您必须在汇编中重写您的处理程序(使用您当前的代码,它应该是微不足道的)。

您可以在 Alexandre 发布的博客文章 ( http://free-electrons.com/blog/fiq-handlers-in-the-arm-linux-kernel/ )中获取更多详细信息,您可以在其中找到工作代码、示例、以及它们如何协同工作的解释。

于 2013-06-24T15:38:11.627 回答
1

您可以看看我的同事 Maxime Ripard 在类似的 SoC (i.mx28) 上使用 FIQ 所做的事情:

http://free-electrons.com/blog/fiq-handlers-in-the-arm-linux-kernel/

于 2013-06-21T20:38:04.160 回答
0

试试这个标志:

int irq_flags;
...
irq_flags = IRQF_TRIGGER_RISING | IRQF_EARLY_RESUME

我有一个内核 3.8.11 并且找不到 IRQF_NO_SOFTIRQ_CALL 定义。仅适用于 3.8.13?我也没有看到 irq_flags 定义。它在哪里?

于 2013-06-22T10:28:29.923 回答