drivers: intc_gicv3: fix gic_rdists[cpu] points to a wrong address

In old way, gic_rdists[cpu] is calculated via MPIDR_TO_CORE(), but in
real hardware, MPIDR_TO_CORE() isn't a value increment from 0 one by
one, and that will lead gic_rdists[cpu] to point to a wrong address.

GICv3 provides the register GICR_TYPER[1] and it has a field named
Affinity_Value. This field can help to determine where gic_rdists[cpu]
should point.

Signed-off-by: Huifeng Zhang <Huifeng.Zhang@arm.com>
This commit is contained in:
Huifeng Zhang 2022-07-05 13:12:12 +08:00 committed by Fabio Baltieri
commit 68b10e8572
2 changed files with 51 additions and 1 deletions

View file

@ -477,12 +477,55 @@ static void gicv3_dist_init(void)
#endif #endif
} }
static uint64_t arm_gic_mpidr_to_affinity(uint64_t mpidr)
{
uint64_t aff3, aff2, aff1, aff0;
#if defined(CONFIG_ARM)
/* There is no Aff3 in AArch32 MPIDR */
aff3 = 0;
#else
aff3 = MPIDR_AFFLVL(mpidr, 3);
#endif
aff2 = MPIDR_AFFLVL(mpidr, 2);
aff1 = MPIDR_AFFLVL(mpidr, 1);
aff0 = MPIDR_AFFLVL(mpidr, 0);
return (aff3 << 24 | aff2 << 16 | aff1 << 8 | aff0);
}
static mem_addr_t arm_gic_iterate_rdists(void)
{
uint64_t aff = arm_gic_mpidr_to_affinity(GET_MPIDR());
for (mem_addr_t rdist_addr = GIC_RDIST_BASE;
rdist_addr < GIC_RDIST_BASE + GIC_RDIST_SIZE;
rdist_addr += 0x20000) {
uint64_t val = sys_read64(rdist_addr + GICR_TYPER);
if (GICR_TYPER_AFFINITY_VALUE_GET(val) == aff) {
return rdist_addr;
}
if (GICR_TYPER_LAST_GET(val) == 1) {
return (mem_addr_t)NULL;
}
}
return (mem_addr_t)NULL;
}
static void __arm_gic_init(void) static void __arm_gic_init(void)
{ {
uint8_t cpu; uint8_t cpu;
mem_addr_t gic_rd_base;
cpu = arch_curr_cpu()->id; cpu = arch_curr_cpu()->id;
gic_rdists[cpu] = GIC_RDIST_BASE + MPIDR_TO_CORE(GET_MPIDR()) * 0x20000; gic_rd_base = arm_gic_iterate_rdists();
__ASSERT(gic_rd_base != (mem_addr_t)NULL, "");
gic_rdists[cpu] = gic_rd_base;
#ifdef CONFIG_GIC_V3_ITS #ifdef CONFIG_GIC_V3_ITS
/* Enable LPIs in Redistributor */ /* Enable LPIs in Redistributor */

View file

@ -30,6 +30,7 @@
*/ */
#define GIC_RDIST_BASE DT_REG_ADDR_BY_IDX(DT_INST(0, arm_gic), 1) #define GIC_RDIST_BASE DT_REG_ADDR_BY_IDX(DT_INST(0, arm_gic), 1)
#define GIC_RDIST_SIZE DT_REG_SIZE_BY_IDX(DT_INST(0, arm_gic), 1)
/* SGI base is at 64K offset from Redistributor */ /* SGI base is at 64K offset from Redistributor */
#define GICR_SGI_BASE_OFF 0x10000 #define GICR_SGI_BASE_OFF 0x10000
@ -62,6 +63,12 @@
#define GICR_CTLR_RWP 3 #define GICR_CTLR_RWP 3
/* GICR_TYPER */ /* GICR_TYPER */
#define GICR_TYPER_AFFINITY_VALUE_SHIFT 32
#define GICR_TYPER_AFFINITY_VALUE_MASK 0xFFFFFFFFUL
#define GICR_TYPER_AFFINITY_VALUE_GET(_val) MASK_GET(_val, GICR_TYPER_AFFINITY_VALUE)
#define GICR_TYPER_LAST_SHIFT 4
#define GICR_TYPER_LAST_MASK 0x10UL
#define GICR_TYPER_LAST_GET(_val) MASK_GET(_val, GICR_TYPER_LAST)
#define GICR_TYPER_PROCESSOR_NUMBER_SHIFT 8 #define GICR_TYPER_PROCESSOR_NUMBER_SHIFT 8
#define GICR_TYPER_PROCESSOR_NUMBER_MASK 0xFFFFUL #define GICR_TYPER_PROCESSOR_NUMBER_MASK 0xFFFFUL
#define GICR_TYPER_PROCESSOR_NUMBER_GET(_val) MASK_GET(_val, GICR_TYPER_PROCESSOR_NUMBER) #define GICR_TYPER_PROCESSOR_NUMBER_GET(_val) MASK_GET(_val, GICR_TYPER_PROCESSOR_NUMBER)