mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
xen/riscv: implementation of aplic and imsic operations
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_enable()
- aplic_irq_disable()
- 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
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.Caritey@microchip.com>
Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
Acked-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
8b5887d8cd
commit
d4676a1398
5 files changed
+267
-2
No files matched your search
@@ -14,6 +14,7 @@
|
||||
#ifndef ASM_RISCV_APLIC_PRIV_H
|
||||
#define ASM_RISCV_APLIC_PRIV_H
|
||||
|
||||
#include <xen/spinlock.h>
|
||||
#include <xen/types.h>
|
||||
|
||||
#include <asm/aplic.h>
|
||||
@@ -26,6 +27,9 @@ struct aplic_priv {
|
||||
/* Registers */
|
||||
volatile struct aplic_regs __iomem *regs;
|
||||
|
||||
/* Lock to protect access to APLIC's registers */
|
||||
spinlock_t lock;
|
||||
|
||||
/* IMSIC configuration */
|
||||
const struct imsic_config *imsic_cfg;
|
||||
};
|
||||
|
||||
+122
-1
@@ -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>
|
||||
|
||||
@@ -23,11 +24,14 @@
|
||||
#include <asm/device.h>
|
||||
#include <asm/imsic.h>
|
||||
#include <asm/intc.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/riscv_encoding.h>
|
||||
|
||||
#define APLIC_DEFAULT_PRIORITY 1
|
||||
|
||||
static struct aplic_priv aplic;
|
||||
static struct aplic_priv aplic = {
|
||||
.lock = SPIN_LOCK_UNLOCKED,
|
||||
};
|
||||
|
||||
static struct intc_info __ro_after_init aplic_info = {
|
||||
.hw_version = INTC_APLIC,
|
||||
@@ -116,9 +120,126 @@ static int __init cf_check aplic_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cf_check aplic_irq_enable(struct irq_desc *desc)
|
||||
{
|
||||
/*
|
||||
* 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(readl(&aplic.regs->domaincfg) & APLIC_DOMAINCFG_DM);
|
||||
|
||||
ASSERT(spin_is_locked(&desc->lock));
|
||||
|
||||
spin_lock(&aplic.lock);
|
||||
|
||||
/* Enable interrupt in IMSIC */
|
||||
imsic_irq_enable(desc->irq);
|
||||
|
||||
/* Enable interrupt in APLIC */
|
||||
writel(desc->irq, &aplic.regs->setienum);
|
||||
|
||||
spin_unlock(&aplic.lock);
|
||||
}
|
||||
|
||||
static void cf_check aplic_irq_disable(struct irq_desc *desc)
|
||||
{
|
||||
/*
|
||||
* 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(readl(&aplic.regs->domaincfg) & APLIC_DOMAINCFG_DM);
|
||||
|
||||
ASSERT(spin_is_locked(&desc->lock));
|
||||
|
||||
spin_lock(&aplic.lock);
|
||||
|
||||
/* Disable interrupt in APLIC */
|
||||
writel(desc->irq, &aplic.regs->clrienum);
|
||||
|
||||
/* Disable interrupt in IMSIC */
|
||||
imsic_irq_disable(desc->irq);
|
||||
|
||||
spin_unlock(&aplic.lock);
|
||||
}
|
||||
|
||||
static unsigned int cf_check aplic_irq_startup(struct irq_desc *desc)
|
||||
{
|
||||
aplic_irq_enable(desc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int aplic_get_cpu_from_mask(const cpumask_t *cpumask)
|
||||
{
|
||||
cpumask_t mask;
|
||||
|
||||
cpumask_and(&mask, cpumask, &cpu_online_map);
|
||||
|
||||
return cpumask_any(&mask);
|
||||
}
|
||||
|
||||
static void cf_check 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(readl(&aplic.regs->domaincfg) & APLIC_DOMAINCFG_DM);
|
||||
|
||||
ASSERT(!cpumask_empty(mask));
|
||||
|
||||
ASSERT(spin_is_locked(&desc->lock));
|
||||
|
||||
cpu = cpuid_to_hartid(aplic_get_cpu_from_mask(mask));
|
||||
hhxw = imsic->group_index_bits;
|
||||
lhxw = imsic->hart_index_bits;
|
||||
/*
|
||||
* Although this variable is used only once in the calculation of
|
||||
* group_index, and it might seem that hhxs could be defined as:
|
||||
* hhxs = imsic->group_index_shift - IMSIC_MMIO_PAGE_SHIFT;
|
||||
* and then the addition of IMSIC_MMIO_PAGE_SHIFT could be omitted
|
||||
* when calculating the group index.
|
||||
* It was done intentionally this way to follow the formula from
|
||||
* the AIA specification for calculating the MSI address.
|
||||
*/
|
||||
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 + IMSIC_MMIO_PAGE_SHIFT)) &
|
||||
(BIT(hhxw, UL) - 1);
|
||||
value = desc->irq;
|
||||
value |= cpu << APLIC_TARGET_HART_IDX_SHIFT;
|
||||
value |= group_index << (lhxw + APLIC_TARGET_HART_IDX_SHIFT);
|
||||
|
||||
spin_lock(&aplic.lock);
|
||||
|
||||
writel(value, &aplic.regs->target[desc->irq - 1]);
|
||||
|
||||
spin_unlock(&aplic.lock);
|
||||
}
|
||||
|
||||
static const hw_irq_controller aplic_xen_irq_type = {
|
||||
.typename = "aplic",
|
||||
.startup = aplic_irq_startup,
|
||||
.shutdown = aplic_irq_disable,
|
||||
.enable = aplic_irq_enable,
|
||||
.disable = aplic_irq_disable,
|
||||
.set_affinity = aplic_set_irq_affinity,
|
||||
};
|
||||
|
||||
static const struct intc_hw_operations aplic_ops = {
|
||||
.info = &aplic_info,
|
||||
.init = aplic_init,
|
||||
.host_irq_type = &aplic_xen_irq_type,
|
||||
};
|
||||
|
||||
static int cf_check aplic_irq_xlate(const uint32_t *intspec,
|
||||
|
||||
+121
-1
@@ -29,7 +29,124 @@ struct imsic_mmios {
|
||||
unsigned long size;
|
||||
};
|
||||
|
||||
static struct imsic_config imsic_cfg;
|
||||
static struct imsic_config imsic_cfg = {
|
||||
.lock = SPIN_LOCK_UNLOCKED,
|
||||
};
|
||||
|
||||
#define IMSIC_DISABLE_EIDELIVERY 0
|
||||
#define IMSIC_ENABLE_EIDELIVERY 1
|
||||
#define IMSIC_DISABLE_EITHRESHOLD 1
|
||||
#define IMSIC_ENABLE_EITHRESHOLD 0
|
||||
|
||||
#define imsic_csr_write(c, v) \
|
||||
do { \
|
||||
csr_write(CSR_SISELECT, c); \
|
||||
csr_write(CSR_SIREG, v); \
|
||||
} while (0)
|
||||
|
||||
#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)
|
||||
|
||||
void __init imsic_ids_local_delivery(bool enable)
|
||||
{
|
||||
if ( enable )
|
||||
{
|
||||
imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_ENABLE_EITHRESHOLD);
|
||||
imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY);
|
||||
}
|
||||
else
|
||||
{
|
||||
imsic_csr_write(IMSIC_EITHRESHOLD, IMSIC_DISABLE_EITHRESHOLD);
|
||||
imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_DISABLE_EIDELIVERY);
|
||||
}
|
||||
}
|
||||
|
||||
static void imsic_local_eix_update(unsigned long base_id, unsigned long num_id,
|
||||
bool pend, bool val)
|
||||
{
|
||||
unsigned long id = base_id, last_id = base_id + num_id;
|
||||
|
||||
while ( id < last_id )
|
||||
{
|
||||
unsigned long isel, ireg;
|
||||
unsigned long start_id = id & (__riscv_xlen - 1);
|
||||
unsigned long chunk = __riscv_xlen - start_id;
|
||||
unsigned long count = min(last_id - id, chunk);
|
||||
|
||||
isel = id / __riscv_xlen;
|
||||
isel *= __riscv_xlen / IMSIC_EIPx_BITS;
|
||||
isel += pend ? IMSIC_EIP0 : IMSIC_EIE0;
|
||||
|
||||
ireg = GENMASK(start_id + count - 1, start_id);
|
||||
|
||||
id += count;
|
||||
|
||||
if ( val )
|
||||
imsic_csr_set(isel, ireg);
|
||||
else
|
||||
imsic_csr_clear(isel, ireg);
|
||||
}
|
||||
}
|
||||
|
||||
void imsic_irq_enable(unsigned int irq)
|
||||
{
|
||||
/*
|
||||
* The only caller of imsic_irq_enable() is aplic_irq_enable(), which
|
||||
* already runs with IRQs disabled. Therefore, there's no need to use
|
||||
* spin_lock_irqsave() in this function.
|
||||
*
|
||||
* This ASSERT is added as a safeguard: if imsic_irq_enable() is ever
|
||||
* called from a context where IRQs are not disabled,
|
||||
* spin_lock_irqsave() should be used instead of spin_lock().
|
||||
*/
|
||||
ASSERT(!local_irq_is_enabled());
|
||||
|
||||
spin_lock(&imsic_cfg.lock);
|
||||
/*
|
||||
* There is no irq - 1 here (look at aplic_set_irq_type()) because:
|
||||
* From the spec:
|
||||
* When an interrupt file supports distinct interrupt identities,
|
||||
* valid identity numbers are between 1 and inclusive. The identity
|
||||
* numbers within this range are said to be implemented by the interrupt
|
||||
* file; numbers outside this range are not implemented. The number zero
|
||||
* is never a valid interrupt identity.
|
||||
* ...
|
||||
* Bit positions in a valid eiek register that don’t correspond to a
|
||||
* supported interrupt identity (such as bit 0 of eie0) are read-only zeros.
|
||||
*
|
||||
* So in EIx registers interrupt i corresponds to bit i in comparison wiht
|
||||
* APLIC's sourcecfg which starts from 0.
|
||||
*/
|
||||
imsic_local_eix_update(irq, 1, false, true);
|
||||
spin_unlock(&imsic_cfg.lock);
|
||||
}
|
||||
|
||||
void imsic_irq_disable(unsigned int irq)
|
||||
{
|
||||
/*
|
||||
* The only caller of imsic_irq_disable() is aplic_irq_disable(), which
|
||||
* already runs with IRQs disabled. Therefore, there's no need to use
|
||||
* spin_lock_irqsave() in this function.
|
||||
*
|
||||
* This ASSERT is added as a safeguard: if imsic_irq_disable() is ever
|
||||
* called from a context where IRQs are not disabled,
|
||||
* spin_lock_irqsave() should be used instead of spin_lock().
|
||||
*/
|
||||
ASSERT(!local_irq_is_enabled());
|
||||
|
||||
spin_lock(&imsic_cfg.lock);
|
||||
imsic_local_eix_update(irq, 1, false, false);
|
||||
spin_unlock(&imsic_cfg.lock);
|
||||
}
|
||||
|
||||
/* Callers aren't intended to changed imsic_cfg so return const. */
|
||||
const struct imsic_config *imsic_get_config(void)
|
||||
@@ -355,6 +472,9 @@ int __init imsic_init(const struct dt_device_node *node)
|
||||
goto imsic_init_err;
|
||||
}
|
||||
|
||||
/* Enable local interrupt delivery */
|
||||
imsic_ids_local_delivery(true);
|
||||
|
||||
imsic_cfg.msi = msi;
|
||||
|
||||
xvfree(mmios);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#define APLIC_DOMAINCFG_IE BIT(8, U)
|
||||
#define APLIC_DOMAINCFG_DM BIT(2, U)
|
||||
|
||||
#define APLIC_TARGET_HART_IDX_SHIFT 18
|
||||
|
||||
struct aplic_regs {
|
||||
uint32_t domaincfg; /* 0x0000 */
|
||||
uint32_t sourcecfg[1023]; /* 0x0004 */
|
||||
|
||||
@@ -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,15 @@
|
||||
#define IMSIC_MIN_ID 63
|
||||
#define IMSIC_MAX_ID 2047
|
||||
|
||||
#define IMSIC_EIDELIVERY 0x70
|
||||
|
||||
#define IMSIC_EITHRESHOLD 0x72
|
||||
|
||||
#define IMSIC_EIP0 0x80
|
||||
#define IMSIC_EIPx_BITS 32
|
||||
|
||||
#define IMSIC_EIE0 0xC0
|
||||
|
||||
struct imsic_msi {
|
||||
paddr_t base_addr;
|
||||
unsigned long offset;
|
||||
@@ -45,6 +55,9 @@ struct imsic_config {
|
||||
|
||||
/* MSI */
|
||||
const struct imsic_msi *msi;
|
||||
|
||||
/* Lock to protect access to IMSIC's stuff */
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct dt_device_node;
|
||||
@@ -52,4 +65,9 @@ int imsic_init(const struct dt_device_node *node);
|
||||
|
||||
const struct imsic_config *imsic_get_config(void);
|
||||
|
||||
void imsic_irq_enable(unsigned int hwirq);
|
||||
void imsic_irq_disable(unsigned int hwirq);
|
||||
|
||||
void imsic_ids_local_delivery(bool enable);
|
||||
|
||||
#endif /* ASM_RISCV_IMSIC_H */
|
||||