GICv3驱动初始化
linux驱动支持GICv1, GICv2, GICv3, GICv4驱动,本节我们重点来描述下GICv3的驱动初始化,结合ARM-Cortex平台详细描述
intc: interrupt-controller@666688888 {compatible = "arm,gic-v3";#interrupt-cells = <3>;interrupt-controller;#redistributor-regions = <1>;redistributor-stride = <0x0 0x20000>;reg = <0x666688888 0x10000>, /* GICD */<0x6666e8888 0x100000>; /* GICR * 8 */interrupts = <GIC_PPI 8 IRQ_TYPE_LEVEL_HIGH>;interrupt-parent = <&intc>;
};
这里面有几个重点的字段:可以参考内核文档arm,gic-v3.txt文档,
* ARM Generic Interrupt Controller, version 3AArch64 SMP cores are often associated with a GICv3, providing Private
Peripheral Interrupts (PPI), Shared Peripheral Interrupts (SPI),
Software Generated Interrupts (SGI), and Locality-specific Peripheral
Interrupts (LPI).Main node required properties:- compatible : should at least contain "arm,gic-v3".
- interrupt-controller : Identifies the node as an interrupt controller
- #interrupt-cells : Specifies the number of cells needed to encode aninterrupt source. Must be a single cell with a value of at least 3.If the system requires describing PPI affinity, then the value mustbe at least 4.The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPIinterrupts. Other values are reserved for future use.The 2nd cell contains the interrupt number for the interrupt type.SPI interrupts are in the range [0-987]. PPI interrupts are in therange [0-15].The 3rd cell is the flags, encoded as follows:bits[3:0] trigger type and level flags.1 = edge triggered4 = level triggeredThe 4th cell is a phandle to a node describing a set of CPUs thisinterrupt is affine to. The interrupt must be a PPI, and the nodepointed must be a subnode of the "ppi-partitions" subnode. Forinterrupt types other than PPI or PPIs that are not partitionned,this cell must be zero. See the "ppi-partitions" node descriptionbelow.Cells 5 and beyond are reserved for future use and must have a valueof 0 if present.- reg : Specifies base physical address(s) and size of the GICregisters, in the following order:- GIC Distributor interface (GICD)- GIC Redistributors (GICR), one range per redistributor region- GIC CPU interface (GICC)- GIC Hypervisor interface (GICH)- GIC Virtual CPU interface (GICV)GICC, GICH and GICV are optional.- interrupts : Interrupt source of the VGIC maintenance interrupt.
- compatible: 用于和对应的驱动匹配,不再详说
- interrupt-cells用于描述一个中断源的详细信息,此值等于3代表interrupts中有三个字段
- 第一个字段代表中断类型(GIC_PPI, GIC_SPI)
- 第二个字段物理中断号,根据中断类型中断号的范围不同。SPI(0-987)PPI(0-15)
- 第三个字段代表的中断触发方式
- interrupt-controller: 描述此字段是一个中断控制器
- interrupt-parent: 代表此中断控制器是否是级联的,如果没有此字段,则跟随父字段
- reg描述的是中断控制器中的涉及的寄存器
- 0x666688888 代表的是Distributor的基址寄存器,GICD
- 0x6666e8888 代表的是Redistributor的基址寄存器,GICR
了解了DTS,我们则继续看下对应GICv3的驱动
IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);
大家可以把这个宏展开开下,展开之后如下,展开之后会有一个__irqchip_of_table的段
static const struct of_device_id __of_table_gic_v3__used __section(__irqchip_of_table)= {.compatible = "arm,gic-v3",.data = gic_of_init}
这个段会在链接脚本中有详细的描述,当开机的时候,会去从__irqchip_of_table去读取此,然后做比较
void __init init_IRQ(void)
{init_irq_stacks();irqchip_init();if (!handle_arch_irq)panic("No interrupt controller found.");
}void __init irqchip_init(void)
{of_irq_init(__irqchip_of_table);acpi_probe_device_table(irqchip);
}
最终在of_irq_init函数中根据dts来匹配到正确的中断控制器。匹配到正确的中断控制器后,会调用上面的data回调函数就是gic_of_init,也就是对中断控制器做初始化操作
for_each_matching_node_and_match(np, matches, &match) {if (!of_property_read_bool(np, "interrupt-controller") || //如果不是中断控制器,则跳过!of_device_is_available(np))continue;/** Here, we allocate and populate an of_intc_desc with the node* pointer, interrupt-parent device_node etc.*/desc = kzalloc(sizeof(*desc), GFP_KERNEL);if (WARN_ON(!desc)) {of_node_put(np);goto err;}desc->irq_init_cb = match->data;desc->dev = of_node_get(np);desc->interrupt_parent = of_irq_find_parent(np);if (desc->interrupt_parent == np)desc->interrupt_parent = NULL;list_add_tail(&desc->list, &intc_desc_list);
}
- 找到存在interrupt-controller的字段
- 分配中断控制器描述符,设置中断控制器的irq_init_cb回调函数
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {int ret;if (desc->interrupt_parent != parent)continue;list_del(&desc->list);of_node_set_flag(desc->dev, OF_POPULATED);pr_debug("of_irq_init: init %pOF (%p), parent %p\n",desc->dev,desc->dev, desc->interrupt_parent);ret = desc->irq_init_cb(desc->dev, //回调设置的中断控制器的初始化处理函数desc->interrupt_parent);
这样一来就调用到gic_of_init函数了
static int __init gic_of_init(struct device_node *node, struct device_node *parent)
{void __iomem *dist_base;struct redist_region *rdist_regs;u64 redist_stride;u32 nr_redist_regions;int err, i;dist_base = of_iomap(node, 0);err = gic_validate_dist_version(dist_base);if (err) {pr_err("%pOF: no distributor detected, giving up\n", node);goto out_unmap_dist;}if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions))nr_redist_regions = 1;rdist_regs = kcalloc(nr_redist_regions, sizeof(*rdist_regs),GFP_KERNEL);for (i = 0; i < nr_redist_regions; i++) {struct resource res;int ret;ret = of_address_to_resource(node, 1 + i, &res);rdist_regs[i].redist_base = of_iomap(node, 1 + i);if (ret || !rdist_regs[i].redist_base) {pr_err("%pOF: couldn't map region %d\n", node, i);err = -ENODEV;goto out_unmap_rdist;}rdist_regs[i].phys_base = res.start;}if (of_property_read_u64(node, "redistributor-stride", &redist_stride))redist_stride = 0;err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,redist_stride, &node->fwnode);return 0;
}
- of_iomap获取终端控制器distributor的基址
- gic_validate_dist_version根据基址判断当前是v3还是v4版本
- 读取redisttibutor的属性,获取对应寄存器的基址
- 最终会调用到gic_init_bases函数中做相应的初始化
static int __init gic_init_bases(void __iomem *dist_base, struct redist_region *rdist_regs, u32 nr_redist_regions, u64 redist_stride, struct fwnode_handle *handle)
{u32 typer;int gic_irqs;int err;if (!is_hyp_mode_available())static_branch_disable(&supports_deactivate_key);if (static_branch_likely(&supports_deactivate_key))pr_info("GIC: Using split EOI/Deactivate mode\n");gic_data.fwnode = handle;gic_data.dist_base = dist_base;gic_data.redist_regions = rdist_regs;gic_data.nr_redist_regions = nr_redist_regions;gic_data.redist_stride = redist_stride;/** Find out how many interrupts are supported.* The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)*/typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);gic_data.rdists.gicd_typer = typer;gic_irqs = GICD_TYPER_IRQS(typer);if (gic_irqs > 1020)gic_irqs = 1020;gic_data.irq_nr = gic_irqs;gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,&gic_data);irq_domain_update_bus_token(gic_data.domain, DOMAIN_BUS_WIRED);gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));gic_data.rdists.has_vlpis = true;gic_data.rdists.has_direct_lpi = true;if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) {err = -ENOMEM;goto out_free;}gic_data.has_rss = !!(typer & GICD_TYPER_RSS);pr_info("Distributor has %sRange Selector support\n",gic_data.has_rss ? "" : "no ");if (typer & GICD_TYPER_MBIS) {err = mbi_init(handle, gic_data.domain);if (err)pr_err("Failed to initialize MBIs\n");}set_handle_irq(gic_handle_irq);gic_update_vlpi_properties();if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis() &&!IS_ENABLED(CONFIG_ARM_GIC_V3_ACL))its_init(handle, &gic_data.rdists, gic_data.domain);gic_smp_init();gic_dist_init();gic_cpu_init();gic_cpu_pm_init();return 0;out_free:if (gic_data.domain)irq_domain_remove(gic_data.domain);free_percpu(gic_data.rdists.rdist);return err;
}
- is_hyp_mode_available判断当前是否在Hyp虚拟化模式
- 根据参数初始化gic_data结构
- 通过读取GICD_TYPER寄存器获取到当前GIC支持的最大中断数量。如果中断数量超过1020则赋值最大值为1020.
- irq_domain_create_tree通过此函数来创建一个irq domain,irq doamin就是对中断的区域的管理,用于级联
- set_handle_irq(gic_handle_irq);重点中的重点,用于设置中断处理的回调函数,当中断处理时,首先会调用此函数的
- gic_smp_init 软中断的初始化,设置软中断的回调
- gic_dist_init distributor的初始化
- gic_cpu_init cpu interface的初始化
- gic_cpu_pm_init power相关的初始化
设置中断回调函数
int __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{if (handle_arch_irq)return -EBUSY;handle_arch_irq = handle_irq;return 0;
}
handle_arch_irq会在汇编中调用的,在中断处理流程中详细说明
static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{u32 irqnr;do {irqnr = gic_read_iar();if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {int err;uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);if (static_branch_likely(&supports_deactivate_key))gic_write_eoir(irqnr);elseisb();err = handle_domain_irq(gic_data.domain, irqnr, regs);if (err) {WARN_ONCE(true, "Unexpected interrupt received!\n");if (static_branch_likely(&supports_deactivate_key)) {if (irqnr < 8192)gic_write_dir(irqnr);} else {gic_write_eoir(irqnr);}}continue;}if (irqnr < 16) {uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);gic_write_eoir(irqnr);if (static_branch_likely(&supports_deactivate_key))gic_write_dir(irqnr);
#ifdef CONFIG_SMP/** Unlike GICv2, we don't need an smp_rmb() here.* The control dependency from gic_read_iar to* the ISB in gic_write_eoir is enough to ensure* that any shared data read by handle_IPI will* be read after the ACK.*/handle_IPI(irqnr, regs);
#elseWARN_ONCE(true, "Unexpected SGI received!\n");
#endifcontinue;}} while (irqnr != ICC_IAR1_EL1_SPURIOUS);
}
- 根据上一篇GIC-500的文章最后一小节中描述,先会去读IAR寄存器确定中断号的,软件上是通过gic_read_iar实现的
- 得到中断号会去判断当前是哪种中断类型,当中断号大于15小于1020的话,则此中断属于PPI或者SPI
- 结合会根据irq domain去处理对应的中断handle_domain_irq(gic_data.domain, irqnr, regs);
- 如果中断号小于16,则此中断号是IPI中断,是core之间用于通信的中断,则会调用handle_IPI(irqnr, regs);去处理对应的中断
而linux中用一个irq chip结构体来描述一个中断控制器,irq_chip称为中断控制器描述符
static struct irq_chip gic_chip = {.name = "GICv3",.irq_mask = gic_mask_irq,.irq_unmask = gic_unmask_irq,.irq_eoi = gic_eoi_irq,.irq_set_type = gic_set_type,.irq_set_affinity = gic_set_affinity,.irq_get_irqchip_state = gic_irq_get_irqchip_state,.irq_set_irqchip_state = gic_irq_set_irqchip_state,.flags = IRQCHIP_SET_TYPE_MASKED |IRQCHIP_SKIP_SET_WAKE |IRQCHIP_MASK_ON_SUSPEND,
};
- irq_chip结构相对于是中断控制器在软件上的抽象
- name,中断控制器的名字,可以在/cat /proc/interrupter中查看
- irq_mask: 用于屏蔽中断源
- irq_unmask: 用于取消屏蔽中断源
- irq_eoi: end of interrupter, 用于表明此中断处理完毕
- irq_set_type:设置中断的触发类型
- irq_set_affinity: 设置中断的亲合性
- 等等
当然了irq_chip提供了很多回调函数,大家可以去看irq_chip的定义
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);unsigned long flags;
};
GICv3驱动初始化相关推荐
- GIC/ITS代码分析(2)GICv3驱动初始化
这里我们介绍GICv3驱动的基础框架,后续再介绍GICv4的支持.GICv3驱动在文件drivers/irqchip/irq-gic-v3.c中.同时仅介绍ACPI方式下GICv3驱动. 该驱动由宏I ...
- GIC/ITS代码分析(3)ITS驱动初始化
这里对ITS再作简要介绍.在GICv3中,外设通过写GITS_TRANSLATER,根据所写内容解析出device_id和event_id,根据device_id找到设备所对应的Device Tabl ...
- DPDK pmd驱动初始化(十九)
在没有引入pmd用户态网卡驱动之前, 网卡在收到报文后,网卡驱动会将报文从网卡缓冲区拷贝到内核, 接着内核在把报文拷贝到应用层,整个过程需要2次的拷贝以及系统调用.当应用层需要发送数据时,应用层将报文 ...
- Android 图形驱动初始化
从应用程序的角度看 OpenGL 图形系统的接口,主要包括两大部分,一部分是 EGL,它为 OpenGL 渲染准备环境:另一部分是 OpenGL,它执行图形渲染.通过这些接口构造渲染环境,并执行渲染的 ...
- c++ map 初始化_如何调整Linux内核启动中的驱动初始化顺序?
如何调整Linux内核启动中的驱动初始化顺序?[问题] 此处我要实现的是将芯片的ID用于网卡MAC地址,网卡驱动是enc28j60_init. 但是,读取芯片ID的函数,在as352x_afe_ini ...
- Android 图形驱动初始化(二十三)
从应用程序的角度看 OpenGL 图形系统的接口,主要包括两大部分,一部分是 EGL,它为 OpenGL 渲染准备环境:另一部分是 OpenGL,它执行图形渲染.通过这些接口构造渲染环境,并执行渲染的 ...
- SD卡驱动初始化失败
各位大佬,小弟正在做一个可穿戴设备,数据是采用SD卡存储的,移植了最小文件系统,但出现概率性出现SD卡驱动初始化失败,通过Debug发现问题如下: disk_initialize = STA_NOIN ...
- dpdk pmd驱动初始化
在没有引入pmd用户态网卡驱动之前, 网卡在收到报文后,网卡驱动会将报文从网卡缓冲区拷贝到内核, 接着内核在把报文拷贝到应用层,整个过程需要2次的拷贝以及系统调用.当应用层需要发送数据时,应用层将报文 ...
- 如何调整Linux内核启动中的驱动初始化顺序
[问题] 此处我要实现的是将芯片的ID用于网卡MAC地址,网卡驱动是enc28j60_init. 但是,读取芯片ID的函数,在as352x_afe_init模块中,所以要先初始化as352x_afe_ ...
- BCM以太网驱动初始化简要
环境 BCM6756驱动程序. 驱动初始化 源码路径: bcmdrivers/opensource/net/enet/impl7/ 以下为BCM网卡驱动初始化的概要.初始化工作主要在bcm_enet_ ...
最新文章
- C++异常实现与longjmp, setjmp,栈指针EBP, Active Record
- (四)协同过滤算法之基于用户的推荐算法python实现
- 【C++深度剖析教程28】C++对象模型分析
- mybatis学习笔记四(动态sql)
- python pdf转html代码_python将html转成PDF的实现代码(包含中文)
- java random api_API中的Scanner、Random、ArrayList、String类
- python实现excel的覆盖写入和追加
- matlab linux命令行窗口,linux命令行运行matlab
- 饥荒一直服务器没有响应,饥荒总是启动服务器进不去 | 手游网游页游攻略大全...
- Handler消息机制详解,另对于MessageQueue阻塞线程的详解
- 怎么打开系统服务器,Win10怎么打开系统服务管理器 Win10打开系统服务管理器操作方法...
- “衣带渐宽终不悔,为伊消得人憔悴” 读《唐宋词十七讲》有感二 —— 2021.12.23 晚八时
- 九九乘法表c语言编程10乘10,C语言编程九九乘法表
- Python_pandas读取数据
- OBS美颜滤镜插件,美白、瘦脸....
- 分布式事务常见解决方案
- input标签checkbox选中触发事件的方法
- 一个程序员的丰功伟绩
- 前端页面项目——博客系统
- oracle实体视图日志,Oracle案例02——ORA-12034: SCOTT.USER_TABLE 上的实体化视图日志比上次刷新后的内容新...