1 /* $NetBSD: i2c_exec.c,v 1.18 2022/10/24 10:17:27 riastradh Exp $ */
2
3 /*
4 * Copyright (c) 2003 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
23 * written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: i2c_exec.c,v 1.18 2022/10/24 10:17:27 riastradh Exp $");
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/cpu.h>
44 #include <sys/device.h>
45 #include <sys/module.h>
46 #include <sys/event.h>
47 #include <sys/conf.h>
48 #include <sys/kernel.h>
49
50 #define _I2C_PRIVATE
51 #include <dev/i2c/i2cvar.h>
52
53 static uint8_t iic_smbus_crc8(uint16_t);
54 static uint8_t iic_smbus_pec(int, uint8_t *, uint8_t *);
55
56 static int i2cexec_modcmd(modcmd_t, void *);
57
58 static inline int
iic_op_flags(int flags)59 iic_op_flags(int flags)
60 {
61
62 return flags | ((cold || shutting_down) ? I2C_F_POLL : 0);
63 }
64
65 /*
66 * iic_tag_init:
67 *
68 * Perform some basic initialization of the i2c controller tag.
69 */
70 void
iic_tag_init(i2c_tag_t tag)71 iic_tag_init(i2c_tag_t tag)
72 {
73
74 memset(tag, 0, sizeof(*tag));
75 mutex_init(&tag->ic_bus_lock, MUTEX_DEFAULT, IPL_NONE);
76 }
77
78 /*
79 * iic_tag_fini:
80 *
81 * Teardown of the i2c controller tag.
82 */
83 void
iic_tag_fini(i2c_tag_t tag)84 iic_tag_fini(i2c_tag_t tag)
85 {
86
87 mutex_destroy(&tag->ic_bus_lock);
88 }
89
90 /*
91 * iic_acquire_bus:
92 *
93 * Acquire the I2C bus for use by a client.
94 */
95 int
iic_acquire_bus(i2c_tag_t tag,int flags)96 iic_acquire_bus(i2c_tag_t tag, int flags)
97 {
98
99 #if 0 /* XXX Not quite ready for this yet. */
100 KASSERT(!cpu_intr_p());
101 #endif
102
103 flags = iic_op_flags(flags);
104
105 if (flags & I2C_F_POLL) {
106 /*
107 * Polling should only be used in rare and/or
108 * extreme circumstances; most i2c clients
109 * should be allowed to sleep.
110 *
111 * Really, the ONLY user of I2C_F_POLL should be
112 * "when cold", i.e. during early autoconfiguration
113 * when there is only proc0, and we might have to
114 * read SEEPROMs, etc. There should be no other
115 * users interfering with our access of the i2c bus
116 * in that case.
117 */
118 if (mutex_tryenter(&tag->ic_bus_lock) == 0) {
119 return EBUSY;
120 }
121 } else {
122 /*
123 * N.B. We implement this as a mutex that we hold across
124 * across a series of requests beause we'd like to get the
125 * priority boost if a higher-priority process wants the
126 * i2c bus while we're asleep waiting for the controller
127 * to perform the I/O.
128 *
129 * XXXJRT Disable preemption here? We'd like to keep
130 * the CPU while holding this resource, unless we release
131 * it voluntarily (which should only happen while waiting
132 * for a controller to complete I/O).
133 */
134 mutex_enter(&tag->ic_bus_lock);
135 }
136
137 int error = 0;
138 if (tag->ic_acquire_bus) {
139 error = (*tag->ic_acquire_bus)(tag->ic_cookie, flags);
140 }
141
142 if (__predict_false(error)) {
143 mutex_exit(&tag->ic_bus_lock);
144 }
145
146 return error;
147 }
148
149 /*
150 * iic_release_bus:
151 *
152 * Release the I2C bus, allowing another client to use it.
153 */
154 void
iic_release_bus(i2c_tag_t tag,int flags)155 iic_release_bus(i2c_tag_t tag, int flags)
156 {
157
158 #if 0 /* XXX Not quite ready for this yet. */
159 KASSERT(!cpu_intr_p());
160 #endif
161
162 flags = iic_op_flags(flags);
163
164 if (tag->ic_release_bus) {
165 (*tag->ic_release_bus)(tag->ic_cookie, flags);
166 }
167
168 mutex_exit(&tag->ic_bus_lock);
169 }
170
171 /*
172 * iic_exec:
173 *
174 * Simplified I2C client interface engine.
175 *
176 * This and the SMBus routines are the preferred interface for
177 * client access to I2C/SMBus, since many automated controllers
178 * do not provide access to the low-level primitives of the I2C
179 * bus protocol.
180 */
181 int
iic_exec(i2c_tag_t tag,i2c_op_t op,i2c_addr_t addr,const void * vcmd,size_t cmdlen,void * vbuf,size_t buflen,int flags)182 iic_exec(i2c_tag_t tag, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
183 size_t cmdlen, void *vbuf, size_t buflen, int flags)
184 {
185 const uint8_t *cmd = vcmd;
186 uint8_t *buf = vbuf;
187 int error;
188 size_t len;
189
190 #if 0 /* XXX Not quite ready for this yet. */
191 KASSERT(!cpu_intr_p());
192 #endif
193
194 flags = iic_op_flags(flags);
195
196 if ((flags & I2C_F_PEC) && cmdlen > 0 && tag->ic_exec != NULL) {
197 uint8_t data[33]; /* XXX */
198 uint8_t b[3];
199
200 b[0] = addr << 1;
201 b[1] = cmd[0];
202
203 switch (buflen) {
204 case 0:
205 data[0] = iic_smbus_pec(2, b, NULL);
206 buflen++;
207 break;
208 case 1:
209 b[2] = buf[0];
210 data[0] = iic_smbus_pec(3, b, NULL);
211 data[1] = b[2];
212 buflen++;
213 break;
214 case 2:
215 break;
216 default:
217 KASSERT(buflen+1 < sizeof(data));
218 memcpy(data, vbuf, buflen);
219 data[buflen] = iic_smbus_pec(2, b, data);
220 buflen++;
221 break;
222 }
223
224 return ((*tag->ic_exec)(tag->ic_cookie, op, addr, cmd,
225 cmdlen, data, buflen, flags));
226 }
227
228 /*
229 * Defer to the controller if it provides an exec function. Use
230 * it if it does.
231 */
232 if (tag->ic_exec != NULL)
233 return ((*tag->ic_exec)(tag->ic_cookie, op, addr, cmd,
234 cmdlen, buf, buflen, flags));
235
236 if ((len = cmdlen) != 0) {
237 if ((error = iic_initiate_xfer(tag, addr, flags)) != 0)
238 goto bad;
239 while (len--) {
240 if ((error = iic_write_byte(tag, *cmd++, flags)) != 0)
241 goto bad;
242 }
243 } else if (buflen == 0) {
244 /*
245 * This is a quick_read()/quick_write() command with
246 * neither command nor data bytes
247 */
248 if (I2C_OP_STOP_P(op))
249 flags |= I2C_F_STOP;
250 if (I2C_OP_READ_P(op))
251 flags |= I2C_F_READ;
252 if ((error = iic_initiate_xfer(tag, addr, flags)) != 0)
253 goto bad;
254 }
255
256 if (I2C_OP_READ_P(op))
257 flags |= I2C_F_READ;
258
259 len = buflen;
260 while (len--) {
261 if (len == 0 && I2C_OP_STOP_P(op))
262 flags |= I2C_F_STOP;
263 if (I2C_OP_READ_P(op)) {
264 /* Send REPEATED START. */
265 if ((len + 1) == buflen &&
266 (error = iic_initiate_xfer(tag, addr, flags)) != 0)
267 goto bad;
268 /* NACK on last byte. */
269 if (len == 0)
270 flags |= I2C_F_LAST;
271 if ((error = iic_read_byte(tag, buf++, flags)) != 0)
272 goto bad;
273 } else {
274 /* Maybe send START. */
275 if ((len + 1) == buflen && cmdlen == 0 &&
276 (error = iic_initiate_xfer(tag, addr, flags)) != 0)
277 goto bad;
278 if ((error = iic_write_byte(tag, *buf++, flags)) != 0)
279 goto bad;
280 }
281 }
282
283 return (0);
284 bad:
285 iic_send_stop(tag, flags);
286 return (error);
287 }
288
289 /*
290 * iic_smbus_write_byte:
291 *
292 * Perform an SMBus "write byte" operation.
293 */
294 int
iic_smbus_write_byte(i2c_tag_t tag,i2c_addr_t addr,uint8_t cmd,uint8_t val,int flags)295 iic_smbus_write_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
296 uint8_t val, int flags)
297 {
298
299 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &cmd, 1,
300 &val, 1, flags));
301 }
302
303 /*
304 * iic_smbus_write_word:
305 *
306 * Perform an SMBus "write word" operation.
307 */
308 int
iic_smbus_write_word(i2c_tag_t tag,i2c_addr_t addr,uint8_t cmd,uint16_t val,int flags)309 iic_smbus_write_word(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
310 uint16_t val, int flags)
311 {
312 uint8_t vbuf[2];
313
314 vbuf[0] = val & 0xff;
315 vbuf[1] = (val >> 8) & 0xff;
316
317 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &cmd, 1,
318 vbuf, 2, flags));
319 }
320
321 /*
322 * iic_smbus_read_byte:
323 *
324 * Perform an SMBus "read byte" operation.
325 */
326 int
iic_smbus_read_byte(i2c_tag_t tag,i2c_addr_t addr,uint8_t cmd,uint8_t * valp,int flags)327 iic_smbus_read_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
328 uint8_t *valp, int flags)
329 {
330
331 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &cmd, 1,
332 valp, 1, flags));
333 }
334
335 /*
336 * iic_smbus_read_word:
337 *
338 * Perform an SMBus "read word" operation.
339 */
340 int
iic_smbus_read_word(i2c_tag_t tag,i2c_addr_t addr,uint8_t cmd,uint16_t * valp,int flags)341 iic_smbus_read_word(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
342 uint16_t *valp, int flags)
343 {
344
345 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &cmd, 1,
346 (uint8_t *)valp, 2, flags));
347 }
348
349 /*
350 * iic_smbus_receive_byte:
351 *
352 * Perform an SMBus "receive byte" operation.
353 */
354 int
iic_smbus_receive_byte(i2c_tag_t tag,i2c_addr_t addr,uint8_t * valp,int flags)355 iic_smbus_receive_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t *valp,
356 int flags)
357 {
358
359 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, NULL, 0,
360 valp, 1, flags));
361 }
362
363 /*
364 * iic_smbus_send_byte:
365 *
366 * Perform an SMBus "send byte" operation.
367 */
368 int
iic_smbus_send_byte(i2c_tag_t tag,i2c_addr_t addr,uint8_t val,int flags)369 iic_smbus_send_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t val, int flags)
370 {
371
372 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, NULL, 0,
373 &val, 1, flags));
374 }
375
376 /*
377 * iic_smbus_quick_read:
378 *
379 * Perform an SMBus "quick read" operation.
380 */
381 int
iic_smbus_quick_read(i2c_tag_t tag,i2c_addr_t addr,int flags)382 iic_smbus_quick_read(i2c_tag_t tag, i2c_addr_t addr, int flags)
383 {
384
385 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, NULL, 0,
386 NULL, 0, flags));
387 }
388
389 /*
390 * iic_smbus_quick_write:
391 *
392 * Perform an SMBus "quick write" operation.
393 */
394 int
iic_smbus_quick_write(i2c_tag_t tag,i2c_addr_t addr,int flags)395 iic_smbus_quick_write(i2c_tag_t tag, i2c_addr_t addr, int flags)
396 {
397
398 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, NULL, 0,
399 NULL, 0, flags));
400 }
401
402 /*
403 * iic_smbus_block_read:
404 *
405 * Perform an SMBus "block read" operation.
406 */
407 int
iic_smbus_block_read(i2c_tag_t tag,i2c_addr_t addr,uint8_t cmd,uint8_t * vbuf,size_t buflen,int flags)408 iic_smbus_block_read(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
409 uint8_t *vbuf, size_t buflen, int flags)
410 {
411
412 return (iic_exec(tag, I2C_OP_READ_BLOCK, addr, &cmd, 1,
413 vbuf, buflen, flags));
414 }
415
416 /*
417 * iic_smbus_block_write:
418 *
419 * Perform an SMBus "block write" operation.
420 */
421 int
iic_smbus_block_write(i2c_tag_t tag,i2c_addr_t addr,uint8_t cmd,uint8_t * vbuf,size_t buflen,int flags)422 iic_smbus_block_write(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd,
423 uint8_t *vbuf, size_t buflen, int flags)
424 {
425
426 return (iic_exec(tag, I2C_OP_WRITE_BLOCK, addr, &cmd, 1,
427 vbuf, buflen, flags));
428 }
429
430 /*
431 * iic_smbus_crc8
432 *
433 * Private helper for calculating packet error checksum
434 */
435 static uint8_t
iic_smbus_crc8(uint16_t data)436 iic_smbus_crc8(uint16_t data)
437 {
438 int i;
439
440 for (i = 0; i < 8; i++) {
441 if (data & 0x8000)
442 data = data ^ (0x1070U << 3);
443 data = data << 1;
444 }
445
446 return (uint8_t)(data >> 8);
447 }
448
449 /*
450 * iic_smbus_pec
451 *
452 * Private function for calculating packet error checking on SMBus
453 * packets.
454 */
455 static uint8_t
iic_smbus_pec(int count,uint8_t * s,uint8_t * r)456 iic_smbus_pec(int count, uint8_t *s, uint8_t *r)
457 {
458 int i;
459 uint8_t crc = 0;
460
461 for (i = 0; i < count; i++)
462 crc = iic_smbus_crc8((crc ^ s[i]) << 8);
463 if (r != NULL)
464 for (i = 0; i <= r[0]; i++)
465 crc = iic_smbus_crc8((crc ^ r[i]) << 8);
466
467 return crc;
468 }
469
470 MODULE(MODULE_CLASS_MISC, i2cexec, NULL);
471
472 static int
i2cexec_modcmd(modcmd_t cmd,void * opaque)473 i2cexec_modcmd(modcmd_t cmd, void *opaque)
474 {
475 switch (cmd) {
476 case MODULE_CMD_INIT:
477 case MODULE_CMD_FINI:
478 return 0;
479 break;
480 default:
481 return ENOTTY;
482 }
483 }
484