目录
- 前言
- 一、整体介绍
- 二、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之间会有转换函数,gic处理是基于硬中断号处理的;
中断状态
中断的生命周期包括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 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函数等位于此文件
include/linux/irqdesc.h:
include/linux/irq.h:
include/linux/irqdomain.h
kernel/irq/目录下
kernel/irq/affinity.c
kernel/irq/devres.c
kernel/irq/irqdesc.c:irqdesc结构处理
kernel/irq/pm.c
kernel/irq/autoprobe.c
kernel/irq/dummychip.c
kernel/irq/irqdomain.c
kernel/irq/proc.c
kernel/irq/chip.c
kernel/irq/handle.c :
kernel/irq/manage.c
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
drivers/irqchip/irq-gic-v3.c:解析设备树后使用gic的寄存器等信息进行gic的初始化
数据结构
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_HIERARCHYstruct irq_data *parent_data;
#endifvoid *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_HIERARCHYstruct irq_domain *parent;
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFSstruct 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开始的逻辑号;
/proc/irq/XX/一些信息,可以看到中断的信息,XX为本机所有中断从0开始的逻辑号;
五、中断的扩展
底半部机制
1、工作队列
2、tasklet
3、软中断