Linux中断子系统:级联中断控制器驱动
Linux中断子系统
Linux中断子系统是个很大的话题,如下面的思维导图所示,包含硬件、驱动、中断上半部、中断下半部等等。本文着眼于中断控制器(PIC),特别是级联中断控制器驱动部分,对驱动的设计和调试要点进行分析总结。
级联中断控制器驱动
中断控制器的核心对象是irq_chip,其提供了很多接口,一般情况下不需要全部实现。下面是几个类似的接口的差别介绍:
- irq_enable:在中断注册或使能时调用,一般在使能中断前需要清除中断状态。
- irq_disable:在中断注销或关闭时调用,一般在关闭中断前不清除中断状态。
- irq_mask_ack:在进入中断处理函数前调用,一般在屏蔽中断前需要清除中断源信号。
- irq_unmask:在退出中断处理函数后调用,一般在去屏蔽中断前不清除清除中断源信号。
级联中断控制器的初始化流程
中断控制器的级联如上图所示。级联中断控制器的初始化流程分为2部分:根中断控制器的初始化和子中断控制器的初始化。
根中断控制器相关部分
- 根据irq获取根PIC的irt;
- 将irt/irq的映射关系配置到根PIC的IRT表寄存器;
- 初始化根PIC的irq_chip接口对象;
- 调用irq_set_chip_and_handler设置根PIC irq的irq_chip及其handler;
- 调用irq_set_chip_data设置根PIC irq_chip接口调用时的环境对象;
子中断控制器相关部分
- 初始化子PIC的irq_chip接口对象;
- 映射子PIC io mem空间,以便后续访问其寄存器;
- 获取PIC的irq domain的irq基值,并调用irq_domain_add_xxx获取子PIC的中断域空间;
- 调用irq_set_chip_and_handler设置子PIC irq的irq_chip及其handler;
- 调用irq_set_chip_data设置子PIC irq_chip接口调用时的环境对象;
- 获取根PIC的irq;
- 调用irq_set_chained_handler设置根PIC irq handler;
- 调用irq_set_handler_data设置根PIC irq handler调用时的环境对象;
级联中断控制器的中断处理流程
首先,CPU只能感知到root PIC的中断,当root PIC的中断触发后,进入root irq handler,在handler中找到触发中断的sub irq,并调用其handler进行中断处理。换句话说,root irq的handler是root PIC的中断路由功能的软件扩展。
找到sub irq的方法依赖sub PIC具体硬件实现,以GPIO中断控制器的root irq handler为例,其方法如下:
- 调用chained_irq_enter,mask_ack root irq;
- 获取gpio 中断pin脚使能情况;
- 读取gpio interrupt status寄存器,判断此gpio 中断使能pin脚是否触发了中断;
- 通过irq_find_mapping获取此gpio pin对应的irq#;
- 通过generic_handle_irq调用 sub irq handler来进行gpio中断处理;
- 调用chained_irq_exit,unmask root irq。
中断控制器驱动调试要点
- 根据中断控制器特点,仔细设计irq_chip的接口。那么哪些需要实现,哪些又不需要是需要仔细考虑;
- root irq的handler要用chained_irq_enter/chained_irq_exit进行保护;
- 理顺中断触发时序。比如设备、子PIC的中断状态、中断源信号等中断标记清除顺序,以此来决定在哪些irq_chip接口中清除中断标记;
- 一般来说,对于任何中断设备,在使能中断前需要清除中断状态,避免垃圾中断干扰。
附关键数据结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | /** * 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 * @irq_resume: function called from core code on resume once per chip * @irq_pm_shutdown: function called from core code on shutdown once per chip * @irq_print_chip: optional to print special chip info in show_interrupts * @flags: chip specific flags */ struct irq_chip { const char *name; unsigned int (*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, const struct cpumask *dest, bool force); int (*irq_retrigger)( struct irq_data *data); int (*irq_set_type)( struct irq_data *data, unsigned int flow_type); int (*irq_set_wake)( struct irq_data *data, unsigned int 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_print_chip)( struct irq_data *data, struct seq_file *p); unsigned long flags; }; /** * struct irq_data - per irq and irq chip data passed down to chip functions * @irq: interrupt number * @hwirq: hardware interrupt number, local to the interrupt domain * @node: node index useful for balancing * @state_use_accessors: status information for irq chip functions. * Use accessor functions to deal with it * @chip: low level interrupt hardware access * @domain: Interrupt translation domain; responsible for mapping * between hwirq number and linux irq number. * @handler_data: per-IRQ data for the irq_chip methods * @chip_data: platform-specific per-chip private data for the chip * methods, to allow shared chip implementations * @msi_desc: MSI descriptor * @affinity: IRQ affinity on SMP * * The fields here need to overlay the ones in irq_desc until we * cleaned up the direct references and switched everything over to * irq_data. */ struct irq_data { unsigned int irq; unsigned long hwirq; unsigned int node; unsigned int state_use_accessors; struct irq_chip *chip; struct irq_domain *domain; void *handler_data; void *chip_data; struct msi_desc *msi_desc; cpumask_var_t affinity; }; /** * struct irq_desc - interrupt descriptor * @irq_data: per irq and chip data passed down to chip functions * @kstat_irqs: irq stats per cpu * @handle_irq: highlevel irq-events handler * @preflow_handler: handler called before the flow handler (currently used by sparc) * @action: the irq action chain * @status: status information * @core_internal_state__do_not_mess_with_it: core internal status information * @depth: disable-depth, for nested irq_disable() calls * @wake_depth: enable depth, for multiple irq_set_irq_wake() callers * @irq_count: stats field to detect stalled irqs * @last_unhandled: aging timer for unhandled count * @irqs_unhandled: stats field for spurious unhandled interrupts * @lock: locking for SMP * @affinity_hint: hint to user space for preferred irq affinity * @affinity_notify: context for notification of affinity changes * @pending_mask: pending rebalanced interrupts * @threads_oneshot: bitfield to handle shared oneshot threads * @threads_active: number of irqaction threads currently running * @wait_for_threads: wait queue for sync_irq to wait for threaded handlers * @dir: /proc/irq/ procfs entry * @name: flow handler name for /proc/interrupts output */ struct irq_desc { struct irq_data irq_data; unsigned int __percpu *kstat_irqs; irq_flow_handler_t handle_irq; #ifdef CONFIG_IRQ_PREFLOW_FASTEOI irq_preflow_handler_t preflow_handler; #endif struct irqaction *action; /* IRQ action list */ unsigned int status_use_accessors; unsigned int core_internal_state__do_not_mess_with_it; unsigned int depth; /* nested irq disables */ unsigned int wake_depth; /* nested wake enables */ unsigned int irq_count; /* For detecting broken IRQs */ unsigned long last_unhandled; /* Aging timer for unhandled count */ unsigned int irqs_unhandled; u64 random_ip; raw_spinlock_t lock; struct cpumask *percpu_enabled; #ifdef CONFIG_SMP const struct cpumask *affinity_hint; struct irq_affinity_notify *affinity_notify; #ifdef CONFIG_GENERIC_PENDING_IRQ cpumask_var_t pending_mask; #endif #endif unsigned long threads_oneshot; atomic_t threads_active; wait_queue_head_t wait_for_threads; #ifdef CONFIG_PROC_FS struct proc_dir_entry *dir; #endif int parent_irq; struct module *owner; const char *name; } ____cacheline_internodealigned_in_smp; |
--EOF--
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?