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