/******************************************************************************
* 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的流程如下所示:
/******************************************************************************
* 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
}