18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright 2013-2016 Freescale Semiconductor Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/io.h>
88c2ecf20Sopenharmony_ci#include <linux/fsl/mc.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "fsl-mc-private.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io,
138c2ecf20Sopenharmony_ci			       struct fsl_mc_device *dpmcp_dev)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	int error;
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	if (mc_io->dpmcp_dev)
188c2ecf20Sopenharmony_ci		return -EINVAL;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	if (dpmcp_dev->mc_io)
218c2ecf20Sopenharmony_ci		return -EINVAL;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	error = dpmcp_open(mc_io,
248c2ecf20Sopenharmony_ci			   0,
258c2ecf20Sopenharmony_ci			   dpmcp_dev->obj_desc.id,
268c2ecf20Sopenharmony_ci			   &dpmcp_dev->mc_handle);
278c2ecf20Sopenharmony_ci	if (error < 0)
288c2ecf20Sopenharmony_ci		return error;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	mc_io->dpmcp_dev = dpmcp_dev;
318c2ecf20Sopenharmony_ci	dpmcp_dev->mc_io = mc_io;
328c2ecf20Sopenharmony_ci	return 0;
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	int error;
388c2ecf20Sopenharmony_ci	struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	error = dpmcp_close(mc_io,
418c2ecf20Sopenharmony_ci			    0,
428c2ecf20Sopenharmony_ci			    dpmcp_dev->mc_handle);
438c2ecf20Sopenharmony_ci	if (error < 0) {
448c2ecf20Sopenharmony_ci		dev_err(&dpmcp_dev->dev, "dpmcp_close() failed: %d\n",
458c2ecf20Sopenharmony_ci			error);
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	mc_io->dpmcp_dev = NULL;
498c2ecf20Sopenharmony_ci	dpmcp_dev->mc_io = NULL;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/**
538c2ecf20Sopenharmony_ci * Creates an MC I/O object
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * @dev: device to be associated with the MC I/O object
568c2ecf20Sopenharmony_ci * @mc_portal_phys_addr: physical address of the MC portal to use
578c2ecf20Sopenharmony_ci * @mc_portal_size: size in bytes of the MC portal
588c2ecf20Sopenharmony_ci * @dpmcp-dev: Pointer to the DPMCP object associated with this MC I/O
598c2ecf20Sopenharmony_ci * object or NULL if none.
608c2ecf20Sopenharmony_ci * @flags: flags for the new MC I/O object
618c2ecf20Sopenharmony_ci * @new_mc_io: Area to return pointer to newly created MC I/O object
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci * Returns '0' on Success; Error code otherwise.
648c2ecf20Sopenharmony_ci */
658c2ecf20Sopenharmony_ciint __must_check fsl_create_mc_io(struct device *dev,
668c2ecf20Sopenharmony_ci				  phys_addr_t mc_portal_phys_addr,
678c2ecf20Sopenharmony_ci				  u32 mc_portal_size,
688c2ecf20Sopenharmony_ci				  struct fsl_mc_device *dpmcp_dev,
698c2ecf20Sopenharmony_ci				  u32 flags, struct fsl_mc_io **new_mc_io)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	int error;
728c2ecf20Sopenharmony_ci	struct fsl_mc_io *mc_io;
738c2ecf20Sopenharmony_ci	void __iomem *mc_portal_virt_addr;
748c2ecf20Sopenharmony_ci	struct resource *res;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL);
778c2ecf20Sopenharmony_ci	if (!mc_io)
788c2ecf20Sopenharmony_ci		return -ENOMEM;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	mc_io->dev = dev;
818c2ecf20Sopenharmony_ci	mc_io->flags = flags;
828c2ecf20Sopenharmony_ci	mc_io->portal_phys_addr = mc_portal_phys_addr;
838c2ecf20Sopenharmony_ci	mc_io->portal_size = mc_portal_size;
848c2ecf20Sopenharmony_ci	if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
858c2ecf20Sopenharmony_ci		raw_spin_lock_init(&mc_io->spinlock);
868c2ecf20Sopenharmony_ci	else
878c2ecf20Sopenharmony_ci		mutex_init(&mc_io->mutex);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	res = devm_request_mem_region(dev,
908c2ecf20Sopenharmony_ci				      mc_portal_phys_addr,
918c2ecf20Sopenharmony_ci				      mc_portal_size,
928c2ecf20Sopenharmony_ci				      "mc_portal");
938c2ecf20Sopenharmony_ci	if (!res) {
948c2ecf20Sopenharmony_ci		dev_err(dev,
958c2ecf20Sopenharmony_ci			"devm_request_mem_region failed for MC portal %pa\n",
968c2ecf20Sopenharmony_ci			&mc_portal_phys_addr);
978c2ecf20Sopenharmony_ci		return -EBUSY;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	mc_portal_virt_addr = devm_ioremap(dev,
1018c2ecf20Sopenharmony_ci						   mc_portal_phys_addr,
1028c2ecf20Sopenharmony_ci						   mc_portal_size);
1038c2ecf20Sopenharmony_ci	if (!mc_portal_virt_addr) {
1048c2ecf20Sopenharmony_ci		dev_err(dev,
1058c2ecf20Sopenharmony_ci			"devm_ioremap failed for MC portal %pa\n",
1068c2ecf20Sopenharmony_ci			&mc_portal_phys_addr);
1078c2ecf20Sopenharmony_ci		return -ENXIO;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	mc_io->portal_virt_addr = mc_portal_virt_addr;
1118c2ecf20Sopenharmony_ci	if (dpmcp_dev) {
1128c2ecf20Sopenharmony_ci		error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev);
1138c2ecf20Sopenharmony_ci		if (error < 0)
1148c2ecf20Sopenharmony_ci			goto error_destroy_mc_io;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	*new_mc_io = mc_io;
1188c2ecf20Sopenharmony_ci	return 0;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cierror_destroy_mc_io:
1218c2ecf20Sopenharmony_ci	fsl_destroy_mc_io(mc_io);
1228c2ecf20Sopenharmony_ci	return error;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/**
1268c2ecf20Sopenharmony_ci * Destroys an MC I/O object
1278c2ecf20Sopenharmony_ci *
1288c2ecf20Sopenharmony_ci * @mc_io: MC I/O object to destroy
1298c2ecf20Sopenharmony_ci */
1308c2ecf20Sopenharmony_civoid fsl_destroy_mc_io(struct fsl_mc_io *mc_io)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct fsl_mc_device *dpmcp_dev;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (!mc_io)
1358c2ecf20Sopenharmony_ci		return;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	dpmcp_dev = mc_io->dpmcp_dev;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (dpmcp_dev)
1408c2ecf20Sopenharmony_ci		fsl_mc_io_unset_dpmcp(mc_io);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	devm_iounmap(mc_io->dev, mc_io->portal_virt_addr);
1438c2ecf20Sopenharmony_ci	devm_release_mem_region(mc_io->dev,
1448c2ecf20Sopenharmony_ci				mc_io->portal_phys_addr,
1458c2ecf20Sopenharmony_ci				mc_io->portal_size);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	mc_io->portal_virt_addr = NULL;
1488c2ecf20Sopenharmony_ci	devm_kfree(mc_io->dev, mc_io);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/**
1528c2ecf20Sopenharmony_ci * fsl_mc_portal_allocate - Allocates an MC portal
1538c2ecf20Sopenharmony_ci *
1548c2ecf20Sopenharmony_ci * @mc_dev: MC device for which the MC portal is to be allocated
1558c2ecf20Sopenharmony_ci * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated
1568c2ecf20Sopenharmony_ci * MC portal.
1578c2ecf20Sopenharmony_ci * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object
1588c2ecf20Sopenharmony_ci * that wraps the allocated MC portal is to be returned
1598c2ecf20Sopenharmony_ci *
1608c2ecf20Sopenharmony_ci * This function allocates an MC portal from the device's parent DPRC,
1618c2ecf20Sopenharmony_ci * from the corresponding MC bus' pool of MC portals and wraps
1628c2ecf20Sopenharmony_ci * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the
1638c2ecf20Sopenharmony_ci * portal is allocated from its own MC bus.
1648c2ecf20Sopenharmony_ci */
1658c2ecf20Sopenharmony_ciint __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev,
1668c2ecf20Sopenharmony_ci					u16 mc_io_flags,
1678c2ecf20Sopenharmony_ci					struct fsl_mc_io **new_mc_io)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct fsl_mc_device *mc_bus_dev;
1708c2ecf20Sopenharmony_ci	struct fsl_mc_bus *mc_bus;
1718c2ecf20Sopenharmony_ci	phys_addr_t mc_portal_phys_addr;
1728c2ecf20Sopenharmony_ci	size_t mc_portal_size;
1738c2ecf20Sopenharmony_ci	struct fsl_mc_device *dpmcp_dev;
1748c2ecf20Sopenharmony_ci	int error = -EINVAL;
1758c2ecf20Sopenharmony_ci	struct fsl_mc_resource *resource = NULL;
1768c2ecf20Sopenharmony_ci	struct fsl_mc_io *mc_io = NULL;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (mc_dev->flags & FSL_MC_IS_DPRC) {
1798c2ecf20Sopenharmony_ci		mc_bus_dev = mc_dev;
1808c2ecf20Sopenharmony_ci	} else {
1818c2ecf20Sopenharmony_ci		if (!dev_is_fsl_mc(mc_dev->dev.parent))
1828c2ecf20Sopenharmony_ci			return error;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent);
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	mc_bus = to_fsl_mc_bus(mc_bus_dev);
1888c2ecf20Sopenharmony_ci	*new_mc_io = NULL;
1898c2ecf20Sopenharmony_ci	error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource);
1908c2ecf20Sopenharmony_ci	if (error < 0)
1918c2ecf20Sopenharmony_ci		return error;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	error = -EINVAL;
1948c2ecf20Sopenharmony_ci	dpmcp_dev = resource->data;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (dpmcp_dev->obj_desc.ver_major < DPMCP_MIN_VER_MAJOR ||
1978c2ecf20Sopenharmony_ci	    (dpmcp_dev->obj_desc.ver_major == DPMCP_MIN_VER_MAJOR &&
1988c2ecf20Sopenharmony_ci	     dpmcp_dev->obj_desc.ver_minor < DPMCP_MIN_VER_MINOR)) {
1998c2ecf20Sopenharmony_ci		dev_err(&dpmcp_dev->dev,
2008c2ecf20Sopenharmony_ci			"ERROR: Version %d.%d of DPMCP not supported.\n",
2018c2ecf20Sopenharmony_ci			dpmcp_dev->obj_desc.ver_major,
2028c2ecf20Sopenharmony_ci			dpmcp_dev->obj_desc.ver_minor);
2038c2ecf20Sopenharmony_ci		error = -ENOTSUPP;
2048c2ecf20Sopenharmony_ci		goto error_cleanup_resource;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	mc_portal_phys_addr = dpmcp_dev->regions[0].start;
2088c2ecf20Sopenharmony_ci	mc_portal_size = resource_size(dpmcp_dev->regions);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	error = fsl_create_mc_io(&mc_bus_dev->dev,
2118c2ecf20Sopenharmony_ci				 mc_portal_phys_addr,
2128c2ecf20Sopenharmony_ci				 mc_portal_size, dpmcp_dev,
2138c2ecf20Sopenharmony_ci				 mc_io_flags, &mc_io);
2148c2ecf20Sopenharmony_ci	if (error < 0)
2158c2ecf20Sopenharmony_ci		goto error_cleanup_resource;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	dpmcp_dev->consumer_link = device_link_add(&mc_dev->dev,
2188c2ecf20Sopenharmony_ci						   &dpmcp_dev->dev,
2198c2ecf20Sopenharmony_ci						   DL_FLAG_AUTOREMOVE_CONSUMER);
2208c2ecf20Sopenharmony_ci	if (!dpmcp_dev->consumer_link) {
2218c2ecf20Sopenharmony_ci		error = -EINVAL;
2228c2ecf20Sopenharmony_ci		goto error_cleanup_mc_io;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	*new_mc_io = mc_io;
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cierror_cleanup_mc_io:
2298c2ecf20Sopenharmony_ci	fsl_destroy_mc_io(mc_io);
2308c2ecf20Sopenharmony_cierror_cleanup_resource:
2318c2ecf20Sopenharmony_ci	fsl_mc_resource_free(resource);
2328c2ecf20Sopenharmony_ci	return error;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_portal_allocate);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/**
2378c2ecf20Sopenharmony_ci * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals
2388c2ecf20Sopenharmony_ci * of a given MC bus
2398c2ecf20Sopenharmony_ci *
2408c2ecf20Sopenharmony_ci * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
2418c2ecf20Sopenharmony_ci */
2428c2ecf20Sopenharmony_civoid fsl_mc_portal_free(struct fsl_mc_io *mc_io)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct fsl_mc_device *dpmcp_dev;
2458c2ecf20Sopenharmony_ci	struct fsl_mc_resource *resource;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/*
2488c2ecf20Sopenharmony_ci	 * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed
2498c2ecf20Sopenharmony_ci	 * to have a DPMCP object associated with.
2508c2ecf20Sopenharmony_ci	 */
2518c2ecf20Sopenharmony_ci	dpmcp_dev = mc_io->dpmcp_dev;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	resource = dpmcp_dev->resource;
2548c2ecf20Sopenharmony_ci	if (!resource || resource->type != FSL_MC_POOL_DPMCP)
2558c2ecf20Sopenharmony_ci		return;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (resource->data != dpmcp_dev)
2588c2ecf20Sopenharmony_ci		return;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	fsl_destroy_mc_io(mc_io);
2618c2ecf20Sopenharmony_ci	fsl_mc_resource_free(resource);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	dpmcp_dev->consumer_link = NULL;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_portal_free);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/**
2688c2ecf20Sopenharmony_ci * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object
2698c2ecf20Sopenharmony_ci *
2708c2ecf20Sopenharmony_ci * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free
2718c2ecf20Sopenharmony_ci */
2728c2ecf20Sopenharmony_ciint fsl_mc_portal_reset(struct fsl_mc_io *mc_io)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	int error;
2758c2ecf20Sopenharmony_ci	struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle);
2788c2ecf20Sopenharmony_ci	if (error < 0) {
2798c2ecf20Sopenharmony_ci		dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error);
2808c2ecf20Sopenharmony_ci		return error;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	return 0;
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_portal_reset);
286