从这篇文章开始,来聊一聊中断子系统。中断是处理器用于异步处理外围设备请求的一种机制,可以说中断处理是操作系统管理外围设备的基石,此外系统调度、核间交互等都离不开中断,它的重要性不言而喻。
来一张概要的分层图:
中断子系统系列文章,会包括硬件相关、中断框架层、上半部与下半部、Softirq、Workqueue等机制的介绍,本文会先介绍硬件相关的原理及驱动,前戏结束,直奔主题。
来一张功能版的框图:
再来一张细节图看看Distributor和CPU Interface的功能:
中断处理的状态机如下图:
GIC检测中断流程如下:
更多linux内核视频教程文档资料免费领取后台私信【内核】自行获取.
ARM平台的设备信息,都是通过Device Tree设备树来添加,设备树信息放置在arch/arm64/boot/dts/下
下图就是一个中断控制器的设备树信息:
设备树的信息,是怎么添加到系统中的呢?Device Tree最终会编译成dtb文件,并通过Uboot传递给内核,在内核启动后会将dtb文件解析成device_node结构。关于设备树的相关知识,本文先不展开,后续再找机会补充。来一张图,先简要介绍下关键路径:
GIC驱动的执行流程如下图所示:
先来张图:
还是上一下具体的数据结构代码吧,关键注释如下:
struct irq_chip {
struct device *parent_device; //指向父设备
const char *name; // /proc/interrupts中显示的名字
unsigned int (*irq_startup)(struct irq_data *data); //启动中断,如果设置成NULL,则默认为enable
void (*irq_shutdown)(struct irq_data *data); //关闭中断,如果设置成NULL,则默认为disable
void (*irq_enable)(struct irq_data *data); //中断使能,如果设置成NULL,则默认为chip->unmask
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); //在SMP中设置CPU亲和力
int (*irq_retrigger)(struct irq_data *data); //重新发送中断到CPU
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_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);
void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);
void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);
unsigned long flags;
};
struct irq_domain {
struct list_head link; //用于添加到全局链表irq_domain_list中
const char *name; //IRQ domain的名字
const struct irq_domain_ops *ops; //IRQ domain映射操作函数集
void *host_data; //在GIC驱动中,指向了irq_gic_data
unsigned int flags;
unsigned int mapcount; //映射中断的个数
/* Optional data */
struct fwnode_handle *fwnode;
enum irq_domain_bus_token bus_token;
struct irq_domain_chip_generic *gc;
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
struct irq_domain *parent; //支持级联的话,指向父设备
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
struct dentry *debugfs_file;
#endif
/* reverse map data. The linear map gets appended to the irq_domain */
irq_hw_number_t hwirq_max; //IRQ domain支持中断数量的最大值
unsigned int revmap_direct_max_irq;
unsigned int revmap_size; //线性映射的大小
struct radix_tree_root revmap_tree; //Radix Tree映射的根节点
unsigned int linear_revmap[]; //线性映射用到的查找表
};
struct irq_domain_ops {
int (*match)(struct irq_domain *d, struct device_node *node,
enum irq_domain_bus_token bus_token); // 用于中断控制器设备与IRQ domain的匹配
int (*select)(struct irq_domain *d, struct irq_fwspec *fwspec,
enum irq_domain_bus_token bus_token);
int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw); //用于硬件中断号与Linux中断号的映射
void (*unmap)(struct irq_domain *d, unsigned int virq);
int (*xlate)(struct irq_domain *d, struct device_node *node,
const u32 *intspec, unsigned int intsize,
unsigned long *out_hwirq, unsigned int *out_type); //通过device_node,解析硬件中断号和触发方式
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
/* extended V2 interfaces to support hierarchy irq_domains */
int (*alloc)(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs, void *arg);
void (*free)(struct irq_domain *d, unsigned int virq,
unsigned int nr_irqs);
void (*activate)(struct irq_domain *d, struct irq_data *irq_data);
void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data);
int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec,
unsigned long *out_hwirq, unsigned int *out_type);
#endif
};
IRQ domain用于将硬件的中断号,转换成Linux系统中的中断号(virtual irq, virq),来张图:
三种映射的方式如下图:
代码比较简单,如下:
/*
* Interrupt handling.
*/
.macro irq_handler
ldr_l x1, handle_arch_irq
mov x0, sp
irq_stack_entry
blr x1
irq_stack_exit
.endm
来张图:
首页 - 内核技术中文网 - 构建全国最权威的内核技术交流分享论坛
转载地址:深入分析Linux中断子系统之中断控制器及驱动 - 圈点 - 内核技术中文网 - 构建全国最权威的内核技术交流分享论坛
页面更新:2024-04-20
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号