From 6843240196356e7e287ff08a4aec502483f57c91 Mon Sep 17 00:00:00 2001 From: Yong Cong Sin Date: Tue, 22 Oct 2024 10:22:52 +0800 Subject: [PATCH] drivers: intc: plic: use per-instance spinlock Instead of doing an `irq_lock()`, use per-instance spinlock instead. Refactored out an unlocked version of `local_irq_is_enabled` from `riscv_plic_irq_is_enabled()` to achieve that. Signed-off-by: Yong Cong Sin --- drivers/interrupt_controller/intc_plic.c | 67 ++++++++++++++---------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/drivers/interrupt_controller/intc_plic.c b/drivers/interrupt_controller/intc_plic.c index af16592d982a..26468d40713f 100644 --- a/drivers/interrupt_controller/intc_plic.c +++ b/drivers/interrupt_controller/intc_plic.c @@ -103,6 +103,7 @@ struct plic_stats { }; struct plic_data { + struct k_spinlock lock; #ifdef CONFIG_PLIC_SHELL_IRQ_COUNT struct plic_stats stats; @@ -279,11 +280,13 @@ static void plic_irq_enable_set_state(uint32_t irq, bool enable) */ void riscv_plic_irq_enable(uint32_t irq) { - uint32_t key = irq_lock(); + const struct device *dev = get_plic_dev_from_irq(irq); + struct plic_data *data = dev->data; + k_spinlock_key_t key = k_spin_lock(&data->lock); plic_irq_enable_set_state(irq, true); - irq_unlock(key); + k_spin_unlock(&data->lock, key); } /** @@ -298,35 +301,26 @@ void riscv_plic_irq_enable(uint32_t irq) */ void riscv_plic_irq_disable(uint32_t irq) { - uint32_t key = irq_lock(); + const struct device *dev = get_plic_dev_from_irq(irq); + struct plic_data *data = dev->data; + k_spinlock_key_t key = k_spin_lock(&data->lock); plic_irq_enable_set_state(irq, false); - irq_unlock(key); + k_spin_unlock(&data->lock, key); } -/** - * @brief Check if a riscv PLIC-specific interrupt line is enabled - * - * This routine checks if a RISCV PLIC-specific interrupt line is enabled. - * @param irq IRQ number to check - * - * @return 1 or 0 - */ -int riscv_plic_irq_is_enabled(uint32_t irq) +/* Check if the local IRQ of a PLIC instance is enabled */ +static int local_irq_is_enabled(const struct device *dev, uint32_t local_irq) { - const struct device *dev = get_plic_dev_from_irq(irq); - const uint32_t local_irq = irq_from_level_2(irq); uint32_t bit_position = local_irq & PLIC_REG_MASK; - uint32_t en_value; int is_enabled = IS_ENABLED(CONFIG_PLIC_IRQ_AFFINITY) ? 0 : 1; - uint32_t key = irq_lock(); for (uint32_t cpu_num = 0; cpu_num < arch_num_cpus(); cpu_num++) { mem_addr_t en_addr = get_context_en_addr(dev, cpu_num) + local_irq_to_reg_offset(local_irq); + uint32_t en_value = sys_read32(en_addr); - en_value = sys_read32(en_addr); if (IS_ENABLED(CONFIG_PLIC_IRQ_AFFINITY)) { is_enabled |= !!(en_value & BIT(bit_position)); } else { @@ -334,11 +328,31 @@ int riscv_plic_irq_is_enabled(uint32_t irq) } } - irq_unlock(key); - return is_enabled; } +/** + * @brief Check if a riscv PLIC-specific interrupt line is enabled + * + * This routine checks if a RISCV PLIC-specific interrupt line is enabled. + * @param irq IRQ number to check + * + * @return 1 or 0 + */ +int riscv_plic_irq_is_enabled(uint32_t irq) +{ + const struct device *dev = get_plic_dev_from_irq(irq); + struct plic_data *data = dev->data; + const uint32_t local_irq = irq_from_level_2(irq); + int ret = 0; + + K_SPINLOCK(&data->lock) { + ret = local_irq_is_enabled(dev, local_irq); + } + + return ret; +} + /** * @brief Set priority of a riscv PLIC-specific interrupt line * @@ -413,9 +427,10 @@ const struct device *riscv_plic_get_dev(void) int riscv_plic_irq_set_affinity(uint32_t irq, uint32_t cpumask) { const struct device *dev = get_plic_dev_from_irq(irq); - const struct plic_data *data = dev->data; + struct plic_data *data = dev->data; __maybe_unused const struct plic_config *config = dev->config; const uint32_t local_irq = irq_from_level_2(irq); + k_spinlock_key_t key; if (local_irq >= config->nr_irqs) { __ASSERT(false, "overflow: irq %d, local_irq %d", irq, local_irq); @@ -427,17 +442,15 @@ int riscv_plic_irq_set_affinity(uint32_t irq, uint32_t cpumask) return -EINVAL; } - uint32_t key = irq_lock(); - + key = k_spin_lock(&data->lock); /* Updated irq_cpumask for next time setting plic enable register */ data->irq_cpumask[local_irq] = (plic_cpumask_t)cpumask; /* If irq is enabled, apply the new irq affinity */ - if (riscv_plic_irq_is_enabled(irq)) { - riscv_plic_irq_enable(irq); + if (local_irq_is_enabled(dev, local_irq)) { + plic_irq_enable_set_state(irq, true); } - - irq_unlock(key); + k_spin_unlock(&data->lock, key); return 0; }