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