static int fsl_mc_device_get_mmio_regions()

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, &region_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,
					  &regions[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;
}