drivers/irqchip/irq-gic-v2m.c

Source file repositories/reference/linux-study-clean/drivers/irqchip/irq-gic-v2m.c

File Facts

System
Linux kernel
Corpus path
drivers/irqchip/irq-gic-v2m.c
Extension
.c
Size
14062 bytes
Lines
550
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 v2m_data {
	struct list_head entry;
	struct fwnode_handle *fwnode;
	struct resource res;	/* GICv2m resource */
	void __iomem *base;	/* GICv2m virt address */
	u32 spi_start;		/* The SPI number that MSIs start */
	u32 nr_spis;		/* The number of SPIs for MSIs */
	u32 spi_offset;		/* offset to be subtracted from SPI number */
	unsigned long *bm;	/* MSI vector bitmap */
	u32 flags;		/* v2m flags for specific implementation */
};

static phys_addr_t gicv2m_get_msi_addr(struct v2m_data *v2m, int hwirq)
{
	if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)
		return v2m->res.start | ((hwirq - 32) << 3);
	else
		return v2m->res.start + V2M_MSI_SETSPI_NS;
}

static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
	struct v2m_data *v2m = irq_data_get_irq_chip_data(data);
	phys_addr_t addr = gicv2m_get_msi_addr(v2m, data->hwirq);

	if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)
		msg->data = 0;
	else
		msg->data = data->hwirq;
	if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
		msg->data -= v2m->spi_offset;

	msi_msg_set_addr(irq_data_get_msi_desc(data), msg, addr);
}

static struct irq_chip gicv2m_irq_chip = {
	.name			= "GICv2m",
	.irq_mask		= irq_chip_mask_parent,
	.irq_unmask		= irq_chip_unmask_parent,
	.irq_eoi		= irq_chip_eoi_parent,
	.irq_set_affinity	= irq_chip_set_affinity_parent,
	.irq_compose_msi_msg	= gicv2m_compose_msi_msg,
};

static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain,
				       unsigned int virq,
				       irq_hw_number_t hwirq)
{
	struct irq_fwspec fwspec;
	struct irq_data *d;
	int err;

	if (is_of_node(domain->parent->fwnode)) {
		fwspec.fwnode = domain->parent->fwnode;
		fwspec.param_count = 3;
		fwspec.param[0] = 0;
		fwspec.param[1] = hwirq - 32;
		fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
	} else if (is_fwnode_irqchip(domain->parent->fwnode)) {
		fwspec.fwnode = domain->parent->fwnode;
		fwspec.param_count = 2;
		fwspec.param[0] = hwirq;
		fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
	} else {
		return -EINVAL;
	}

	err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
	if (err)
		return err;

	/* Configure the interrupt line to be edge */
	d = irq_domain_get_irq_data(domain->parent, virq);
	d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
	return 0;
}

static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq,
			       int nr_irqs)
{
	spin_lock(&v2m_lock);
	bitmap_release_region(v2m->bm, hwirq - v2m->spi_start,
			      get_count_order(nr_irqs));
	spin_unlock(&v2m_lock);
}

static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
				   unsigned int nr_irqs, void *args)
{
	msi_alloc_info_t *info = args;

Annotation

Implementation Notes