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;
}