对于一个特定的irq_desc[irq],其上的中断处理分为两级,第一级是调用irq_desc[irq].handle_irq,第二级是设备特定的中断处理例程ISR,在handle_irq的内部通过irq_desc[irq].action->handler调用。
第一级函数在平台初始化期间被安装到irq_desc数组中,第二级函数的注册发生在设备驱动程序调用request_irq安装对应设备的中断处理例程时。第一级函数主要面向PIC的某一中断线IRQ line,第二级函数则面向该中断线上连接的具体设备。
static int mxc_gpio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct mxc_gpio_port *port; struct resource *iores; int irq_base; int err; mxc_gpio_get_hw(pdev); port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); port->base = devm_ioremap_resource(&pdev->dev, iores); if (IS_ERR(port->base)) return PTR_ERR(port->base); port->irq_high = platform_get_irq(pdev, 1); port->irq = platform_get_irq(pdev, 0); if (port->irq < 0) return port->irq; /* disable the interrupt and clear the status */ writel(0, port->base + GPIO_IMR); writel(~0, port->base + GPIO_ISR); if (mxc_gpio_hwtype == IMX21_GPIO) { /* * Setup one handler for all GPIO interrupts. Actually setting * the handler is needed only once, but doing it for every port * is more robust and easier. */ irq_set_chained_handler(port->irq, mx2_gpio_irq_handler); } else { /* setup one handler for each entry */ irq_set_chained_handler(port->irq, mx3_gpio_irq_handler); irq_set_handler_data(port->irq, port); if (port->irq_high > 0) { /* setup handler for GPIO 16 to 31 */ irq_set_chained_handler(port->irq_high, mx3_gpio_irq_handler); irq_set_handler_data(port->irq_high, port); } } /* gpio-mxc can be a generic irq chip */ mxc_gpio_init_gc(port, irq_base); }
linux内核提供两个函数 irq_set_handler 和 irq_set_chained_handler 来设置第一级中断处理函数。
static void __init mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base) { struct irq_chip_generic *gc; struct irq_chip_type *ct; gc = irq_alloc_generic_chip("gpio-mxc", 1, irq_base, port->base, handle_level_irq); gc->private = port; ct = gc->chip_types; ct->chip.irq_ack = irq_gc_ack_set_bit; ct->chip.irq_mask = irq_gc_mask_clr_bit; ct->chip.irq_unmask = irq_gc_mask_set_bit; ct->chip.irq_set_type = gpio_set_irq_type; ct->chip.irq_set_wake = gpio_set_wake_irq; ct->regs.ack = GPIO_ISR; ct->regs.mask = GPIO_IMR; irq_setup_generic_chip(gc, IRQ_MSK(32), IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST,0); }
内核中通过irq_chip结构体来描述中断控制器,描述了中断mask,ack等成员函数。多个控制器之间还可以级联,多中断控制器的情况下,我们使用内核提供的通用的irq_chip驱动架构irq_chip_generic,目前,主流的arm芯片内部的一级中断控制器都是用了arm公司的GIC。
void handle_level_irq(unsigned int irq, struct irq_desc *desc) { raw_spin_lock(&desc->lock); mask_ack_irq(desc); if (!irq_may_run(desc)) goto out_unlock; desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); kstat_incr_irqs_this_cpu(irq, desc); /* * If its disabled or no action available * keep it masked and get out of here */ if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) { desc->istate |= IRQS_PENDING; goto out_unlock; } handle_irq_event(desc); cond_unmask_irq(desc); out_unlock: raw_spin_unlock(&desc->lock); }
handle_level_irq 是通用中断子系统提供的标准流控回调函数,用于电平触发中断的流控处理,其他函数还有handle_simple_irq 用于简易流控处理,handle_edge_irq 用于边沿触发中断的流控处理,handle_fasteoi_irq 用于需要响应eoi的中断控制器,handle_percpu_irq 用于只在单一cpu响应的中断,handle_nested_irq 用于处理使用线程的嵌套中断。
irqreturn_t handle_irq_event(struct irq_desc *desc) { struct irqaction *action = desc->action; irqreturn_t ret; desc->istate &= ~IRQS_PENDING; irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS); raw_spin_unlock(&desc->lock); ret = handle_irq_event_percpu(desc, action); raw_spin_lock(&desc->lock); irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS); return ret; }
函数handle_irq_event 为调用设备驱动程序安装的中断处理例程做最后的准备工作,
清除IRQS_PENDING位,将当前设备的中断线设置IRQD_IRQ_INPROGRESS状态,表明
该中断线上一个中断正在被处理。
irqreturn_t handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action) { irqreturn_t retval = IRQ_NONE; unsigned int flags = 0, irq = desc->irq_data.irq; do { irqreturn_t res; trace_irq_handler_entry(irq, action); res = action->handler(irq, action->dev_id); trace_irq_handler_exit(irq, action, res); if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n", irq, action->handler)) local_irq_disable(); switch (res) { case IRQ_WAKE_THREAD: /* * Catch drivers which return WAKE_THREAD but * did not set up a thread function */ if (unlikely(!action->thread_fn)) { warn_no_thread(irq, action); break; } __irq_wake_thread(desc, action); /* Fall through to add to randomness */ case IRQ_HANDLED: flags |= action->flags; break; default: break; } retval |= res; action = action->next; } while (action); add_interrupt_randomness(irq, flags); if (!noirqdebug) note_interrupt(irq, desc, retval); return retval; }
函数遍历action上的中断处理例程链表,通过action->handler来调用具体设备的中断处理例程,
即通过request_irq函数安装的中断处理函数。如果返回值是IRQ_WAKE_THREAD,那么函数将调用
irq_wake_thread来唤醒action->thread表示的一个内核线程。