[sw, dif_plic] Reset the PLIC peripheral on initialisation
Resetting a peripheral on initialisation ensures that there is no
implicit configuration, for example, from boot_rom after jump to
flash.
Signed-off-by: Silvestrs Timofejevs <silvestrst@lowrisc.org>
diff --git a/sw/device/lib/dif/dif_plic.c b/sw/device/lib/dif/dif_plic.c
index e4b3d2f..da25128 100644
--- a/sw/device/lib/dif/dif_plic.c
+++ b/sw/device/lib/dif/dif_plic.c
@@ -114,6 +114,23 @@
};
/**
+ * Get a number of IE, IP or LE registers (IE00, IE01, ...).
+ *
+ * With more than 32 IRQ sources, there is a multiple of these registers to
+ * accommodate all the bits (1 bit per IRQ source).
+ */
+static size_t plic_num_irq_reg(void) {
+ size_t register_num = kDifPlicIrqIdLast / PLIC_ID_TO_INDEX_REG_SIZE;
+
+ // Determine whether there are remaining IRQ sources that have a register.
+ if ((kDifPlicIrqIdLast % PLIC_ID_TO_INDEX_REG_SIZE) != 0) {
+ ++register_num;
+ }
+
+ return register_num;
+}
+
+/**
* Get an IE, IP or LE register offset (IE00, IE01, ...) from an IRQ source ID.
*
* With more than 32 IRQ sources, there is a multiple of these registers to
@@ -169,6 +186,14 @@
}
/**
+ * Get a total number of priority registers (one for every IRQ source).
+ *
+ * The IRQ source IDs start from 1, so the last IRQ ID variant is also
+ * the number of priority registers (one per IRQ source).
+ */
+static size_t plic_num_priority_reg(void) { return kDifPlicIrqIdLast; }
+
+/**
* Get a PRIO register offset (PRIO0, PRIO1, ...) from an IRQ source ID.
*
* There is one PRIO register per IRQ source, this function calculates the IRQ
@@ -179,6 +204,41 @@
return RV_PLIC_PRIO0_REG_OFFSET + offset;
}
+/**
+ * Reset the requested PLIC peripheral.
+ *
+ * This function resets all the relevant PLIC registers, apart from the CC
+ * register. There is no reliable way of knowing the ID of an IRQ that has
+ * claimed the CC register, so we assume that the previous "owner" of the
+ * resource has cleared/completed the CC access.
+ */
+static void plic_reset(const dif_plic_t *plic) {
+ // Clear all of Interrupt Enable and Level/Edge registers.
+ for (int i = 0; i < plic_num_irq_reg(); ++i) {
+ ptrdiff_t offset = RV_PLIC_IE00_REG_OFFSET + (i * sizeof(uint32_t));
+ mmio_region_write32(plic->base_addr, offset, 0);
+
+ offset = RV_PLIC_LE0_REG_OFFSET + (i * sizeof(uint32_t));
+ mmio_region_write32(plic->base_addr, offset, 0);
+ }
+
+ // Clear all of the priority registers.
+ for (int i = 0; i < plic_num_priority_reg(); ++i) {
+ ptrdiff_t offset = RV_PLIC_PRIO0_REG_OFFSET + (i * sizeof(uint32_t));
+ mmio_region_write32(plic->base_addr, offset, 0);
+ }
+
+ // Clear all of the target threshold registers.
+ for (dif_plic_target_t target = kDifPlicTargetIbex0;
+ target <= kDifPlicTargetLast; ++target) {
+ ptrdiff_t offset = plic_target_reg_offsets[target].threshold;
+ mmio_region_write32(plic->base_addr, offset, 0);
+ }
+
+ // Clear software interrupt pending register.
+ mmio_region_write32(plic->base_addr, RV_PLIC_MSIP0_REG_OFFSET, 0);
+}
+
bool dif_plic_init(mmio_region_t base_addr, dif_plic_t *plic) {
if (plic == NULL) {
return false;
@@ -186,6 +246,8 @@
plic->base_addr = base_addr;
+ plic_reset(plic);
+
return true;
}