drivers/irqchip/irq-wpcm450-aic.c

Source file repositories/reference/linux-study-clean/drivers/irqchip/irq-wpcm450-aic.c

File Facts

System
Linux kernel
Corpus path
drivers/irqchip/irq-wpcm450-aic.c
Extension
.c
Size
4473 bytes
Lines
163
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 wpcm450_aic {
	void __iomem *regs;
	struct irq_domain *domain;
};

static struct wpcm450_aic *aic;

static void wpcm450_aic_init_hw(void)
{
	int i;

	/* Disable (mask) all interrupts */
	writel(0xffffffff, aic->regs + AIC_MDCR);

	/*
	 * Make sure the interrupt controller is ready to serve new interrupts.
	 * Reading from IPER indicates that the nIRQ signal may be deasserted,
	 * and writing to EOSCR indicates that interrupt handling has finished.
	 */
	readl(aic->regs + AIC_IPER);
	writel(0, aic->regs + AIC_EOSCR);

	/* Initialize trigger mode and priority of each interrupt source */
	for (i = 0; i < AIC_NUM_IRQS; i++)
		writel(AIC_SCR_SRCTYPE_HIGH_LEVEL | AIC_SCR_PRIORITY(7),
		       aic->regs + AIC_SCR(i));
}

static void __exception_irq_entry wpcm450_aic_handle_irq(struct pt_regs *regs)
{
	int hwirq;

	/* Determine the interrupt source */
	/* Read IPER to signal that nIRQ can be de-asserted */
	hwirq = readl(aic->regs + AIC_IPER) / 4;

	generic_handle_domain_irq(aic->domain, hwirq);
}

static void wpcm450_aic_eoi(struct irq_data *d)
{
	/* Signal end-of-service */
	writel(0, aic->regs + AIC_EOSCR);
}

static void wpcm450_aic_mask(struct irq_data *d)
{
	unsigned int mask = BIT(d->hwirq);

	/* Disable (mask) the interrupt */
	writel(mask, aic->regs + AIC_MDCR);
}

static void wpcm450_aic_unmask(struct irq_data *d)
{
	unsigned int mask = BIT(d->hwirq);

	/* Enable (unmask) the interrupt */
	writel(mask, aic->regs + AIC_MECR);
}

static int wpcm450_aic_set_type(struct irq_data *d, unsigned int flow_type)
{
	/*
	 * The hardware supports high/low level, as well as rising/falling edge
	 * modes, and the DT binding accommodates for that, but as long as
	 * other modes than high level mode are not used and can't be tested,
	 * they are rejected in this driver.
	 */
	if ((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH)
		return -EINVAL;

	return 0;
}

static struct irq_chip wpcm450_aic_chip = {
	.name = "wpcm450-aic",
	.irq_eoi = wpcm450_aic_eoi,
	.irq_mask = wpcm450_aic_mask,
	.irq_unmask = wpcm450_aic_unmask,
	.irq_set_type = wpcm450_aic_set_type,
};

static int wpcm450_aic_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq)
{
	if (hwirq >= AIC_NUM_IRQS)
		return -EPERM;

	irq_set_chip_and_handler(irq, &wpcm450_aic_chip, handle_fasteoi_irq);
	irq_set_chip_data(irq, aic);

Annotation

Implementation Notes