1 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 2 /* 3 * Copyright 2013-2016 Freescale Semiconductor Inc. 4 * 5 */ 6 7 #include <linux/io.h> 8 #include <linux/fsl/mc.h> 9 10 #include "fsl-mc-private.h" 11 12 static int fsl_mc_io_set_dpmcp(struct fsl_mc_io *mc_io, 13 struct fsl_mc_device *dpmcp_dev) 14 { 15 int error; 16 17 if (mc_io->dpmcp_dev) 18 return -EINVAL; 19 20 if (dpmcp_dev->mc_io) 21 return -EINVAL; 22 23 error = dpmcp_open(mc_io, 24 0, 25 dpmcp_dev->obj_desc.id, 26 &dpmcp_dev->mc_handle); 27 if (error < 0) 28 return error; 29 30 mc_io->dpmcp_dev = dpmcp_dev; 31 dpmcp_dev->mc_io = mc_io; 32 return 0; 33 } 34 35 static void fsl_mc_io_unset_dpmcp(struct fsl_mc_io *mc_io) 36 { 37 int error; 38 struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; 39 40 error = dpmcp_close(mc_io, 41 0, 42 dpmcp_dev->mc_handle); 43 if (error < 0) { 44 dev_err(&dpmcp_dev->dev, "dpmcp_close() failed: %d\n", 45 error); 46 } 47 48 mc_io->dpmcp_dev = NULL; 49 dpmcp_dev->mc_io = NULL; 50 } 51 52 /** 53 * Creates an MC I/O object 54 * 55 * @dev: device to be associated with the MC I/O object 56 * @mc_portal_phys_addr: physical address of the MC portal to use 57 * @mc_portal_size: size in bytes of the MC portal 58 * @dpmcp-dev: Pointer to the DPMCP object associated with this MC I/O 59 * object or NULL if none. 60 * @flags: flags for the new MC I/O object 61 * @new_mc_io: Area to return pointer to newly created MC I/O object 62 * 63 * Returns '0' on Success; Error code otherwise. 64 */ 65 int __must_check fsl_create_mc_io(struct device *dev, 66 phys_addr_t mc_portal_phys_addr, 67 u32 mc_portal_size, 68 struct fsl_mc_device *dpmcp_dev, 69 u32 flags, struct fsl_mc_io **new_mc_io) 70 { 71 int error; 72 struct fsl_mc_io *mc_io; 73 void __iomem *mc_portal_virt_addr; 74 struct resource *res; 75 76 mc_io = devm_kzalloc(dev, sizeof(*mc_io), GFP_KERNEL); 77 if (!mc_io) 78 return -ENOMEM; 79 80 mc_io->dev = dev; 81 mc_io->flags = flags; 82 mc_io->portal_phys_addr = mc_portal_phys_addr; 83 mc_io->portal_size = mc_portal_size; 84 if (flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL) 85 raw_spin_lock_init(&mc_io->spinlock); 86 else 87 mutex_init(&mc_io->mutex); 88 89 res = devm_request_mem_region(dev, 90 mc_portal_phys_addr, 91 mc_portal_size, 92 "mc_portal"); 93 if (!res) { 94 dev_err(dev, 95 "devm_request_mem_region failed for MC portal %pa\n", 96 &mc_portal_phys_addr); 97 return -EBUSY; 98 } 99 100 mc_portal_virt_addr = devm_ioremap(dev, 101 mc_portal_phys_addr, 102 mc_portal_size); 103 if (!mc_portal_virt_addr) { 104 dev_err(dev, 105 "devm_ioremap failed for MC portal %pa\n", 106 &mc_portal_phys_addr); 107 return -ENXIO; 108 } 109 110 mc_io->portal_virt_addr = mc_portal_virt_addr; 111 if (dpmcp_dev) { 112 error = fsl_mc_io_set_dpmcp(mc_io, dpmcp_dev); 113 if (error < 0) 114 goto error_destroy_mc_io; 115 } 116 117 *new_mc_io = mc_io; 118 return 0; 119 120 error_destroy_mc_io: 121 fsl_destroy_mc_io(mc_io); 122 return error; 123 } 124 125 /** 126 * Destroys an MC I/O object 127 * 128 * @mc_io: MC I/O object to destroy 129 */ 130 void fsl_destroy_mc_io(struct fsl_mc_io *mc_io) 131 { 132 struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; 133 134 if (dpmcp_dev) 135 fsl_mc_io_unset_dpmcp(mc_io); 136 137 devm_iounmap(mc_io->dev, mc_io->portal_virt_addr); 138 devm_release_mem_region(mc_io->dev, 139 mc_io->portal_phys_addr, 140 mc_io->portal_size); 141 142 mc_io->portal_virt_addr = NULL; 143 devm_kfree(mc_io->dev, mc_io); 144 } 145 146 /** 147 * fsl_mc_portal_allocate - Allocates an MC portal 148 * 149 * @mc_dev: MC device for which the MC portal is to be allocated 150 * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated 151 * MC portal. 152 * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object 153 * that wraps the allocated MC portal is to be returned 154 * 155 * This function allocates an MC portal from the device's parent DPRC, 156 * from the corresponding MC bus' pool of MC portals and wraps 157 * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the 158 * portal is allocated from its own MC bus. 159 */ 160 int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev, 161 u16 mc_io_flags, 162 struct fsl_mc_io **new_mc_io) 163 { 164 struct fsl_mc_device *mc_bus_dev; 165 struct fsl_mc_bus *mc_bus; 166 phys_addr_t mc_portal_phys_addr; 167 size_t mc_portal_size; 168 struct fsl_mc_device *dpmcp_dev; 169 int error = -EINVAL; 170 struct fsl_mc_resource *resource = NULL; 171 struct fsl_mc_io *mc_io = NULL; 172 173 if (mc_dev->flags & FSL_MC_IS_DPRC) { 174 mc_bus_dev = mc_dev; 175 } else { 176 if (!dev_is_fsl_mc(mc_dev->dev.parent)) 177 return error; 178 179 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); 180 } 181 182 mc_bus = to_fsl_mc_bus(mc_bus_dev); 183 *new_mc_io = NULL; 184 error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource); 185 if (error < 0) 186 return error; 187 188 error = -EINVAL; 189 dpmcp_dev = resource->data; 190 191 if (dpmcp_dev->obj_desc.ver_major < DPMCP_MIN_VER_MAJOR || 192 (dpmcp_dev->obj_desc.ver_major == DPMCP_MIN_VER_MAJOR && 193 dpmcp_dev->obj_desc.ver_minor < DPMCP_MIN_VER_MINOR)) { 194 dev_err(&dpmcp_dev->dev, 195 "ERROR: Version %d.%d of DPMCP not supported.\n", 196 dpmcp_dev->obj_desc.ver_major, 197 dpmcp_dev->obj_desc.ver_minor); 198 error = -ENOTSUPP; 199 goto error_cleanup_resource; 200 } 201 202 mc_portal_phys_addr = dpmcp_dev->regions[0].start; 203 mc_portal_size = resource_size(dpmcp_dev->regions); 204 205 error = fsl_create_mc_io(&mc_bus_dev->dev, 206 mc_portal_phys_addr, 207 mc_portal_size, dpmcp_dev, 208 mc_io_flags, &mc_io); 209 if (error < 0) 210 goto error_cleanup_resource; 211 212 dpmcp_dev->consumer_link = device_link_add(&mc_dev->dev, 213 &dpmcp_dev->dev, 214 DL_FLAG_AUTOREMOVE_CONSUMER); 215 if (!dpmcp_dev->consumer_link) { 216 error = -EINVAL; 217 goto error_cleanup_mc_io; 218 } 219 220 *new_mc_io = mc_io; 221 return 0; 222 223 error_cleanup_mc_io: 224 fsl_destroy_mc_io(mc_io); 225 error_cleanup_resource: 226 fsl_mc_resource_free(resource); 227 return error; 228 } 229 EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate); 230 231 /** 232 * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals 233 * of a given MC bus 234 * 235 * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free 236 */ 237 void fsl_mc_portal_free(struct fsl_mc_io *mc_io) 238 { 239 struct fsl_mc_device *dpmcp_dev; 240 struct fsl_mc_resource *resource; 241 242 /* 243 * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed 244 * to have a DPMCP object associated with. 245 */ 246 dpmcp_dev = mc_io->dpmcp_dev; 247 248 resource = dpmcp_dev->resource; 249 if (!resource || resource->type != FSL_MC_POOL_DPMCP) 250 return; 251 252 if (resource->data != dpmcp_dev) 253 return; 254 255 fsl_destroy_mc_io(mc_io); 256 fsl_mc_resource_free(resource); 257 258 dpmcp_dev->consumer_link = NULL; 259 } 260 EXPORT_SYMBOL_GPL(fsl_mc_portal_free); 261 262 /** 263 * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object 264 * 265 * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free 266 */ 267 int fsl_mc_portal_reset(struct fsl_mc_io *mc_io) 268 { 269 int error; 270 struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; 271 272 error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle); 273 if (error < 0) { 274 dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error); 275 return error; 276 } 277 278 return 0; 279 } 280 EXPORT_SYMBOL_GPL(fsl_mc_portal_reset); 281