中断

2.5.2-rmk5

这是第一个对某些主要架构特定子系统进行重大重组的内核。

首先,它对我们处理 MMU TLB 的方式进行了相当大的更改。现在,每个 MMU TLB 变体都完全独立处理——我们有 TLB v3、TLB v4(无写缓冲)、TLB v4(带写缓冲),以及最后的 TLB v4(带写缓冲,带有 I TLB 无效化条目)。这些函数内部包含更多的汇编代码,主要是为了允许未来进行更灵活的 TLB 处理。

其次是 IRQ 子系统。

2.5 内核将对 IRQ 的处理方式进行重大更改。不幸的是,这意味着触及 irq_desc[] 数组的机器类型(基本上是所有机器类型)将会失效,这也意味着我们当前拥有的每种机器类型都会受到影响。

让我们举个例子。在带 Neponset 的 Assabet 上,我们有

          GPIO25                 IRR:2
SA1100 ------------> Neponset -----------> SA1111
                                 IIR:1
                              -----------> USAR
                                 IIR:0
                              -----------> SMC9196

按目前的工作方式,所有 SA1111 中断都是互相排斥的——如果你正在处理来自 SA1111 的一个中断而又来了一个新中断,你必须等待该中断处理完成,然后才能为新中断提供服务。例如,SA1111 上的基于 IDE PIO 的中断会排斥所有其他 SA1111 和 SMC9196 中断,直到它完成多扇区数据的传输,这可能需要很长时间。还要注意的是,由于我们在 SA1111 IRQ 处理程序中进行循环,SA1111 IRQ 可能会无限期地延迟 SMC9196 IRQ。

新方法带来了几个新概念……

我们引入了“父(parent)”和“子(child)”的概念。例如,对于 Neponset 处理程序,“父”是 GPIO25,“子”是 SA1111、SMC9196 和 USAR。

我们还引入了 IRQ “芯片(chip)”的概念(主要是为了减小 irqdesc 数组的大小)。这不一定是一个真正的“IC”;事实上,SA11x0 IRQ 是由两个独立的“芯片”结构处理的,一个用于 GPIO0-10,另一个用于其余所有。它只是各种操作的一个容器(也许以后会改成一个更好的名字)。该结构包含以下操作

struct irqchip {
        /*
         * Acknowledge the IRQ.
         * If this is a level-based IRQ, then it is expected to mask the IRQ
         * as well.
         */
        void (*ack)(unsigned int irq);
        /*
         * Mask the IRQ in hardware.
         */
        void (*mask)(unsigned int irq);
        /*
         * Unmask the IRQ in hardware.
         */
        void (*unmask)(unsigned int irq);
        /*
         * Re-run the IRQ
         */
        void (*rerun)(unsigned int irq);
        /*
         * Set the type of the IRQ.
         */
        int (*type)(unsigned int irq, unsigned int, type);
};
ack
  • 必需。对于由 do_level_IRQ 处理的中断,其函数可能与 mask 相同。

mask
  • 必需。

unmask
  • 必需。

rerun
  • 可选。如果对于使用此 ‘irqchip’ 的所有 IRQ 都使用 do_level_IRQ,则不是必需的。通常期望如果可能的话重新触发硬件 IRQ。如果不能,可以直接调用处理程序。

type
  • 可选。如果你不支持更改 IRQ 的类型,它应该为 null,以便人们能够检测他们是否无法设置 IRQ 类型。

对于每个 IRQ,我们保留以下信息

  • “disable” 深度(没有配对 disable_irq()enable_irq() 的数量)

  • 指示我们可以对该 IRQ 做什么的标志(valid, probe, noautounmask),同以前一样

  • IRQ 的状态(probing、enable 等)

  • chip

  • 每个 IRQ 的处理程序

  • irqaction 结构体链表

处理程序可以是 3 个标准处理程序之一——“电平(level)”、“边沿(edge)”和“简单(simple)”,或者如果你需要做一些特殊的事情,可以使用你自己的特定处理程序。

“level”处理程序就是我们目前所拥有的——它相当简单。“edge”了解此类 IRQ 实现的缺陷——即在处理它时你需要保持硬件 IRQ 处于启用状态,并且如果在处理过程中再次发生该 IRQ,则排队后续的 IRQ 事件。“simple”处理程序非常基础,既不执行任何硬件操作,也不进行状态跟踪。这对于上面的 SMC9196 和 USAR 等设备非常有用。

那么,有什么改变呢?

  1. 机器实现绝对不能写入 irqdesc 数组。

  2. 用于操作 irqdesc 数组的新函数。前 4 个预计仅对机器特定的代码有用。最后一个建议仅由机器特定代码使用,但在绝对必要时也可用于驱动程序中。

    set_irq_chip(irq,chip)

    设置用于处理此 IRQ 的 mask/unmask 方法

    set_irq_handler(irq,handler)

    设置此 IRQ 的处理程序(level, edge, simple)

    set_irq_chained_handler(irq,handler)

    为此 IRQ 设置一个“链式(chained)”处理程序——自动启用此 IRQ(例如,Neponset 和 SA1111 处理程序)。

    set_irq_flags(irq,flags)

    设置 valid/probe/noautoenable 标志。

    set_irq_type(irq,type)

    设置处于活动状态的 IRQ 边沿/电平。这取代了 SA1111 INTPOL 操作以及 set_GPIO_IRQ_edge() 函数。类型应该是 <linux/irq.h> 中定义的 IRQ_TYPE_xxx 之一

  3. set_GPIO_IRQ_edge() 已废弃,应由 set_irq_type 代替。

  4. 直接访问 SA1111 INTPOL 已被弃用。请改用 set_irq_type。

  5. 处理程序应通过正确的芯片特定函数对父 IRQ 执行任何必要的确认。例如,如果 SA1111 直接连接到 SA1110 GPIO,那么每次重新读取 SA1111 IRQ 状态时,都应确认 SA1110 IRQ。

  6. 对于任何没有自己的 IRQ 启用/禁用控制的子设备(例如 SMC9196),在调用子处理程序时,处理程序必须屏蔽或确认父 IRQ,并且子处理程序应该是“simple”处理程序(而不是“edge”或“level”)。处理程序完成后,应解除对父 IRQ 的屏蔽,并且必须重新检查所有子设备的挂起事件状态。(详见 Neponset IRQ 处理程序)。

  7. fixup_irq() 已不复存在,arch/arm/mach-*/include/mach/irq.h 也是如此

请注意,这并不能解决所有问题——其中一些是基于硬件的。在同一个父信号(例如 neponset)上混合使用基于电平和基于边沿的 IRQ 就是这样一个领域,基于软件的解决方案无法为低 IRQ 延迟提供完整的答案。