Skip to content

Commit

Permalink
drivers: plic: support multiple instances
Browse files Browse the repository at this point in the history
Add support for multiple PLIC instances

Signed-off-by: Yong Cong Sin <[email protected]>
  • Loading branch information
ycsin committed Sep 25, 2023
1 parent 652a78c commit e923b1b
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 33 deletions.
8 changes: 4 additions & 4 deletions arch/riscv/core/irq_manage.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ FUNC_NORETURN void z_irq_spurious(const void *unused)
LOG_ERR("Spurious interrupt detected! IRQ: %ld", mcause);
#if defined(CONFIG_RISCV_HAS_PLIC)
if (mcause == RISCV_MACHINE_EXT_IRQ) {
LOG_ERR("PLIC interrupt line causing the IRQ: %d",
riscv_plic_get_irq());
unsigned int save_irq = riscv_plic_get_irq();
const struct device *save_dev = riscv_plic_get_dev();

LOG_ERR("PLIC interrupt line causing the IRQ: %d (%p)", save_irq, save_dev);
}
#endif
z_riscv_fatal_error(K_ERR_SPURIOUS_IRQ, NULL);
Expand All @@ -42,8 +44,6 @@ int arch_irq_connect_dynamic(unsigned int irq, unsigned int priority,

#if defined(CONFIG_RISCV_HAS_PLIC)
if (irq_get_level(irq) == 2) {
irq = irq_from_level_2(irq);

riscv_plic_set_priority(irq, priority);
}
#else
Expand Down
53 changes: 33 additions & 20 deletions drivers/interrupt_controller/intc_plic.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ struct plic_config {
};

static uint32_t save_irq;
static const struct device *save_dev;

static inline uint32_t get_plic_enabled_size(const struct device *dev)
{
Expand All @@ -56,15 +57,15 @@ static inline uint32_t get_plic_enabled_size(const struct device *dev)
/**
* @brief Determine the PLIC device from the IRQ
*
* FIXME: This function is currently hardcoded to return the first instance.
*
* @param irq IRQ number
*
* @return PLIC device of that IRQ
*/
static inline const struct device *get_plic_dev_from_irq(uint32_t irq)
{
return DEVICE_DT_INST_GET(0);
const struct device *dev = z_get_sw_isr_device_from_irq(irq);

return dev == NULL ? DEVICE_DT_INST_GET(0) : dev;
}

/**
Expand Down Expand Up @@ -107,11 +108,12 @@ void riscv_plic_irq_enable(uint32_t irq)
const struct device *dev = get_plic_dev_from_irq(irq);
const struct plic_config *config = dev->config;
volatile uint32_t *en = (volatile uint32_t *) config->irq_en;
const uint32_t local_irq = irq_from_level_2(irq);
uint32_t key;

key = irq_lock();
en += (irq >> 5);
*en |= (1 << (irq & 31));
en += (local_irq >> 5);
*en |= (1 << (local_irq & 31));
irq_unlock(key);
}

Expand All @@ -130,11 +132,12 @@ void riscv_plic_irq_disable(uint32_t irq)
const struct device *dev = get_plic_dev_from_irq(irq);
const struct plic_config *config = dev->config;
volatile uint32_t *en = (volatile uint32_t *) config->irq_en;
const uint32_t local_irq = irq_from_level_2(irq);
uint32_t key;

key = irq_lock();
en += (irq >> 5);
*en &= ~(1 << (irq & 31));
en += (local_irq >> 5);
*en &= ~(1 << (local_irq & 31));
irq_unlock(key);
}

Expand All @@ -151,9 +154,10 @@ int riscv_plic_irq_is_enabled(uint32_t irq)
const struct device *dev = get_plic_dev_from_irq(irq);
const struct plic_config *config = dev->config;
volatile uint32_t *en = (volatile uint32_t *) config->irq_en;
const uint32_t local_irq = irq_from_level_2(irq);

en += (irq >> 5);
return !!(*en & (1 << (irq & 31)));
en += (local_irq >> 5);
return !!(*en & (1 << (local_irq & 31)));
}

/**
Expand All @@ -171,11 +175,12 @@ void riscv_plic_set_priority(uint32_t irq, uint32_t priority)
const struct device *dev = get_plic_dev_from_irq(irq);
const struct plic_config *config = dev->config;
volatile uint32_t *prio = (volatile uint32_t *) config->prio;
const uint32_t local_irq = irq_from_level_2(irq);

if (priority > config->max_prio)
priority = config->max_prio;

prio += irq;
prio += local_irq;
*prio = priority;
}

Expand All @@ -185,53 +190,61 @@ void riscv_plic_set_priority(uint32_t irq, uint32_t priority)
* This routine returns the RISCV PLIC-specific interrupt line causing an
* interrupt.
*
* @param dev Optional device pointer to get the interrupt line's controller
*
* @return PLIC-specific interrupt line causing an interrupt.
*/
int riscv_plic_get_irq(void)
unsigned int riscv_plic_get_irq(void)
{
return save_irq;
}

const struct device *riscv_plic_get_dev(void)
{
return save_dev;
}

static void plic_irq_handler(const struct device *dev)
{
const struct plic_config *config = dev->config;
volatile struct plic_regs_t *regs = (volatile struct plic_regs_t *) config->reg;
uint32_t irq;
struct _isr_table_entry *ite;
int edge_irq;

/* Get the IRQ number generating the interrupt */
irq = regs->claim_complete;
const uint32_t local_irq = regs->claim_complete;

/*
* Save IRQ in save_irq. To be used, if need be, by
* subsequent handlers registered in the _sw_isr_table table,
* as IRQ number held by the claim_complete register is
* cleared upon read.
*/
save_irq = irq;
save_irq = local_irq;
save_dev = dev;

/*
* If the IRQ is out of range, call z_irq_spurious.
* A call to z_irq_spurious will not return.
*/
if (irq == 0U || irq >= config->num_irqs)
if (local_irq == 0U || local_irq >= config->num_irqs)
z_irq_spurious(NULL);

edge_irq = riscv_plic_is_edge_irq(dev, irq);
edge_irq = riscv_plic_is_edge_irq(dev, local_irq);

/*
* For edge triggered interrupts, write to the claim_complete register
* to indicate to the PLIC controller that the IRQ has been handled
* for edge triggered interrupts.
*/
if (edge_irq)
regs->claim_complete = save_irq;
regs->claim_complete = local_irq;

irq += CONFIG_2ND_LVL_ISR_TBL_OFFSET;
const uint32_t irq = irq_to_level_2(local_irq) | z_get_sw_isr_irq_from_device(dev);
const unsigned int isr_offset = z_get_sw_isr_table_idx(irq);

/* Call the corresponding IRQ handler in _sw_isr_table */
ite = (struct _isr_table_entry *)&_sw_isr_table[irq];
ite = (struct _isr_table_entry *)&_sw_isr_table[isr_offset];
ite->isr(ite->arg);

/*
Expand All @@ -240,7 +253,7 @@ static void plic_irq_handler(const struct device *dev)
* for level triggered interrupts.
*/
if (!edge_irq)
regs->claim_complete = save_irq;
regs->claim_complete = local_irq;
}

/**
Expand Down
19 changes: 14 additions & 5 deletions include/zephyr/drivers/interrupt_controller/riscv_plic.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,34 @@
#ifndef ZEPHYR_INCLUDE_DRIVERS_RISCV_PLIC_H_
#define ZEPHYR_INCLUDE_DRIVERS_RISCV_PLIC_H_

#include <zephyr/device.h>

/**
* @brief Enable interrupt
*
* @param irq interrupt ID
* @param irq Multi-level encoded interrupt ID
*/
void riscv_plic_irq_enable(uint32_t irq);

/**
* @brief Disable interrupt
*
* @param irq interrupt ID
* @param irq Multi-level encoded interrupt ID
*/
void riscv_plic_irq_disable(uint32_t irq);

/**
* @brief Check if an interrupt is enabled
*
* @param irq interrupt ID
* @param irq Multi-level encoded interrupt ID
* @return Returns true if interrupt is enabled, false otherwise
*/
int riscv_plic_irq_is_enabled(uint32_t irq);

/**
* @brief Set interrupt priority
*
* @param irq interrupt ID
* @param irq Multi-level encoded interrupt ID
* @param prio interrupt priority
*/
void riscv_plic_set_priority(uint32_t irq, uint32_t prio);
Expand All @@ -47,6 +49,13 @@ void riscv_plic_set_priority(uint32_t irq, uint32_t prio);
*
* @return Returns the ID of an active interrupt
*/
int riscv_plic_get_irq(void);
unsigned int riscv_plic_get_irq(void);

/**
* @brief Get active interrupt controller device
*
* @return Returns device pointer of the active interrupt device
*/
const struct device *riscv_plic_get_dev(void);

#endif /* ZEPHYR_INCLUDE_DRIVERS_RISCV_PLIC_H_ */
4 changes: 0 additions & 4 deletions soc/riscv/riscv-privileged/common/soc_common_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ void arch_irq_enable(unsigned int irq)
unsigned int level = irq_get_level(irq);

if (level == 2) {
irq = irq_from_level_2(irq);
riscv_plic_irq_enable(irq);
return;
}
Expand All @@ -67,7 +66,6 @@ void arch_irq_disable(unsigned int irq)
unsigned int level = irq_get_level(irq);

if (level == 2) {
irq = irq_from_level_2(irq);
riscv_plic_irq_disable(irq);
return;
}
Expand All @@ -88,7 +86,6 @@ int arch_irq_is_enabled(unsigned int irq)
unsigned int level = irq_get_level(irq);

if (level == 2) {
irq = irq_from_level_2(irq);
return riscv_plic_irq_is_enabled(irq);
}
#endif
Expand All @@ -104,7 +101,6 @@ void z_riscv_irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flag
unsigned int level = irq_get_level(irq);

if (level == 2) {
irq = irq_from_level_2(irq);
riscv_plic_set_priority(irq, prio);
}
}
Expand Down

0 comments on commit e923b1b

Please sign in to comment.