drivers/irqchip/qcom-irq-combiner.c

Source file repositories/reference/linux-study-clean/drivers/irqchip/qcom-irq-combiner.c

File Facts

System
Linux kernel
Corpus path
drivers/irqchip/qcom-irq-combiner.c
Extension
.c
Size
7078 bytes
Lines
277
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 combiner_reg {
	void __iomem *addr;
	unsigned long enabled;
};

struct combiner {
	struct irq_domain   *domain;
	int                 parent_irq;
	u32                 nirqs;
	u32                 nregs;
	struct combiner_reg regs[];
};

static inline int irq_nr(u32 reg, u32 bit)
{
	return reg * REG_SIZE + bit;
}

/*
 * Handler for the cascaded IRQ.
 */
static void combiner_handle_irq(struct irq_desc *desc)
{
	struct combiner *combiner = irq_desc_get_handler_data(desc);
	struct irq_chip *chip = irq_desc_get_chip(desc);
	u32 reg;

	chained_irq_enter(chip, desc);

	for (reg = 0; reg < combiner->nregs; reg++) {
		int hwirq;
		u32 bit;
		u32 status;

		bit = readl_relaxed(combiner->regs[reg].addr);
		status = bit & combiner->regs[reg].enabled;
		if (bit && !status)
			pr_warn_ratelimited("Unexpected IRQ on CPU%d: (%08x %08lx %p)\n",
					    smp_processor_id(), bit,
					    combiner->regs[reg].enabled,
					    combiner->regs[reg].addr);

		while (status) {
			bit = __ffs(status);
			status &= ~(1 << bit);
			hwirq = irq_nr(reg, bit);
			generic_handle_domain_irq(combiner->domain, hwirq);
		}
	}

	chained_irq_exit(chip, desc);
}

static void combiner_irq_chip_mask_irq(struct irq_data *data)
{
	struct combiner *combiner = irq_data_get_irq_chip_data(data);
	struct combiner_reg *reg = combiner->regs + data->hwirq / REG_SIZE;

	clear_bit(data->hwirq % REG_SIZE, &reg->enabled);
}

static void combiner_irq_chip_unmask_irq(struct irq_data *data)
{
	struct combiner *combiner = irq_data_get_irq_chip_data(data);
	struct combiner_reg *reg = combiner->regs + data->hwirq / REG_SIZE;

	set_bit(data->hwirq % REG_SIZE, &reg->enabled);
}

static struct irq_chip irq_chip = {
	.irq_mask = combiner_irq_chip_mask_irq,
	.irq_unmask = combiner_irq_chip_unmask_irq,
	.name = "qcom-irq-combiner"
};

static int combiner_irq_map(struct irq_domain *domain, unsigned int irq,
				   irq_hw_number_t hwirq)
{
	irq_set_chip_and_handler(irq, &irq_chip, handle_level_irq);
	irq_set_chip_data(irq, domain->host_data);
	irq_set_noprobe(irq);
	return 0;
}

static void combiner_irq_unmap(struct irq_domain *domain, unsigned int irq)
{
	irq_domain_reset_irq_data(irq_get_irq_data(irq));
}

static int combiner_irq_translate(struct irq_domain *d, struct irq_fwspec *fws,

Annotation

Implementation Notes