]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/i2c/busses/i2c-ljca.c
UBUNTU: SAUCE: intel visual sensing controller(VSC) driver first release
[mirror_ubuntu-jammy-kernel.git] / drivers / i2c / busses / i2c-ljca.c
CommitLineData
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 */
14enum i2c_cmd {
15 I2C_INIT = 1,
16 I2C_XFER,
17 I2C_START,
18 I2C_STOP,
19 I2C_READ,
20 I2C_WRITE,
21};
22
23enum i2c_address_mode {
24 I2C_ADDRESS_MODE_7BIT,
25 I2C_ADDRESS_MODE_10BIT,
26};
27
28enum 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 */
54struct 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 */
64struct 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
74struct 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
83static 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
91static 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
104static 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
139static 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
169static 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
206static 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
226static 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
260static 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
279static 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
309static u32 ljca_i2c_func(struct i2c_adapter *adap)
310{
311 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
312}
313
314static 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
319static const struct i2c_algorithm ljca_i2c_algo = {
320 .master_xfer = ljca_i2c_xfer,
321 .functionality = ljca_i2c_func,
322};
323
324static 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
360static 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
369static struct platform_driver ljca_i2c_driver = {
370 .driver.name = "ljca-i2c",
371 .probe = ljca_i2c_probe,
372 .remove = ljca_i2c_remove,
373};
374
375module_platform_driver(ljca_i2c_driver);
376
377MODULE_AUTHOR("Ye Xiang <xiang.ye@intel.com>");
378MODULE_AUTHOR("Zhang Lixu <lixu.zhang@intel.com>");
379MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB-I2C driver");
380MODULE_LICENSE("GPL v2");
381MODULE_ALIAS("platform:ljca-i2c");