ARM A7 Возможна ли обработка необработанных прерываний в Linux?

Я хотел бы написать основной драйвер с открытым исходным кодом для управления шаговыми двигателями в Linux. В данном случае специально для 3D-принтеров.

Основная идея заключается в том, что драйвер резервирует контакты на одном порту ввода-вывода, а затем манипулирует этими выводами сразу. Он получает полный буфер значений "toggle, toggle that", а затем отправляет их в порт, используя аппаратный таймер.

Теперь вопрос: есть ли способ обработать конкретное аппаратное прерывание как можно быстрее?

Данный чип является Allwinner 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-принтера, мне очень нравится более "стабильное" прерывание таймера.

Итак, мой вопрос: есть ли способ иметь очень короткую процедуру IRQ в Linux, которая имеет максимально возможный приоритет? Или даже не заботится о планировщике Linux вообще, а просто "делает свое дело"? По сути, необработанный обработчик IRQ, игнорирующий то, что, по мнению Linux, должно быть?

В любом случае ядро, на котором оно работает, предназначено именно для этой задачи. Обработчик будет максимально коротким: извлеките u32 из массива, запишите его в порт, готово.

Желательно, чтобы у меня было что-то, что просто игнорирует весь остальной Linux. Да, я знаю, что это не способ сделать это. Но это предназначено для довольно особого случая, поэтому у меня нет проблем с адаптацией обычных исходных кодов ядра под эти нужды.

О, это напоминает мне, ядро ​​3.4.112 с подходящими патчами preempt-rt.

Любая помощь с благодарностью.

Привет,

Крис

2 ответа

Вы думали об использовании FIQ для этого. У нас есть запись в блоге об этом: http://free-electrons.com/blog/fiq-handlers-in-the-arm-linux-kernel/

Вот общее решение этой проблемы. Вы можете написать модуль ядра, который перезапишет существующую подпрограмму обработки прерываний и будет заменен вашей собственной подпрограммой, где вы можете обработать интересующий вас запрос и перенаправить весь запрос в существующую подпрограмму обработки прерываний ядра. Для арки x86 можно получить низкоуровневые инструкции процессора, чтобы получить существующий адрес процедуры описания прерывания (lidt). Я считаю, что это должно быть возможно и для ARM. Теперь в Linux есть техника изоляции процессора isolcpus Используя эту технику, вы можете вывести процессор из домена планировщика, т.е. никакая задача не будет запланирована на этом конкретном процессоре, пока вы не укажете задачу, которая будет выполняться на этом конкретном процессоре (используя набор задач). После того, как вы вывели процессор из домена планировщика, вы можете воспользоваться техникой связывания прерывания с этим изолированным процессором, вы можете сделать это с помощью /proc/irq/IRQ_NUMBER/smp_affinity, Теперь все ваши прерывания будут обрабатываться этим изолированным процессором и на 100% выделены для этого прерывания. А с вашей собственной процедурой IRQ у вас есть полный контроль над обработкой прерываний.

Надеюсь, это поможет!

Другие вопросы по тегам