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