c - ARM A7 Linux 原始中断处理可能吗?

标签 c linux linux-kernel irq

我想编写一个开源核心驱动程序,用于在 Linux 中控制步进电机。在这种情况下,特别是对于 3D 打印机。

基本思想是驱动程序在一个 IO 端口上保留引脚,然后立即操作这些引脚。它接收一个充满“切换这个、切换那个”值的缓冲区,然后使用硬件定时器将这些值发送到端口。

现在的问题是:有没有办法尽快处理特定的硬件中断?

有问题的芯片是全志H3,我正在使用该芯片的TMR1资源(IRQ 51)。我可以很好地使用它,并且它也可以用作中断:

static irqreturn_t stepCore_timer_interrupt(int irq, void *dev_id)
{
        writel(TMR1_IRQ_PEND, TMR_IRQ_ST_VREG);
        icnt++;

        porta_state = readl(PA_VDAT);
        porta_state &= porta_mask;

        if(icnt & 0x00000001)
        {
            porta_state |= 0x00000001;
        }

        writel(porta_state, PA_VDAT);

        return IRQ_HANDLED;
}

static struct irqaction stepCore_timer_irq = {
        .name = "stepCore_timer",
        .flags = IRQF_DISABLED | IRQF_NOBALANCING , IRQF_PERCPU,
        .handler = stepCore_timer_interrupt,
        .dev_id = NULL,
};

static void stepCore_timer_interrupt_setup(void)
{
    int ret;
    u32 val;

    writel( 24000000, TMR1_INTV_VALUE_VREG );
    writel( ( TMR1_MODE_CONTINUOUS | TMR1_CLK_PRES_1 | TMR1_CLK_SRC_OSC24M ), TMR1_CTRL_VREG );

    ret = setup_irq(SUNXI_IRQ_TIMER1, &stepCore_timer_irq);
    if (ret)
            printk("%s: ERROR: failed to install irq %d\n", __func__, SUNXI_IRQ_TIMER1);
    else
            printk("%s: irq %d installed\n", __func__, SUNXI_IRQ_TIMER1);

    ret = irq_set_affinity_hint(SUNXI_IRQ_TIMER1, cpumask_of(3));
    if (ret)
            printk("%s: ERROR: failed to set irq affinity for irq %d\n", __func__, SUNXI_IRQ_TIMER1);
    else
            printk("%s: set irq affinity for irq %d\n", __func__, SUNXI_IRQ_TIMER1);
    /* Enable timer0 interrupt */
    val = readl(TMR_IRQ_EN_VREG);
    writel(val | TMR1_IRQ_EN, TMR_IRQ_EN_VREG);

}

TMR1 在其他方面未使用(事实上,我必须自己添加它),并且到目前为止可以正常工作。然而,在处理相当简单的 IRQ 例程时存在相当多的延迟。由于我想生成一些可用于 3D 打印机的代码,因此我非常喜欢更“稳定”的计时器中断。

所以,我的问题是:有没有办法在 Linux 中拥有一个非常短的 IRQ 例程,并且具有最高的优先级?或者甚至根本不关心 Linux 调度程序,而只是“它是重要的东西”?基本上是一个原始的 IRQ 处理程序,忽略 Linux 认为它应该是什么?

无论如何,它运行的核心专门用于该任务。处理程序将尽可能短:从数组中获取 u32,将其写入端口,完成。

我希望有一些东西可以忽略 Linux 的其余部分。是的,我知道这不是办法。但这是针对相当特殊的情况,因此我毫不犹豫地调整常规内核源代码来满足这些需求。

哦,这提醒我了,内核是 3.4.112,带有合适的 preempt-rt 补丁。

非常感谢任何帮助。

问候,

克里斯

最佳答案

这是此问题的通用解决方案。您可以编写一个内核模块,该模块将覆盖现有的中断处理例程并将其替换为您自己的例程,您可以在其中处理您感兴趣的 irq 并将所有 irq 重定向到现有的内核中断处理例程。对于 x86 架构,您可以获得低级 CPU 指令来获取中断描述例程 (lidt) 的现有地址。我相信ARM也应该可以。现在,Linux 具有 CPU 隔离技术 isolcpus 通过利用此技术,您可以将 CPU 移出调度程序域,即不会在该特定 CPU 上调度任何任务,直到您指定要在该 CPU 上运行的任务特定的CPU(使用任务集)。将 CPU 从调度程序域中取出后,您可以借助将中断仿射到该独立 CPU 的技术,可以通过/proc/irq/IRQ_NUMBER/smp_affinity 来完成。现在,所有中断都将由这个隔离的 CPU 处理,并且 100% 专用于该中断。通过您自己的 IRQ 例程,您可以完全控制中断处理。

希望这会有所帮助!

关于c - ARM A7 Linux 原始中断处理可能吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38927607/

相关文章:

c++ - C/C++ - 覆盖默认函数

c - 简单的 C 程序无法正确计算

linux - Bash 算术表达式 (( 在复合语句 [[ 返回真?

c - 为什么 Linux 内核不会在返回 IRQ_HANDLED 的共享 IRQ 的第一个处理程序处停止?

linux - mfd_cell 结构在 linux 内核驱动程序中描述了什么。它是描述子设备还是子设备的层级节点

c - 可以在堆栈上分配大结构吗?

无法理解如何创建没有参数的过程原型(prototype)

linux - 如何使用linux访问其他系统中的文件

.net - 我需要在每台 Linux PC 上安装 mono 才能在 Linux 平台上运行 .NET 应用程序吗?

linux - 如何使用 GDB 和 QEMU 调试 Linux 内核?