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