drivers/irqchip/irq-aspeed-scu-ic.c

Source file repositories/reference/linux-study-clean/drivers/irqchip/irq-aspeed-scu-ic.c

File Facts

System
Linux kernel
Corpus path
drivers/irqchip/irq-aspeed-scu-ic.c
Extension
.c
Size
8996 bytes
Lines
294
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 aspeed_scu_ic_variant {
	const char	*compatible;
	unsigned long	irq_enable;
	unsigned long	irq_shift;
	unsigned int	num_irqs;
	unsigned long	ier;
	unsigned long	isr;
};

#define SCU_VARIANT(_compat, _shift, _enable, _num, _ier, _isr) {	\
	.compatible		=	_compat,	\
	.irq_shift		=	_shift,		\
	.irq_enable		=	_enable,	\
	.num_irqs		=	_num,		\
	.ier			=	_ier,		\
	.isr			=	_isr,		\
}

static const struct aspeed_scu_ic_variant scu_ic_variants[]	__initconst = {
	SCU_VARIANT("aspeed,ast2400-scu-ic",	0, GENMASK(15, 0),	7, 0x00, 0x00),
	SCU_VARIANT("aspeed,ast2500-scu-ic",	0, GENMASK(15, 0),	7, 0x00, 0x00),
	SCU_VARIANT("aspeed,ast2600-scu-ic0",	0, GENMASK(5, 0),	6, 0x00, 0x00),
	SCU_VARIANT("aspeed,ast2600-scu-ic1",	4, GENMASK(5, 4),	2, 0x00, 0x00),
	SCU_VARIANT("aspeed,ast2700-scu-ic0",	0, GENMASK(3, 0),	4, 0x00, 0x04),
	SCU_VARIANT("aspeed,ast2700-scu-ic1",	0, GENMASK(3, 0),	4, 0x00, 0x04),
	SCU_VARIANT("aspeed,ast2700-scu-ic2",	0, GENMASK(3, 0),	4, 0x04, 0x00),
	SCU_VARIANT("aspeed,ast2700-scu-ic3",	0, GENMASK(1, 0),	2, 0x04, 0x00),
};

struct aspeed_scu_ic {
	unsigned long		irq_enable;
	unsigned long		irq_shift;
	unsigned int		num_irqs;
	void __iomem		*base;
	struct irq_domain	*irq_domain;
	unsigned long		ier;
	unsigned long		isr;
};

static inline bool scu_has_split_isr(struct aspeed_scu_ic *scu)
{
	return scu->ier != scu->isr;
}

static void aspeed_scu_ic_irq_handler_combined(struct irq_desc *desc)
{
	struct aspeed_scu_ic *scu_ic = irq_desc_get_handler_data(desc);
	struct irq_chip *chip = irq_desc_get_chip(desc);
	unsigned long bit, enabled, max, status;
	unsigned int sts, mask;

	chained_irq_enter(chip, desc);

	mask = scu_ic->irq_enable << ASPEED_SCU_IC_STATUS_SHIFT;
	/*
	 * The SCU IC has just one register to control its operation and read
	 * status. The interrupt enable bits occupy the lower 16 bits of the
	 * register, while the interrupt status bits occupy the upper 16 bits.
	 * The status bit for a given interrupt is always 16 bits shifted from
	 * the enable bit for the same interrupt.
	 * Therefore, perform the IRQ operations in the enable bit space by
	 * shifting the status down to get the mapping and then back up to
	 * clear the bit.
	 */
	sts = readl(scu_ic->base);
	enabled = sts & scu_ic->irq_enable;
	status = (sts >> ASPEED_SCU_IC_STATUS_SHIFT) & enabled;

	bit = scu_ic->irq_shift;
	max = scu_ic->num_irqs + bit;

	for_each_set_bit_from(bit, &status, max) {
		generic_handle_domain_irq(scu_ic->irq_domain, bit - scu_ic->irq_shift);
		writel((readl(scu_ic->base) & ~mask) | BIT(bit + ASPEED_SCU_IC_STATUS_SHIFT),
		       scu_ic->base);
	}

	chained_irq_exit(chip, desc);
}

static void aspeed_scu_ic_irq_handler_split(struct irq_desc *desc)
{
	struct aspeed_scu_ic *scu_ic = irq_desc_get_handler_data(desc);
	struct irq_chip *chip = irq_desc_get_chip(desc);
	unsigned long bit, enabled, max, status;
	unsigned int sts;

	chained_irq_enter(chip, desc);

	sts = readl(scu_ic->base + scu_ic->isr);

Annotation

Implementation Notes