162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2013-2016 Freescale Semiconductor Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/io.h> 862306a36Sopenharmony_ci#include <linux/fsl/mc.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "fsl-mc-private.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io, 1362306a36Sopenharmony_ci struct fsl_mc_device *dpmcp_dev) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci int error; 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci if (mc_io->dpmcp_dev) 1862306a36Sopenharmony_ci return -EINVAL; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci if (dpmcp_dev->mc_io) 2162306a36Sopenharmony_ci return -EINVAL; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci error = dpmcp_open(mc_io, 2462306a36Sopenharmony_ci 0, 2562306a36Sopenharmony_ci dpmcp_dev->obj_desc.id, 2662306a36Sopenharmony_ci &dpmcp_dev->mc_handle); 2762306a36Sopenharmony_ci if (error < 0) 2862306a36Sopenharmony_ci return error; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci mc_io->dpmcp_dev = dpmcp_dev; 3162306a36Sopenharmony_ci dpmcp_dev->mc_io = mc_io; 3262306a36Sopenharmony_ci return 0; 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci int error; 3862306a36Sopenharmony_ci struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci error = dpmcp_close(mc_io, 4162306a36Sopenharmony_ci 0, 4262306a36Sopenharmony_ci dpmcp_dev->mc_handle); 4362306a36Sopenharmony_ci if (error < 0) { 4462306a36Sopenharmony_ci dev_err(&dpmcp_dev->dev, "dpmcp_close() failed: %d\n", 4562306a36Sopenharmony_ci error); 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci mc_io->dpmcp_dev = NULL; 4962306a36Sopenharmony_ci dpmcp_dev->mc_io = NULL; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * fsl_create_mc_io() - Creates an MC I/O object 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * @dev: device to be associated with the MC I/O object 5662306a36Sopenharmony_ci * @mc_portal_phys_addr: physical address of the MC portal to use 5762306a36Sopenharmony_ci * @mc_portal_size: size in bytes of the MC portal 5862306a36Sopenharmony_ci * @dpmcp_dev: Pointer to the DPMCP object associated with this MC I/O 5962306a36Sopenharmony_ci * object or NULL if none. 6062306a36Sopenharmony_ci * @flags: flags for the new MC I/O object 6162306a36Sopenharmony_ci * @new_mc_io: Area to return pointer to newly created MC I/O object 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * Returns '0' on Success; Error code otherwise. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ciint __must_check fsl_create_mc_io(struct device *dev, 6662306a36Sopenharmony_ci phys_addr_t mc_portal_phys_addr, 6762306a36Sopenharmony_ci u32 mc_portal_size, 6862306a36Sopenharmony_ci struct fsl_mc_device *dpmcp_dev, 6962306a36Sopenharmony_ci u32 flags, struct fsl_mc_io **new_mc_io) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci int error; 7262306a36Sopenharmony_ci struct fsl_mc_io *mc_io; 7362306a36Sopenharmony_ci void __iomem *mc_portal_virt_addr; 7462306a36Sopenharmony_ci struct resource *res; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL); 7762306a36Sopenharmony_ci if (!mc_io) 7862306a36Sopenharmony_ci return -ENOMEM; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci mc_io->dev = dev; 8162306a36Sopenharmony_ci mc_io->flags = flags; 8262306a36Sopenharmony_ci mc_io->portal_phys_addr = mc_portal_phys_addr; 8362306a36Sopenharmony_ci mc_io->portal_size = mc_portal_size; 8462306a36Sopenharmony_ci if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) 8562306a36Sopenharmony_ci raw_spin_lock_init(&mc_io->spinlock); 8662306a36Sopenharmony_ci else 8762306a36Sopenharmony_ci mutex_init(&mc_io->mutex); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci res = devm_request_mem_region(dev, 9062306a36Sopenharmony_ci mc_portal_phys_addr, 9162306a36Sopenharmony_ci mc_portal_size, 9262306a36Sopenharmony_ci "mc_portal"); 9362306a36Sopenharmony_ci if (!res) { 9462306a36Sopenharmony_ci dev_err(dev, 9562306a36Sopenharmony_ci "devm_request_mem_region failed for MC portal %pa\n", 9662306a36Sopenharmony_ci &mc_portal_phys_addr); 9762306a36Sopenharmony_ci return -EBUSY; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci mc_portal_virt_addr = devm_ioremap(dev, 10162306a36Sopenharmony_ci mc_portal_phys_addr, 10262306a36Sopenharmony_ci mc_portal_size); 10362306a36Sopenharmony_ci if (!mc_portal_virt_addr) { 10462306a36Sopenharmony_ci dev_err(dev, 10562306a36Sopenharmony_ci "devm_ioremap failed for MC portal %pa\n", 10662306a36Sopenharmony_ci &mc_portal_phys_addr); 10762306a36Sopenharmony_ci return -ENXIO; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci mc_io->portal_virt_addr = mc_portal_virt_addr; 11162306a36Sopenharmony_ci if (dpmcp_dev) { 11262306a36Sopenharmony_ci error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev); 11362306a36Sopenharmony_ci if (error < 0) 11462306a36Sopenharmony_ci goto error_destroy_mc_io; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci *new_mc_io = mc_io; 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cierror_destroy_mc_io: 12162306a36Sopenharmony_ci fsl_destroy_mc_io(mc_io); 12262306a36Sopenharmony_ci return error; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/** 12662306a36Sopenharmony_ci * fsl_destroy_mc_io() - Destroys an MC I/O object 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * @mc_io: MC I/O object to destroy 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_civoid fsl_destroy_mc_io(struct fsl_mc_io *mc_io) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct fsl_mc_device *dpmcp_dev; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (!mc_io) 13562306a36Sopenharmony_ci return; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci dpmcp_dev = mc_io->dpmcp_dev; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (dpmcp_dev) 14062306a36Sopenharmony_ci fsl_mc_io_unset_dpmcp(mc_io); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci devm_iounmap(mc_io->dev, mc_io->portal_virt_addr); 14362306a36Sopenharmony_ci devm_release_mem_region(mc_io->dev, 14462306a36Sopenharmony_ci mc_io->portal_phys_addr, 14562306a36Sopenharmony_ci mc_io->portal_size); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci mc_io->portal_virt_addr = NULL; 14862306a36Sopenharmony_ci devm_kfree(mc_io->dev, mc_io); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/** 15262306a36Sopenharmony_ci * fsl_mc_portal_allocate - Allocates an MC portal 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * @mc_dev: MC device for which the MC portal is to be allocated 15562306a36Sopenharmony_ci * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated 15662306a36Sopenharmony_ci * MC portal. 15762306a36Sopenharmony_ci * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object 15862306a36Sopenharmony_ci * that wraps the allocated MC portal is to be returned 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * This function allocates an MC portal from the device's parent DPRC, 16162306a36Sopenharmony_ci * from the corresponding MC bus' pool of MC portals and wraps 16262306a36Sopenharmony_ci * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the 16362306a36Sopenharmony_ci * portal is allocated from its own MC bus. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ciint __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev, 16662306a36Sopenharmony_ci u16 mc_io_flags, 16762306a36Sopenharmony_ci struct fsl_mc_io **new_mc_io) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct fsl_mc_device *mc_bus_dev; 17062306a36Sopenharmony_ci struct fsl_mc_bus *mc_bus; 17162306a36Sopenharmony_ci phys_addr_t mc_portal_phys_addr; 17262306a36Sopenharmony_ci size_t mc_portal_size; 17362306a36Sopenharmony_ci struct fsl_mc_device *dpmcp_dev; 17462306a36Sopenharmony_ci int error = -EINVAL; 17562306a36Sopenharmony_ci struct fsl_mc_resource *resource = NULL; 17662306a36Sopenharmony_ci struct fsl_mc_io *mc_io = NULL; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (mc_dev->flags & FSL_MC_IS_DPRC) { 17962306a36Sopenharmony_ci mc_bus_dev = mc_dev; 18062306a36Sopenharmony_ci } else { 18162306a36Sopenharmony_ci if (!dev_is_fsl_mc(mc_dev->dev.parent)) 18262306a36Sopenharmony_ci return error; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci mc_bus = to_fsl_mc_bus(mc_bus_dev); 18862306a36Sopenharmony_ci *new_mc_io = NULL; 18962306a36Sopenharmony_ci error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource); 19062306a36Sopenharmony_ci if (error < 0) 19162306a36Sopenharmony_ci return error; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci error = -EINVAL; 19462306a36Sopenharmony_ci dpmcp_dev = resource->data; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (dpmcp_dev->obj_desc.ver_major < DPMCP_MIN_VER_MAJOR || 19762306a36Sopenharmony_ci (dpmcp_dev->obj_desc.ver_major == DPMCP_MIN_VER_MAJOR && 19862306a36Sopenharmony_ci dpmcp_dev->obj_desc.ver_minor < DPMCP_MIN_VER_MINOR)) { 19962306a36Sopenharmony_ci dev_err(&dpmcp_dev->dev, 20062306a36Sopenharmony_ci "ERROR: Version %d.%d of DPMCP not supported.\n", 20162306a36Sopenharmony_ci dpmcp_dev->obj_desc.ver_major, 20262306a36Sopenharmony_ci dpmcp_dev->obj_desc.ver_minor); 20362306a36Sopenharmony_ci error = -ENOTSUPP; 20462306a36Sopenharmony_ci goto error_cleanup_resource; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci mc_portal_phys_addr = dpmcp_dev->regions[0].start; 20862306a36Sopenharmony_ci mc_portal_size = resource_size(dpmcp_dev->regions); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci error = fsl_create_mc_io(&mc_bus_dev->dev, 21162306a36Sopenharmony_ci mc_portal_phys_addr, 21262306a36Sopenharmony_ci mc_portal_size, dpmcp_dev, 21362306a36Sopenharmony_ci mc_io_flags, &mc_io); 21462306a36Sopenharmony_ci if (error < 0) 21562306a36Sopenharmony_ci goto error_cleanup_resource; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci dpmcp_dev->consumer_link = device_link_add(&mc_dev->dev, 21862306a36Sopenharmony_ci &dpmcp_dev->dev, 21962306a36Sopenharmony_ci DL_FLAG_AUTOREMOVE_CONSUMER); 22062306a36Sopenharmony_ci if (!dpmcp_dev->consumer_link) { 22162306a36Sopenharmony_ci error = -EINVAL; 22262306a36Sopenharmony_ci goto error_cleanup_mc_io; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci *new_mc_io = mc_io; 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cierror_cleanup_mc_io: 22962306a36Sopenharmony_ci fsl_destroy_mc_io(mc_io); 23062306a36Sopenharmony_cierror_cleanup_resource: 23162306a36Sopenharmony_ci fsl_mc_resource_free(resource); 23262306a36Sopenharmony_ci return error; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_portal_allocate); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/** 23762306a36Sopenharmony_ci * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals 23862306a36Sopenharmony_ci * of a given MC bus 23962306a36Sopenharmony_ci * 24062306a36Sopenharmony_ci * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_civoid fsl_mc_portal_free(struct fsl_mc_io *mc_io) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct fsl_mc_device *dpmcp_dev; 24562306a36Sopenharmony_ci struct fsl_mc_resource *resource; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed 24962306a36Sopenharmony_ci * to have a DPMCP object associated with. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci dpmcp_dev = mc_io->dpmcp_dev; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci resource = dpmcp_dev->resource; 25462306a36Sopenharmony_ci if (!resource || resource->type != FSL_MC_POOL_DPMCP) 25562306a36Sopenharmony_ci return; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (resource->data != dpmcp_dev) 25862306a36Sopenharmony_ci return; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci fsl_destroy_mc_io(mc_io); 26162306a36Sopenharmony_ci fsl_mc_resource_free(resource); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci dpmcp_dev->consumer_link = NULL; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_portal_free); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/** 26862306a36Sopenharmony_ci * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ciint fsl_mc_portal_reset(struct fsl_mc_io *mc_io) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci int error; 27562306a36Sopenharmony_ci struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle); 27862306a36Sopenharmony_ci if (error < 0) { 27962306a36Sopenharmony_ci dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error); 28062306a36Sopenharmony_ci return error; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fsl_mc_portal_reset); 286