ARM GICv3 ITS介绍及代码分析

前言:

在ARM gicv3中断控制器,有提到过ITS的作用,本篇就ITS进行更详细的介绍以及分析linux 内核中ITS代码的实现。
本文基于linux 4.19,介绍DT方式初始化的ITS代码。

ITS概述:

在GICv3中定义了一种新的中断类型,LPI(locality-specific peripheral interrupts)。LPI是一种基于消息的中断。中断信息不再通过中断线进行传递。

GICv3定义了两种方法实现LPI中断:

  • forwarding方式
    外设可以通过访问redistributor的寄存器GICR_SERLPIR,直接发送LPI中断
  • 使用ITS方式
    ITS(Interrupt Translation Service)在GICv3中是可选的。ITS负责接收来自外设的中断,并将它们转化为LPI INTID发送到相应的Redistributor

一般而言比较推荐使用ITS实现LPI,因为ITS提供了很多特性,在中断源比较多的场景,可以更加高效。

外设通过写GITS_TRANSLATER寄存器,发起LPI中断。此时ITS会获得2个信息:
EventID: 值保存在GITS_TRANSLATER寄存器中,表示外设发送中断的事件类型
DeviceID: 表示哪一个外设发起LPI中断。
ITS将DeviceID和eventID,通过一系列查表,得到LPI中断号,再使用LPI中断号查表,得到该中断的目标cpu。

The ITS table

当前,ITS使用三种类型的表来处理LPI的转换和路由:
device table: 映射deviceID到中断转换表
interrupt translation table:映射EventID到INTID。以及INTID属于的collection组
collection table:映射collection到Redistributor
在这里插入图片描述

所以一个ITS完整的处理流程是:
当外设往GITS_TRANSLATER寄存器中写数据后,ITS做如下操作:

  1. 使用DeviceID,从设备表(device table entry)中选择索引为DeviceID的表项。从该表项中,得到中断 转换表(interrupt translation table)的位置
  2. 使用EventID,从中断转换表中选择索引为EventID的表项。得到中断号,以及中断所属的collection号
  3. 使用collection号,从collection表格中,选择索引为collection号的表项。得到redistributor的映射信息
  4. 根据collection表项的映射信息,将中断信息,发送给对应的redistributor
    在这里插入图片描述
The ITS Command:

its是由its的命令控制的。命令队列是一个循环buffer, 由三个寄存器定义。
GITS_CBASER: 指定命令队列的基地址和大小。命令队列必须64KB对齐,大小必须是4K的倍数。命令队列中的每一个索引是32字节。该寄存器还指定访问命令队列时its的cacheability和shareability的设置。
GITS_CREADR: 指向ITS将处理的下一个命令
GITS_CWRITER: 指向队列中应写入下一个新命令的索引。
在这里插入图片描述

在its的初始化过程以及lpi中断上报等过程中,会涉及到ITS command的发送。 具体的its commad指令参考spec.


现在我们已经知道ITS的具体作用以及处理流程,结合linux内核的实现进行分析。

ITS代码分析

its的代码位于drivers/irqchip/irq-gic-v3-its.c

1. ITS数据结构
struct its_node {
	raw_spinlock_t		lock;
	struct list_head	entry;
	void __iomem		*base;
	phys_addr_t		phys_base;
	struct its_cmd_block	*cmd_base;
	struct its_cmd_block	*cmd_write;
	struct its_baser	tables[GITS_BASER_NR_REGS];
	struct its_collection	*collections;
	struct fwnode_handle	*fwnode_handle;
	u64			(*get_msi_base)(struct its_device *its_dev);
	u64			cbaser_save;
	u32			ctlr_save;
	struct list_head	its_device_list;
	u64			flags;
	unsigned long		list_nr;
	u32			ite_size;
	u32			device_ids;
	int			numa_node;
	unsigned int		msi_domain_flags;
	u32			pre_its_base; /* for Socionext Synquacer */
	bool			is_v4;
	int			vlpi_redist_offset;
};

base : its node的虚拟地址
phys_base: its node的物理地址
cmd_base: 命令队列的基地址
cmd_write: 指向队列中下一个命令的地址
tables[]: 指向device table或vpe table的结构体
collection: 指向its_collection结构体, 主要保存映射到的gicr的地址
cbaser_save: 保存cbaser寄存器的信息
ctlr_save:保存ctlr寄存器的

2. ITS的初始化

在gic初始化时,会进行ITS的初始化。
its的初始化操作主要是为its的device table以及collection table分配内存,并使能its.

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)
{
	.......
	if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis()) -------- (1)
		its_init(handle, &gic_data.rdists, gic_data.domain); ---- (2)
		its_cpu_init();         ----- (3)
}

(1) ITS需要使能内核配置 CONFIG_ARM_GIC_V3_ITS. 如果架构支持LPI, 则进行ITS的初始化。
通过读GICD_TYPER(Interrupt Controller Type Register)寄存器的bit17查看架构是否支持LPI.

(2)its_init是 its的初始化入口。第三个参数需要注意下,它指定了its的parent domain是gic domain
在这里插入图片描述

(3) its_cpu_init 是在its初始化完成后,进行its的一些额外的配置,如enable lpi以及绑定its collection到its 目的redistributour。

1.1 its初始化函数its_init

int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
		    struct irq_domain *parent_domain)
{
	...
	its_parent = parent_domain;
	of_node = to_of_node(handle);
	if (of_node)
		its_of_probe(of_node);               --------- (1)

	if (list_empty(&its_nodes)) {
		pr_warn("ITS: No ITS available, not enabling LPIs\n");
		return -ENXIO;
	}

	gic_rdists = rdists;
	err = its_alloc_lpi_tables();            ------- (2)
	if (err)
		return err;

	...
	register_syscore_ops(&its_syscore_ops);      ------(3)

	return 0;
}

(1) its_of_probe

+->its_of_probe
	+->its_probe_one
		+->its_force_quiescent   //让ITS处于非活动状态,在非静止状态改变ITS的配置会有安全的风险
		+->its node malloc and init   //为its_node分配空间,并对其进行初始化配置
		+->its_alloc_tables  //为device table 和 vpe table分配内存
		+->its_alloc_collections //为collection table中映射到的gicr 地址分配内存; 每一个its都有一个collection table, ct可以保存在寄存器(GITS_BASER)或者内存(GITS_TYPER.HCC)
		+->its_init_domain // its domain初始化,注册its domain相关操作

its probe过程, 主要是初始化its node数据结构, 为its tables分配内存, 初始化its domain并注册its domain相关操作。
its_domain初始化过程中,会指定its irq_domain的host_data为msi_domain_info, 在info-ops.prepare过程中会去创建ITS设备, its translation table会在那个阶段分配内存。

此外,its probe过程中还有一个标志位会被设置。

if (GITS_TYPER_HCC(typer))
	its->flags |= ITS_FLAGS_SAVE_SUSPEND_STATE;

在这里插入图片描述
如果GITS_TYPER.hcc不为0, 那么就会将its->flags置为SUSPEND。
这个标志位可以判断its需不需要进行suspend或者resume流程,下文再详细描述。

(2) its_alloc_lpi_tables

+-> its_alloc_lpi_tables
	+-> its_allocate_prop_table // 初始化lpi中断的配置表和状态表
		+-> its_lpi_init   

ITS 是为LPI服务的,所以在ITS初始化过程中还需要初始化LPI需要的两张表
(LPI configuration table, LPI pending tables ), 然后进行lpi的初始化。

LPI的这两张表就是LPI和其他类型中断的区别所在: LPI的中断的配置,以及中断的状态,是保存在memory的表中,而不是保存在gic的寄存器中的。

LPI 中断配置表:
中断配置表的基地址由GICR_PROPBASER寄存器决定。
对于LPI配置表,每个LPI中断占用1个字节(bit[7:0]),指定了该中断的使能(bit 0)和中断优先级(bit[7:2])。

当外部发送LPI中断给redistributor,redistributor首先要访问memory来获取LPI中断的配置表。为了加速这过程,redistributor中可以配置cache,用来缓存LPI中断的配置信息。

因为有了cache,所以LPI中断的配置信息,就有了2份拷贝,一份在memory中,一份在redistributor的cache中。如果软件修改了memory中的LPI中断的配置信息,需要将redistributor中的cache信息给无效掉。
通过该接口刷相关dcache

gic_flush_dcache_to_poc()

LPI 中断状态表
中单状态表的基地址由GICR_PENDBASER寄存器决定, 该寄存器还可以设置LPI中断状态表memory的属性,如shareability,cache属性等。
该状态表主要用于查看LPI是否pending状态。
在这里插入图片描述
该中断状态表由redistributor来设置。每个LPI中断,占用一个bit空间。
0: 该LPI中断,没有处于pending状态
1: 该LPI中断,处于pending状态

(3) register_syscore_ops
该操作主要是注册两个低功耗流程会用到的函数, suspend和resume。
在系统进行低功耗流程时(suspend 或者hibernate, 当然目前4.19还不支持its的hibernate), suspend时会调用its_save_disable, 保存its的一些寄存器状态,并disable its, 在 resume时调用its_restore_enable, 恢复之前its保存的寄存器状态,并enable its.

static struct syscore_ops its_syscore_ops = {
	.suspend = its_save_disable,
	.resume = its_restore_enable,
};

这个流程由低功耗的框架保证, 只需要通过register_syscore_ops函数注册suspend和resume函数即可。

1.2 its_cpu_init

+-> its_cpu_init
	+-> its_cpu_init_lpis // 配置lpi 配置表和状态表, 以及使能lpi
	+-> its_cpu_init_collections // 绑定每一个collection到target redistributor
		+-> its_send_mapc // 发送its mapc command, mapc主要用于映射collection到目的redistributor
		+-> its_send_invall //指定 memory中的LPI中断的配置信息和cache中保存的必须一致
3. its中断上报

和gic类似, 在中断上报时,如果设备挂载在its 下, 会调用到its domain的一系列operation

static const struct irq_domain_ops its_domain_ops = {
	.alloc			= its_irq_domain_alloc,
	.free			= its_irq_domain_free,
	.activate		= its_irq_domain_activate,
	.deactivate		= its_irq_domain_deactivate,
};

参考资料

  1. Arm Generic Interrupt Controller Architecture Specification
  2. GICv3 and GICv4 Software Overview
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值