]>
Commit | Line | Data |
---|---|---|
8857f00d WW |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Intel La Jolla Cove Adapter USB-I2C driver | |
4 | * | |
5 | * Copyright (c) 2021, Intel Corporation. | |
6 | */ | |
7 | ||
a50ce5b0 | 8 | #include <linux/acpi.h> |
8857f00d WW |
9 | #include <linux/i2c.h> |
10 | #include <linux/mfd/ljca.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/platform_device.h> | |
13 | ||
14 | /* I2C commands */ | |
15 | enum i2c_cmd { | |
16 | I2C_INIT = 1, | |
17 | I2C_XFER, | |
18 | I2C_START, | |
19 | I2C_STOP, | |
20 | I2C_READ, | |
21 | I2C_WRITE, | |
22 | }; | |
23 | ||
24 | enum i2c_address_mode { | |
25 | I2C_ADDRESS_MODE_7BIT, | |
26 | I2C_ADDRESS_MODE_10BIT, | |
27 | }; | |
28 | ||
29 | enum xfer_type { | |
30 | READ_XFER_TYPE, | |
31 | WRITE_XFER_TYPE, | |
32 | }; | |
33 | ||
34 | #define DEFAULT_I2C_CONTROLLER_ID 1 | |
35 | #define DEFAULT_I2C_CAPACITY 0 | |
36 | #define DEFAULT_I2C_INTR_PIN 0 | |
37 | ||
38 | /* I2C r/w Flags */ | |
39 | #define I2C_SLAVE_TRANSFER_WRITE (0) | |
40 | #define I2C_SLAVE_TRANSFER_READ (1) | |
41 | ||
42 | /* i2c init flags */ | |
43 | #define I2C_INIT_FLAG_MODE_MASK (0x1 << 0) | |
44 | #define I2C_INIT_FLAG_MODE_POLLING (0x0 << 0) | |
45 | #define I2C_INIT_FLAG_MODE_INTERRUPT (0x1 << 0) | |
46 | ||
47 | #define I2C_FLAG_ADDR_16BIT (0x1 << 0) | |
48 | ||
49 | #define I2C_INIT_FLAG_FREQ_MASK (0x3 << 1) | |
50 | #define I2C_FLAG_FREQ_100K (0x0 << 1) | |
51 | #define I2C_FLAG_FREQ_400K (0x1 << 1) | |
52 | #define I2C_FLAG_FREQ_1M (0x2 << 1) | |
53 | ||
54 | /* I2C Transfer */ | |
55 | struct i2c_xfer { | |
56 | u8 id; | |
57 | u8 slave; | |
58 | u16 flag; /* speed, 8/16bit addr, addr increase, etc */ | |
59 | u16 addr; | |
60 | u16 len; | |
61 | u8 data[]; | |
62 | } __packed; | |
63 | ||
64 | /* I2C raw commands: Init/Start/Read/Write/Stop */ | |
65 | struct i2c_rw_packet { | |
66 | u8 id; | |
67 | __le16 len; | |
68 | u8 data[]; | |
69 | } __packed; | |
70 | ||
71 | #define LJCA_I2C_MAX_XFER_SIZE 256 | |
72 | #define LJCA_I2C_BUF_SIZE \ | |
73 | (LJCA_I2C_MAX_XFER_SIZE + sizeof(struct i2c_rw_packet)) | |
74 | ||
75 | struct ljca_i2c_dev { | |
76 | struct platform_device *pdev; | |
77 | struct ljca_i2c_info *ctr_info; | |
78 | struct i2c_adapter adap; | |
79 | ||
80 | u8 obuf[LJCA_I2C_BUF_SIZE]; | |
81 | u8 ibuf[LJCA_I2C_BUF_SIZE]; | |
82 | }; | |
83 | ||
84 | static u8 ljca_i2c_format_slave_addr(u8 slave_addr, enum i2c_address_mode mode) | |
85 | { | |
86 | if (mode == I2C_ADDRESS_MODE_7BIT) | |
87 | return slave_addr << 1; | |
88 | ||
89 | return 0xFF; | |
90 | } | |
91 | ||
92 | static int ljca_i2c_init(struct ljca_i2c_dev *ljca_i2c, u8 id) | |
93 | { | |
94 | struct i2c_rw_packet *w_packet = (struct i2c_rw_packet *)ljca_i2c->obuf; | |
95 | ||
96 | memset(w_packet, 0, sizeof(*w_packet)); | |
97 | w_packet->id = id; | |
98 | w_packet->len = cpu_to_le16(1); | |
99 | w_packet->data[0] = I2C_FLAG_FREQ_400K; | |
100 | ||
101 | return ljca_transfer(ljca_i2c->pdev, I2C_INIT, w_packet, | |
102 | sizeof(*w_packet) + 1, NULL, NULL); | |
103 | } | |
104 | ||
105 | static int ljca_i2c_start(struct ljca_i2c_dev *ljca_i2c, u8 slave_addr, | |
106 | enum xfer_type type) | |
107 | { | |
108 | struct i2c_rw_packet *w_packet = (struct i2c_rw_packet *)ljca_i2c->obuf; | |
109 | struct i2c_rw_packet *r_packet = (struct i2c_rw_packet *)ljca_i2c->ibuf; | |
110 | int ret; | |
111 | int ibuf_len; | |
112 | ||
113 | memset(w_packet, 0, sizeof(*w_packet)); | |
114 | w_packet->id = ljca_i2c->ctr_info->id; | |
115 | w_packet->len = cpu_to_le16(1); | |
116 | w_packet->data[0] = | |
117 | ljca_i2c_format_slave_addr(slave_addr, I2C_ADDRESS_MODE_7BIT); | |
118 | w_packet->data[0] |= (type == READ_XFER_TYPE) ? | |
119 | I2C_SLAVE_TRANSFER_READ : | |
120 | I2C_SLAVE_TRANSFER_WRITE; | |
121 | ||
122 | ret = ljca_transfer(ljca_i2c->pdev, I2C_START, w_packet, | |
123 | sizeof(*w_packet) + 1, r_packet, &ibuf_len); | |
124 | ||
125 | if (ret || ibuf_len < sizeof(*r_packet)) | |
126 | return -EIO; | |
127 | ||
128 | if ((s16)le16_to_cpu(r_packet->len) < 0 || | |
129 | r_packet->id != w_packet->id) { | |
130 | dev_err(&ljca_i2c->adap.dev, | |
131 | "i2c start failed len:%d id:%d %d\n", | |
132 | (s16)le16_to_cpu(r_packet->len), r_packet->id, | |
133 | w_packet->id); | |
134 | return -EIO; | |
135 | } | |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
140 | static int ljca_i2c_stop(struct ljca_i2c_dev *ljca_i2c, u8 slave_addr) | |
141 | { | |
142 | struct i2c_rw_packet *w_packet = (struct i2c_rw_packet *)ljca_i2c->obuf; | |
143 | struct i2c_rw_packet *r_packet = (struct i2c_rw_packet *)ljca_i2c->ibuf; | |
144 | int ret; | |
145 | int ibuf_len; | |
146 | ||
147 | memset(w_packet, 0, sizeof(*w_packet)); | |
148 | w_packet->id = ljca_i2c->ctr_info->id; | |
149 | w_packet->len = cpu_to_le16(1); | |
150 | w_packet->data[0] = 0; | |
151 | ||
152 | ret = ljca_transfer(ljca_i2c->pdev, I2C_STOP, w_packet, | |
153 | sizeof(*w_packet) + 1, r_packet, &ibuf_len); | |
154 | ||
155 | if (ret || ibuf_len < sizeof(*r_packet)) | |
156 | return -EIO; | |
157 | ||
158 | if ((s16)le16_to_cpu(r_packet->len) < 0 || | |
159 | r_packet->id != w_packet->id) { | |
160 | dev_err(&ljca_i2c->adap.dev, | |
161 | "i2c stop failed len:%d id:%d %d\n", | |
162 | (s16)le16_to_cpu(r_packet->len), r_packet->id, | |
163 | w_packet->id); | |
164 | return -EIO; | |
165 | } | |
166 | ||
167 | return 0; | |
168 | } | |
169 | ||
170 | static int ljca_i2c_pure_read(struct ljca_i2c_dev *ljca_i2c, u8 *data, int len) | |
171 | { | |
172 | struct i2c_rw_packet *w_packet = (struct i2c_rw_packet *)ljca_i2c->obuf; | |
173 | struct i2c_rw_packet *r_packet = (struct i2c_rw_packet *)ljca_i2c->ibuf; | |
174 | int ibuf_len; | |
175 | int ret; | |
176 | ||
177 | if (len > LJCA_I2C_MAX_XFER_SIZE) | |
178 | return -EINVAL; | |
179 | ||
180 | memset(w_packet, 0, sizeof(*w_packet)); | |
181 | w_packet->id = ljca_i2c->ctr_info->id; | |
182 | w_packet->len = cpu_to_le16(len); | |
183 | ret = ljca_transfer(ljca_i2c->pdev, I2C_READ, w_packet, | |
184 | sizeof(*w_packet) + 1, r_packet, &ibuf_len); | |
185 | if (ret) { | |
186 | dev_err(&ljca_i2c->adap.dev, "I2C_READ failed ret:%d\n", ret); | |
187 | return ret; | |
188 | } | |
189 | ||
190 | if (ibuf_len < sizeof(*r_packet)) | |
191 | return -EIO; | |
192 | ||
193 | if ((s16)le16_to_cpu(r_packet->len) != len || | |
194 | r_packet->id != w_packet->id) { | |
195 | dev_err(&ljca_i2c->adap.dev, | |
196 | "i2c raw read failed len:%d id:%d %d\n", | |
197 | (s16)le16_to_cpu(r_packet->len), r_packet->id, | |
198 | w_packet->id); | |
199 | return -EIO; | |
200 | } | |
201 | ||
202 | memcpy(data, r_packet->data, len); | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | static int ljca_i2c_read(struct ljca_i2c_dev *ljca_i2c, u8 slave_addr, u8 *data, | |
208 | u8 len) | |
209 | { | |
210 | int ret; | |
211 | ||
212 | ret = ljca_i2c_start(ljca_i2c, slave_addr, READ_XFER_TYPE); | |
213 | if (ret) | |
214 | return ret; | |
215 | ||
216 | ret = ljca_i2c_pure_read(ljca_i2c, data, len); | |
217 | if (ret) { | |
218 | dev_err(&ljca_i2c->adap.dev, "i2c raw read failed ret:%d\n", | |
219 | ret); | |
220 | ||
221 | return ret; | |
222 | } | |
223 | ||
224 | return ljca_i2c_stop(ljca_i2c, slave_addr); | |
225 | } | |
226 | ||
227 | static int ljca_i2c_pure_write(struct ljca_i2c_dev *ljca_i2c, u8 *data, u8 len) | |
228 | { | |
229 | struct i2c_rw_packet *w_packet = (struct i2c_rw_packet *)ljca_i2c->obuf; | |
230 | struct i2c_rw_packet *r_packet = (struct i2c_rw_packet *)ljca_i2c->ibuf; | |
231 | int ret; | |
232 | int ibuf_len; | |
233 | ||
234 | if (len > LJCA_I2C_MAX_XFER_SIZE) | |
235 | return -EINVAL; | |
236 | ||
237 | memset(w_packet, 0, sizeof(*w_packet)); | |
238 | w_packet->id = ljca_i2c->ctr_info->id; | |
239 | w_packet->len = cpu_to_le16(len); | |
240 | memcpy(w_packet->data, data, len); | |
241 | ||
242 | ret = ljca_transfer(ljca_i2c->pdev, I2C_WRITE, w_packet, | |
243 | sizeof(*w_packet) + w_packet->len, r_packet, | |
244 | &ibuf_len); | |
245 | ||
246 | if (ret || ibuf_len < sizeof(*r_packet)) | |
247 | return -EIO; | |
248 | ||
249 | if ((s16)le16_to_cpu(r_packet->len) != len || | |
250 | r_packet->id != w_packet->id) { | |
251 | dev_err(&ljca_i2c->adap.dev, | |
252 | "i2c write failed len:%d id:%d/%d\n", | |
253 | (s16)le16_to_cpu(r_packet->len), r_packet->id, | |
254 | w_packet->id); | |
255 | return -EIO; | |
256 | } | |
257 | ||
258 | return 0; | |
259 | } | |
260 | ||
261 | static int ljca_i2c_write(struct ljca_i2c_dev *ljca_i2c, u8 slave_addr, | |
262 | u8 *data, u8 len) | |
263 | { | |
264 | int ret; | |
265 | ||
266 | if (!data) | |
267 | return -EINVAL; | |
268 | ||
269 | ret = ljca_i2c_start(ljca_i2c, slave_addr, WRITE_XFER_TYPE); | |
270 | if (ret) | |
271 | return ret; | |
272 | ||
273 | ret = ljca_i2c_pure_write(ljca_i2c, data, len); | |
274 | if (ret) | |
275 | return ret; | |
276 | ||
277 | return ljca_i2c_stop(ljca_i2c, slave_addr); | |
278 | } | |
279 | ||
280 | static int ljca_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg, | |
281 | int num) | |
282 | { | |
283 | struct ljca_i2c_dev *ljca_i2c; | |
284 | struct i2c_msg *cur_msg; | |
285 | int i, ret; | |
286 | ||
287 | ljca_i2c = i2c_get_adapdata(adapter); | |
288 | if (!ljca_i2c) | |
289 | return -EINVAL; | |
290 | ||
291 | for (i = 0; !ret && i < num; i++) { | |
292 | cur_msg = &msg[i]; | |
293 | dev_dbg(&adapter->dev, "i:%d msg:(%d %d)\n", i, cur_msg->flags, | |
294 | cur_msg->len); | |
295 | if (cur_msg->flags & I2C_M_RD) | |
296 | ret = ljca_i2c_read(ljca_i2c, cur_msg->addr, | |
297 | cur_msg->buf, cur_msg->len); | |
298 | ||
299 | else | |
300 | ret = ljca_i2c_write(ljca_i2c, cur_msg->addr, | |
301 | cur_msg->buf, cur_msg->len); | |
302 | ||
303 | if (ret) | |
304 | return ret; | |
305 | } | |
306 | ||
307 | return num; | |
308 | } | |
309 | ||
310 | static u32 ljca_i2c_func(struct i2c_adapter *adap) | |
311 | { | |
312 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | |
313 | } | |
314 | ||
315 | static const struct i2c_adapter_quirks ljca_i2c_quirks = { | |
316 | .max_read_len = LJCA_I2C_MAX_XFER_SIZE, | |
317 | .max_write_len = LJCA_I2C_MAX_XFER_SIZE, | |
318 | }; | |
319 | ||
320 | static const struct i2c_algorithm ljca_i2c_algo = { | |
321 | .master_xfer = ljca_i2c_xfer, | |
322 | .functionality = ljca_i2c_func, | |
323 | }; | |
324 | ||
a50ce5b0 YX |
325 | static void try_bind_acpi(struct platform_device *pdev, |
326 | struct ljca_i2c_dev *ljca_i2c) | |
327 | { | |
328 | struct acpi_device *parent, *child; | |
329 | struct acpi_device *cur = ACPI_COMPANION(&pdev->dev); | |
330 | const char *hid1; | |
331 | const char *uid1; | |
332 | char uid2[2] = { 0 }; | |
333 | ||
334 | if (!cur) | |
335 | return; | |
336 | ||
337 | hid1 = acpi_device_hid(cur); | |
338 | uid1 = acpi_device_uid(cur); | |
339 | snprintf(uid2, sizeof(uid2), "%d", ljca_i2c->ctr_info->id); | |
340 | ||
341 | dev_dbg(&pdev->dev, "hid %s uid %s new uid%s\n", hid1, uid1, uid2); | |
342 | /* | |
343 | * If the pdev is bound to the right acpi device, just forward it to the | |
344 | * adapter. Otherwise, we find that of current adapter manually. | |
345 | */ | |
346 | if (!strcmp(uid1, uid2)) { | |
347 | ACPI_COMPANION_SET(&ljca_i2c->adap.dev, cur); | |
348 | return; | |
349 | } | |
350 | ||
351 | parent = ACPI_COMPANION(pdev->dev.parent); | |
352 | if (!parent) | |
353 | return; | |
354 | ||
355 | list_for_each_entry(child, &parent->children, node) { | |
356 | if (acpi_dev_hid_uid_match(child, hid1, uid2)) { | |
357 | ACPI_COMPANION_SET(&ljca_i2c->adap.dev, child); | |
358 | return; | |
359 | } | |
360 | } | |
361 | } | |
362 | ||
8857f00d WW |
363 | static int ljca_i2c_probe(struct platform_device *pdev) |
364 | { | |
365 | struct ljca_i2c_dev *ljca_i2c; | |
366 | struct ljca_platform_data *pdata = dev_get_platdata(&pdev->dev); | |
367 | int ret; | |
368 | ||
369 | ljca_i2c = devm_kzalloc(&pdev->dev, sizeof(*ljca_i2c), GFP_KERNEL); | |
370 | if (!ljca_i2c) | |
371 | return -ENOMEM; | |
372 | ||
373 | ljca_i2c->pdev = pdev; | |
374 | ljca_i2c->ctr_info = &pdata->i2c_info; | |
375 | ||
376 | ljca_i2c->adap.owner = THIS_MODULE; | |
377 | ljca_i2c->adap.class = I2C_CLASS_HWMON; | |
378 | ljca_i2c->adap.algo = &ljca_i2c_algo; | |
379 | ljca_i2c->adap.dev.parent = &pdev->dev; | |
a50ce5b0 YX |
380 | |
381 | try_bind_acpi(pdev, ljca_i2c); | |
382 | ||
8857f00d WW |
383 | ljca_i2c->adap.dev.of_node = pdev->dev.of_node; |
384 | i2c_set_adapdata(&ljca_i2c->adap, ljca_i2c); | |
385 | snprintf(ljca_i2c->adap.name, sizeof(ljca_i2c->adap.name), "%s-%s-%d", | |
386 | "ljca-i2c", dev_name(pdev->dev.parent), | |
387 | ljca_i2c->ctr_info->id); | |
388 | ||
389 | platform_set_drvdata(pdev, ljca_i2c); | |
390 | ||
391 | ret = ljca_i2c_init(ljca_i2c, ljca_i2c->ctr_info->id); | |
392 | if (ret) { | |
393 | dev_err(&pdev->dev, "i2c init failed id:%d\n", | |
394 | ljca_i2c->ctr_info->id); | |
395 | return -EIO; | |
396 | } | |
397 | ||
398 | return i2c_add_adapter(&ljca_i2c->adap); | |
399 | } | |
400 | ||
401 | static int ljca_i2c_remove(struct platform_device *pdev) | |
402 | { | |
403 | struct ljca_i2c_dev *ljca_i2c = platform_get_drvdata(pdev); | |
404 | ||
405 | i2c_del_adapter(&ljca_i2c->adap); | |
406 | ||
407 | return 0; | |
408 | } | |
409 | ||
410 | static struct platform_driver ljca_i2c_driver = { | |
411 | .driver.name = "ljca-i2c", | |
412 | .probe = ljca_i2c_probe, | |
413 | .remove = ljca_i2c_remove, | |
414 | }; | |
415 | ||
416 | module_platform_driver(ljca_i2c_driver); | |
417 | ||
418 | MODULE_AUTHOR("Ye Xiang <xiang.ye@intel.com>"); | |
419 | MODULE_AUTHOR("Zhang Lixu <lixu.zhang@intel.com>"); | |
420 | MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB-I2C driver"); | |
421 | MODULE_LICENSE("GPL v2"); | |
422 | MODULE_ALIAS("platform:ljca-i2c"); |