Phytium-S2500 GIC驱动解析
驱动由宏 IRQCHIP_ACPI_DECLARE(gic_phyt_2500, ACPI_MADT_TYPE_PHYTIUM_2500, acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_V3, gic_acpi_init); 对飞腾中断控制器进行初始化,使用 acpi_avalidate_gic_table() 进行版本检查
IRQCHIP_ACPI_DECLARE(name, subtable, validate, data, fn):对不同中断控制器版本进行不同的初始化
name:同一文件所有的IRQCHIP_ACPI_DECLARE宏name必须唯一
subtable:MADT中标识的子表
validate:检查子表的有效性,可以为空
data:被validate检查的数据
fn:初始化函数
acpi_avalidate_gic_table() 会从MADT(Multiple APIC Description Table 多中断控制器表)中获取GICD版本,与驱动的版本进行比对,比对成功后获取GICR的数量,如果GICR数量获取为0,就去获取GICC的数量,并将acpi_data.single_redist标记为true,然后返回给上面的宏false(检查失败了)
gic_acpi_init() 进行GIC初始化
//对GICD基地址进行映射
acpi_data.dist_base = ioremap(dist->base_address,
ACPI_GICV3_DIST_MEM_SIZE);
//验证GICD版本
err = gic_validate_dist_version(acpi_data.dist_base);
//初始化0号socket的GICD地址
mars3_gic_dists[0].phys_base = dist->base_address;
mars3_gic_dists[0].size = ACPI_GICV3_DIST_MEM_SIZE;
mars3_gic_dists[0].dist_base = acpi_data.dist_base;
//获取socket数量,单路为0b00000001,双路为0b00000011,以此类推
mars3_sockets_bitmap = gic_mars3_sockets_bitmap();
//获取非0号socket上的GICD地址
for (skt = 1; skt < MAX_MARS3_SOC_COUNT; skt++) {
if ((((unsigned int)1 << skt) & mars3_sockets_bitmap) == 0)
continue;
//MARS3_ADDR_SKTID_SHIFT=41,S2500的IO空间地址的bit[43:41]位代表socket号,故最多支持8路
mars3_gic_dists[skt].phys_base = ((unsigned long)skt << MARS3_ADDR_SKTID_SHIFT) |
mars3_gic_dists[0].phys_base;
mars3_gic_dists[skt].size = mars3_gic_dists[0].size;
mars3_gic_dists[skt].dist_base = ioremap(mars3_gic_dists[skt].phys_base,
mars3_gic_dists[skt].size);
}
//kzalloc用于申请物理上连续,内容为0的内存
size = sizeof(*acpi_data.redist_regs) * acpi_data.nr_redist_regions;
acpi_data.redist_regs = kzalloc(size, GFP_KERNEL);
//获取GICR的地址
err = gic_acpi_collect_gicr_base();
//分配irq_domain_handle,fwnode(frmware device node)
domain_handle = irq_domain_alloc_fwnode(acpi_data.dist_base);
//GIC初始化函数
err = gic_init_bases(acpi_data.dist_base, acpi_data.redist_regs,
acpi_data.nr_redist_regions, 0, domain_handle);
//
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
//为KVM虚拟中断作准备
if (static_branch_likely(&supports_deactivate_key))
gic_acpi_setup_kvm_info();
对几个重要的结构体进行说明
#ifdef CONFIG_ACPI
static struct
{
void __iomem *dist_base; //GICD寄存器映射后的基地址
struct redist_region *redist_regs; //
u32 nr_redist_regions; //GICR数目
bool single_redist; //是否为单个redist
u32 maint_irq; //vGIC维护中断
int maint_irq_mode; //vGIC维护中断模式
phys_addr_t vcpu_base;
} acpi_data __initdata;
struct redist_region {
void __iomem *redist_base; //GICR映射后的基地址
phys_addr_t phys_base; //GICR的物理地址
bool single_redist;
};
gic_init_bases()内容说明
//获取GICD_TYPER 32位只读寄存器内容,包含的信息如下
//1.GIC是否实现安全扩展,2.如果实现安全扩展,则包括Lockable SPI(LSPIs)的数量
//3.GIC支持的最大中断号,4.cpu interfaces数量
typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);
gic_data.rdists.gicd_typer = typer;
//获取GIC支持的最大中断数
gic_irqs = GICD_TYPER_IRQS(typer);
//中断号1020-1023保留做特殊用途
if (gic_irqs > 1020)
gic_irqs = 1020;
gic_data.irq_nr = gic_irqs;
//建立一个Radix Tree来维护HW interrupt ID到IRQ number映射关系
//根据fwnode为GIC分配irq domain
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;
gic_data.has_rss = !!(typer & GICD_TYPER_RSS);
//将中断处理函数设置为gic_handle_irq()
set_handle_irq(gic_handle_irq);
gic_update_vlpi_properties();
//分配并设置SGI中断
gic_smp_init();
//GICD初始化
gic_dist_init();
//GICC初始化
gic_cpu_init();
//设置GIC电源相关的初始化
gic_cpu_pm_init();
//若支持LPI,进行ITS初始化
if (gic_dist_supports_lpis()) {
phytium_its_init(handle, &gic_data.rdists, gic_data.domain);
phytium_its_cpu_init();
}
gic_smp_init()
//设置smp核间交互的回调函数
set_smp_cross_call(gic_raise_softirq);
//设置cpu热插拔时GIC的回调函数
cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
"irqchip/arm/gic_phytium_2500:starting",
gic_starting_cpu, NULL);
GICD部分寄存器描述
| 寄存器 | 描述 |
|---|---|
| GICD_CTLR | GICD开关,[0]位为开关,其他均为保留位 |
| GICD_IGROUPR | offset:[0x0080-0x00F8] (136),状态位,显示每个位对应的中断在组0还是组1 |
| GICD_ICENABLERnE | 禁用ESPI |
| GICD_ICACTIVERnE | 清ESPI的active状态 |
| GICD_IGROUPRnE | 配置ESPI为非安全group1中断 |
| GICD_ICFGRnE | 配置ESPI为中断类型 |
| GICD_IPRIORITYRnE | 配置ESPI优先级 |
| GIC_DIST_CONF | 设置所有SPI中断类型 |
| GIC_DIST_PRI | 设置所有SPI中断优先级 |
| GIC_DIST_ACTIVER_CLEAR | 清所有SPI中断active状态 |
| GIC_DIST_ENABLE_CLEAR | 清所有SPI中断enable状态 |
中断路由
<affinity level 3>.<affinity level 2>.<affinity level 1>.<affinity level 0>组成一个PE的路由
每一个core的affnity值可以通过MPDIR_EL1寄存器获取(gic_mpidr_to_affinity(cpu_logical_map(cpu))),MPIDR_EL1所指示的亲和值与连接到PE的Redistributor的GICR_TYPER所指示的亲和值相同,配置对应affinity值到GICD_IROUTER,可以将中断路由到该core上,
其中MPIDR_EL1是一个64位的寄存器,
[39:32] [23:16] [15:8] [7:0] 分别为Affinity level 3,2,1,0
[30]:0b0表示处理器是多处理器系统的一部分,为0b1表示处理器是单处理器系统的一部分
每一级的affinity由soc自己定义,比如S2500目前是 <unknown>.<socket>.<cluster>.<core>
gic_dist_init()
//cpu最多8路
for (skt = 0; skt < MAX_MARS3_SOC_COUNT; skt++) {
if ((((unsigned int)1 << skt) & mars3_sockets_bitmap) == 0)
continue;
base = mars3_gic_dists[skt].dist_base;
//参考上表,关闭GICD
writel_relaxed(0, base + GICD_CTLR);
//等待戈多,确保GICD寄存器写入0了
gic_do_wait_for_rwp(base);
/*
* Configure SPIs as non-secure Group-1. This will only matter
* if the GIC only has a single security state. This will not
* do the right thing if the kernel is running in secure mode,
* but that's not the intended use case anyway.
*/
//将SGI/PPI设置为非安全group1
for (i = 32; i < gic_data.irq_nr; i += 32)
writel_relaxed(~0, base + GICD_IGROUPR + i / 8);
//设置全局中断为水平触发(低电平有效)和优先级,停用并禁用所有SPI,只留下PPI和SGI(因为他们归GICR管)
gic_dist_config(base, gic_data.irq_nr, NULL);
//等待戈多
gic_do_wait_for_rwp(base);
/* Enable distributor with ARE, Group1 */
//使能GICD,打开GIC可扩展(写.ARE_S or .ARE_NS),0b010011,G1A不知道干啥的
writel_relaxed(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1,
base + GICD_CTLR);
/*
* Set all global interrupts to the boot CPU only. ARE must be
* enabled.
*/
//将全局中断全部绑到cpu0号上
//1.获取待绑定中断的目标core的中断路由(中断绑定到指定core上,得知道core在哪对吧)
affinity = gic_mpidr_to_affinity(cpu_logical_map(smp_processor_id()));
for (i = 32; i < gic_data.irq_nr; i++)
//2.将中断路由写进GIC irouter寄存器
gic_write_irouter(affinity, base + GICD_IROUTER + i * 8);
}
gic_cpu_init()
//初始化每个rdist的地址
if (gic_populate_rdist())
return;
//由于rdist和interface不在同一个电压domain,使能cpu interface和PE
gic_enable_redist(true);
//sgi_base = rdist_base + 64K ,获取RD0 SGI 和 PPI 寄存器地址
rbase = gic_data_rdist_sgi_base();
//配置所有SGIs/PPIs为 non-secure group-1
writel_relaxed(~0, rbase + GICR_IGROUPR0);
//初始化SGIs和PPIs,包括清除banked interrupt,设置优先级
gic_cpu_config(rbase, gic_redist_wait_for_rwp);
//mpidr = socket:cluster:core; e.g. 0x00010601 mean socket 1, cluster 6, core 1
mpidr = (unsigned long)cpu_logical_map(smp_processor_id());
//如果是boot-cpu
if ((mpidr & 0xFFFF) == 0) {
rbase = rbase + 64*SZ_128K;
//S2500有68个(0~67)个rdist,多出的4个用于发送中断到其他skt
for (i = 0; i < 4; i++) {
/* Configure SGIs/PPIs as non-secure Group-1 */
writel_relaxed(~0, rbase + GICR_IGROUPR0);
gic_cpu_config(rbase, NULL);
gic_do_wait_for_rwp(rbase - SZ_64K);
rbase = rbase + SZ_128K;
}
}
gic_cpu_sys_reg_init();
其中 gic_populate_rdist() 用于初始化每个rdist的信息
gic_populate_rdist()
+-->gic_iterate_rdists(__gic_populate_rdist)
static int gic_iterate_rdists(int (*fn)(struct redist_region *, void __iomem *))
{
//处理每个rdist
for (i = 0; i < gic_data.nr_redist_regions; i++) {
void __iomem *ptr = gic_data.redist_regions[i].redist_base;
u64 typer;
u32 reg;
do {
//typer = GICR_TYPER
//GICR_TYPER: [63:32]Affinity Value
typer = gic_read_typer(ptr + GICR_TYPER);
ret = fn(gic_data.redist_regions + i, ptr);
if (!ret)
return 0;
if (gic_data.redist_regions[i].single_redist)
break;
if (gic_data.redist_stride) {
ptr += gic_data.redist_stride;
} else {
ptr += SZ_64K * 2; /* Skip RD_base + SGI_base */
if (typer & GICR_TYPER_VLPIS)
ptr += SZ_64K * 2; /* Skip VLPI_base + reserved page */
}
} while (!(typer & GICR_TYPER_LAST));//GICR_TYPER第四位为1时说明此rdist是这个芯片上最后一个rdist
}
return ret ? -ENODEV : 0;
}
static int __gic_populate_rdist(struct redist_region *region, void __iomem *ptr)
{ //mpidr = socket:cluster:core; e.g. 0x00010601 mean socket 1, cluster 6, core 1
unsigned long mpidr = cpu_logical_map(smp_processor_id());
u64 typer;
u32 aff, aff2_skt, rdist_skt;
/*
* Convert affinity to a 32bit value that can be matched to
* GICR_TYPER bits [63:32].:
*/
//当前cpu的亲和性,参考中断路由,此处只有<level 1>.<level 0>
//如果mpidr=0x0000701,此处aff=0x0701
aff = (MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 |
MPIDR_AFFINITY_LEVEL(mpidr, 0));
//当前cpu所在的socket
aff2_skt = MPIDR_AFFINITY_LEVEL(mpidr, 2) & 0x7;
//rdist所在的socket,物理地址右移41位和上0b111即socket号
rdist_skt = (((u64)region->phys_base >> MARS3_ADDR_SKTID_SHIFT) & 0x7);
//确保在同一个socket上
if (aff2_skt != rdist_skt)
return 1;
//typer = GICR_TYPER
typer = gic_read_typer(ptr + GICR_TYPER);
if ((typer >> 32) == aff) {
//填充rdist的信息并且打印
u64 offset = ptr - region->redist_base;
gic_data_rdist_rd_base() = ptr;
gic_data_rdist()->phys_base = region->phys_base + offset;
pr_info("CPU%d: found redistributor %lx region %d:%pa\n",
smp_processor_id(), mpidr,
(int)(region - gic_data.redist_regions),
&gic_data_rdist()->phys_base);
return 0;
}
/* Try next one */
return 1;
}
To be continued...

浙公网安备 33010602011771号