Source
x
IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2017 SiFive
* Copyright (C) 2018 Christoph Hellwig
*/
/*
* This driver implements a version of the RISC-V PLIC with the actual layout
* specified in chapter 8 of the SiFive U5 Coreplex Series Manual:
*
* https://static.dev.sifive.com/U54-MC-RVCoreIP.pdf
*
* The largest number supported by devices marked as 'sifive,plic-1.0.0', is
* 1024, of which device 0 is defined as non-existent by the RISC-V Privileged
* Spec.
*/
/*
* Each interrupt source has a priority register associated with it.
* We always hardwire it to one in Linux.
*/
/*
* Each hart context has a vector of interrupt enable bits associated with it.
* There's one bit for each interrupt source.
*/
/*
* Each hart context has a set of control registers associated with it. Right
* now there's only two: a source priority threshold over which the hart will
* take an interrupt, and a register to claim interrupts.
*/
static void __iomem *plic_regs;
struct plic_handler {
bool present;
void __iomem *hart_base;
/*
* Protect mask operations on the registers given that we can't
* assume atomic memory operations work on them.
*/
raw_spinlock_t enable_lock;
void __iomem *enable_base;
};
static DEFINE_PER_CPU(struct plic_handler, plic_handlers);
static inline void plic_toggle(struct plic_handler *handler,
int hwirq, int enable)
{
u32 __iomem *reg = handler->enable_base + (hwirq / 32) * sizeof(u32);
u32 hwirq_mask = 1 << (hwirq % 32);
raw_spin_lock(&handler->enable_lock);
if (enable)
writel(readl(reg) | hwirq_mask, reg);
else
writel(readl(reg) & ~hwirq_mask, reg);
raw_spin_unlock(&handler->enable_lock);
}
static inline void plic_irq_toggle(const struct cpumask *mask,
int hwirq, int enable)
{
int cpu;
writel(enable, plic_regs + PRIORITY_BASE + hwirq * PRIORITY_PER_ID);
for_each_cpu(cpu, mask) {