linux内核驱动:GIC中断总结

前言

本文基于linux5.10.xxx总结gic使用,gic版本为gicv3,包括gic结构、驱动代码、使用等,等,处理器架构为基于armv8指令集的cortex-A72内核的SOC,参考gicv3&v3技术规范手册和cortex-a72架构手册;

一、整体介绍

GICv3架构旨在与Armv8-A和Armv8-R兼容的处理元件PE一起运行。
通用中断控制器(GIC)架构定义了:
•处理连接到GIC的任何PE的所有中断源的架构要求。
•适用于单处理器或多处理器系统的通用中断控制器编程接口。
GIC是一种支持和控制中断的架构资源。它提供:
•用于管理中断源、中断行为以及将中断路由到一个或多个PE的寄存器。
•支持:
–Armv8架构。
–特定于本地的外围中断(LPI)。
–专用外围中断(PPI)。
–软件生成的中断(SGI)。
–共享外围中断(SPI)。
–中断屏蔽和优先级排序。
–单处理器和多处理器系统。
–电源管理环境中的唤醒事件。
对于每个PE,GIC架构描述了如何从不同类型的PE生成IRQ和FIQ中断系统内的中断。Armv8-A异常模型描述了PE如何处理这些IRQ和FIQ中断

中断处理还取决于Armv8架构的其他方面,例如安全状态和支持用于虚拟化。Arm架构提供两种安全状态,每种状态都有一个相关的物理内存地址空间:
•安全状态。
•不安全状态。
GIC架构支持路由和处理与两种安全状态相关的中断。
GIC架构支持Armv8模型,用于处理与虚拟中断相关的虚拟中断机器、VM。虚拟化系统具有:
•管理程序必须包括一个在EL2上执行的组件,负责在EL2和EL2之间进行切换VM。
•几个虚拟机在EL1上执行。
•在VM上以EL0执行的应用程序。

–以上来自《Arm® Generic Interrupt Controller
Architecture Specification
GIC architecture version 3 and version 4》

二、GIC的模块功能说明

逻辑框图
在这里插入图片描述
GICv3架构由一组逻辑组件组成:
•中断分配器
•中断再分配器,每个CPU核对应一个
•GIC接口控制器,每个CPU核对应一个
•中断务组件(ITS),支持可选的传输事件给LPI(局部外设中断)

GIC和具体处理器的关系可以参考下图:
在这里插入图片描述
GIC的CPU接口部分可以理解成在处理器内部,通过总线如AXI和GIC其它部分通信,实际操作中会通过系统寄存器中一些寄存器去控制GIC的CPU INTERFACE部分 ;

中断类型
GICv3支持Locality-specific Peripheral Interrupt (LPI)本地特定外设中断、Software Generated Interrupt (SGI)软件触发中断、Private Peripheral Interrupt (PPI)私有外设中断、Shared Peripheral Interrupt (SPI)共享外设中断;
中断号范围一般SGI为0~15,PPI为16 ~31,SPI为32 ~某个数值具体支持数量看SOC设计的需求;
在linux系统下设备树下会配置中断号为irq和gic支持的中断号hwirq之间会有转换函数,具体看设备树写法,

写法一 #interrupt-cells = <3> ,其它驱动节点引用时 interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>,hwirq = 100+偏移,GIC_SPI 一般表示偏移为32;
写法二 #interrupt-cells = <2> ,其它驱动节点引用时 interrupts = <100 IRQ_TYPE_LEVEL_HIGH>,hwirq = 100+偏移,这里偏移一般为0;
gic驱动中还有一个virq这是linux内核在软件层面的定义会和 hwirq通过数据结构进行map 

中断状态
中断的生命周期包括Inactive、pending、active、active and pending状态,处理状态机如下,
在这里插入图片描述
中断寄存器
GIC的几个部件由相应的寄存器来进行控制,通过寄存器去对中断进行使能、失能、优先级配置、优先级分组等的处理
寄存器命名规则
所有GIC寄存器的名称都为寄存器的功能提供了一个简短的助记符(前缀):
•内存映射寄存器前缀为以下之一:
–GICC,表示CPU接口寄存器。
–GICD,表示分配器寄存器。
–GICH,表示虚拟接口控制寄存器,通常由管理程序访问。
–GICR,表示再分发器寄存器。
–GICV,表示虚拟CPU接口寄存器。
–GITS,表示ITS寄存器。
•系统寄存器前缀为:
–ICC表示物理GIC CPU接口系统寄存器。
–ICV用于指示虚拟GIC CPU接口系统寄存器。
–ICH表示虚拟接口控制系统寄存器。

GIC分配器寄存器
在这里插入图片描述
在这里插入图片描述
GIC再分配器寄存器

在这里插入图片描述
GIC的CPU interface寄存器
在这里插入图片描述

gic相关的系统寄存器
在这里插入图片描述
在这里插入图片描述

系统寄存器和gic内存映射寄存器中GIC cpu interface寄存器的关系
在这里插入图片描述
规范手册中有句描述如下:
在这里插入图片描述
意思为同时访问以上表格中对应的内存映射映射部分的寄存器和系统寄存器可能导致状态不一致现象,arm推荐通过设置后,访问系统寄存器来代替表格中内存映射的寄存器(备注 内存映射的寄存器可以理解成被SOC实现为外设的一部分可以看到寄存器对应的外设地址,而系统寄存器只能看到名字),代码中使用如下宏访问系统寄存器

//in file arch_gicv3.h
#define read_gicreg(r)			read_sysreg_s(SYS_ ## r)
#define write_gicreg(v, r)		write_sysreg_s(v, SYS_ ## r)

//in file  sysreg.h
#define read_sysreg_s(r) ({						\
	u64 __val;							\
	asm volatile(__mrs_s("%0", r) : "=r" (__val));			\
	__val;								\
})
#define write_sysreg_s(v, r) do {					\
	u64 __val = (u64)(v);						\
	asm volatile(__msr_s(r, "%x0") : : "rZ" (__val));		\
} while (0)

三、函数接口、数据结构和驱动文件

驱动文件

头文件
include/linux/interrupt.h: devm_request_irq函数等位于此文件,结构struct irqaction定义,中断标识定义如共享中断等,一些内联函数;
include/linux/irqdesc.h:主要定义struct irq_desc和一些内联函数;
include/linux/irq.h: struct irq_data的定义和一些内联函数,中断触发类型的枚举定义
include/linux/irqdomain.h:定义struct irqdomain和 struct irq_domain_ops,一些内联函数定义;

kernel/irq/目录下
kernel/irq/affinity.c : 中断和cpu亲和性设置接口实现;
kernel/irq/devres.c
kernel/irq/irqdesc.c:struct irqdesc结构的分配、初始化、成员等处理;
kernel/irq/pm.c:中断相关的电源管理;
kernel/irq/autoprobe.c
kernel/irq/dummychip.c
kernel/irq/irqdomain.c:struct irqdomain结构的分配、初始化等 ;
kernel/irq/proc.c
kernel/irq/chip.c
kernel/irq/handle.c : 中断执行最终的中断服务处理接口,如遍历action的操作;
kernel/irq/manage.c: 以irq为入参的一些操作函数、亲和性设置的函数、struct irqdesc和struct action绑定结构等;
kernel/irq/resend.c
kernel/irq/cpuhotplug.c
kernel/irq/ipi.c
kernel/irq/msi.c
kernel/irq/spurious.c

drivers/irqchip/目录下
drivers/irqchip/irqchip.c: gic节点的platform_driver的注册
drivers/irqchip/irq-gic-common.c: gic一些通用接口;
drivers/irqchip/irq-gic-v3.c:解析设备树后使用gic的寄存器等信息进行gic的初始化,irq_chip结构对gic寄存器的钩子函数实现;

arch/arm64/kernel/irq.c
函数init_IRQ实现,由start_kernel 最开始调用,start_kernel -> init_IRQ -> irqchip_init(drivers/irqchip/irqchip.c)

数据结构

struct irqaction
中断动作描述符,表示一个动作或者处理过程,作为struct irq_desc结构的一个指针成员,定义于include/linux/interrupt.h

struct irqaction {
	irq_handler_t		handler; //中断服务函数
	void			*dev_id; //当对同一个中断号进行中断请求,中断标识为shared时这个字段需要明确定义
	void __percpu		*percpu_dev_id;
	struct irqaction	*next; //当同一个中断号被申请多次中断时,中断服务会次形成链表
	irq_handler_t		thread_fn; //为线程化的中断服务函数,
	struct task_struct	*thread;
	struct irqaction	*secondary;
	unsigned int		irq; //中断号
	unsigned int		flags;
	unsigned long		thread_flags;
	unsigned long		thread_mask;
	const char		*name;
	struct proc_dir_entry	*dir;
} ____cacheline_internodealigned_in_smp;

struct irq_desc
一个中断号会对应一个这个结构,实际上有个irq_desc irq_desc[]的数组,数组的下标索引为中断号,定义于include/linux/irqdesc.h

struct irq_desc {
	struct irq_common_data	irq_common_data;
	struct irq_data		irq_data;  //具体中断的数据,如irq  hwirq  irqchip等
	unsigned int __percpu	*kstat_irqs;
	irq_flow_handler_t	handle_irq;
	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		tot_count;
	unsigned int		irq_count;	/* For detecting broken IRQs */
	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
	unsigned int		irqs_unhandled;
	atomic_t		threads_handled;
	int			threads_handled_last;
	raw_spinlock_t		lock;
	struct cpumask		*percpu_enabled;
	const struct cpumask	*percpu_affinity;
    //。。。。。。。。省略。。。。。。。
    //。。。。。。。。省略。。。。。。。
	const char		*name;
} ____cacheline_internodealigned_in_smp;

struct irq_data
对应一个中断号的数据描述,定义于include/linux/irq.h,是irq_desc 的一个数据成员

struct irq_data {
	u32			mask;
	unsigned int		irq; //中断号
	unsigned long		hwirq;  //硬件中断号
	struct irq_common_data	*common;
	struct irq_chip		*chip; //底层硬件表示,表示一个中断控制器
	struct irq_domain	*domain;
#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
	struct irq_data		*parent_data;
#endif
	void			*chip_data;
};

struct irq_chip
表示一个中断控制器,内部包括具体中断控制器需要实现的函数,定义于include/linux/irq.h ,是irq_data的一个指针成员

struct irq_chip {
	struct device	*parent_device;
	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_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);
	int		(*irq_nmi_setup)(struct irq_data *data);
	void		(*irq_nmi_teardown)(struct irq_data *data);
	unsigned long	flags;
};

struct irq_domain
对于中断控制器支持的所有中断进行管理,定义于include/linux/irqdomain.h

struct irq_domain {
	struct list_head link;
	const char *name;
	const struct irq_domain_ops *ops; //操作函数级
	void *host_data;
	unsigned int flags;
	unsigned int mapcount;

	/* Optional data */
	struct fwnode_handle *fwnode; //中断控制器的设备树节点dev_node 中的数组成员
	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; //硬件中断号最大编号,从0开始
	unsigned int revmap_direct_max_irq;
	unsigned int revmap_size;
	struct radix_tree_root revmap_tree; //对所有中断进行管理,键值为硬件中断号 
	struct mutex revmap_tree_mutex;
	unsigned int linear_revmap[];
};

四、中断使用流程

初始化流程
gic初始化如下
在这里插入图片描述

应用请求irq初始化
在这里插入图片描述
中断处理流程
在这里插入图片描述

在这里插入图片描述
中断的调试方式
cat /proc/interupts 显示硬件中断号,在各个CPU核上发生的情况;
sys/kernel/irq/XX/hwirq,可以查看到硬件中断号,XX为本机所有中断从0开始的逻辑中断号virq;
/proc/irq/XX/一些信息,可以看到中断的信息,XX为本机所有中断从0开始的逻辑中断号virq;

五、中断的扩展

底半部机制
1、工作队列
相关数据结构、函数定义于文件include/linux/workqueue.h
使用时创建句柄、绑定句柄的处理函数、在中断服务函数中调度
2、tasklet
相关数据结构、函数定义于文件include/linux/interrupt.h
使用时创建句柄、绑定句柄的处理函数、在中断服务函数中调度
3、软中断
相关数据结构、函数定义于文件include/linux/interrupt.h
4、中断线程化接口

  • 13
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值