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