drivers: intc: plic: set edge-triggered register address using compat

Define the edge-trigger register base address based on whether
the PLIC node in the devicetree has an additional compatible
that supports edge-triggered interrupt.

Limited the implementation to Andes NCEPLIC100 only, updated
the devicetree binding of `andes_v5_ae350` accordingly.

Signed-off-by: Yong Cong Sin <ycsin@meta.com>
This commit is contained in:
Yong Cong Sin 2023-11-25 22:31:49 +08:00 committed by Chris Friedt
parent 4e9f73b0dd
commit 0a3fe40505
2 changed files with 37 additions and 14 deletions

View file

@ -42,7 +42,19 @@
* However, it is defined and supported by at least the Andes & Telink datasheet, and supported
* in Linux's SiFive PLIC driver
*/
#define PLIC_TRIG_LEVEL ((uint32_t)~BIT(0))
#define PLIC_TRIG_EDGE ((uint32_t)BIT(0))
#define PLIC_DRV_HAS_COMPAT(compat) \
DT_NODE_HAS_COMPAT(DT_COMPAT_GET_ANY_STATUS_OKAY(DT_DRV_COMPAT), compat)
#if PLIC_DRV_HAS_COMPAT(andestech_nceplic100)
#define PLIC_SUPPORTS_TRIG_TYPE 1
#define PLIC_REG_TRIG_TYPE_WIDTH 1
#define PLIC_REG_TRIG_TYPE_OFFSET 0x1080
#else
/* Trigger-type not supported */
#define PLIC_REG_TRIG_TYPE_WIDTH 0
#endif
/* PLIC registers are 32-bit memory-mapped */
#define PLIC_REG_SIZE 32
@ -124,7 +136,7 @@ static inline const struct device *get_plic_dev_from_irq(uint32_t irq)
}
/**
* @brief return edge irq value or zero
* @brief Return the value of the trigger type register for the IRQ
*
* In the event edge irq is enable this will return the trigger
* value of the irq. In the event edge irq is not supported this
@ -133,14 +145,19 @@ static inline const struct device *get_plic_dev_from_irq(uint32_t irq)
* @param dev PLIC-instance device
* @param local_irq PLIC-instance IRQ number to add to the trigger
*
* @return irq value when enabled 0 otherwise
* @return Trigger type register value if PLIC supports trigger type, PLIC_TRIG_LEVEL otherwise
*/
static int riscv_plic_is_edge_irq(const struct device *dev, uint32_t local_irq)
static uint32_t __maybe_unused riscv_plic_irq_trig_val(const struct device *dev, uint32_t local_irq)
{
if (!IS_ENABLED(PLIC_SUPPORTS_TRIG_TYPE)) {
return PLIC_TRIG_LEVEL;
}
const struct plic_config *config = dev->config;
mem_addr_t trig_addr = config->trig + local_irq_to_reg_offset(local_irq);
uint32_t offset = local_irq * PLIC_REG_TRIG_TYPE_WIDTH;
return sys_read32(trig_addr) & BIT(local_irq & PLIC_REG_MASK);
return sys_read32(trig_addr) & GENMASK(offset + PLIC_REG_TRIG_TYPE_WIDTH - 1, offset);
}
static void plic_irq_enable_set_state(uint32_t irq, bool enable)
@ -259,7 +276,7 @@ static void plic_irq_handler(const struct device *dev)
const struct plic_config *config = dev->config;
mem_addr_t claim_complete_addr = get_claim_complete_addr(dev);
struct _isr_table_entry *ite;
int edge_irq;
uint32_t __maybe_unused trig_val;
/* Get the IRQ number generating the interrupt */
const uint32_t local_irq = sys_read32(claim_complete_addr);
@ -291,16 +308,16 @@ static void plic_irq_handler(const struct device *dev)
z_irq_spurious(NULL);
}
edge_irq = riscv_plic_is_edge_irq(dev, local_irq);
#if IS_ENABLED(PLIC_DRV_HAS_COMPAT(andestech_nceplic100))
trig_val = riscv_plic_irq_trig_val(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.
* Edge-triggered interrupts on Andes NCEPLIC100 have to be acknowledged first before
* getting handled so that we don't miss on the next edge-triggered interrupt.
*/
if (edge_irq != 0) {
if (trig_val == PLIC_TRIG_EDGE) {
sys_write32(local_irq, claim_complete_addr);
}
#endif
const uint32_t parent_irq = COND_CODE_1(IS_ENABLED(CONFIG_DYNAMIC_INTERRUPTS),
(z_get_sw_isr_irq_from_device(dev)), (0U));
@ -318,9 +335,14 @@ static void plic_irq_handler(const struct device *dev)
* PLIC controller that the IRQ has been handled
* for level triggered interrupts.
*/
if (edge_irq == 0) {
#if IS_ENABLED(PLIC_DRV_HAS_COMPAT(andestech_nceplic100))
/* For NCEPLIC100, handle only if level-triggered */
if (trig_val == PLIC_TRIG_LEVEL) {
sys_write32(local_irq, claim_complete_addr);
}
#else
sys_write32(local_irq, claim_complete_addr);
#endif
}
/**
@ -495,7 +517,8 @@ SHELL_CMD_ARG_REGISTER(plic, &plic_cmds, "PLIC shell commands",
.prio = PLIC_BASE_ADDR(n) + PLIC_REG_PRIO_OFFSET, \
.irq_en = PLIC_BASE_ADDR(n) + PLIC_REG_IRQ_EN_OFFSET, \
.reg = PLIC_BASE_ADDR(n) + PLIC_REG_REGS_OFFSET, \
.trig = PLIC_BASE_ADDR(n) + PLIC_REG_TRIG_TYPE_OFFSET, \
IF_ENABLED(PLIC_SUPPORTS_TRIG_TYPE, \
(.trig = PLIC_BASE_ADDR(n) + PLIC_REG_TRIG_TYPE_OFFSET,)) \
.max_prio = DT_INST_PROP(n, riscv_max_priority), \
.num_irqs = DT_INST_PROP(n, riscv_ndev), \
.irq_config_func = plic_irq_config_func_##n, \

View file

@ -167,7 +167,7 @@
ranges;
plic0: interrupt-controller@e4000000 {
compatible = "sifive,plic-1.0.0";
compatible = "sifive,plic-1.0.0", "andestech,nceplic100";
#address-cells = <1>;
#interrupt-cells = <2>;
interrupt-controller;