LinuxARM中断后的处理(5)【转】

编程

转自:https://blog.csdn.net/zgtzqzg2020/article/details/105703394

1. 中断进入自定义函数 

在中断发生后,经历ARM通用的处理阶段,到达irq_handler宏,转入C语言阶段。

  1. //arch/arm/kernel/entry-armv.S
  2. /*
  3. *注释:
  4. *宏CONFIG_MULTI_IRQ_HANDLER在.config中有定义,会将handle_arch_irq里的值赋值给pc去执行。
  5. *给handle_arch_irq请看后面的C语言阶段分析
  6. */
  7. /*
  8. * Interrupt handling.
  9. */
  10. .macro irq_handler
  11. #ifdef CONFIG_MULTI_IRQ_HANDLER
  12. ldr r1, =handle_arch_irq
  13. mov r0, sp
  14. badr lr, 9997f
  15. ldr pc, [r1] //进入C语言阶段的中断处理
  16. #else
  17. arch_irq_handler_default
  18. #endif
  19. 9997:
  20. .endm
  21. //arch/arm/kernel/entry-armv.S
  22. #ifdef CONFIG_MULTI_IRQ_HANDLER
  23. .globl handle_arch_irq
  24. handle_arch_irq:
  25. .space 4
  26. #endif

2. handle_arch_irq

handle_arch_irq赋值在/drivers/irqchip/irq-vic.c的vic_register函数中调用set_handle_irq进行赋值

  1. //driver/irqchip/irq-vic.c
  2. /**
  3. * vic_register() - Register a VIC.
  4. * @base: The base address of the VIC.
  5. * @parent_irq: The parent IRQ if cascaded, else 0.
  6. * @irq: The base IRQ for the VIC.
  7. * @valid_sources: bitmask of valid interrupts
  8. * @resume_sources: bitmask of interrupts allowed for resume sources.
  9. * @node: The device tree node associated with the VIC.
  10. *
  11. * Register the VIC with the system device tree so that it can be notified
  12. * of suspend and resume requests and ensure that the correct actions are
  13. * taken to re-instate the settings on resume.
  14. *
  15. * This also configures the IRQ domain for the VIC.
  16. */
  17. // __vic_init(regs, 0, 0, interrupt_mask, wakeup_mask, node);
  18. //vic_register(base, 0, irq_start, vic_sources, 0, node);
  19. staticvoid __initvic_register(void __iomem *base, unsigned int parent_irq,
  20. unsigned int irq,
  21. u32 valid_sources, u32 resume_sources,
  22. struct device_node *node)
  23. {
  24. struct vic_device *v;
  25. int i;
  26. if (vic_id >= ARRAY_SIZE(vic_devices)) {
  27. printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR

    ", __func__);

  28. return;
  29. }
  30. v = &vic_devices[vic_id];
  31. v->base = base;
  32. v->valid_sources = valid_sources;
  33. v->resume_sources = resume_sources;
  34. set_handle_irq(vic_handle_irq); //此处被调用
  35. vic_id++;
  36. if (parent_irq) {
  37. irq_set_chained_handler_and_data(parent_irq,
  38. vic_handle_irq_cascaded, v);
  39. }
  40. v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,
  41. &vic_irqdomain_ops, v);
  42. /* create an IRQ mapping for each valid IRQ */
  43. for (i = 0; i < fls(valid_sources); i++) {
  44. if (valid_sources & (1 << i)) {
  45. irq_create_mapping(v->domain, i);
  46. }
  47. }
  48. /* If no base IRQ was passed, figure out our allocated base */
  49. if (irq)
  50. v->irq = irq;
  51. else
  52. v->irq = irq_find_mapping(v->domain, 0);
  53. }
  54. //arch/arm/kernel/irq.c
  55. #ifdef CONFIG_MULTI_IRQ_HANDLER
  56. void __initset_handle_irq(void (*handle_irq)(struct pt_regs *))
  57. {
  58. if (handle_arch_irq)
  59. return;
  60. handle_arch_irq = handle_irq;
  61. }
  62. #endif

3. vic_handle_irq

  1. //drivers/irqchip/irq-vic.c
  2. /*
  3. * Keep iterating over all registered VIC"s until there are no pending
  4. * interrupts.
  5. */
  6. staticvoid __exception_irq_entry vic_handle_irq(struct pt_regs *regs)
  7. {
  8. int i, handled;
  9. do {
  10. for (i = 0, handled = 0; i < vic_id; ++i)
  11. handled |= handle_one_vic(&vic_devices[i], regs);
  12. } while (handled);
  13. }

4. handle_one_vic

  1. //drivers/irqchip/irq-vic.c
  2. /*
  3. * Handle each interrupt in a single VIC. Returns non-zero if we"ve
  4. * handled at least one interrupt. This reads the status register
  5. * before handling each interrupt, which is necessary given that
  6. * handle_IRQ may briefly re-enable interrupts for soft IRQ handling.
  7. */
  8. staticinthandle_one_vic(struct vic_device *vic, struct pt_regs *regs)
  9. {
  10. u32 stat, irq;
  11. int handled = 0;
  12. while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) {
  13. irq = ffs(stat) - 1;
  14. handle_domain_irq(vic->domain, irq, regs);
  15. handled = 1;
  16. }
  17. return handled;
  18. }
  19. //具体代码参看arch/arm/include/asm/io.h
  20. /*
  21. * Memory access primitives
  22. * ------------------------
  23. *
  24. * These perform PCI memory accesses via an ioremap region. They don"t
  25. * take an address as such, but a cookie.
  26. *
  27. * Again, this are defined to perform little endian accesses. See the
  28. * IO port primitives for more information.
  29. */
  30. #ifndef readl
  31. #define readb_relaxed(c) ({ u8 __r = __raw_readb(c); __r; })
  32. #define readw_relaxed(c) ({ u16 __r = le16_to_cpu((__force __le16)
  33. __raw_readw(c)); __r; })
  34. #define readl_relaxed(c) ({ u32 __r = le32_to_cpu((__force __le32)
  35. __raw_readl(c)); __r; })
  36. #define writeb_relaxed(v,c) __raw_writeb(v,c)
  37. #define writew_relaxed(v,c) __raw_writew((__force u16) cpu_to_le16(v),c)
  38. #define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c)
  39. #define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; })
  40. #define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; })
  41. #define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; })
  42. #define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); })
  43. #define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); })
  44. #define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })
  45. #define readsb(p,d,l) __raw_readsb(p,d,l)
  46. #define readsw(p,d,l) __raw_readsw(p,d,l)
  47. #define readsl(p,d,l) __raw_readsl(p,d,l)
  48. #define writesb(p,d,l) __raw_writesb(p,d,l)
  49. #define writesw(p,d,l) __raw_writesw(p,d,l)
  50. #define writesl(p,d,l) __raw_writesl(p,d,l)

5. handle_domain_irq

  1. //include/linux/irqdesc.h
  2. #ifdef CONFIG_HANDLE_DOMAIN_IRQ
  3. /*
  4. * Convert a HW interrupt number to a logical one using a IRQ domain,
  5. * and handle the result interrupt number. Return -EINVAL if
  6. * conversion failed. Providing a NULL domain indicates that the
  7. * conversion has already been done.
  8. */
  9. int __handle_domain_irq(struct irq_domain *domain, unsignedint hwirq,
  10. bool lookup, struct pt_regs *regs);
  11. staticinlineinthandle_domain_irq(struct irq_domain *domain,
  12. unsignedint hwirq, struct pt_regs *regs)
  13. {
  14. return __handle_domain_irq(domain, hwirq, true, regs);
  15. }
  16. #endif

6. __handle_domain_irq

  1. //kernel/irq/irqdesc.c
  2. #ifdef CONFIG_HANDLE_DOMAIN_IRQ
  3. /**
  4. * __handle_domain_irq - Invoke the handler for a HW irq belonging to a domain
  5. * @domain: The domain where to perform the lookup
  6. * @hwirq: The HW irq number to convert to a logical one
  7. * @lookup: Whether to perform the domain lookup or not
  8. * @regs: Register file coming from the low-level handling code
  9. *
  10. * Returns: 0 on success, or -EINVAL if conversion has failed
  11. */
  12. int __handle_domain_irq(struct irq_domain *domain, unsignedint hwirq,
  13. bool lookup, struct pt_regs *regs)
  14. {
  15. structpt_regs *old_regs = set_irq_regs(regs);
  16. unsignedint irq = hwirq;
  17. int ret = 0;
  18. irq_enter();
  19. #ifdef CONFIG_IRQ_DOMAIN
  20. if (lookup)
  21. irq = irq_find_mapping(domain, hwirq);
  22. #endif
  23. /*
  24. * Some hardware gives randomly wrong interrupts. Rather
  25. * than crashing, do something sensible.
  26. */
  27. if (unlikely(!irq || irq >= nr_irqs)) {
  28. ack_bad_irq(irq);
  29. ret = -EINVAL;
  30. } else {
  31. generic_handle_irq(irq);
  32. }
  33. irq_exit();
  34. set_irq_regs(old_regs);
  35. return ret;
  36. }
  37. #endif

7. generic_handle_irq

  1. //kernel/irq/irqdesc.c
  2. /**
  3. * generic_handle_irq - Invoke the handler for a particular irq
  4. * @irq: The irq number to handle
  5. *
  6. */
  7. intgeneric_handle_irq(unsignedint irq)
  8. {
  9. structirq_desc *desc = irq_to_desc(irq);
  10. if (!desc)
  11. return -EINVAL;
  12. generic_handle_irq_desc(desc);
  13. return0;
  14. }
  15. EXPORT_SYMBOL_GPL(generic_handle_irq);

8. generic_handle_irq_desc

  1. //include/linux/irqdesc.h
  2. /*
  3. * Architectures call this to let the generic IRQ layer
  4. * handle an interrupt.
  5. */
  6. staticinlinevoidgeneric_handle_irq_desc(struct irq_desc *desc)
  7. {
  8. /*
  9. *handle_irq在struct domain的struct domain_ops的map函数中调用
  10. *irq_set_chip_and_handler赋值为handle_level_irq
  11. */
  12. desc->handle_irq(desc);
  13. }
  14. intgeneric_handle_irq(unsignedint irq);

9. handle_level_irq

  1. //kernel/irq/chip.c
  2. /**
  3. * handle_level_irq - Level type irq handler
  4. * @desc: the interrupt description structure for this irq
  5. *
  6. * Level type interrupts are active as long as the hardware line has
  7. * the active level. This may require to mask the interrupt and unmask
  8. * it after the associated handler has acknowledged the device, so the
  9. * interrupt line is back to inactive.
  10. */
  11. voidhandle_level_irq(struct irq_desc *desc)
  12. {
  13. raw_spin_lock(&desc->lock);
  14. mask_ack_irq(desc);
  15. if (!irq_may_run(desc))
  16. goto out_unlock;
  17. desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
  18. kstat_incr_irqs_this_cpu(desc);
  19. /*
  20. * If its disabled or no action available
  21. * keep it masked and get out of here
  22. */
  23. if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
  24. desc->istate |= IRQS_PENDING;
  25. goto out_unlock;
  26. }
  27. handle_irq_event(desc);
  28. cond_unmask_irq(desc);
  29. out_unlock:
  30. raw_spin_unlock(&desc->lock);
  31. }
  32. EXPORT_SYMBOL_GPL(handle_level_irq);
  33. //kernel/irq/chip.c
  34. staticinlinevoidmask_ack_irq(struct irq_desc *desc)
  35. {
  36. /*desc->irq_data.chip在struct domain的struct domain_ops的map函数中调用
  37. *irq_set_chip_and_handler赋值为vic_chip.
  38. */
  39. if (desc->irq_data.chip->irq_mask_ack)
  40. desc->irq_data.chip->irq_mask_ack(&desc->irq_data);
  41. else {
  42. desc->irq_data.chip->irq_mask(&desc->irq_data);
  43. if (desc->irq_data.chip->irq_ack)
  44. desc->irq_data.chip->irq_ack(&desc->irq_data);
  45. }
  46. irq_state_set_masked(desc);
  47. }
  48. //include/linux/irq.h
  49. /**
  50. * struct irq_chip - hardware interrupt chip descriptor
  51. *
  52. * @name: name for /proc/interrupts
  53. * @irq_startup: start up the interrupt (defaults to ->enable if NULL)
  54. * @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
  55. * @irq_enable: enable the interrupt (defaults to chip->unmask if NULL)
  56. * @irq_disable: disable the interrupt
  57. * @irq_ack: start of a new interrupt
  58. * @irq_mask: mask an interrupt source
  59. * @irq_mask_ack: ack and mask an interrupt source
  60. * @irq_unmask: unmask an interrupt source
  61. * @irq_eoi: end of interrupt
  62. * @irq_set_affinity: set the CPU affinity on SMP machines
  63. * @irq_retrigger: resend an IRQ to the CPU
  64. * @irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
  65. * @irq_set_wake: enable/disable power-management wake-on of an IRQ
  66. * @irq_bus_lock: function to lock access to slow bus (i2c) chips
  67. * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips
  68. * @irq_cpu_online: configure an interrupt source for a secondary CPU
  69. * @irq_cpu_offline: un-configure an interrupt source for a secondary CPU
  70. * @irq_suspend: function called from core code on suspend once per
  71. * chip, when one or more interrupts are installed
  72. * @irq_resume: function called from core code on resume once per chip,
  73. * when one ore more interrupts are installed
  74. * @irq_pm_shutdown: function called from core code on shutdown once per chip
  75. * @irq_calc_mask: Optional function to set irq_data.mask for special cases
  76. * @irq_print_chip: optional to print special chip info in show_interrupts
  77. * @irq_request_resources: optional to request resources before calling
  78. * any other callback related to this irq
  79. * @irq_release_resources: optional to release resources acquired with
  80. * irq_request_resources
  81. * @irq_compose_msi_msg: optional to compose message content for MSI
  82. * @irq_write_msi_msg: optional to write message content for MSI
  83. * @irq_get_irqchip_state: return the internal state of an interrupt
  84. * @irq_set_irqchip_state: set the internal state of a interrupt
  85. * @irq_set_vcpu_affinity: optional to target a vCPU in a virtual machine
  86. * @flags: chip specific flags
  87. */
  88. structirq_chip {
  89. constchar *name;
  90. unsignedint(*irq_startup)(struct irq_data *data);
  91. void (*irq_shutdown)(struct irq_data *data);
  92. void (*irq_enable)(struct irq_data *data);
  93. void (*irq_disable)(struct irq_data *data);
  94. void (*irq_ack)(struct irq_data *data);
  95. void (*irq_mask)(struct irq_data *data);
  96. void (*irq_mask_ack)(struct irq_data *data);
  97. void (*irq_unmask)(struct irq_data *data);
  98. void (*irq_eoi)(struct irq_data *data);
  99. int (*irq_set_affinity)(struct irq_data *data, conststruct cpumask *dest, bool force);
  100. int (*irq_retrigger)(struct irq_data *data);
  101. int (*irq_set_type)(struct irq_data *data, unsignedint flow_type);
  102. int (*irq_set_wake)(struct irq_data *data, unsignedint on);
  103. void (*irq_bus_lock)(struct irq_data *data);
  104. void (*irq_bus_sync_unlock)(struct irq_data *data);
  105. void (*irq_cpu_online)(struct irq_data *data);
  106. void (*irq_cpu_offline)(struct irq_data *data);
  107. void (*irq_suspend)(struct irq_data *data);
  108. void (*irq_resume)(struct irq_data *data);
  109. void (*irq_pm_shutdown)(struct irq_data *data);
  110. void (*irq_calc_mask)(struct irq_data *data);
  111. void (*irq_print_chip)(struct irq_data *data, struct seq_file *p);
  112. int (*irq_request_resources)(struct irq_data *data);
  113. void (*irq_release_resources)(struct irq_data *data);
  114. void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
  115. void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);
  116. int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
  117. int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);
  118. int (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);
  119. unsignedlong flags;
  120. };
  121. //drivers/irqchip/irq-vic.c 在
  122. staticstructirq_chip vic_chip = {
  123. .name = "VIC",
  124. .irq_ack = vic_ack_irq,
  125. .irq_mask = vic_mask_irq,
  126. .irq_unmask = vic_unmask_irq,
  127. .irq_set_wake = vic_set_wake,
  128. };
  129. staticvoidvic_ack_irq(struct irq_data *d)
  130. {
  131. void __iomem *base = irq_data_get_irq_chip_data(d);
  132. unsignedint irq = d->hwirq;
  133. writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);
  134. /* moreover, clear the soft-triggered, in case it was the reason */
  135. writel(1 << irq, base + VIC_INT_SOFT_CLEAR);
  136. }
  137. staticvoidvic_mask_irq(struct irq_data *d)
  138. {
  139. void __iomem *base = irq_data_get_irq_chip_data(d);
  140. unsignedint irq = d->hwirq;
  141. writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);
  142. }
  143. staticvoidvic_unmask_irq(struct irq_data *d)
  144. {
  145. void __iomem *base = irq_data_get_irq_chip_data(d);
  146. unsignedint irq = d->hwirq;
  147. writel(1 << irq, base + VIC_INT_ENABLE);
  148. }
  149. #if defined(CONFIG_PM)
  150. staticstructvic_device *vic_from_irq(unsignedint irq)
  151. {
  152. structvic_device *v = vic_devices;
  153. unsignedint base_irq = irq & ~31;
  154. int id;
  155. for (id = 0; id < vic_id; id++, v++) {
  156. if (v->irq == base_irq)
  157. return v;
  158. }
  159. returnNULL;
  160. }
  161. staticintvic_set_wake(struct irq_data *d, unsignedint on)
  162. {
  163. structvic_device *v = vic_from_irq(d->irq);
  164. unsignedint off = d->hwirq;
  165. u32 bit = 1 << off;
  166. if (!v)
  167. return -EINVAL;
  168. if (!(bit & v->resume_sources))
  169. return -EINVAL;
  170. if (on)
  171. v->resume_irqs |= bit;
  172. else
  173. v->resume_irqs &= ~bit;
  174. return0;
  175. }
  176. #else
  177. #define vic_set_wake NULL
  178. #endif/* CONFIG_PM */
  179. //include/linux/irq.h
  180. staticinlinevoid *irq_data_get_irq_chip_data(struct irq_data *d)
  181. {
  182. /*
  183. *d->chip_data在struct domain的struct domain_ops的map函数中调用
  184. *irq_set_chip_data赋值为中断控制器寄存器的虚拟地址
  185. *
  186. */
  187. return d->chip_data;
  188. }

10. handle_irq_event

  1. //kernel/irq/handle.c
  2. irqreturn_thandle_irq_event(struct irq_desc *desc)
  3. {
  4. irqreturn_t ret;
  5. desc->istate &= ~IRQS_PENDING;
  6. irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
  7. raw_spin_unlock(&desc->lock);
  8. ret = handle_irq_event_percpu(desc);
  9. raw_spin_lock(&desc->lock);
  10. irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
  11. return ret;
  12. }

11. handle_irq_event_percpu:

  1. //kernel/irq/handle.c
  2. irqreturn_thandle_irq_event_percpu(struct irq_desc *desc)
  3. {
  4. irqreturn_t retval = IRQ_NONE;
  5. unsignedint flags = 0, irq = desc->irq_data.irq;
  6. structirqaction *action = desc->action;
  7. /* action might have become NULL since we dropped the lock */
  8. while (action) {
  9. irqreturn_t res;
  10. trace_irq_handler_entry(irq, action);
  11. /*
  12. *action->handler处理函数为驱动中注册的处理函数
  13. */
  14. res = action->handler(irq, action->dev_id);
  15. trace_irq_handler_exit(irq, action, res);
  16. if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts

    ",

  17. irq, action->handler))
  18. local_irq_disable();
  19. switch (res) {
  20. case IRQ_WAKE_THREAD:
  21. /*
  22. * Catch drivers which return WAKE_THREAD but
  23. * did not set up a thread function
  24. */
  25. if (unlikely(!action->thread_fn)) {
  26. warn_no_thread(irq, action);
  27. break;
  28. }
  29. __irq_wake_thread(desc, action);
  30. /* Fall through to add to randomness */
  31. case IRQ_HANDLED:
  32. flags |= action->flags;
  33. break;
  34. default:
  35. break;
  36. }
  37. retval |= res;
  38. action = action->next;
  39. }
  40. add_interrupt_randomness(irq, flags);
  41. if (!noirqdebug)
  42. note_interrupt(desc, retval);
  43. return retval;
  44. }

【作者】陈金

【出处】https://www.cnblogs.com/yifeichongtian2021/

【云海天】https://www.cnblogs.com/yifeichongtian2021/

本文版权归作者和云海天共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

以上是 LinuxARM中断后的处理(5)【转】 的全部内容, 来源链接: utcz.com/z/520014.html

回到顶部