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; 133 134 if (!mc_io) 135 return; 136 137 dpmcp_dev = mc_io->dpmcp_dev; 138 139 if (dpmcp_dev) 140 fsl_mc_io_unset_dpmcp(mc_io); 141 142 devm_iounmap(mc_io->dev, mc_io->portal_virt_addr); 143 devm_release_mem_region(mc_io->dev, 144 mc_io->portal_phys_addr, 145 mc_io->portal_size); 146 147 mc_io->portal_virt_addr = NULL; 148 devm_kfree(mc_io->dev, mc_io); 149 } 150 151 /** 152 * fsl_mc_portal_allocate - Allocates an MC portal 153 * 154 * @mc_dev: MC device for which the MC portal is to be allocated 155 * @mc_io_flags: Flags for the fsl_mc_io object that wraps the allocated 156 * MC portal. 157 * @new_mc_io: Pointer to area where the pointer to the fsl_mc_io object 158 * that wraps the allocated MC portal is to be returned 159 * 160 * This function allocates an MC portal from the device's parent DPRC, 161 * from the corresponding MC bus' pool of MC portals and wraps 162 * it in a new fsl_mc_io object. If 'mc_dev' is a DPRC itself, the 163 * portal is allocated from its own MC bus. 164 */ 165 int __must_check fsl_mc_portal_allocate(struct fsl_mc_device *mc_dev, 166 u16 mc_io_flags, 167 struct fsl_mc_io **new_mc_io) 168 { 169 struct fsl_mc_device *mc_bus_dev; 170 struct fsl_mc_bus *mc_bus; 171 phys_addr_t mc_portal_phys_addr; 172 size_t mc_portal_size; 173 struct fsl_mc_device *dpmcp_dev; 174 int error = -EINVAL; 175 struct fsl_mc_resource *resource = NULL; 176 struct fsl_mc_io *mc_io = NULL; 177 178 if (mc_dev->flags & FSL_MC_IS_DPRC) { 179 mc_bus_dev = mc_dev; 180 } else { 181 if (!dev_is_fsl_mc(mc_dev->dev.parent)) 182 return error; 183 184 mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); 185 } 186 187 mc_bus = to_fsl_mc_bus(mc_bus_dev); 188 *new_mc_io = NULL; 189 error = fsl_mc_resource_allocate(mc_bus, FSL_MC_POOL_DPMCP, &resource); 190 if (error < 0) 191 return error; 192 193 error = -EINVAL; 194 dpmcp_dev = resource->data; 195 196 if (dpmcp_dev->obj_desc.ver_major < DPMCP_MIN_VER_MAJOR || 197 (dpmcp_dev->obj_desc.ver_major == DPMCP_MIN_VER_MAJOR && 198 dpmcp_dev->obj_desc.ver_minor < DPMCP_MIN_VER_MINOR)) { 199 dev_err(&dpmcp_dev->dev, 200 "ERROR: Version %d.%d of DPMCP not supported.\n", 201 dpmcp_dev->obj_desc.ver_major, 202 dpmcp_dev->obj_desc.ver_minor); 203 error = -ENOTSUPP; 204 goto error_cleanup_resource; 205 } 206 207 mc_portal_phys_addr = dpmcp_dev->regions[0].start; 208 mc_portal_size = resource_size(dpmcp_dev->regions); 209 210 error = fsl_create_mc_io(&mc_bus_dev->dev, 211 mc_portal_phys_addr, 212 mc_portal_size, dpmcp_dev, 213 mc_io_flags, &mc_io); 214 if (error < 0) 215 goto error_cleanup_resource; 216 217 dpmcp_dev->consumer_link = device_link_add(&mc_dev->dev, 218 &dpmcp_dev->dev, 219 DL_FLAG_AUTOREMOVE_CONSUMER); 220 if (!dpmcp_dev->consumer_link) { 221 error = -EINVAL; 222 goto error_cleanup_mc_io; 223 } 224 225 *new_mc_io = mc_io; 226 return 0; 227 228 error_cleanup_mc_io: 229 fsl_destroy_mc_io(mc_io); 230 error_cleanup_resource: 231 fsl_mc_resource_free(resource); 232 return error; 233 } 234 EXPORT_SYMBOL_GPL(fsl_mc_portal_allocate); 235 236 /** 237 * fsl_mc_portal_free - Returns an MC portal to the pool of free MC portals 238 * of a given MC bus 239 * 240 * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free 241 */ 242 void fsl_mc_portal_free(struct fsl_mc_io *mc_io) 243 { 244 struct fsl_mc_device *dpmcp_dev; 245 struct fsl_mc_resource *resource; 246 247 /* 248 * Every mc_io obtained by calling fsl_mc_portal_allocate() is supposed 249 * to have a DPMCP object associated with. 250 */ 251 dpmcp_dev = mc_io->dpmcp_dev; 252 253 resource = dpmcp_dev->resource; 254 if (!resource || resource->type != FSL_MC_POOL_DPMCP) 255 return; 256 257 if (resource->data != dpmcp_dev) 258 return; 259 260 fsl_destroy_mc_io(mc_io); 261 fsl_mc_resource_free(resource); 262 263 dpmcp_dev->consumer_link = NULL; 264 } 265 EXPORT_SYMBOL_GPL(fsl_mc_portal_free); 266 267 /** 268 * fsl_mc_portal_reset - Resets the dpmcp object for a given fsl_mc_io object 269 * 270 * @mc_io: Pointer to the fsl_mc_io object that wraps the MC portal to free 271 */ 272 int fsl_mc_portal_reset(struct fsl_mc_io *mc_io) 273 { 274 int error; 275 struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev; 276 277 error = dpmcp_reset(mc_io, 0, dpmcp_dev->mc_handle); 278 if (error < 0) { 279 dev_err(&dpmcp_dev->dev, "dpmcp_reset() failed: %d\n", error); 280 return error; 281 } 282 283 return 0; 284 } 285 EXPORT_SYMBOL_GPL(fsl_mc_portal_reset); 286