GIC 驅(qū)動(dòng)
設(shè)備樹(shù)
初始化
中斷的映射
數(shù)據(jù)結(jié)構(gòu)
中斷控制器注冊(cè) irq_domain
外設(shè)的驅(qū)動(dòng)創(chuàng)建硬中斷和虛擬中斷號(hào)的映射關(guān)系
中斷的注冊(cè)
中斷的處理
保護(hù)現(xiàn)場(chǎng)
中斷處理
恢復(fù)現(xiàn)場(chǎng)
總結(jié)
GIC 驅(qū)動(dòng)
這里主要分析 linux kernel 中 GIC v3 中斷控制器的代碼(drivers/irqchip/irq-gic-v3.c)。
設(shè)備樹(shù)
先來(lái)看下一個(gè)中斷控制器的設(shè)備樹(shù)信息:
gic:?interrupt-controller@48000000?{
?compatible?=?"arm,gic-v3";
?reg?=?<0?0x48000000?0?0x10000>,
???????<0?0x48040000?0?0xc0000>;
?#interrupt-cells?=?<3>;
?interrupt-controller;
?interrupts?=?<GIC_PPI?9?IRQ_TYPE_LEVEL_HIGH>;
?interrupt-parent?=?<&gic>;
};
compatible
-
- :用于匹配GICv3驅(qū)動(dòng)
reg
-
- :GIC的物理基地址,分別對(duì)應(yīng)GICD,GICR,GICC…
#interrupt-cells
-
- :這是一個(gè)中斷控制器節(jié)點(diǎn)的屬性。它聲明了該中斷控制器的中斷指示符(interrupts)中 cell 的個(gè)數(shù)
interrupt-controller
-
- : 表示該節(jié)點(diǎn)是一個(gè)中斷控制器
interrupts
- : 分別代表中斷類型,中斷號(hào),中斷類型, PPI中斷親和, 保留字段
關(guān)于設(shè)備數(shù)的各個(gè)字段含義,詳細(xì)可以參考 Documentation/devicetree/bindings 下的對(duì)應(yīng)信息。
初始化
1. irq chip driver 的聲明:
IRQCHIP_DECLARE(gic_v3,?"arm,gic-v3",?gic_of_init);
定義 IRQCHIP_DECLARE 之后,相應(yīng)的內(nèi)容會(huì)保存到 __irqchip_of_table 里邊:
#define?IRQCHIP_DECLARE(name,?compat,?fn)?OF_DECLARE_2(irqchip,?name,?compat,?fn)
#define?OF_DECLARE_2(table,?name,?compat,?fn)??
????????_OF_DECLARE(table,?name,?compat,?fn,?of_init_fn_2)
#define?_OF_DECLARE(table,?name,?compat,?fn,?fn_type)?????????????
????static?const?struct?of_device_id?__of_table_##name?????????
????????__used?__section(__##table##_of_table)?????????????
?????????=?{?.compatible?=?compat,?????????????????
?????????????.data?=?(fn?==?(fn_type)NULL)???fn?:?fn??}
__irqchip_of_table 在鏈接腳本 vmlinux.lds 里,被放到了 __irqchip_begin 和 __irqchip_of_end 之間,該段用于存放中斷控制器信息:
#ifdef?CONFIG_IRQCHIP
????#define?IRQCHIP_OF_MATCH_TABLE()????????????????????
????????.?=?ALIGN(8);???????????????????????????
????????VMLINUX_SYMBOL(__irqchip_begin)?=?.;????????????????
????????*(__irqchip_of_table)???????????????????????
????????*(__irqchip_of_end)
#endif
在內(nèi)核啟動(dòng)初始化中斷的函數(shù)中,of_irq_init 函數(shù)會(huì)去查找設(shè)備節(jié)點(diǎn)信息,該函數(shù)的傳入?yún)?shù)就是 __irqchip_of_table 段,由于 IRQCHIP_DECLARE 已經(jīng)將信息填充好了,of_irq_init 函數(shù)會(huì)根據(jù) “arm,gic-v3” 去查找對(duì)應(yīng)的設(shè)備節(jié)點(diǎn),并獲取設(shè)備的信息。or_irq_init 函數(shù)中,最終會(huì)回調(diào) IRQCHIP_DECLARE 聲明的回調(diào)函數(shù),也就是 gic_of_init,而這個(gè)函數(shù)就是 GIC 驅(qū)動(dòng)的初始化入口。
2. gic_of_init 流程:
static?int?__init?gic_of_init(struct?device_node?*node,?struct?device_node?*parent)
{
??......
?dist_base?=?of_iomap(node,?0);???????????????????????????????????????????------(1)
?if?(!dist_base)?{
??pr_err("%pOF:?unable?to?map?gic?dist?registersn",?node);
??return?-ENXIO;
?}
?err?=?gic_validate_dist_version(dist_base);??????????????????????????????------(2)
?if?(err)?{
??pr_err("%pOF:?no?distributor?detected,?giving?upn",?node);
??goto?out_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;
??goto?out_unmap_dist;
?}
?for?(i?=?0;?i?<?nr_redist_regions;?i++)?{????????????????????????????????------(4)
??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?%dn",?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))??------(5)
??redist_stride?=?0;
?err?=?gic_init_bases(dist_base,?rdist_regs,?nr_redist_regions,???????????------(6)
????????redist_stride,?&node->fwnode);
?if?(err)
??goto?out_unmap_rdist;
?gic_populate_ppi_partitions(node);???????????????????????????????????????------(7)
?gic_of_setup_kvm_info(node);
?return?0;
??......
?return?err;
}
- 映射 GICD 的寄存器地址空間。驗(yàn)證 GICD 的版本是 GICv3 還是 GICv4(主要通過(guò)讀GICD_PIDR2寄存器bit[7:4]. 0x1代表GICv1, 0x2代表GICv2…以此類推)。通過(guò) DTS 讀取 redistributor-regions 的值。為一個(gè) GICR 域分配基地址。通過(guò) DTS 讀取 redistributor-stride 的值。下面詳細(xì)介紹。設(shè)置一組 PPI 的親和性。
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)
{
??......
?typer?=?readl_relaxed(gic_data.dist_base?+?GICD_TYPER);????????????????------(1)
?gic_data.rdists.id_bits?=?GICD_TYPER_ID_BITS(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,??------(2)
???????&gic_data);
?gic_data.rdists.rdist?=?alloc_percpu(typeof(*gic_data.rdists.rdist));
?gic_data.rdists.has_vlpis?=?true;
?gic_data.rdists.has_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_data.rdists,?gic_data.domain);??????????????????------(5)
?gic_smp_init();????????????????????????????????????????????????????????------(6)
?gic_dist_init();???????????????????????????????????????????????????????------(7)
?gic_cpu_init();????????????????????????????????????????????????????????------(8)
?gic_cpu_pm_init();?????????????????????????????????????????????????????------(9)
?return?0;
??......
}
- 確認(rèn)支持 SPI 中斷號(hào)最大的值為多少。向系統(tǒng)中注冊(cè)一個(gè) irq domain 的數(shù)據(jù)結(jié)構(gòu),irq_domain 主要作用是將硬件中斷號(hào)映射到 irq number,后面會(huì)做詳細(xì)的介紹。設(shè)定 arch 相關(guān)的 irq handler。gic_irq_handle 是內(nèi)核 gic 中斷處理的入口函數(shù),后面會(huì)做詳細(xì)的介紹。gic 虛擬化相關(guān)的內(nèi)容。初始化 ITS。設(shè)置 SMP 核間交互的回調(diào)函數(shù),用于 IPI,回到函數(shù)為 gic_raise_softir。初始化 Distributor。初始化 CPU interface。初始化 GIC 電源管理。
中斷的映射
當(dāng)早期的系統(tǒng)只存在一個(gè)中斷控制器,而且中斷數(shù)目也不多的時(shí)候,一個(gè)很簡(jiǎn)單的做法就是一個(gè)中斷號(hào)對(duì)應(yīng)到中斷控制器的一個(gè)號(hào),可以說(shuō)是簡(jiǎn)單的線性映射:
但當(dāng)一個(gè)系統(tǒng)中有多個(gè)中斷控制器,而且中斷號(hào)也逐漸增加的時(shí)候。linux 內(nèi)核為了應(yīng)對(duì)此問(wèn)題,引入了 irq_domain 的概念。