Example #1
0
/**
 * fsl_mc_device_remove - Remove a MC object device from being visible to
 * Linux
 *
 * @mc_dev: Pointer to a MC object device object
 */
void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
{
	struct fsl_mc_bus *mc_bus = NULL;

	kfree(mc_dev->regions);

	/*
	 * The device-specific remove callback will get invoked by device_del()
	 */
	device_del(&mc_dev->dev);
	put_device(&mc_dev->dev);

	if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) {
		mc_bus = to_fsl_mc_bus(mc_dev);
		if (mc_dev->mc_io) {
			fsl_destroy_mc_io(mc_dev->mc_io);
			mc_dev->mc_io = NULL;
		}

		if (fsl_mc_is_root_dprc(&mc_dev->dev)) {
			if (atomic_read(&root_dprc_count) > 0)
				atomic_dec(&root_dprc_count);
			else
				WARN_ON(1);
		}
	}

	if (mc_bus)
		devm_kfree(mc_dev->dev.parent, mc_bus);
	else
		kmem_cache_free(mc_dev_cache, mc_dev);
}
Example #2
0
/**
 * Creates an MC I/O object
 *
 * @dev: device to be associated with the MC I/O object
 * @mc_portal_phys_addr: physical address of the MC portal to use
 * @mc_portal_size: size in bytes of the MC portal
 * @dpmcp-dev: Pointer to the DPMCP object associated with this MC I/O
 * object or NULL if none.
 * @flags: flags for the new MC I/O object
 * @new_mc_io: Area to return pointer to newly created MC I/O object
 *
 * Returns '0' on Success; Error code otherwise.
 */
int __must_check fsl_create_mc_io(struct device *dev,
				  phys_addr_t mc_portal_phys_addr,
				  u32 mc_portal_size,
				  struct fsl_mc_device *dpmcp_dev,
				  u32 flags, struct fsl_mc_io **new_mc_io)
{
	int error;
	struct fsl_mc_io *mc_io;
	void __iomem *mc_portal_virt_addr;
	struct resource *res;

	mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL);
	if (!mc_io)
		return -ENOMEM;

	mc_io->dev = dev;
	mc_io->flags = flags;
	mc_io->portal_phys_addr = mc_portal_phys_addr;
	mc_io->portal_size = mc_portal_size;
	if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
		spin_lock_init(&mc_io->spinlock);
	else
		mutex_init(&mc_io->mutex);

	res = devm_request_mem_region(dev,
				      mc_portal_phys_addr,
				      mc_portal_size,
				      "mc_portal");
	if (!res) {
		dev_err(dev,
			"devm_request_mem_region failed for MC portal %#llx\n",
			mc_portal_phys_addr);
		return -EBUSY;
	}

	mc_portal_virt_addr = devm_ioremap_nocache(dev,
						   mc_portal_phys_addr,
						   mc_portal_size);
	if (!mc_portal_virt_addr) {
		dev_err(dev,
			"devm_ioremap_nocache failed for MC portal %#llx\n",
			mc_portal_phys_addr);
		return -ENXIO;
	}

	mc_io->portal_virt_addr = mc_portal_virt_addr;
	if (dpmcp_dev) {
		error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev);
		if (error < 0)
			goto error_destroy_mc_io;
	}

	*new_mc_io = mc_io;
	return 0;

error_destroy_mc_io:
	fsl_destroy_mc_io(mc_io);
	return error;
}
Example #3
0
/**
 * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals
 * of a given MC bus
 *
 * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
 */
void fsl_mc_portal_free(struct fsl_mc_io *mc_io)
{
	struct fsl_mc_resource *resource;

	resource = mc_io->resource;
	if (WARN_ON(resource->type != FSL_MC_POOL_DPMCP))
		return;
	if (WARN_ON(!resource->data))
		return;

	fsl_destroy_mc_io(mc_io);
	fsl_mc_resource_free(resource);
}
Example #4
0
/**
 * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals
 * of a given MC bus
 *
 * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
 */
void fsl_mc_portal_free(struct fsl_mc_io *mc_io)
{
	struct fsl_mc_device *dpmcp_dev;
	struct fsl_mc_resource *resource;

	/*
	 * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed
	 * to have a DPMCP object associated with.
	 */
	dpmcp_dev = mc_io->dpmcp_dev;
	if (WARN_ON(!dpmcp_dev))
		return;

	resource = dpmcp_dev->resource;
	if (WARN_ON(!resource || resource->type != FSL_MC_POOL_DPMCP))
		return;

	if (WARN_ON(resource->data != dpmcp_dev))
		return;

	fsl_destroy_mc_io(mc_io);
	fsl_mc_resource_free(resource);
}
Example #5
0
/**
 * dprc_remove - callback invoked when a DPRC is being unbound from this driver
 *
 * @mc_dev: Pointer to fsl-mc device representing the DPRC
 *
 * It removes the DPRC's child objects from Linux (not from the MC) and
 * closes the DPRC device in the MC.
 * It tears down the interrupts that were configured for the DPRC device.
 * It destroys the interrupt pool associated with this MC bus.
 */
static int dprc_remove(struct fsl_mc_device *mc_dev)
{
	int error;
	struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);

	if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0))
		return -EINVAL;
	if (WARN_ON(!mc_dev->mc_io))
		return -EINVAL;

	if (WARN_ON(!mc_bus->irq_resources))
		return -EINVAL;

	if (dev_get_msi_domain(&mc_dev->dev))
		dprc_teardown_irq(mc_dev);

	device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);

	if (dev_get_msi_domain(&mc_dev->dev)) {
		fsl_mc_cleanup_irq_pool(mc_bus);
		dev_set_msi_domain(&mc_dev->dev, NULL);
	}

	fsl_mc_cleanup_all_resource_pools(mc_dev);

	error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
	if (error < 0)
		dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);

	if (!fsl_mc_is_root_dprc(&mc_dev->dev)) {
		fsl_destroy_mc_io(mc_dev->mc_io);
		mc_dev->mc_io = NULL;
	}

	dev_info(&mc_dev->dev, "DPRC device unbound from driver");
	return 0;
}
Example #6
0
/**
 * dprc_probe - callback invoked when a DPRC is being bound to this driver
 *
 * @mc_dev: Pointer to fsl-mc device representing a DPRC
 *
 * It opens the physical DPRC in the MC.
 * It scans the DPRC to discover the MC objects contained in it.
 * It creates the interrupt pool for the MC bus associated with the DPRC.
 * It configures the interrupts for the DPRC device itself.
 */
static int dprc_probe(struct fsl_mc_device *mc_dev)
{
	int error;
	size_t region_size;
	struct device *parent_dev = mc_dev->dev.parent;
	struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
	bool mc_io_created = false;
	bool msi_domain_set = false;

	if (WARN_ON(strcmp(mc_dev->obj_desc.type, "dprc") != 0))
		return -EINVAL;

	if (WARN_ON(dev_get_msi_domain(&mc_dev->dev)))
		return -EINVAL;

	if (!mc_dev->mc_io) {
		/*
		 * This is a child DPRC:
		 */
		if (WARN_ON(!dev_is_fsl_mc(parent_dev)))
			return -EINVAL;

		if (WARN_ON(mc_dev->obj_desc.region_count == 0))
			return -EINVAL;

		region_size = mc_dev->regions[0].end -
			      mc_dev->regions[0].start + 1;

		error = fsl_create_mc_io(&mc_dev->dev,
					 mc_dev->regions[0].start,
					 region_size,
					 NULL,
					 FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
					 &mc_dev->mc_io);
		if (error < 0)
			return error;

		mc_io_created = true;

		/*
		 * Inherit parent MSI domain:
		 */
		dev_set_msi_domain(&mc_dev->dev,
				   dev_get_msi_domain(parent_dev));
		msi_domain_set = true;
	} else {
		/*
		 * This is a root DPRC
		 */
		struct irq_domain *mc_msi_domain;

		if (WARN_ON(dev_is_fsl_mc(parent_dev)))
			return -EINVAL;

		error = fsl_mc_find_msi_domain(parent_dev,
					       &mc_msi_domain);
		if (error < 0) {
			dev_warn(&mc_dev->dev,
				 "WARNING: MC bus without interrupt support\n");
		} else {
			dev_set_msi_domain(&mc_dev->dev, mc_msi_domain);
			msi_domain_set = true;
		}
	}

	error = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
			  &mc_dev->mc_handle);
	if (error < 0) {
		dev_err(&mc_dev->dev, "dprc_open() failed: %d\n", error);
		goto error_cleanup_msi_domain;
	}

	error = dprc_get_attributes(mc_dev->mc_io, 0, mc_dev->mc_handle,
				    &mc_bus->dprc_attr);
	if (error < 0) {
		dev_err(&mc_dev->dev, "dprc_get_attributes() failed: %d\n",
			error);
		goto error_cleanup_open;
	}

	if (mc_bus->dprc_attr.version.major < DPRC_MIN_VER_MAJOR ||
	   (mc_bus->dprc_attr.version.major == DPRC_MIN_VER_MAJOR &&
	    mc_bus->dprc_attr.version.minor < DPRC_MIN_VER_MINOR)) {
		dev_err(&mc_dev->dev,
			"ERROR: DPRC version %d.%d not supported\n",
			mc_bus->dprc_attr.version.major,
			mc_bus->dprc_attr.version.minor);
		error = -ENOTSUPP;
		goto error_cleanup_open;
	}

	mutex_init(&mc_bus->scan_mutex);

	/*
	 * Discover MC objects in DPRC object:
	 */
	error = dprc_scan_container(mc_dev);
	if (error < 0)
		goto error_cleanup_open;

	/*
	 * Configure interrupt for the DPRC object associated with this MC bus:
	 */
	error = dprc_setup_irq(mc_dev);
	if (error < 0)
		goto error_cleanup_open;

	dev_info(&mc_dev->dev, "DPRC device bound to driver");
	return 0;

error_cleanup_open:
	(void)dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);

error_cleanup_msi_domain:
	if (msi_domain_set)
		dev_set_msi_domain(&mc_dev->dev, NULL);

	if (mc_io_created) {
		fsl_destroy_mc_io(mc_dev->mc_io);
		mc_dev->mc_io = NULL;
	}

	return error;
}
Example #7
0
/**
 * fsl_mc_bus_probe - callback invoked when the root MC bus is being
 * added
 */
static int fsl_mc_bus_probe(struct platform_device *pdev)
{
	struct dprc_obj_desc obj_desc;
	int error;
	struct fsl_mc *mc;
	struct fsl_mc_device *mc_bus_dev = NULL;
	struct fsl_mc_io *mc_io = NULL;
	int container_id;
	phys_addr_t mc_portal_phys_addr;
	u32 mc_portal_size;
	struct mc_version mc_version;
	struct resource res;

	dev_info(&pdev->dev, "Root MC bus device probed");

	mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
	if (!mc)
		return -ENOMEM;

	platform_set_drvdata(pdev, mc);

	/*
	 * Get physical address of MC portal for the root DPRC:
	 */
	error = of_address_to_resource(pdev->dev.of_node, 0, &res);
	if (error < 0) {
		dev_err(&pdev->dev,
			"of_address_to_resource() failed for %s\n",
			pdev->dev.of_node->full_name);
		return error;
	}

	mc_portal_phys_addr = res.start;
	mc_portal_size = resource_size(&res);
	error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr,
				 mc_portal_size, NULL,
				 FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &mc_io);
	if (error < 0)
		return error;

	error = mc_get_version(mc_io, 0, &mc_version);
	if (error != 0) {
		dev_err(&pdev->dev,
			"mc_get_version() failed with error %d\n", error);
		goto error_cleanup_mc_io;
	}

	dev_info(&pdev->dev,
		 "Freescale Management Complex Firmware version: %u.%u.%u\n",
		 mc_version.major, mc_version.minor, mc_version.revision);

	if (mc_version.major < MC_VER_MAJOR) {
		dev_err(&pdev->dev,
			"ERROR: MC firmware version not supported by driver (driver version: %u.%u)\n",
			MC_VER_MAJOR, MC_VER_MINOR);
		error = -ENOTSUPP;
		goto error_cleanup_mc_io;
	}

	if (mc_version.major > MC_VER_MAJOR) {
		dev_warn(&pdev->dev,
			 "WARNING: driver may not support newer MC firmware features (driver version: %u.%u)\n",
			 MC_VER_MAJOR, MC_VER_MINOR);
	}

	error = get_mc_addr_translation_ranges(&pdev->dev,
					       &mc->translation_ranges,
					       &mc->num_translation_ranges);
	if (error < 0)
		goto error_cleanup_mc_io;

	error = dpmng_get_container_id(mc_io, 0, &container_id);
	if (error < 0) {
		dev_err(&pdev->dev,
			"dpmng_get_container_id() failed: %d\n", error);
		goto error_cleanup_mc_io;
	}

	obj_desc.vendor = FSL_MC_VENDOR_FREESCALE;
	strcpy(obj_desc.type, "dprc");
	obj_desc.id = container_id;
	obj_desc.ver_major = DPRC_VER_MAJOR;
	obj_desc.ver_minor = DPRC_VER_MINOR;
	obj_desc.irq_count = 1;
	obj_desc.region_count = 0;

	error = fsl_mc_device_add(&obj_desc, mc_io, &pdev->dev, &mc_bus_dev);
	if (error < 0)
		goto error_cleanup_mc_io;

	mc->root_mc_bus_dev = mc_bus_dev;
	return 0;

error_cleanup_mc_io:
	fsl_destroy_mc_io(mc_io);
	return error;
}