drivers/irqchip/irq-aspeed-vic.c

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

File Facts

System
Linux kernel
Corpus path
drivers/irqchip/irq-aspeed-vic.c
Extension
.c
Size
5852 bytes
Lines
222
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_vic {
	void __iomem		*base;
	u32			edge_sources[2];
	struct irq_domain	*dom;
};
static struct aspeed_vic *system_avic;

static void vic_init_hw(struct aspeed_vic *vic)
{
	u32 sense;

	/* Disable all interrupts */
	writel(0xffffffff, vic->base + AVIC_INT_ENABLE_CLR);
	writel(0xffffffff, vic->base + AVIC_INT_ENABLE_CLR + 4);

	/* Make sure no soft trigger is on */
	writel(0xffffffff, vic->base + AVIC_INT_TRIGGER_CLR);
	writel(0xffffffff, vic->base + AVIC_INT_TRIGGER_CLR + 4);

	/* Set everything to be IRQ */
	writel(0, vic->base + AVIC_INT_SELECT);
	writel(0, vic->base + AVIC_INT_SELECT + 4);

	/* Some interrupts have a programmable high/low level trigger
	 * (4 GPIO direct inputs), for now we assume this was configured
	 * by firmware. We read which ones are edge now.
	 */
	sense = readl(vic->base + AVIC_INT_SENSE);
	vic->edge_sources[0] = ~sense;
	sense = readl(vic->base + AVIC_INT_SENSE + 4);
	vic->edge_sources[1] = ~sense;

	/* Clear edge detection latches */
	writel(0xffffffff, vic->base + AVIC_EDGE_CLR);
	writel(0xffffffff, vic->base + AVIC_EDGE_CLR + 4);
}

static void __exception_irq_entry avic_handle_irq(struct pt_regs *regs)
{
	struct aspeed_vic *vic = system_avic;
	u32 stat, irq;

	for (;;) {
		irq = 0;
		stat = readl_relaxed(vic->base + AVIC_IRQ_STATUS);
		if (!stat) {
			stat = readl_relaxed(vic->base + AVIC_IRQ_STATUS + 4);
			irq = 32;
		}
		if (stat == 0)
			break;
		irq += ffs(stat) - 1;
		generic_handle_domain_irq(vic->dom, irq);
	}
}

static void avic_ack_irq(struct irq_data *d)
{
	struct aspeed_vic *vic = irq_data_get_irq_chip_data(d);
	unsigned int sidx = d->hwirq >> 5;
	unsigned int sbit = 1u << (d->hwirq & 0x1f);

	/* Clear edge latch for edge interrupts, nop for level */
	if (vic->edge_sources[sidx] & sbit)
		writel(sbit, vic->base + AVIC_EDGE_CLR + sidx * 4);
}

static void avic_mask_irq(struct irq_data *d)
{
	struct aspeed_vic *vic = irq_data_get_irq_chip_data(d);
	unsigned int sidx = d->hwirq >> 5;
	unsigned int sbit = 1u << (d->hwirq & 0x1f);

	writel(sbit, vic->base + AVIC_INT_ENABLE_CLR + sidx * 4);
}

static void avic_unmask_irq(struct irq_data *d)
{
	struct aspeed_vic *vic = irq_data_get_irq_chip_data(d);
	unsigned int sidx = d->hwirq >> 5;
	unsigned int sbit = 1u << (d->hwirq & 0x1f);

	writel(sbit, vic->base + AVIC_INT_ENABLE + sidx * 4);
}

/* For level irq, faster than going through a nop "ack" and mask */
static void avic_mask_ack_irq(struct irq_data *d)
{
	struct aspeed_vic *vic = irq_data_get_irq_chip_data(d);
	unsigned int sidx = d->hwirq >> 5;

Annotation

Implementation Notes