drivers/irqchip/irq-al-fic.c

Source file repositories/reference/linux-study-clean/drivers/irqchip/irq-al-fic.c

File Facts

System
Linux kernel
Corpus path
drivers/irqchip/irq-al-fic.c
Extension
.c
Size
7064 bytes
Lines
279
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 al_fic {
	void __iomem *base;
	struct irq_domain *domain;
	const char *name;
	unsigned int parent_irq;
	enum al_fic_state state;
};

static void al_fic_set_trigger(struct al_fic *fic,
			       struct irq_chip_generic *gc,
			       enum al_fic_state new_state)
{
	irq_flow_handler_t handler;
	u32 control = readl_relaxed(fic->base + AL_FIC_CONTROL);

	if (new_state == AL_FIC_CONFIGURED_LEVEL) {
		handler = handle_level_irq;
		control &= ~CONTROL_TRIGGER_RISING;
	} else {
		handler = handle_edge_irq;
		control |= CONTROL_TRIGGER_RISING;
	}
	gc->chip_types->handler = handler;
	fic->state = new_state;
	writel_relaxed(control, fic->base + AL_FIC_CONTROL);
}

static int al_fic_irq_set_type(struct irq_data *data, unsigned int flow_type)
{
	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
	struct al_fic *fic = gc->private;
	enum al_fic_state new_state;

	guard(raw_spinlock)(&gc->lock);

	if (((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH) &&
	    ((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_EDGE_RISING)) {
		pr_debug("fic doesn't support flow type %d\n", flow_type);
		return -EINVAL;
	}

	new_state = (flow_type & IRQ_TYPE_LEVEL_HIGH) ?
		AL_FIC_CONFIGURED_LEVEL : AL_FIC_CONFIGURED_RISING_EDGE;

	/*
	 * A given FIC instance can be either all level or all edge triggered.
	 * This is generally fixed depending on what pieces of HW it's wired up
	 * to.
	 *
	 * We configure it based on the sensitivity of the first source
	 * being setup, and reject any subsequent attempt at configuring it in a
	 * different way.
	 */
	if (fic->state == AL_FIC_UNCONFIGURED) {
		al_fic_set_trigger(fic, gc, new_state);
	} else if (fic->state != new_state) {
		pr_debug("fic %s state already configured to %d\n", fic->name, fic->state);
		return -EINVAL;
	}
	return 0;
}

static void al_fic_irq_handler(struct irq_desc *desc)
{
	struct al_fic *fic = irq_desc_get_handler_data(desc);
	struct irq_domain *domain = fic->domain;
	struct irq_chip *irqchip = irq_desc_get_chip(desc);
	struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0);
	unsigned long pending;
	u32 hwirq;

	chained_irq_enter(irqchip, desc);

	pending = readl_relaxed(fic->base + AL_FIC_CAUSE);
	pending &= ~gc->mask_cache;

	for_each_set_bit(hwirq, &pending, NR_FIC_IRQS)
		generic_handle_domain_irq(domain, hwirq);

	chained_irq_exit(irqchip, desc);
}

static int al_fic_irq_retrigger(struct irq_data *data)
{
	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
	struct al_fic *fic = gc->private;

	writel_relaxed(BIT(data->hwirq), fic->base + AL_FIC_SET_CAUSE);

	return 1;

Annotation

Implementation Notes