中断¶
- 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 等设备非常有用。
那么,有什么改变呢?¶
机器实现绝对不能写入 irqdesc 数组。
用于操作 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 之一
set_GPIO_IRQ_edge()已废弃,应由 set_irq_type 代替。直接访问 SA1111 INTPOL 已被弃用。请改用 set_irq_type。
处理程序应通过正确的芯片特定函数对父 IRQ 执行任何必要的确认。例如,如果 SA1111 直接连接到 SA1110 GPIO,那么每次重新读取 SA1111 IRQ 状态时,都应确认 SA1110 IRQ。
对于任何没有自己的 IRQ 启用/禁用控制的子设备(例如 SMC9196),在调用子处理程序时,处理程序必须屏蔽或确认父 IRQ,并且子处理程序应该是“simple”处理程序(而不是“edge”或“level”)。处理程序完成后,应解除对父 IRQ 的屏蔽,并且必须重新检查所有子设备的挂起事件状态。(详见 Neponset IRQ 处理程序)。
fixup_irq()已不复存在,arch/arm/mach-*/include/mach/irq.h 也是如此
请注意,这并不能解决所有问题——其中一些是基于硬件的。在同一个父信号(例如 neponset)上混合使用基于电平和基于边沿的 IRQ 就是这样一个领域,基于软件的解决方案无法为低 IRQ 延迟提供完整的答案。