目录

  • 背景介绍
  • Redistributor系统位置
  • 系统上电,CPU如何与GIC redistributor connect
  • GICR_WAKER寄存器
  • 上电流程,行为描述
  • 系统下电,CPU如何与GIC redistributor disconnect
  • 下电流程,行为描述


背景介绍

GIC电源管理,ARM官方手册,只有一页描述:

arm架构部署es 启动报错 arm架构pe系统_GIC功耗管理

值得注意的是:

1、在符合GICv3体系结构的实现中,CPU接口和PE必须位于同一个位置power domain,但它不必与关联的Redistributor所在的power domain相同。这意味着可能出现PE及其CPU接口断电的情况,Redistributor、Distributor及其子系统都已通电。在这种情况下,GIC架构需要支持,向PE和CPU接口发送通电事件信号机制。

2、ARM强烈建议,如果该PE上的唤醒软件无法处理中断,GIC的配置方式不应使中断唤醒特定的PE。GICv3提供电源管理来控制这种情况,因为该架构的设计允许由一个组织设计的再分配器,用于由一个组织设计的PEs和CPU接口不同的组织。

3、Redistributor上电时,在关闭CPU接口和PE之前,软件必须将CPU接口和Redistributor之间的接口进入静态状态,否则系统将变为不可预期状态。

4、GIC支持通过设置GICR_WAKER,可以启动到静态状态的转换。进程睡眠到1。当CPU处于静止状态时,GICR_WAKER。ChildrenAsleep也设置为1。

那么Redistributor、GICR_WAKER在系统位置是怎么样的呢?

Redistributor系统位置

在带有gicv3的soc架构中,其框图如下所示:

arm架构部署es 启动报错 arm架构pe系统_arm架构部署es 启动报错_02


gicv3中的redistributor与core中的cpu interface通过AXI-Stream进行通信。

系统上电,CPU如何与GIC redistributor connect

当core上电之后,需要将core中cpu interface与gic中的redistributor进行connect,这样将来gic才可以将中断发送给core。

connection的流程如下所示:

arm架构部署es 启动报错 arm架构pe系统_GIC功耗管理_03

GICR_WAKER寄存器

arm架构部署es 启动报错 arm架构pe系统_GIC功耗管理_04

arm架构部署es 启动报错 arm架构pe系统_GIC电源管理_05

上电流程,行为描述

  1. 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置低,表示要connect redistributor。
  2. redistributor在完成connect之后,将GICR_WAKER.ChildrenAsleep位给置低,表示connect完成。
  3. 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为0,如果不是,表示redistributor还没有完成connect操作,就继续查询。
  4. 如果查询到0,表示connect完成,接着做之后的初始化工作。

其汇编代码如下:

arm架构部署es 启动报错 arm架构pe系统_PSCI_06

/******************************************************************************
 * This function marks the core as awake in the re-distributor and
 * ensures that the interface is active.
 *****************************************************************************/
void gicv3_rdistif_mark_core_awake(uintptr_t gicr_base)
{
	/*
	 * The WAKER_PS_BIT should be changed to 0
	 * only when WAKER_CA_BIT is 1.
	 */
	assert((gicr_read_waker(gicr_base) & WAKER_CA_BIT) != 0U);

	/* Mark the connected core as awake */
	/* 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置低,
	 * 表示要connect redistributor。
	 */
	gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) & ~WAKER_PS_BIT);

	/* Wait till the WAKER_CA_BIT changes to 0 */
	/* 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为0,
	 * 如果不是,表示redistributor还没有完成connect操作,就继续查询。
	 */
	while ((gicr_read_waker(gicr_base) & WAKER_CA_BIT) != 0U) {
	}
}

/*******************************************************************************
 * This function enables the GIC CPU interface of the calling CPU using only
 * system register accesses.
 ******************************************************************************/
void gicv3_cpuif_enable(unsigned int proc_num)
{
	uintptr_t gicr_base;
	u_register_t scr_el3;
	unsigned int icc_sre_el3;

	assert(gicv3_driver_data != NULL);
	assert(proc_num < gicv3_driver_data->rdistif_num);
	assert(gicv3_driver_data->rdistif_base_addrs != NULL);
	assert(IS_IN_EL3());

	/* Mark the connected core as awake */
	/* 表示要connect redistributor */
	gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
	gicv3_rdistif_mark_core_awake(gicr_base);

	/* Disable the legacy interrupt bypass */
	icc_sre_el3 = ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT;

	/*
	 * Enable system register access for EL3 and allow lower exception
	 * levels to configure the same for themselves. If the legacy mode is
	 * not supported, the SRE bit is RAO/WI
	 */
	icc_sre_el3 |= (ICC_SRE_EN_BIT | ICC_SRE_SRE_BIT);
	write_icc_sre_el3(read_icc_sre_el3() | icc_sre_el3);

	scr_el3 = read_scr_el3();

	/*
	 * Switch to NS state to write Non secure ICC_SRE_EL1 and
	 * ICC_SRE_EL2 registers.
	 */
	write_scr_el3(scr_el3 | SCR_NS_BIT);
	isb();

	write_icc_sre_el2(read_icc_sre_el2() | icc_sre_el3);
	write_icc_sre_el1(ICC_SRE_SRE_BIT);
	isb();

	/* Switch to secure state. */
	write_scr_el3(scr_el3 & (~SCR_NS_BIT));
	isb();

	/* Write the secure ICC_SRE_EL1 register */
	write_icc_sre_el1(ICC_SRE_SRE_BIT);
	isb();

	/* Program the idle priority in the PMR */
	write_icc_pmr_el1(GIC_PRI_MASK);

	/* Enable Group0 interrupts */
	write_icc_igrpen0_el1(IGRPEN1_EL1_ENABLE_G0_BIT);

	/* Enable Group1 Secure interrupts */
	write_icc_igrpen1_el3(read_icc_igrpen1_el3() |
				IGRPEN1_EL3_ENABLE_G1S_BIT);
	isb();
}

/*******************************************************************************
 * This function initialises the GIC Redistributor interface of the calling CPU
 * (identified by the 'proc_num' parameter) based upon the data provided by the
 * platform while initialising the driver.
 ******************************************************************************/
void gicv3_rdistif_init(unsigned int proc_num)
{
	uintptr_t gicr_base;
	unsigned int bitmap;
	uint32_t ctlr;

	assert(gicv3_driver_data != NULL);
	assert(proc_num < gicv3_driver_data->rdistif_num);
	assert(gicv3_driver_data->rdistif_base_addrs != NULL);
	assert(gicv3_driver_data->gicd_base != 0U);

	ctlr = gicd_read_ctlr(gicv3_driver_data->gicd_base);
	assert((ctlr & CTLR_ARE_S_BIT) != 0U);

	assert(IS_IN_EL3());

	/* Power on redistributor */
	gicv3_rdistif_on(proc_num);

	gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
	assert(gicr_base != 0U);

	/* Set the default attribute of all SGIs and (E)PPIs */
	gicv3_ppi_sgi_config_defaults(gicr_base);

	bitmap = gicv3_secure_ppi_sgi_config_props(gicr_base,
			gicv3_driver_data->interrupt_props,
			gicv3_driver_data->interrupt_props_num);

	/* Enable interrupt groups as required, if not already */
	if ((ctlr & bitmap) != bitmap) {
		gicd_set_ctlr(gicv3_driver_data->gicd_base, bitmap, RWP_TRUE);
	}
}

/******************************************************************************
 * ARM common helper to initialize the GIC. Only invoked by BL31
 *****************************************************************************/
void __init plat_arm_gic_init(void)
{
	gicv3_distif_init();
	gicv3_rdistif_init(plat_my_core_pos());
	gicv3_cpuif_enable(plat_my_core_pos());
}

系统下电,CPU如何与GIC redistributor disconnect

当core下电之后,需要将core中cpu interface与gic中的redistributor进行disconnect,这样将来gic才不会将中断发送给core。

disconnection的流程如下所示:

arm架构部署es 启动报错 arm架构pe系统_PSCI_07

下电流程,行为描述

  1. 执行在core的程序,先将cpu interface的中断组使能给disable。
  2. 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置高,表示要disconnect redistributor。
  3. redistributor给cpu interface发送 Quiesce包。
  4. cpu interface清掉内部所有pending的中断。
  5. 清除完毕后,cpu interface回发Quiesce Acknowledge包给redistibutor。
  6. redistributor收到cpu interface回发的响应之后,将GICR_WAKER.ChildrenAsleep位给置高,表示disconnect完成。
  7. 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为1,如果不是,表示redistributor还没有完成connect操作,就继续查询。
  8. 如果查询到1,表示disconnect完成。

其汇编代码如下:

arm架构部署es 启动报错 arm架构pe系统_GICR_WAKER_08

/******************************************************************************
 * This function marks the core as asleep in the re-distributor and ensures
 * that the interface is quiescent.
 *****************************************************************************/
void gicv3_rdistif_mark_core_asleep(uintptr_t gicr_base)
{
	/* Mark the connected core as asleep */
	/* 执行在core的程序,将GICR_WAKER.ProcessorSleep位给置高,
	 * 表示要disconnect redistributor。
	 */
	gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) | WAKER_PS_BIT);

	/* Wait till the WAKER_CA_BIT changes to 1 */
	/* 执行在core的程序,查询GICR_WAKER.ChildAsleep位是否为1,
	 * 如果不是,表示redistributor还没有完成connect操作,就继续查询。
	 */
	while ((gicr_read_waker(gicr_base) & WAKER_CA_BIT) == 0U) {
	}
}

/*******************************************************************************
 * This function disables the GIC CPU interface of the calling CPU using
 * only system register accesses.
 ******************************************************************************/
void gicv3_cpuif_disable(unsigned int proc_num)
{
	uintptr_t gicr_base;

	assert(gicv3_driver_data != NULL);
	assert(proc_num < gicv3_driver_data->rdistif_num);
	assert(gicv3_driver_data->rdistif_base_addrs != NULL);

	assert(IS_IN_EL3());

	/* Disable legacy interrupt bypass */
	write_icc_sre_el3(read_icc_sre_el3() |
			  (ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT));
	
	/* 1. 执行在core的程序,先将cpu interface的中断组使能给disable。 */
	/* Disable Group0 interrupts */
	write_icc_igrpen0_el1(read_icc_igrpen0_el1() &
			      ~IGRPEN1_EL1_ENABLE_G0_BIT);

	/* Disable Group1 Secure and Non-Secure interrupts */
	write_icc_igrpen1_el3(read_icc_igrpen1_el3() &
			      ~(IGRPEN1_EL3_ENABLE_G1NS_BIT |
			      IGRPEN1_EL3_ENABLE_G1S_BIT));

	/* Synchronise accesses to group enable registers */
	isb();

	/* Mark the connected core as asleep */
	gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
	assert(gicr_base != 0U);
	gicv3_rdistif_mark_core_asleep(gicr_base);
}

以下是GIC-600 支持redistributor单独下电。

static void gic600_pwr_off(uintptr_t base)
{
	/* Wait until group not transitioning */
	gicr_wait_group_not_in_transit(base);

	/* Power off redistributor */
	gicr_write_pwrr(base, PWRR_OFF);

	/*
	 * If this is the last man, turning this redistributor frame off will
	 * result in the group itself being powered off and RDGPD = 1.
	 * In that case, wait as long as it's in transition, or has aborted
	 * the transition altogether for any reason.
	 */
	if ((gicr_read_pwrr(base) & PWRR_RDGPD) != 0U) {
		/* Wait until group not transitioning */
		gicr_wait_group_not_in_transit(base);
	}
}

/*
 * The Arm GIC-600 and GIC-Clayton models have their redistributors
 * powered down at reset.
 */
/*
 * Power off GIC-600 redistributor (if configured and detected)
 */
void gicv3_rdistif_off(unsigned int proc_num)
{
#if GICV3_SUPPORT_GIC600
	uintptr_t gicr_base = get_gicr_base(proc_num);

	/* Attempt to power redistributor off */
	if (gicv3_redists_need_power_mgmt(gicr_base)) {
		gic600_pwr_off(gicr_base);
	}
#endif
}