fix(mediatek): support saving/restoring GICR registers
The GICR_IPRIORITYR[x] registers are not saved or restored in the
original design. When the kernel tries to use them, such as the
pseudo-NMI, it leads crashes and freezes. This patch adds support for
saving/restoring GICR registers.
Change-Id: I9718a75a1410ca14826710dfdf5f3226299fa6e2
diff --git a/plat/mediatek/drivers/gic600/mt_gic_v3.c b/plat/mediatek/drivers/gic600/mt_gic_v3.c
index cca5d0a..85f9e37 100644
--- a/plat/mediatek/drivers/gic600/mt_gic_v3.c
+++ b/plat/mediatek/drivers/gic600/mt_gic_v3.c
@@ -42,13 +42,19 @@
struct gic_chip_data {
/* All cores share the same configuration */
+ unsigned int saved_ctlr;
unsigned int saved_group;
unsigned int saved_enable;
unsigned int saved_conf0;
unsigned int saved_conf1;
unsigned int saved_grpmod;
+ unsigned int saved_ispendr;
+ unsigned int saved_isactiver;
+ unsigned int saved_nsacr;
/* Per-core sgi */
unsigned int saved_sgi[PLATFORM_CORE_COUNT];
+ /* Per-core priority */
+ unsigned int saved_prio[PLATFORM_CORE_COUNT][GICR_NUM_REGS(IPRIORITYR)];
};
static struct gic_chip_data gic_data;
@@ -94,53 +100,84 @@
void mt_gic_rdistif_save(void)
{
- unsigned int proc_num;
+ unsigned int i, proc_num;
uintptr_t gicr_base;
proc_num = plat_my_core_pos();
gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
+ /*
+ * Wait for any write to GICR_CTLR to complete before trying to save any
+ * state.
+ */
+ gicr_wait_for_pending_write(gicr_base);
+
+ gic_data.saved_ctlr = mmio_read_32(gicr_base + GICR_CTLR);
gic_data.saved_group = mmio_read_32(gicr_base + GICR_IGROUPR0);
gic_data.saved_enable = mmio_read_32(gicr_base + GICR_ISENABLER0);
gic_data.saved_conf0 = mmio_read_32(gicr_base + GICR_ICFGR0);
gic_data.saved_conf1 = mmio_read_32(gicr_base + GICR_ICFGR1);
gic_data.saved_grpmod = mmio_read_32(gicr_base + GICR_IGRPMODR0);
+ gic_data.saved_ispendr = mmio_read_32(gicr_base + GICR_ISPENDR0);
+ gic_data.saved_isactiver = mmio_read_32(gicr_base + GICR_ISACTIVER0);
+ gic_data.saved_nsacr = mmio_read_32(gicr_base + GICR_NSACR);
+
+ for (i = 0U; i < 8U; ++i)
+ gic_data.saved_prio[proc_num][i] = gicr_ipriorityr_read(gicr_base, i);
rdist_has_saved[proc_num] = 1;
}
void mt_gic_rdistif_restore(void)
{
- unsigned int proc_num;
+ unsigned int i, proc_num;
uintptr_t gicr_base;
proc_num = plat_my_core_pos();
if (rdist_has_saved[proc_num] == 1) {
gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
+
mmio_write_32(gicr_base + GICR_IGROUPR0, gic_data.saved_group);
- mmio_write_32(gicr_base + GICR_ISENABLER0,
- gic_data.saved_enable);
+ mmio_write_32(gicr_base + GICR_IGRPMODR0, gic_data.saved_grpmod);
+ mmio_write_32(gicr_base + GICR_NSACR, gic_data.saved_nsacr);
mmio_write_32(gicr_base + GICR_ICFGR0, gic_data.saved_conf0);
mmio_write_32(gicr_base + GICR_ICFGR1, gic_data.saved_conf1);
- mmio_write_32(gicr_base + GICR_IGRPMODR0,
- gic_data.saved_grpmod);
+
+ for (i = 0U; i < 8U; ++i)
+ gicr_ipriorityr_write(gicr_base, i, gic_data.saved_prio[proc_num][i]);
+
+ mmio_write_32(gicr_base + GICR_ISPENDR0, gic_data.saved_ispendr);
+ mmio_write_32(gicr_base + GICR_ISACTIVER0, gic_data.saved_isactiver);
+ mmio_write_32(gicr_base + GICR_ISENABLER0, gic_data.saved_enable);
+ mmio_write_32(gicr_base + GICR_CTLR, gic_data.saved_ctlr);
+
+ gicr_wait_for_pending_write(gicr_base);
}
}
void mt_gic_rdistif_restore_all(void)
{
- unsigned int proc_num;
+ unsigned int i, proc_num;
uintptr_t gicr_base;
for (proc_num = 0; proc_num < PLATFORM_CORE_COUNT; proc_num++) {
gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num];
+
mmio_write_32(gicr_base + GICR_IGROUPR0, gic_data.saved_group);
- mmio_write_32(gicr_base + GICR_ISENABLER0,
- gic_data.saved_enable);
+ mmio_write_32(gicr_base + GICR_IGRPMODR0, gic_data.saved_grpmod);
+ mmio_write_32(gicr_base + GICR_NSACR, gic_data.saved_nsacr);
mmio_write_32(gicr_base + GICR_ICFGR0, gic_data.saved_conf0);
mmio_write_32(gicr_base + GICR_ICFGR1, gic_data.saved_conf1);
- mmio_write_32(gicr_base + GICR_IGRPMODR0,
- gic_data.saved_grpmod);
+
+ for (i = 0U; i < 8U; ++i)
+ gicr_ipriorityr_write(gicr_base, i, gic_data.saved_prio[proc_num][i]);
+
+ mmio_write_32(gicr_base + GICR_ISPENDR0, gic_data.saved_ispendr);
+ mmio_write_32(gicr_base + GICR_ISACTIVER0, gic_data.saved_isactiver);
+ mmio_write_32(gicr_base + GICR_ISENABLER0, gic_data.saved_enable);
+ mmio_write_32(gicr_base + GICR_CTLR, gic_data.saved_ctlr);
+
+ gicr_wait_for_pending_write(gicr_base);
}
}