16bd067c4SBogdan Purcareata // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 26bd067c4SBogdan Purcareata /* 36bd067c4SBogdan Purcareata * Copyright 2013-2016 Freescale Semiconductor Inc. 46bd067c4SBogdan Purcareata * 56bd067c4SBogdan Purcareata */ 66bd067c4SBogdan Purcareata 76bd067c4SBogdan Purcareata #include <linux/io.h> 86bd067c4SBogdan Purcareata #include <linux/fsl/mc.h> 96bd067c4SBogdan Purcareata 106bd067c4SBogdan Purcareata #include "fsl-mc-private.h" 116bd067c4SBogdan Purcareata 126bd067c4SBogdan Purcareata static int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io, 136bd067c4SBogdan Purcareata struct fsl_mc_device *dpmcp_dev) 146bd067c4SBogdan Purcareata { 156bd067c4SBogdan Purcareata int error; 166bd067c4SBogdan Purcareata 176bd067c4SBogdan Purcareata if (mc_io->dpmcp_dev) 186bd067c4SBogdan Purcareata return -EINVAL; 196bd067c4SBogdan Purcareata 206bd067c4SBogdan Purcareata if (dpmcp_dev->mc_io) 216bd067c4SBogdan Purcareata return -EINVAL; 226bd067c4SBogdan Purcareata 236bd067c4SBogdan Purcareata error = dpmcp_open(mc_io, 246bd067c4SBogdan Purcareata 0, 256bd067c4SBogdan Purcareata dpmcp_dev->obj_desc.id, 266bd067c4SBogdan Purcareata &dpmcp_dev->mc_handle); 276bd067c4SBogdan Purcareata if (error < 0) 286bd067c4SBogdan Purcareata return error; 296bd067c4SBogdan Purcareata 306bd067c4SBogdan Purcareata mc_io->dpmcp_dev = dpmcp_dev; 316bd067c4SBogdan Purcareata dpmcp_dev->mc_io = mc_io; 326bd067c4SBogdan Purcareata return 0; 336bd067c4SBogdan Purcareata } 346bd067c4SBogdan Purcareata 356bd067c4SBogdan Purcareata static void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io) 366bd067c4SBogdan Purcareata { 376bd067c4SBogdan Purcareata int error; 386bd067c4SBogdan Purcareata struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; 396bd067c4SBogdan Purcareata 406bd067c4SBogdan Purcareata error = dpmcp_close(mc_io, 416bd067c4SBogdan Purcareata 0, 426bd067c4SBogdan Purcareata dpmcp_dev->mc_handle); 436bd067c4SBogdan Purcareata if (error < 0) { 446bd067c4SBogdan Purcareata dev_err(&dpmcp_dev->dev, "dpmcp_close() failed: %d\n", 456bd067c4SBogdan Purcareata error); 466bd067c4SBogdan Purcareata } 476bd067c4SBogdan Purcareata 486bd067c4SBogdan Purcareata mc_io->dpmcp_dev = NULL; 496bd067c4SBogdan Purcareata dpmcp_dev->mc_io = NULL; 506bd067c4SBogdan Purcareata } 516bd067c4SBogdan Purcareata 526bd067c4SBogdan Purcareata /** 536bd067c4SBogdan Purcareata * Creates an MC I/O object 546bd067c4SBogdan Purcareata * 556bd067c4SBogdan Purcareata * @dev: device to be associated with the MC I/O object 566bd067c4SBogdan Purcareata * @mc_portal_phys_addr: physical address of the MC portal to use 576bd067c4SBogdan Purcareata * @mc_portal_size: size in bytes of the MC portal 586bd067c4SBogdan Purcareata * @dpmcp-dev: Pointer to the DPMCP object associated with this MC I/O 596bd067c4SBogdan Purcareata * object or NULL if none. 606bd067c4SBogdan Purcareata * @flags: flags for the new MC I/O object 616bd067c4SBogdan Purcareata * @new_mc_io: Area to return pointer to newly created MC I/O object 626bd067c4SBogdan Purcareata * 636bd067c4SBogdan Purcareata * Returns '0' on Success; Error code otherwise. 646bd067c4SBogdan Purcareata */ 656bd067c4SBogdan Purcareata int __must_check fsl_create_mc_io(struct device *dev, 666bd067c4SBogdan Purcareata phys_addr_t mc_portal_phys_addr, 676bd067c4SBogdan Purcareata u32 mc_portal_size, 686bd067c4SBogdan Purcareata struct fsl_mc_device *dpmcp_dev, 696bd067c4SBogdan Purcareata u32 flags, struct fsl_mc_io **new_mc_io) 706bd067c4SBogdan Purcareata { 716bd067c4SBogdan Purcareata int error; 726bd067c4SBogdan Purcareata struct fsl_mc_io *mc_io; 736bd067c4SBogdan Purcareata void __iomem *mc_portal_virt_addr; 746bd067c4SBogdan Purcareata struct resource *res; 756bd067c4SBogdan Purcareata 766bd067c4SBogdan Purcareata mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL); 776bd067c4SBogdan Purcareata if (!mc_io) 786bd067c4SBogdan Purcareata return -ENOMEM; 796bd067c4SBogdan Purcareata 806bd067c4SBogdan Purcareata mc_io->dev = dev; 816bd067c4SBogdan Purcareata mc_io->flags = flags; 826bd067c4SBogdan Purcareata mc_io->portal_phys_addr = mc_portal_phys_addr; 836bd067c4SBogdan Purcareata mc_io->portal_size = mc_portal_size; 846bd067c4SBogdan Purcareata if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) 859a872defSLaurentiu Tudor raw_spin_lock_init(&mc_io->spinlock); 866bd067c4SBogdan Purcareata else 876bd067c4SBogdan Purcareata mutex_init(&mc_io->mutex); 886bd067c4SBogdan Purcareata 896bd067c4SBogdan Purcareata res = devm_request_mem_region(dev, 906bd067c4SBogdan Purcareata mc_portal_phys_addr, 916bd067c4SBogdan Purcareata mc_portal_size, 926bd067c4SBogdan Purcareata "mc_portal"); 936bd067c4SBogdan Purcareata if (!res) { 946bd067c4SBogdan Purcareata dev_err(dev, 956bd067c4SBogdan Purcareata "devm_request_mem_region failed for MC portal %pa\n", 966bd067c4SBogdan Purcareata &mc_portal_phys_addr); 976bd067c4SBogdan Purcareata return -EBUSY; 986bd067c4SBogdan Purcareata } 996bd067c4SBogdan Purcareata 1004bdc0d67SChristoph Hellwig mc_portal_virt_addr = devm_ioremap(dev, 1016bd067c4SBogdan Purcareata mc_portal_phys_addr, 1026bd067c4SBogdan Purcareata mc_portal_size); 1036bd067c4SBogdan Purcareata if (!mc_portal_virt_addr) { 1046bd067c4SBogdan Purcareata dev_err(dev, 1054bdc0d67SChristoph Hellwig "devm_ioremap failed for MC portal %pa\n", 1066bd067c4SBogdan Purcareata &mc_portal_phys_addr); 1076bd067c4SBogdan Purcareata return -ENXIO; 1086bd067c4SBogdan Purcareata } 1096bd067c4SBogdan Purcareata 1106bd067c4SBogdan Purcareata mc_io->portal_virt_addr = mc_portal_virt_addr; 1116bd067c4SBogdan Purcareata if (dpmcp_dev) { 1126bd067c4SBogdan Purcareata error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev); 1136bd067c4SBogdan Purcareata if (error < 0) 1146bd067c4SBogdan Purcareata goto error_destroy_mc_io; 1156bd067c4SBogdan Purcareata } 1166bd067c4SBogdan Purcareata 1176bd067c4SBogdan Purcareata *new_mc_io = mc_io; 1186bd067c4SBogdan Purcareata return 0; 1196bd067c4SBogdan Purcareata 1206bd067c4SBogdan Purcareata error_destroy_mc_io: 1216bd067c4SBogdan Purcareata fsl_destroy_mc_io(mc_io); 1226bd067c4SBogdan Purcareata return error; 1236bd067c4SBogdan Purcareata } 1246bd067c4SBogdan Purcareata 1256bd067c4SBogdan Purcareata /** 1266bd067c4SBogdan Purcareata * Destroys an MC I/O object 1276bd067c4SBogdan Purcareata * 1286bd067c4SBogdan Purcareata * @mc_io: MC I/O object to destroy 1296bd067c4SBogdan Purcareata */ 1306bd067c4SBogdan Purcareata void fsl_destroy_mc_io(struct fsl_mc_io *mc_io) 1316bd067c4SBogdan Purcareata { 132*5026cf60SDiana Craciun struct fsl_mc_device *dpmcp_dev; 133*5026cf60SDiana Craciun 134*5026cf60SDiana Craciun if (!mc_io) 135*5026cf60SDiana Craciun return; 136*5026cf60SDiana Craciun 137*5026cf60SDiana Craciun dpmcp_dev = mc_io->dpmcp_dev; 1386bd067c4SBogdan Purcareata 1396bd067c4SBogdan Purcareata if (dpmcp_dev) 1406bd067c4SBogdan Purcareata fsl_mc_io_unset_dpmcp(mc_io); 1416bd067c4SBogdan Purcareata 1426bd067c4SBogdan Purcareata devm_iounmap(mc_io->dev, mc_io->portal_virt_addr); 1436bd067c4SBogdan Purcareata devm_release_mem_region(mc_io->dev, 1446bd067c4SBogdan Purcareata mc_io->portal_phys_addr, 1456bd067c4SBogdan Purcareata mc_io->portal_size); 1466bd067c4SBogdan Purcareata 1476bd067c4SBogdan Purcareata mc_io->portal_virt_addr = NULL; 1486bd067c4SBogdan Purcareata devm_kfree(mc_io->dev, mc_io); 1496bd067c4SBogdan Purcareata } 1506bd067c4SBogdan Purcareata 1516bd067c4SBogdan Purcareata /** 1526bd067c4SBogdan Purcareata * fsl_mc_portal_allocate - Allocates an MC portal 1536bd067c4SBogdan Purcareata * 1546bd067c4SBogdan Purcareata * @mc_dev: MC device for which the MC portal is to be allocated 1556bd067c4SBogdan Purcareata * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated 1566bd067c4SBogdan Purcareata * MC portal. 1576bd067c4SBogdan Purcareata * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object 1586bd067c4SBogdan Purcareata * that wraps the allocated MC portal is to be returned 1596bd067c4SBogdan Purcareata * 1606bd067c4SBogdan Purcareata * This function allocates an MC portal from the device's parent DPRC, 1616bd067c4SBogdan Purcareata * from the corresponding MC bus' pool of MC portals and wraps 1626bd067c4SBogdan Purcareata * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the 1636bd067c4SBogdan Purcareata * portal is allocated from its own MC bus. 1646bd067c4SBogdan Purcareata */ 1656bd067c4SBogdan Purcareata int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev, 1666bd067c4SBogdan Purcareata u16 mc_io_flags, 1676bd067c4SBogdan Purcareata struct fsl_mc_io **new_mc_io) 1686bd067c4SBogdan Purcareata { 1696bd067c4SBogdan Purcareata struct fsl_mc_device *mc_bus_dev; 1706bd067c4SBogdan Purcareata struct fsl_mc_bus *mc_bus; 1716bd067c4SBogdan Purcareata phys_addr_t mc_portal_phys_addr; 1726bd067c4SBogdan Purcareata size_t mc_portal_size; 1736bd067c4SBogdan Purcareata struct fsl_mc_device *dpmcp_dev; 1746bd067c4SBogdan Purcareata int error = -EINVAL; 1756bd067c4SBogdan Purcareata struct fsl_mc_resource *resource = NULL; 1766bd067c4SBogdan Purcareata struct fsl_mc_io *mc_io = NULL; 1776bd067c4SBogdan Purcareata 1786bd067c4SBogdan Purcareata if (mc_dev->flags & FSL_MC_IS_DPRC) { 1796bd067c4SBogdan Purcareata mc_bus_dev = mc_dev; 1806bd067c4SBogdan Purcareata } else { 1816bd067c4SBogdan Purcareata if (!dev_is_fsl_mc(mc_dev->dev.parent)) 1826bd067c4SBogdan Purcareata return error; 1836bd067c4SBogdan Purcareata 1846bd067c4SBogdan Purcareata mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); 1856bd067c4SBogdan Purcareata } 1866bd067c4SBogdan Purcareata 1876bd067c4SBogdan Purcareata mc_bus = to_fsl_mc_bus(mc_bus_dev); 1886bd067c4SBogdan Purcareata *new_mc_io = NULL; 1896bd067c4SBogdan Purcareata error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource); 1906bd067c4SBogdan Purcareata if (error < 0) 1916bd067c4SBogdan Purcareata return error; 1926bd067c4SBogdan Purcareata 1936bd067c4SBogdan Purcareata error = -EINVAL; 1946bd067c4SBogdan Purcareata dpmcp_dev = resource->data; 1956bd067c4SBogdan Purcareata 1966bd067c4SBogdan Purcareata if (dpmcp_dev->obj_desc.ver_major < DPMCP_MIN_VER_MAJOR || 1976bd067c4SBogdan Purcareata (dpmcp_dev->obj_desc.ver_major == DPMCP_MIN_VER_MAJOR && 1986bd067c4SBogdan Purcareata dpmcp_dev->obj_desc.ver_minor < DPMCP_MIN_VER_MINOR)) { 1996bd067c4SBogdan Purcareata dev_err(&dpmcp_dev->dev, 2006bd067c4SBogdan Purcareata "ERROR: Version %d.%d of DPMCP not supported.\n", 2016bd067c4SBogdan Purcareata dpmcp_dev->obj_desc.ver_major, 2026bd067c4SBogdan Purcareata dpmcp_dev->obj_desc.ver_minor); 2036bd067c4SBogdan Purcareata error = -ENOTSUPP; 2046bd067c4SBogdan Purcareata goto error_cleanup_resource; 2056bd067c4SBogdan Purcareata } 2066bd067c4SBogdan Purcareata 2076bd067c4SBogdan Purcareata mc_portal_phys_addr = dpmcp_dev->regions[0].start; 2086bd067c4SBogdan Purcareata mc_portal_size = resource_size(dpmcp_dev->regions); 2096bd067c4SBogdan Purcareata 2106bd067c4SBogdan Purcareata error = fsl_create_mc_io(&mc_bus_dev->dev, 2116bd067c4SBogdan Purcareata mc_portal_phys_addr, 2126bd067c4SBogdan Purcareata mc_portal_size, dpmcp_dev, 2136bd067c4SBogdan Purcareata mc_io_flags, &mc_io); 2146bd067c4SBogdan Purcareata if (error < 0) 2156bd067c4SBogdan Purcareata goto error_cleanup_resource; 2166bd067c4SBogdan Purcareata 217afb77422SIoana Ciornei dpmcp_dev->consumer_link = device_link_add(&mc_dev->dev, 218afb77422SIoana Ciornei &dpmcp_dev->dev, 219afb77422SIoana Ciornei DL_FLAG_AUTOREMOVE_CONSUMER); 220afb77422SIoana Ciornei if (!dpmcp_dev->consumer_link) { 221afb77422SIoana Ciornei error = -EINVAL; 222afb77422SIoana Ciornei goto error_cleanup_mc_io; 223afb77422SIoana Ciornei } 224afb77422SIoana Ciornei 2256bd067c4SBogdan Purcareata *new_mc_io = mc_io; 2266bd067c4SBogdan Purcareata return 0; 2276bd067c4SBogdan Purcareata 228afb77422SIoana Ciornei error_cleanup_mc_io: 229afb77422SIoana Ciornei fsl_destroy_mc_io(mc_io); 2306bd067c4SBogdan Purcareata error_cleanup_resource: 2316bd067c4SBogdan Purcareata fsl_mc_resource_free(resource); 2326bd067c4SBogdan Purcareata return error; 2336bd067c4SBogdan Purcareata } 2346bd067c4SBogdan Purcareata EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate); 2356bd067c4SBogdan Purcareata 2366bd067c4SBogdan Purcareata /** 2376bd067c4SBogdan Purcareata * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals 2386bd067c4SBogdan Purcareata * of a given MC bus 2396bd067c4SBogdan Purcareata * 2406bd067c4SBogdan Purcareata * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free 2416bd067c4SBogdan Purcareata */ 2426bd067c4SBogdan Purcareata void fsl_mc_portal_free(struct fsl_mc_io *mc_io) 2436bd067c4SBogdan Purcareata { 2446bd067c4SBogdan Purcareata struct fsl_mc_device *dpmcp_dev; 2456bd067c4SBogdan Purcareata struct fsl_mc_resource *resource; 2466bd067c4SBogdan Purcareata 2476bd067c4SBogdan Purcareata /* 2486bd067c4SBogdan Purcareata * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed 2496bd067c4SBogdan Purcareata * to have a DPMCP object associated with. 2506bd067c4SBogdan Purcareata */ 2516bd067c4SBogdan Purcareata dpmcp_dev = mc_io->dpmcp_dev; 2526bd067c4SBogdan Purcareata 2536bd067c4SBogdan Purcareata resource = dpmcp_dev->resource; 2546bd067c4SBogdan Purcareata if (!resource || resource->type != FSL_MC_POOL_DPMCP) 2556bd067c4SBogdan Purcareata return; 2566bd067c4SBogdan Purcareata 2576bd067c4SBogdan Purcareata if (resource->data != dpmcp_dev) 2586bd067c4SBogdan Purcareata return; 2596bd067c4SBogdan Purcareata 2606bd067c4SBogdan Purcareata fsl_destroy_mc_io(mc_io); 2616bd067c4SBogdan Purcareata fsl_mc_resource_free(resource); 262afb77422SIoana Ciornei 263afb77422SIoana Ciornei dpmcp_dev->consumer_link = NULL; 2646bd067c4SBogdan Purcareata } 2656bd067c4SBogdan Purcareata EXPORT_SYMBOL_GPL(fsl_mc_portal_free); 2666bd067c4SBogdan Purcareata 2676bd067c4SBogdan Purcareata /** 2686bd067c4SBogdan Purcareata * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object 2696bd067c4SBogdan Purcareata * 2706bd067c4SBogdan Purcareata * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free 2716bd067c4SBogdan Purcareata */ 2726bd067c4SBogdan Purcareata int fsl_mc_portal_reset(struct fsl_mc_io *mc_io) 2736bd067c4SBogdan Purcareata { 2746bd067c4SBogdan Purcareata int error; 2756bd067c4SBogdan Purcareata struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; 2766bd067c4SBogdan Purcareata 2776bd067c4SBogdan Purcareata error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle); 2786bd067c4SBogdan Purcareata if (error < 0) { 2796bd067c4SBogdan Purcareata dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error); 2806bd067c4SBogdan Purcareata return error; 2816bd067c4SBogdan Purcareata } 2826bd067c4SBogdan Purcareata 2836bd067c4SBogdan Purcareata return 0; 2846bd067c4SBogdan Purcareata } 2856bd067c4SBogdan Purcareata EXPORT_SYMBOL_GPL(fsl_mc_portal_reset); 286