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...

posted @ 2023-03-09 16:15  Ditvelo  阅读(352)  评论(0)    收藏  举报