drivers/irqchip/irq-sifive-plic.c

Source file repositories/reference/linux-study-clean/drivers/irqchip/irq-sifive-plic.c

File Facts

System
Linux kernel
Corpus path
drivers/irqchip/irq-sifive-plic.c
Extension
.c
Size
22313 bytes
Lines
849
Domain
Driver Families
Bucket
drivers/irqchip
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct plic_priv {
	struct fwnode_handle	*fwnode;
	struct cpumask		lmask;
	struct irq_domain	*irqdomain;
	void __iomem		*regs;
	unsigned long		plic_quirks;
	/* device interrupts + 1 to compensate for the reserved hwirq 0 */
	unsigned int __private	total_irqs;
	unsigned int		irq_groups;
	unsigned long		*prio_save;
	u32			gsi_base;
	int			acpi_plic_id;
};

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;
	u32			*enable_save;
	struct plic_priv	*priv;
};

/*
 * Macro to deal with the insanity of hardware interrupt 0 being reserved */
#define for_each_device_irq(iter, priv)	\
	for (unsigned int iter = 1; iter < ACCESS_PRIVATE(priv, total_irqs); iter++)

static int plic_parent_irq __ro_after_init;
static bool plic_global_setup_done __ro_after_init;
static DEFINE_PER_CPU(struct plic_handler, plic_handlers);

static int plic_irq_set_type(struct irq_data *d, unsigned int type);

static void __plic_toggle(struct plic_handler *handler, int hwirq, int enable)
{
	u32 __iomem *base = handler->enable_base;
	u32 hwirq_mask = 1 << (hwirq % 32);
	int group = hwirq / 32;
	u32 value;

	value = readl(base + group);

	if (enable)
		value |= hwirq_mask;
	else
		value &= ~hwirq_mask;

	handler->enable_save[group] = value;
	writel(value, base + group);
}

static void plic_toggle(struct plic_handler *handler, int hwirq, int enable)
{
	unsigned long flags;

	raw_spin_lock_irqsave(&handler->enable_lock, flags);
	__plic_toggle(handler, hwirq, enable);
	raw_spin_unlock_irqrestore(&handler->enable_lock, flags);
}

static inline void plic_irq_toggle(const struct cpumask *mask,
				   struct irq_data *d, int enable)
{
	int cpu;

	for_each_cpu(cpu, mask) {
		struct plic_handler *handler = per_cpu_ptr(&plic_handlers, cpu);

		plic_toggle(handler, d->hwirq, enable);
	}
}

static void plic_irq_unmask(struct irq_data *d)
{
	struct plic_priv *priv = irq_data_get_irq_chip_data(d);

	writel(1, priv->regs + PRIORITY_BASE + d->hwirq * PRIORITY_PER_ID);
}

static void plic_irq_mask(struct irq_data *d)
{
	struct plic_priv *priv = irq_data_get_irq_chip_data(d);

	writel(0, priv->regs + PRIORITY_BASE + d->hwirq * PRIORITY_PER_ID);
}

Annotation

Implementation Notes