您现在的位置是:首页 > 牙膏推荐
中断控制器的驱动解析(上)
皓齿牙科网
2026-02-23【牙膏推荐】90人已围观
简介这里主要分析linuxkernel中GICv3中断控制器的代码(drivers/irqchip/)。设备树先来看下一个中断控制器的设备树信息:interrupt-controller:表示该节点是一个中断控制器interrupts:分别代表中断类型,中断号,中断类型,PPI中断亲和,保留字段关于设备...
这里主要分析linuxkernel中GICv3中断控制器的代码(
drivers/irqchip/)。
先来看下一个中断控制器的设备树信息:
interrupt-controller:表示该节点是一个中断控制器
interrupts:分别代表中断类型,中断号,中断类型,PPI中断亲和,保留字段
关于设备数的各个字段含义,详细可以参考
Documentation/devicetree/bindings下的对应信息。
IRQCHIP_DECLARE(gic_v3,"arm,gic-v3",gic_of_init);
定义IRQCHIP_DECLARE之后,相应的内容会保存到__irqchip_of_table里边:
2.gic_of_init流程:
staticint__initgic_of_init(structdevice_node*node,structdevice_node*parent)
{
dist_base=of_iomap(node,0);------(1)
if(!dist_base){
pr_err("%pOF:unabletomapgicdistregisters\n",node);
return-ENXIO;
}
err=gic_validate_dist_version(dist_base);------(2)
if(err){
pr_err("%pOF:nodistributordetected,givingup\n",node);
gotoout_unmap_dist;
}
if(of_property_read_u32(node,"#redistributor-regions",nr_redist_regions))------(3)
nr_redist_regions=1;
rdist_regs=kzalloc(sizeof(*rdist_regs)*nr_redist_regions,GFP_KERNEL);
if(!rdist_regs){
err=-ENOMEM;
gotoout_unmap_dist;
}
for(i=0;inr_redist_regions;i++){------(4)
structresourceres;
intret;
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'tmapregion%d\n",node,i);
err=-ENODEV;
gotoout_unmap_rdist;
}
rdist_regs[i].phys_base=;
}
if(of_property_read_u64(node,"redistributor-stride",redist_stride))------(5)
redist_stride=0;
err=gic_init_bases(dist_base,rdist_regs,nr_redist_regions,------(6)
redist_stride,node-fwnode);
if(err)
gotoout_unmap_rdist;
gic_populate_ppi_partitions(node);------(7)
gic_of_setup_kvm_info(node);
return0;
returnerr;
}
映射GICD的寄存器地址空间。
验证GICD的版本是GICv3还是GICv4(主要通过读GICD_PIDR2寄存器bit[7:4].0x1代表GICv1,0x2代表GICv2…以此类推)。
通过DTS读取redistributor-regions的值。
为一个GICR域分配基地址。
通过DTS读取redistributor-stride的值。
下面详细介绍。
设置一组PPI的亲和性。
staticint__initgic_init_bases(void__iomem*dist_base,
structredist_region*rdist_regs,
u32nr_redist_regions,
u64redist_stride,
structfwnode_handle*handle)
{
typer=readl_relaxed(gic__base+GICD_TYPER);------(1)
gic__bits=GICD_TYPER_ID_BITS(typer);
gic_irqs=GICD_TYPER_IRQS(typer);
if(gic_irqs1020)
gic_irqs=1020;
gic__nr=gic_irqs;
gic_=irq_domain_create_tree(handle,gic_irq_domain_ops,------(2)
gic_data);
gic_=alloc_percpu(typeof(*gic_));
gic__vlpis=true;
gic__direct_lpi=true;
set_handle_irq(gic_handle_irq);------(3)
gic_update_vlpi_properties();------(4)
if(IS_ENABLED(CONFIG_ARM_GIC_V3_ITS)gic_dist_supports_lpis())
its_init(handle,gic_,gic_);------(5)
gic_smp_init();------(6)
gic_dist_init();------(7)
gic_cpu_init();------(8)
gic_cpu_pm_init();------(9)
return0;
}
确认支持SPI中断号最大的值为多少。
向系统中注册一个irqdomain的数据结构,irq_domain主要作用是将硬件中断号映射到IRQnumber,后面会做详细的介绍。
设定arch相关的irqhandler。gic_irq_handle是内核gic中断处理的入口函数,后面会做详细的介绍。
gic虚拟化相关的内容。
初始化ITS。
设置SMP核间交互的回调函数,用于IPI,回到函数为gic_raise_softir。
初始化Distributor。
初始化CPUinterface。
初始化GIC电源管理。
中断映射当早期的系统只存在一个中断控制器,而且中断数目也不多的时候,一个很简单的做法就是一个中断号对应到中断控制器的一个号,可以说是简单的线性映射:
但当一个系统中有多个中断控制器,而且中断号也逐渐增加的时候。linux内核为了应对此问题,引入了irq_domain的概念。
irq_domain的引入相当于一个中断控制器就是一个irq_domain。这样一来所有的中断控制器就会出现级联的布局。利用树状的结构可以充分的利用irq数目,而且每一个irq_domain区域可以自己去管理自己interrupt的特性。
每一个中断控制器对应多个中断号,而硬件中断号在不同的中断控制器上是会重复编码的,这时仅仅用硬中断号已经不能唯一标识一个外设中断,因此linuxkernel提供了一个虚拟中断号的概念。
接下来我们看下硬件中断号是如何映射到虚拟中断号的。
5T技术资源大放送!包括但不限于:C/C++,Arm,Linux,Android,人工智能,单片机,树莓派,等等。在上面的【人人都是极客】公众号内回复「peter」,即可免费获取!!
很赞哦!(132)
下一篇:大白牙 自信笑起来