in fsl-mc/fsl-mc-bus.c [662:753]
static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev,
struct fsl_mc_device *mc_bus_dev)
{
int i;
int error;
struct resource *regions;
struct fsl_mc_obj_desc *obj_desc = &mc_dev->obj_desc;
struct device *parent_dev = mc_dev->dev.parent;
enum dprc_region_type mc_region_type;
if (is_fsl_mc_bus_dprc(mc_dev) ||
is_fsl_mc_bus_dpmcp(mc_dev)) {
mc_region_type = DPRC_REGION_TYPE_MC_PORTAL;
} else if (is_fsl_mc_bus_dpio(mc_dev)) {
mc_region_type = DPRC_REGION_TYPE_QBMAN_PORTAL;
} else {
/*
* This function should not have been called for this MC object
* type, as this object type is not supposed to have MMIO
* regions
*/
return -EINVAL;
}
regions = kmalloc_array(obj_desc->region_count,
sizeof(regions[0]), GFP_KERNEL);
if (!regions)
return -ENOMEM;
for (i = 0; i < obj_desc->region_count; i++) {
struct dprc_region_desc region_desc;
error = dprc_get_obj_region(mc_bus_dev->mc_io,
0,
mc_bus_dev->mc_handle,
obj_desc->type,
obj_desc->id, i, ®ion_desc);
if (error < 0) {
dev_err(parent_dev,
"dprc_get_obj_region() failed: %d\n", error);
goto error_cleanup_regions;
}
/*
* Older MC only returned region offset and no base address
* If base address is in the region_desc use it otherwise
* revert to old mechanism
*/
if (region_desc.base_address) {
regions[i].start = region_desc.base_address +
region_desc.base_offset;
} else {
error = translate_mc_addr(mc_dev, mc_region_type,
region_desc.base_offset,
®ions[i].start);
/*
* Some versions of the MC firmware wrongly report
* 0 for register base address of the DPMCP associated
* with child DPRC objects thus rendering them unusable.
* This is particularly troublesome in ACPI boot
* scenarios where the legacy way of extracting this
* base address from the device tree does not apply.
* Given that DPMCPs share the same base address,
* workaround this by using the base address extracted
* from the root DPRC container.
*/
if (is_fsl_mc_bus_dprc(mc_dev) &&
regions[i].start == region_desc.base_offset)
regions[i].start += mc_portal_base_phys_addr;
}
if (error < 0) {
dev_err(parent_dev,
"Invalid MC offset: %#x (for %s.%d\'s region %d)\n",
region_desc.base_offset,
obj_desc->type, obj_desc->id, i);
goto error_cleanup_regions;
}
regions[i].end = regions[i].start + region_desc.size - 1;
regions[i].name = "fsl-mc object MMIO region";
regions[i].flags = region_desc.flags & IORESOURCE_BITS;
regions[i].flags |= IORESOURCE_MEM;
}
mc_dev->regions = regions;
return 0;
error_cleanup_regions:
kfree(regions);
return error;
}