LinuxARM中断后的处理(5)【转】
转自:https://blog.csdn.net/zgtzqzg2020/article/details/105703394
1. 中断进入自定义函数
在中断发生后,经历ARM通用的处理阶段,到达irq_handler宏,转入C语言阶段。
- //arch/arm/kernel/entry-armv.S
- /*
- *注释:
- *宏CONFIG_MULTI_IRQ_HANDLER在.config中有定义,会将handle_arch_irq里的值赋值给pc去执行。
- *给handle_arch_irq请看后面的C语言阶段分析
- */
- /*
- * Interrupt handling.
- */
- .macro irq_handler
- #ifdef CONFIG_MULTI_IRQ_HANDLER
- ldr r1, =handle_arch_irq
- mov r0, sp
- badr lr, 9997f
- ldr pc, [r1] //进入C语言阶段的中断处理
- #else
- arch_irq_handler_default
- #endif
- 9997:
- .endm
- //arch/arm/kernel/entry-armv.S
- #ifdef CONFIG_MULTI_IRQ_HANDLER
- .globl handle_arch_irq
- handle_arch_irq:
- .space 4
- #endif
2. handle_arch_irq
handle_arch_irq赋值在/drivers/irqchip/irq-vic.c的vic_register函数中调用set_handle_irq进行赋值
- //driver/irqchip/irq-vic.c
- /**
- * vic_register() - Register a VIC.
- * @base: The base address of the VIC.
- * @parent_irq: The parent IRQ if cascaded, else 0.
- * @irq: The base IRQ for the VIC.
- * @valid_sources: bitmask of valid interrupts
- * @resume_sources: bitmask of interrupts allowed for resume sources.
- * @node: The device tree node associated with the VIC.
- *
- * Register the VIC with the system device tree so that it can be notified
- * of suspend and resume requests and ensure that the correct actions are
- * taken to re-instate the settings on resume.
- *
- * This also configures the IRQ domain for the VIC.
- */
- // __vic_init(regs, 0, 0, interrupt_mask, wakeup_mask, node);
- //vic_register(base, 0, irq_start, vic_sources, 0, node);
- staticvoid __initvic_register(void __iomem *base, unsigned int parent_irq,
- unsigned int irq,
- u32 valid_sources, u32 resume_sources,
- struct device_node *node)
- {
- struct vic_device *v;
- int i;
- if (vic_id >= ARRAY_SIZE(vic_devices)) {
- printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR
"
, __func__); - return;
- }
- v = &vic_devices[vic_id];
- v->base = base;
- v->valid_sources = valid_sources;
- v->resume_sources = resume_sources;
- set_handle_irq(vic_handle_irq); //此处被调用
- vic_id++;
- if (parent_irq) {
- irq_set_chained_handler_and_data(parent_irq,
- vic_handle_irq_cascaded, v);
- }
- v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,
- &vic_irqdomain_ops, v);
- /* create an IRQ mapping for each valid IRQ */
- for (i = 0; i < fls(valid_sources); i++) {
- if (valid_sources & (1 << i)) {
- irq_create_mapping(v->domain, i);
- }
- }
- /* If no base IRQ was passed, figure out our allocated base */
- if (irq)
- v->irq = irq;
- else
- v->irq = irq_find_mapping(v->domain, 0);
- }
- //arch/arm/kernel/irq.c
- #ifdef CONFIG_MULTI_IRQ_HANDLER
- void __initset_handle_irq(void (*handle_irq)(struct pt_regs *))
- {
- if (handle_arch_irq)
- return;
- handle_arch_irq = handle_irq;
- }
- #endif
3. vic_handle_irq
- //drivers/irqchip/irq-vic.c
- /*
- * Keep iterating over all registered VIC"s until there are no pending
- * interrupts.
- */
- staticvoid __exception_irq_entry vic_handle_irq(struct pt_regs *regs)
- {
- int i, handled;
- do {
- for (i = 0, handled = 0; i < vic_id; ++i)
- handled |= handle_one_vic(&vic_devices[i], regs);
- } while (handled);
- }
4. handle_one_vic
- //drivers/irqchip/irq-vic.c
- /*
- * Handle each interrupt in a single VIC. Returns non-zero if we"ve
- * handled at least one interrupt. This reads the status register
- * before handling each interrupt, which is necessary given that
- * handle_IRQ may briefly re-enable interrupts for soft IRQ handling.
- */
- staticinthandle_one_vic(struct vic_device *vic, struct pt_regs *regs)
- {
- u32 stat, irq;
- int handled = 0;
- while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) {
- irq = ffs(stat) - 1;
- handle_domain_irq(vic->domain, irq, regs);
- handled = 1;
- }
- return handled;
- }
- //具体代码参看arch/arm/include/asm/io.h
- /*
- * Memory access primitives
- * ------------------------
- *
- * These perform PCI memory accesses via an ioremap region. They don"t
- * take an address as such, but a cookie.
- *
- * Again, this are defined to perform little endian accesses. See the
- * IO port primitives for more information.
- */
- #ifndef readl
- #define readb_relaxed(c) ({ u8 __r = __raw_readb(c); __r; })
- #define readw_relaxed(c) ({ u16 __r = le16_to_cpu((__force __le16)
- __raw_readw(c)); __r; })
- #define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32)
- __raw_readl(c)); __r; })
- #define writeb_relaxed(v,c) __raw_writeb(v,c)
- #define writew_relaxed(v,c) __raw_writew((__force u16) cpu_to_le16(v),c)
- #define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c)
- #define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; })
- #define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; })
- #define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; })
- #define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); })
- #define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); })
- #define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })
- #define readsb(p,d,l) __raw_readsb(p,d,l)
- #define readsw(p,d,l) __raw_readsw(p,d,l)
- #define readsl(p,d,l) __raw_readsl(p,d,l)
- #define writesb(p,d,l) __raw_writesb(p,d,l)
- #define writesw(p,d,l) __raw_writesw(p,d,l)
- #define writesl(p,d,l) __raw_writesl(p,d,l)
5. handle_domain_irq
- //include/linux/irqdesc.h
- #ifdef CONFIG_HANDLE_DOMAIN_IRQ
- /*
- * Convert a HW interrupt number to a logical one using a IRQ domain,
- * and handle the result interrupt number. Return -EINVAL if
- * conversion failed. Providing a NULL domain indicates that the
- * conversion has already been done.
- */
- int __handle_domain_irq(struct irq_domain *domain, unsignedint hwirq,
- bool lookup, struct pt_regs *regs);
- staticinlineinthandle_domain_irq(struct irq_domain *domain,
- unsignedint hwirq, struct pt_regs *regs)
- {
- return __handle_domain_irq(domain, hwirq, true, regs);
- }
- #endif
6. __handle_domain_irq
- //kernel/irq/irqdesc.c
- #ifdef CONFIG_HANDLE_DOMAIN_IRQ
- /**
- * __handle_domain_irq - Invoke the handler for a HW irq belonging to a domain
- * @domain: The domain where to perform the lookup
- * @hwirq: The HW irq number to convert to a logical one
- * @lookup: Whether to perform the domain lookup or not
- * @regs: Register file coming from the low-level handling code
- *
- * Returns: 0 on success, or -EINVAL if conversion has failed
- */
- int __handle_domain_irq(struct irq_domain *domain, unsignedint hwirq,
- bool lookup, struct pt_regs *regs)
- {
- structpt_regs *old_regs = set_irq_regs(regs);
- unsignedint irq = hwirq;
- int ret = 0;
- irq_enter();
- #ifdef CONFIG_IRQ_DOMAIN
- if (lookup)
- irq = irq_find_mapping(domain, hwirq);
- #endif
- /*
- * Some hardware gives randomly wrong interrupts. Rather
- * than crashing, do something sensible.
- */
- if (unlikely(!irq || irq >= nr_irqs)) {
- ack_bad_irq(irq);
- ret = -EINVAL;
- } else {
- generic_handle_irq(irq);
- }
- irq_exit();
- set_irq_regs(old_regs);
- return ret;
- }
- #endif
7. generic_handle_irq
- //kernel/irq/irqdesc.c
- /**
- * generic_handle_irq - Invoke the handler for a particular irq
- * @irq: The irq number to handle
- *
- */
- intgeneric_handle_irq(unsignedint irq)
- {
- structirq_desc *desc = irq_to_desc(irq);
- if (!desc)
- return -EINVAL;
- generic_handle_irq_desc(desc);
- return0;
- }
- EXPORT_SYMBOL_GPL(generic_handle_irq);
8. generic_handle_irq_desc
- //include/linux/irqdesc.h
- /*
- * Architectures call this to let the generic IRQ layer
- * handle an interrupt.
- */
- staticinlinevoidgeneric_handle_irq_desc(struct irq_desc *desc)
- {
- /*
- *handle_irq在struct domain的struct domain_ops的map函数中调用
- *irq_set_chip_and_handler赋值为handle_level_irq
- */
- desc->handle_irq(desc);
- }
- intgeneric_handle_irq(unsignedint irq);
9. handle_level_irq
- //kernel/irq/chip.c
- /**
- * handle_level_irq - Level type irq handler
- * @desc: the interrupt description structure for this irq
- *
- * Level type interrupts are active as long as the hardware line has
- * the active level. This may require to mask the interrupt and unmask
- * it after the associated handler has acknowledged the device, so the
- * interrupt line is back to inactive.
- */
- voidhandle_level_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(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);
- }
- EXPORT_SYMBOL_GPL(handle_level_irq);
- //kernel/irq/chip.c
- staticinlinevoidmask_ack_irq(struct irq_desc *desc)
- {
- /*desc->irq_data.chip在struct domain的struct domain_ops的map函数中调用
- *irq_set_chip_and_handler赋值为vic_chip.
- */
- if (desc->irq_data.chip->irq_mask_ack)
- desc->irq_data.chip->irq_mask_ack(&desc->irq_data);
- else {
- desc->irq_data.chip->irq_mask(&desc->irq_data);
- if (desc->irq_data.chip->irq_ack)
- desc->irq_data.chip->irq_ack(&desc->irq_data);
- }
- irq_state_set_masked(desc);
- }
- //include/linux/irq.h
- /**
- * struct irq_chip - hardware interrupt chip descriptor
- *
- * @name: name for /proc/interrupts
- * @irq_startup: start up the interrupt (defaults to ->enable if NULL)
- * @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
- * @irq_enable: enable the interrupt (defaults to chip->unmask if NULL)
- * @irq_disable: disable the interrupt
- * @irq_ack: start of a new interrupt
- * @irq_mask: mask an interrupt source
- * @irq_mask_ack: ack and mask an interrupt source
- * @irq_unmask: unmask an interrupt source
- * @irq_eoi: end of interrupt
- * @irq_set_affinity: set the CPU affinity on SMP machines
- * @irq_retrigger: resend an IRQ to the CPU
- * @irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
- * @irq_set_wake: enable/disable power-management wake-on of an IRQ
- * @irq_bus_lock: function to lock access to slow bus (i2c) chips
- * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
- * @irq_cpu_online: configure an interrupt source for a secondary CPU
- * @irq_cpu_offline: un-configure an interrupt source for a secondary CPU
- * @irq_suspend: function called from core code on suspend once per
- * chip, when one or more interrupts are installed
- * @irq_resume: function called from core code on resume once per chip,
- * when one ore more interrupts are installed
- * @irq_pm_shutdown: function called from core code on shutdown once per chip
- * @irq_calc_mask: Optional function to set irq_data.mask for special cases
- * @irq_print_chip: optional to print special chip info in show_interrupts
- * @irq_request_resources: optional to request resources before calling
- * any other callback related to this irq
- * @irq_release_resources: optional to release resources acquired with
- * irq_request_resources
- * @irq_compose_msi_msg: optional to compose message content for MSI
- * @irq_write_msi_msg: optional to write message content for MSI
- * @irq_get_irqchip_state: return the internal state of an interrupt
- * @irq_set_irqchip_state: set the internal state of a interrupt
- * @irq_set_vcpu_affinity: optional to target a vCPU in a virtual machine
- * @flags: chip specific flags
- */
- structirq_chip {
- constchar *name;
- unsignedint(*irq_startup)(struct irq_data *data);
- void (*irq_shutdown)(struct irq_data *data);
- void (*irq_enable)(struct irq_data *data);
- void (*irq_disable)(struct irq_data *data);
- void (*irq_ack)(struct irq_data *data);
- void (*irq_mask)(struct irq_data *data);
- void (*irq_mask_ack)(struct irq_data *data);
- void (*irq_unmask)(struct irq_data *data);
- void (*irq_eoi)(struct irq_data *data);
- int (*irq_set_affinity)(struct irq_data *data, conststruct cpumask *dest, bool force);
- int (*irq_retrigger)(struct irq_data *data);
- int (*irq_set_type)(struct irq_data *data, unsignedint flow_type);
- int (*irq_set_wake)(struct irq_data *data, unsignedint on);
- void (*irq_bus_lock)(struct irq_data *data);
- void (*irq_bus_sync_unlock)(struct irq_data *data);
- void (*irq_cpu_online)(struct irq_data *data);
- void (*irq_cpu_offline)(struct irq_data *data);
- void (*irq_suspend)(struct irq_data *data);
- void (*irq_resume)(struct irq_data *data);
- void (*irq_pm_shutdown)(struct irq_data *data);
- void (*irq_calc_mask)(struct irq_data *data);
- void (*irq_print_chip)(struct irq_data *data, struct seq_file *p);
- int (*irq_request_resources)(struct irq_data *data);
- void (*irq_release_resources)(struct irq_data *data);
- void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
- void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);
- int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
- int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);
- int (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);
- unsignedlong flags;
- };
- //drivers/irqchip/irq-vic.c 在
- staticstructirq_chip vic_chip = {
- .name = "VIC",
- .irq_ack = vic_ack_irq,
- .irq_mask = vic_mask_irq,
- .irq_unmask = vic_unmask_irq,
- .irq_set_wake = vic_set_wake,
- };
- staticvoidvic_ack_irq(struct irq_data *d)
- {
- void __iomem *base = irq_data_get_irq_chip_data(d);
- unsignedint irq = d->hwirq;
- writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);
- /* moreover, clear the soft-triggered, in case it was the reason */
- writel(1 << irq, base + VIC_INT_SOFT_CLEAR);
- }
- staticvoidvic_mask_irq(struct irq_data *d)
- {
- void __iomem *base = irq_data_get_irq_chip_data(d);
- unsignedint irq = d->hwirq;
- writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);
- }
- staticvoidvic_unmask_irq(struct irq_data *d)
- {
- void __iomem *base = irq_data_get_irq_chip_data(d);
- unsignedint irq = d->hwirq;
- writel(1 << irq, base + VIC_INT_ENABLE);
- }
- #if defined(CONFIG_PM)
- staticstructvic_device *vic_from_irq(unsignedint irq)
- {
- structvic_device *v = vic_devices;
- unsignedint base_irq = irq & ~31;
- int id;
- for (id = 0; id < vic_id; id++, v++) {
- if (v->irq == base_irq)
- return v;
- }
- returnNULL;
- }
- staticintvic_set_wake(struct irq_data *d, unsignedint on)
- {
- structvic_device *v = vic_from_irq(d->irq);
- unsignedint off = d->hwirq;
- u32 bit = 1 << off;
- if (!v)
- return -EINVAL;
- if (!(bit & v->resume_sources))
- return -EINVAL;
- if (on)
- v->resume_irqs |= bit;
- else
- v->resume_irqs &= ~bit;
- return0;
- }
- #else
- #define vic_set_wake NULL
- #endif/* CONFIG_PM */
- //include/linux/irq.h
- staticinlinevoid *irq_data_get_irq_chip_data(struct irq_data *d)
- {
- /*
- *d->chip_data在struct domain的struct domain_ops的map函数中调用
- *irq_set_chip_data赋值为中断控制器寄存器的虚拟地址
- *
- */
- return d->chip_data;
- }
10. handle_irq_event
- //kernel/irq/handle.c
- irqreturn_thandle_irq_event(struct irq_desc *desc)
- {
- 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);
- raw_spin_lock(&desc->lock);
- irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
- return ret;
- }
11. handle_irq_event_percpu:
- //kernel/irq/handle.c
- irqreturn_thandle_irq_event_percpu(struct irq_desc *desc)
- {
- irqreturn_t retval = IRQ_NONE;
- unsignedint flags = 0, irq = desc->irq_data.irq;
- structirqaction *action = desc->action;
- /* action might have become NULL since we dropped the lock */
- while (action) {
- irqreturn_t res;
- trace_irq_handler_entry(irq, action);
- /*
- *action->handler处理函数为驱动中注册的处理函数
- */
- 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
"
, - 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;
- }
- add_interrupt_randomness(irq, flags);
- if (!noirqdebug)
- note_interrupt(desc, retval);
- return retval;
- }
【作者】陈金
【出处】https://www.cnblogs.com/yifeichongtian2021/
【云海天】https://www.cnblogs.com/yifeichongtian2021/
本文版权归作者和云海天共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
以上是 LinuxARM中断后的处理(5)【转】 的全部内容, 来源链接: utcz.com/z/520014.html