Linuxドライバーは、GICv1、GICv2、GICv3、およびGICv4ドライバーをサポートしています。このセクションでは、ARM-Cortexプラットフォームの詳細な説明と組み合わせて、GICv3ドライバーの初期化について説明します。
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>;
};
そして、いくつかの重要なフィールドがあります:カーネルのドキュメントを参照してくださいアーム、GIC-v3.txtの文書、
* ARM Generic Interrupt Controller, version 3
AArch64 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 an
interrupt source. Must be a single cell with a value of at least 3.
If the system requires describing PPI affinity, then the value must
be at least 4.
The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
interrupts. 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 the
range [0-15].
The 3rd cell is the flags, encoded as follows:
bits[3:0] trigger type and level flags.
1 = edge triggered
4 = level triggered
The 4th cell is a phandle to a node describing a set of CPUs this
interrupt is affine to. The interrupt must be a PPI, and the node
pointed must be a subnode of the "ppi-partitions" subnode. For
interrupt types other than PPI or PPIs that are not partitionned,
this cell must be zero. See the "ppi-partitions" node description
below.
Cells 5 and beyond are reserved for future use and must have a value
of 0 if present.
- reg : Specifies base physical address(s) and size of the GIC
registers, 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.
- 互換性:対応するドライバーと一致するために使用され、これ以上の詳細はありません
- interrupt-cellsは、割り込みソースの詳細情報を説明するために使用されます。この値は3に等しく、割り込みには3つのフィールドがあります。
- 最初のフィールドは割り込みタイプを表します(GIC_PPI、GIC_SPI)
- 2番目のフィールドは、物理的な割り込み番号で、割り込みのタイプによって異なります。SPI(0〜987)PPI(0〜15)
- 3番目のフィールドで表される割り込みトリガーモード
- interrupt-controller:説明このフィールドは割り込みコントローラーです
- interrupt-parent:この割り込みコントローラーがカスケード接続されているかどうかを表します。このフィールドがない場合は、親フィールドに従います。
- regは、割り込みコントローラに関係するレジスタを記述します
- 0x666688888は、ディストリビューター、GICDのベースレジスタを表します
- 0x6666e8888は、再頒布者、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に従って、正しい割り込みコントローラーが照合されます。正しい割り込みコントローラーを照合した後、上記のデータコールバック関数は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);
}
- 割り込みコントローラが存在するフィールドを見つける
- 割り込みコントローラー記述子を割り当て、割り込みコントローラーの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は、ターミナルコントローラディストリビュータのベースアドレスを取得します。
- gic_validate_dist_versionは、ベースアドレスに従って現在のバージョンがv3かv4かを判断します
- 再配布者の属性を読み取り、対応するレジスタのベースアドレスを取得します
- 最終的に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構造体を初期化します
- 現在のGICでサポートされている割り込みの最大数は、GICD_TYPERレジスタを読み取ることによって取得されます。割り込みの数が1020を超える場合、割り当てられる最大値は1020です。
- irq_domain_create_treeは、この関数を使用してirqドメインを作成します。irqdoaminは、中断された領域の管理であり、カスケードに使用されます
- set_handle_irq(gic_handle_irq); キーのキーポイントを使用して、割り込み処理のコールバック関数を設定します。割り込みが処理されると、この関数が最初に呼び出されます
- gic_smp_initソフト割り込みの初期化、ソフト割り込みのコールバックの設定
- gic_dist_initディストリビューターの初期化
- CPUインターフェースのgic_cpu_init初期化
- gic_cpu_pm_init電源関連の初期化
割り込みコールバック関数を設定する
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);
else
isb();
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);
#else
WARN_ONCE(true, "Unexpected SGI received!\n");
#endif
continue;
}
} while (irqnr != ICC_IAR1_EL1_SPURIOUS);
}
- 最後のGIC-500記事の最後のセクションの説明に従って、最初にIARレジスタを読み取って割り込み番号を決定します。ソフトウェアはgic_read_iarを介して実装されます
- 割り込み番号を取得して、現在どの割り込みタイプであるかを判別します。割り込み番号が15より大きく1020より小さい場合、割り込みはPPIまたはSPIに属しています。
- 組み合わせは、irqドメインに従って対応する割り込みhandle_domain_irq(gic_data.domain、irqnr、regs)を処理します。
- 割り込み番号が16未満の場合、割り込み番号はIPI割り込み(コア間の通信に使用される割り込み)であり、handle_IPI(irqnr、regs)が呼び出され、対応する割り込みを処理します
Linuxでは、割り込みコントローラーを記述するためにirqチップ構造が使用され、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構造は、割り込みコントローラーのソフトウェア抽象化に関連しています
- 割り込みコントローラーの名前である名前は、/ cat / proc / interrupterで確認できます
- irq_mask:割り込みソースをマスクするために使用されます
- irq_unmask:割り込みソースのマスクを解除するために使用されます
- irq_eoi:割り込みの終了、この割り込みが処理されたことを示すために使用されます
- 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;
};