static int __init gicv2m_init_one()

in irq-gic-v2m.c [317:405]


static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
				  u32 spi_start, u32 nr_spis,
				  struct resource *res, u32 flags)
{
	int ret;
	struct v2m_data *v2m;

	v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL);
	if (!v2m)
		return -ENOMEM;

	INIT_LIST_HEAD(&v2m->entry);
	v2m->fwnode = fwnode;
	v2m->flags = flags;

	memcpy(&v2m->res, res, sizeof(struct resource));

	v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res));
	if (!v2m->base) {
		pr_err("Failed to map GICv2m resource\n");
		ret = -ENOMEM;
		goto err_free_v2m;
	}

	if (spi_start && nr_spis) {
		v2m->spi_start = spi_start;
		v2m->nr_spis = nr_spis;
	} else {
		u32 typer;

		/* Graviton should always have explicit spi_start/nr_spis */
		if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) {
			ret = -EINVAL;
			goto err_iounmap;
		}
		typer = readl_relaxed(v2m->base + V2M_MSI_TYPER);

		v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer);
		v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer);
	}

	if (!is_msi_spi_valid(v2m->spi_start, v2m->nr_spis)) {
		ret = -EINVAL;
		goto err_iounmap;
	}

	/*
	 * APM X-Gene GICv2m implementation has an erratum where
	 * the MSI data needs to be the offset from the spi_start
	 * in order to trigger the correct MSI interrupt. This is
	 * different from the standard GICv2m implementation where
	 * the MSI data is the absolute value within the range from
	 * spi_start to (spi_start + num_spis).
	 *
	 * Broadcom NS2 GICv2m implementation has an erratum where the MSI data
	 * is 'spi_number - 32'
	 *
	 * Reading that register fails on the Graviton implementation
	 */
	if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) {
		switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) {
		case XGENE_GICV2M_MSI_IIDR:
			v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
			v2m->spi_offset = v2m->spi_start;
			break;
		case BCM_NS2_GICV2M_MSI_IIDR:
			v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
			v2m->spi_offset = 32;
			break;
		}
	}
	v2m->bm = bitmap_zalloc(v2m->nr_spis, GFP_KERNEL);
	if (!v2m->bm) {
		ret = -ENOMEM;
		goto err_iounmap;
	}

	list_add_tail(&v2m->entry, &v2m_nodes);

	pr_info("range%pR, SPI[%d:%d]\n", res,
		v2m->spi_start, (v2m->spi_start + v2m->nr_spis - 1));
	return 0;

err_iounmap:
	iounmap(v2m->base);
err_free_v2m:
	kfree(v2m);
	return ret;
}