drivers/irqchip/irq-bcm7120-l2.c

Source file repositories/reference/linux-study-clean/drivers/irqchip/irq-bcm7120-l2.c

File Facts

System
Linux kernel
Corpus path
drivers/irqchip/irq-bcm7120-l2.c
Extension
.c
Size
9374 bytes
Lines
348
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 bcm7120_l1_intc_data {
	struct bcm7120_l2_intc_data *b;
	u32 irq_map_mask[MAX_WORDS];
};

struct bcm7120_l2_intc_data {
	unsigned int n_words;
	void __iomem *map_base[MAX_MAPPINGS];
	void __iomem *pair_base[MAX_WORDS];
	int en_offset[MAX_WORDS];
	int stat_offset[MAX_WORDS];
	struct irq_domain *domain;
	bool can_wake;
	u32 irq_fwd_mask[MAX_WORDS];
	struct bcm7120_l1_intc_data *l1_data;
	int num_parent_irqs;
	const __be32 *map_mask_prop;
};

static void bcm7120_l2_intc_irq_handle(struct irq_desc *desc)
{
	struct bcm7120_l1_intc_data *data = irq_desc_get_handler_data(desc);
	struct bcm7120_l2_intc_data *b = data->b;
	struct irq_chip *chip = irq_desc_get_chip(desc);
	unsigned int idx;

	chained_irq_enter(chip, desc);

	for (idx = 0; idx < b->n_words; idx++) {
		int base = idx * IRQS_PER_WORD;
		struct irq_chip_generic *gc;
		unsigned long pending;
		int hwirq;

		gc = irq_get_domain_generic_chip(b->domain, base);
		scoped_guard (raw_spinlock, &gc->lock) {
			pending = irq_reg_readl(gc, b->stat_offset[idx]) & gc->mask_cache &
				data->irq_map_mask[idx];
		}

		for_each_set_bit(hwirq, &pending, IRQS_PER_WORD)
			generic_handle_domain_irq(b->domain, base + hwirq);
	}

	chained_irq_exit(chip, desc);
}

static void bcm7120_l2_intc_suspend(struct irq_chip_generic *gc)
{
	struct bcm7120_l2_intc_data *b = gc->private;
	struct irq_chip_type *ct = gc->chip_types;

	guard(raw_spinlock)(&gc->lock);
	if (b->can_wake)
		irq_reg_writel(gc, gc->mask_cache | gc->wake_active, ct->regs.mask);
}

static void bcm7120_l2_intc_resume(struct irq_chip_generic *gc)
{
	struct irq_chip_type *ct = gc->chip_types;

	/* Restore the saved mask */
	guard(raw_spinlock)(&gc->lock);
	irq_reg_writel(gc, gc->mask_cache, ct->regs.mask);
}

static int bcm7120_l2_intc_init_one(struct device_node *dn,
					struct bcm7120_l2_intc_data *data,
					int irq, u32 *valid_mask)
{
	struct bcm7120_l1_intc_data *l1_data = &data->l1_data[irq];
	int parent_irq;
	unsigned int idx;

	parent_irq = irq_of_parse_and_map(dn, irq);
	if (!parent_irq) {
		pr_err("failed to map interrupt %d\n", irq);
		return -EINVAL;
	}

	/* For multiple parent IRQs with multiple words, this looks like:
	 * <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...>
	 *
	 * We need to associate a given parent interrupt with its corresponding
	 * map_mask in order to mask the status register with it because we
	 * have the same handler being called for multiple parent interrupts.
	 *
	 * This is typically something needed on BCM7xxx (STB chips).
	 */
	for (idx = 0; idx < data->n_words; idx++) {

Annotation

Implementation Notes