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