Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 Cadence Design Systems Inc. * * Author: Boris Brezillon <boris.brezillon@bootlin.com> */ #include <linux/atomic.h> #include <linux/bug.h> #include <linux/completion.h> #include <linux/device.h> #include <linux/mutex.h> #include <linux/slab.h> #include "internals.h" /** * i3c_device_do_priv_xfers() - do I3C SDR private transfers directed to a * specific device * * @dev: device with which the transfers should be done * @xfers: array of transfers * @nxfers: number of transfers * * Initiate one or several private SDR transfers with @dev. * * This function can sleep and thus cannot be called in atomic context. * * Return: 0 in case of success, a negative error core otherwise. */ int i3c_device_do_priv_xfers(struct i3c_device *dev, struct i3c_priv_xfer *xfers, int nxfers) { int ret, i; if (nxfers < 1) return 0; for (i = 0; i < nxfers; i++) { if (!xfers[i].len || !xfers[i].data.in) return -EINVAL; } i3c_bus_normaluse_lock(dev->bus); ret = i3c_dev_do_priv_xfers_locked(dev->desc, xfers, nxfers); i3c_bus_normaluse_unlock(dev->bus); return ret; } EXPORT_SYMBOL_GPL(i3c_device_do_priv_xfers); /** * i3c_device_do_setdasa() - do I3C dynamic address assignement with * static address * * @dev: device with which the DAA should be done * * Return: 0 in case of success, a negative error core otherwise. */ int i3c_device_do_setdasa(struct i3c_device *dev) { int ret; i3c_bus_normaluse_lock(dev->bus); ret = i3c_dev_setdasa_locked(dev->desc); i3c_bus_normaluse_unlock(dev->bus); return ret; } EXPORT_SYMBOL_GPL(i3c_device_do_setdasa); /** * i3c_device_get_info() - get I3C device information * * @dev: device we want information on * @info: the information object to fill in * * Retrieve I3C dev info. */ void i3c_device_get_info(struct i3c_device *dev, struct i3c_device_info *info) { if (!info) return; i3c_bus_normaluse_lock(dev->bus); if (dev->desc) *info = dev->desc->info; i3c_bus_normaluse_unlock(dev->bus); } EXPORT_SYMBOL_GPL(i3c_device_get_info); /** * i3c_device_disable_ibi() - Disable IBIs coming from a specific device * @dev: device on which IBIs should be disabled * * This function disable IBIs coming from a specific device and wait for * all pending IBIs to be processed. * * Return: 0 in case of success, a negative error core otherwise. */ int i3c_device_disable_ibi(struct i3c_device *dev) { int ret = -ENOENT; i3c_bus_normaluse_lock(dev->bus); if (dev->desc) { mutex_lock(&dev->desc->ibi_lock); ret = i3c_dev_disable_ibi_locked(dev->desc); mutex_unlock(&dev->desc->ibi_lock); } i3c_bus_normaluse_unlock(dev->bus); return ret; } EXPORT_SYMBOL_GPL(i3c_device_disable_ibi); /** * i3c_device_enable_ibi() - Enable IBIs coming from a specific device * @dev: device on which IBIs should be enabled * * This function enable IBIs coming from a specific device and wait for * all pending IBIs to be processed. This should be called on a device * where i3c_device_request_ibi() has succeeded. * * Note that IBIs from this device might be received before this function * returns to its caller. * * Return: 0 in case of success, a negative error core otherwise. */ int i3c_device_enable_ibi(struct i3c_device *dev) { int ret = -ENOENT; i3c_bus_normaluse_lock(dev->bus); if (dev->desc) { mutex_lock(&dev->desc->ibi_lock); ret = i3c_dev_enable_ibi_locked(dev->desc); mutex_unlock(&dev->desc->ibi_lock); } i3c_bus_normaluse_unlock(dev->bus); return ret; } EXPORT_SYMBOL_GPL(i3c_device_enable_ibi); /** * i3c_device_request_ibi() - Request an IBI * @dev: device for which we should enable IBIs * @req: setup requested for this IBI * * This function is responsible for pre-allocating all resources needed to * process IBIs coming from @dev. When this function returns, the IBI is not * enabled until i3c_device_enable_ibi() is called. * * Return: 0 in case of success, a negative error core otherwise. */ int i3c_device_request_ibi(struct i3c_device *dev, const struct i3c_ibi_setup *req) { int ret = -ENOENT; if (!req->handler || !req->num_slots) return -EINVAL; i3c_bus_normaluse_lock(dev->bus); if (dev->desc) { mutex_lock(&dev->desc->ibi_lock); ret = i3c_dev_request_ibi_locked(dev->desc, req); mutex_unlock(&dev->desc->ibi_lock); } i3c_bus_normaluse_unlock(dev->bus); return ret; } EXPORT_SYMBOL_GPL(i3c_device_request_ibi); /** * i3c_device_free_ibi() - Free all resources needed for IBI handling * @dev: device on which you want to release IBI resources * * This function is responsible for de-allocating resources previously * allocated by i3c_device_request_ibi(). It should be called after disabling * IBIs with i3c_device_disable_ibi(). */ void i3c_device_free_ibi(struct i3c_device *dev) { i3c_bus_normaluse_lock(dev->bus); if (dev->desc) { mutex_lock(&dev->desc->ibi_lock); i3c_dev_free_ibi_locked(dev->desc); mutex_unlock(&dev->desc->ibi_lock); } i3c_bus_normaluse_unlock(dev->bus); } EXPORT_SYMBOL_GPL(i3c_device_free_ibi); /** * i3cdev_to_dev() - Returns the device embedded in @i3cdev * @i3cdev: I3C device * * Return: a pointer to a device object. */ struct device *i3cdev_to_dev(struct i3c_device *i3cdev) { return &i3cdev->dev; } EXPORT_SYMBOL_GPL(i3cdev_to_dev); /** * dev_to_i3cdev() - Returns the I3C device containing @dev * @dev: device object * * Return: a pointer to an I3C device object. */ struct i3c_device *dev_to_i3cdev(struct device *dev) { return container_of(dev, struct i3c_device, dev); } EXPORT_SYMBOL_GPL(dev_to_i3cdev); /** * i3c_device_match_id() - Returns the i3c_device_id entry matching @i3cdev * @i3cdev: I3C device * @id_table: I3C device match table * * Return: a pointer to an i3c_device_id object or NULL if there's no match. */ const struct i3c_device_id * i3c_device_match_id(struct i3c_device *i3cdev, const struct i3c_device_id *id_table) { struct i3c_device_info devinfo; const struct i3c_device_id *id; u16 manuf, part, ext_info; bool rndpid; i3c_device_get_info(i3cdev, &devinfo); manuf = I3C_PID_MANUF_ID(devinfo.pid); part = I3C_PID_PART_ID(devinfo.pid); ext_info = I3C_PID_EXTRA_INFO(devinfo.pid); rndpid = I3C_PID_RND_LOWER_32BITS(devinfo.pid); for (id = id_table; id->match_flags != 0; id++) { if ((id->match_flags & I3C_MATCH_DCR) && id->dcr != devinfo.dcr) continue; if ((id->match_flags & I3C_MATCH_MANUF) && id->manuf_id != manuf) continue; if ((id->match_flags & I3C_MATCH_PART) && (rndpid || id->part_id != part)) continue; if ((id->match_flags & I3C_MATCH_EXTRA_INFO) && (rndpid || id->extra_info != ext_info)) continue; return id; } return NULL; } EXPORT_SYMBOL_GPL(i3c_device_match_id); /** * i3c_driver_register_with_owner() - register an I3C device driver * * @drv: driver to register * @owner: module that owns this driver * * Register @drv to the core. * * Return: 0 in case of success, a negative error core otherwise. */ int i3c_driver_register_with_owner(struct i3c_driver *drv, struct module *owner) { drv->driver.owner = owner; drv->driver.bus = &i3c_bus_type; if (!drv->probe) { pr_err("Trying to register an i3c driver without probe callback\n"); return -EINVAL; } return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(i3c_driver_register_with_owner); /** * i3c_driver_unregister() - unregister an I3C device driver * * @drv: driver to unregister * * Unregister @drv. */ void i3c_driver_unregister(struct i3c_driver *drv) { driver_unregister(&drv->driver); } EXPORT_SYMBOL_GPL(i3c_driver_unregister); |