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:
parent
4e9f73b0dd
commit
0a3fe40505
2 changed files with 37 additions and 14 deletions
|
|
@ -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, \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue