Introduce interrupt controller descriptor for host APLIC to describe the low-lovel hardare. It includes implementation of the following functions: - aplic_irq_startup() - aplic_irq_shutdown() - aplic_irq_enable() - aplic_irq_disable() - aplic_irq_ack() - aplic_host_irq_end() - aplic_set_irq_affinity()
As APLIC is used in MSI mode it requires to enable/disable interrupts not only for APLIC but also for IMSIC. Thereby for the purpose of aplic_irq_{enable,disable}() it is introduced imsic_irq_{enable,disable)(). For the purpose of aplic_set_irq_affinity() aplic_get_cpu_from_mask() is introduced to get hart id. Also, introduce additional interrupt controller h/w operations and host_irq_type for APLIC: - aplic_host_irq_type - aplic_set_irq_priority() - aplic_set_irq_type() Patch is based on the code from [1]. [1] https://gitlab.com/xen-project/people/olkur/xen/-/commit/7390e2365828b83e27ead56b03114a56e3699dd5 Co-developed-by: Romain Caritey <romain.cari...@microchip.com> Signed-off-by: Oleksii Kurochko <oleksii.kuroc...@gmail.com> --- xen/arch/riscv/aplic.c | 169 ++++++++++++++++++++++++++++- xen/arch/riscv/imsic.c | 63 +++++++++++ xen/arch/riscv/include/asm/aplic.h | 12 ++ xen/arch/riscv/include/asm/imsic.h | 15 +++ 4 files changed, 258 insertions(+), 1 deletion(-) diff --git a/xen/arch/riscv/aplic.c b/xen/arch/riscv/aplic.c index d1aa835c3e..4b60cb9a77 100644 --- a/xen/arch/riscv/aplic.c +++ b/xen/arch/riscv/aplic.c @@ -15,6 +15,7 @@ #include <xen/irq.h> #include <xen/mm.h> #include <xen/sections.h> +#include <xen/spinlock.h> #include <xen/types.h> #include <xen/vmap.h> @@ -110,9 +111,173 @@ static int __init aplic_init(void) return 0; } -static const struct intc_hw_operations __ro_after_init aplic_ops = { +static void aplic_set_irq_type(struct irq_desc *desc, unsigned int type) +{ + unsigned int irq = desc->irq - 1; + + spin_lock(&aplic.lock); + switch(type) { + case IRQ_TYPE_EDGE_RISING: + aplic.regs->sourcecfg[irq] = APLIC_SOURCECFG_SM_EDGE_RISE; + break; + case IRQ_TYPE_EDGE_FALLING: + aplic.regs->sourcecfg[irq] = APLIC_SOURCECFG_SM_EDGE_FALL; + break; + case IRQ_TYPE_LEVEL_HIGH: + aplic.regs->sourcecfg[irq] = APLIC_SOURCECFG_SM_LEVEL_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + aplic.regs->sourcecfg[irq] = APLIC_SOURCECFG_SM_LEVEL_LOW; + break; + default: + aplic.regs->sourcecfg[irq] = APLIC_SOURCECFG_SM_INACTIVE; + break; + } + spin_unlock(&aplic.lock); +} + +static void aplic_set_irq_priority(struct irq_desc *desc, + unsigned int priority) +{ + /* No priority, do nothing */ +} + +static void aplic_irq_enable(struct irq_desc *desc) +{ + unsigned long flags; + + /* + * TODO: Currently, APLIC is supported only with MSI interrupts. + * If APLIC without MSI interrupts is required in the future, + * this function will need to be updated accordingly. + */ + ASSERT(aplic.imsic_cfg->is_used); + + ASSERT(spin_is_locked(&desc->lock)); + + spin_lock_irqsave(&aplic.lock, flags); + + clear_bit(_IRQ_DISABLED, &desc->status); + + /* enable interrupt in IMSIC */ + imsic_irq_enable(desc->irq); + + /* enable interrupt in APLIC */ + aplic.regs->setienum = desc->irq; + + spin_unlock_irqrestore(&aplic.lock, flags); +} + +static void aplic_irq_disable(struct irq_desc *desc) +{ + unsigned long flags; + + /* + * TODO: Currently, APLIC is supported only with MSI interrupts. + * If APLIC without MSI interrupts is required in the future, + * this function will need to be updated accordingly. + */ + ASSERT(aplic.imsic_cfg->is_used); + + ASSERT(spin_is_locked(&desc->lock)); + + spin_lock_irqsave(&aplic.lock, flags); + + set_bit(_IRQ_DISABLED, &desc->status); + + /* disable interrupt in APLIC */ + aplic.regs->clrienum = desc->irq; + + /* disable interrupt in IMSIC */ + imsic_irq_disable(desc->irq); + + spin_unlock_irqrestore(&aplic.lock, flags); +} + +static unsigned int aplic_irq_startup(struct irq_desc *desc) +{ + aplic_irq_enable(desc); + + return 0; +} + +static void aplic_irq_shutdown(struct irq_desc *desc) +{ + aplic_irq_disable(desc); +} + +static void aplic_irq_ack(struct irq_desc *desc) +{ + /* nothing to do */ +} + +static void aplic_host_irq_end(struct irq_desc *desc) +{ + /* nothing to do */ +} + +static unsigned int aplic_get_cpu_from_mask(const cpumask_t *cpumask) +{ + unsigned int cpu; + cpumask_t possible_mask; + + cpumask_and(&possible_mask, cpumask, &cpu_possible_map); + cpu = cpumask_any(&possible_mask); + + return cpu; +} + +static void aplic_set_irq_affinity(struct irq_desc *desc, const cpumask_t *mask) +{ + unsigned int cpu; + uint64_t group_index, base_ppn; + uint32_t hhxw, lhxw ,hhxs, value; + const struct imsic_config *imsic = aplic.imsic_cfg; + + /* + * TODO: Currently, APLIC is supported only with MSI interrupts. + * If APLIC without MSI interrupts is required in the future, + * this function will need to be updated accordingly. + */ + ASSERT(aplic.imsic_cfg->is_used); + + ASSERT(!cpumask_empty(mask)); + + spin_lock(&aplic.lock); + + cpu = cpuid_to_hartid(aplic_get_cpu_from_mask(mask)); + hhxw = imsic->group_index_bits; + lhxw = imsic->hart_index_bits; + hhxs = imsic->group_index_shift - IMSIC_MMIO_PAGE_SHIFT * 2; + base_ppn = imsic->msi[cpu].base_addr >> IMSIC_MMIO_PAGE_SHIFT; + + /* update hart and EEID in the target register */ + group_index = (base_ppn >> (hhxs + 12)) & (BIT(hhxw, UL) - 1); + value = desc->irq; + value |= cpu << APLIC_TARGET_HART_IDX_SHIFT; + value |= group_index << (lhxw + APLIC_TARGET_HART_IDX_SHIFT) ; + aplic.regs->target[desc->irq - 1] = value; + + spin_unlock(&aplic.lock); +} + +static hw_irq_controller aplic_host_irq_type = { + .typename = "aplic", + .startup = aplic_irq_startup, + .shutdown = aplic_irq_shutdown, + .enable = aplic_irq_enable, + .disable = aplic_irq_disable, + .ack = aplic_irq_ack, + .end = aplic_host_irq_end, + .set_affinity = aplic_set_irq_affinity, +}; + +static const struct intc_hw_operations aplic_ops = { .info = &aplic_info, .init = aplic_init, + .host_irq_type = &aplic_host_irq_type, + .set_irq_priority = aplic_set_irq_priority, + .set_irq_type = aplic_set_irq_type, }; static int aplic_irq_xlate(const uint32_t *intspec, unsigned int intsize, @@ -149,6 +314,8 @@ static int __init aplic_preinit(struct dt_device_node *node, const void *dat) dt_irq_xlate = aplic_irq_xlate; + spin_lock_init(&aplic.lock); + register_intc_ops(&aplic_ops); return 0; diff --git a/xen/arch/riscv/imsic.c b/xen/arch/riscv/imsic.c index 99def9af2d..8198d008ef 100644 --- a/xen/arch/riscv/imsic.c +++ b/xen/arch/riscv/imsic.c @@ -14,12 +14,68 @@ #include <xen/errno.h> #include <xen/init.h> #include <xen/macros.h> +#include <xen/spinlock.h> #include <xen/xmalloc.h> #include <asm/imsic.h> static struct imsic_config imsic_cfg; +#define imsic_csr_set(c, v) \ +do { \ + csr_write(CSR_SISELECT, c); \ + csr_set(CSR_SIREG, v); \ +} while (0) + +#define imsic_csr_clear(c, v) \ +do { \ + csr_write(CSR_SISELECT, c); \ + csr_clear(CSR_SIREG, v); \ +} while (0) + +static void imsic_local_eix_update(unsigned long base_id, unsigned long num_id, + bool pend, bool val) +{ + unsigned long i, isel, ireg; + unsigned long id = base_id, last_id = base_id + num_id; + + while ( id < last_id ) + { + isel = id / __riscv_xlen; + isel *= __riscv_xlen / IMSIC_EIPx_BITS; + isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0; + + ireg = 0; + for ( i = id & (__riscv_xlen - 1); + (id < last_id) && (i < __riscv_xlen); + i++, id++ ) + ireg |= (1 << i); + + if ( val ) + imsic_csr_set(isel, ireg); + else + imsic_csr_clear(isel, ireg); + } +} + +void imsic_irq_enable(unsigned int hwirq) +{ + unsigned long flags; + + spin_lock_irqsave(&imsic_cfg.lock, flags); + imsic_local_eix_update(hwirq, 1, false, true); + spin_unlock_irqrestore(&imsic_cfg.lock, flags); +} + +void imsic_irq_disable(unsigned int hwirq) +{ + unsigned long flags; + + spin_lock_irqsave(&imsic_cfg.lock, flags); + imsic_local_eix_update(hwirq, 1, false, false); + spin_unlock_irqrestore(&imsic_cfg.lock, flags); +} + const struct imsic_config *imsic_get_config(void) { return &imsic_cfg; @@ -277,6 +333,13 @@ int __init imsic_init(struct dt_device_node *node) goto imsic_init_err; } + spin_lock_init(&imsic_cfg.lock); + + /* Enable local interrupt delivery */ + imsic_ids_local_delivery(true); + + imsic_cfg.is_used = true; + return 0; imsic_init_err: diff --git a/xen/arch/riscv/include/asm/aplic.h b/xen/arch/riscv/include/asm/aplic.h index 94b3d0b616..ce858663a9 100644 --- a/xen/arch/riscv/include/asm/aplic.h +++ b/xen/arch/riscv/include/asm/aplic.h @@ -18,6 +18,15 @@ #define APLIC_DOMAINCFG_IE BIT(8, UL) #define APLIC_DOMAINCFG_DM BIT(2, UL) +#define APLIC_SOURCECFG_SM_INACTIVE 0x0 +#define APLIC_SOURCECFG_SM_DETACH 0x1 +#define APLIC_SOURCECFG_SM_EDGE_RISE 0x4 +#define APLIC_SOURCECFG_SM_EDGE_FALL 0x5 +#define APLIC_SOURCECFG_SM_LEVEL_HIGH 0x6 +#define APLIC_SOURCECFG_SM_LEVEL_LOW 0x7 + +#define APLIC_TARGET_HART_IDX_SHIFT 18 + struct aplic_regs { uint32_t domaincfg; uint32_t sourcecfg[1023]; @@ -70,6 +79,9 @@ struct aplic_priv { /* registers */ volatile struct aplic_regs *regs; + /* lock */ + spinlock_t lock; + /* imsic configuration */ const struct imsic_config *imsic_cfg; }; diff --git a/xen/arch/riscv/include/asm/imsic.h b/xen/arch/riscv/include/asm/imsic.h index 126e651863..d2c0178529 100644 --- a/xen/arch/riscv/include/asm/imsic.h +++ b/xen/arch/riscv/include/asm/imsic.h @@ -11,6 +11,7 @@ #ifndef ASM__RISCV__IMSIC_H #define ASM__RISCV__IMSIC_H +#include <xen/spinlock.h> #include <xen/types.h> #define IMSIC_MMIO_PAGE_SHIFT 12 @@ -19,6 +20,11 @@ #define IMSIC_MIN_ID 63 #define IMSIC_MAX_ID 2048 +#define IMSIC_EIP0 0x80 +#define IMSIC_EIPx_BITS 32 + +#define IMSIC_EIE0 0xC0 + struct imsic_msi { paddr_t base_addr; unsigned long offset; @@ -55,6 +61,12 @@ struct imsic_config { /* MSI */ struct imsic_msi msi[NR_CPUS]; + + /* a check that IMSIC is used */ + bool is_used; + + /* lock */ + spinlock_t lock; }; struct dt_device_node; @@ -63,4 +75,7 @@ int imsic_init(struct dt_device_node *n); struct imsic_config; const struct imsic_config *imsic_get_config(void); +void imsic_irq_enable(unsigned int hwirq); +void imsic_irq_disable(unsigned int hwirq); + #endif /* ASM__RISCV__IMSIC_H */ -- 2.49.0