1 /* $NetBSD: i2c_exec.c,v 1.6 2007/12/11 12:09:22 lukem Exp $ */
4 * Copyright (c) 2003 Wasabi Systems, Inc.
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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
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.
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: i2c_exec.c,v 1.6 2007/12/11 12:09:22 lukem Exp $");
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/device.h>
44 #include <sys/event.h>
48 #include <dev/i2c/i2cvar.h>
50 static uint8_t iic_smbus_crc8(uint16_t);
51 static uint8_t iic_smbus_pec(int, uint8_t *, uint8_t *);
56 * Simplified I2C client interface engine.
58 * This and the SMBus routines are the preferred interface for
59 * client access to I2C/SMBus, since many automated controllers
60 * do not provide access to the low-level primitives of the I2C
64 iic_exec(i2c_tag_t tag
, i2c_op_t op
, i2c_addr_t addr
, const void *vcmd
,
65 size_t cmdlen
, void *vbuf
, size_t buflen
, int flags
)
67 const uint8_t *cmd
= vcmd
;
72 if ((flags
& I2C_F_PEC
) && cmdlen
> 0 && tag
->ic_exec
!= NULL
) {
73 uint8_t data
[33]; /* XXX */
81 data
[0] = iic_smbus_pec(2, b
, NULL
);
86 data
[0] = iic_smbus_pec(3, b
, NULL
);
93 memcpy(data
, vbuf
, sizeof(vbuf
));
94 data
[sizeof(vbuf
)] = iic_smbus_pec(2, b
, data
);
99 return ((*tag
->ic_exec
)(tag
->ic_cookie
, op
, addr
, cmd
,
100 cmdlen
, data
, buflen
, flags
));
104 * Defer to the controller if it provides an exec function. Use
107 if (tag
->ic_exec
!= NULL
)
108 return ((*tag
->ic_exec
)(tag
->ic_cookie
, op
, addr
, cmd
,
109 cmdlen
, buf
, buflen
, flags
));
111 if ((len
= cmdlen
) != 0) {
112 if ((error
= iic_initiate_xfer(tag
, addr
, flags
)) != 0)
115 if ((error
= iic_write_byte(tag
, *cmd
++, flags
)) != 0)
118 } else if (buflen
== 0) {
120 * This is a quick_read()/quick_write() command with
121 * neither command nor data bytes
123 if (I2C_OP_STOP_P(op
))
125 if (I2C_OP_READ_P(op
))
127 if ((error
= iic_initiate_xfer(tag
, addr
, flags
)) != 0)
131 if (I2C_OP_READ_P(op
))
136 if (len
== 0 && I2C_OP_STOP_P(op
))
138 if (I2C_OP_READ_P(op
)) {
139 /* Send REPEATED START. */
140 if ((len
+ 1) == buflen
&&
141 (error
= iic_initiate_xfer(tag
, addr
, flags
)) != 0)
143 /* NACK on last byte. */
146 if ((error
= iic_read_byte(tag
, buf
++, flags
)) != 0)
149 /* Maybe send START. */
150 if ((len
+ 1) == buflen
&& cmdlen
== 0 &&
151 (error
= iic_initiate_xfer(tag
, addr
, flags
)) != 0)
153 if ((error
= iic_write_byte(tag
, *buf
++, flags
)) != 0)
160 iic_send_stop(tag
, flags
);
165 * iic_smbus_write_byte:
167 * Perform an SMBus "write byte" operation.
170 iic_smbus_write_byte(i2c_tag_t tag
, i2c_addr_t addr
, uint8_t cmd
,
171 uint8_t val
, int flags
)
174 return (iic_exec(tag
, I2C_OP_WRITE_WITH_STOP
, addr
, &cmd
, 1,
179 * iic_smbus_write_word:
181 * Perform an SMBus "write word" operation.
184 iic_smbus_write_word(i2c_tag_t tag
, i2c_addr_t addr
, uint8_t cmd
,
185 uint16_t val
, int flags
)
189 vbuf
[0] = val
& 0xff;
190 vbuf
[1] = (val
>> 8) & 0xff;
192 return (iic_exec(tag
, I2C_OP_WRITE_WITH_STOP
, addr
, &cmd
, 1,
197 * iic_smbus_read_byte:
199 * Perform an SMBus "read byte" operation.
202 iic_smbus_read_byte(i2c_tag_t tag
, i2c_addr_t addr
, uint8_t cmd
,
203 uint8_t *valp
, int flags
)
206 return (iic_exec(tag
, I2C_OP_READ_WITH_STOP
, addr
, &cmd
, 1,
211 * iic_smbus_read_word:
213 * Perform an SMBus "read word" operation.
216 iic_smbus_read_word(i2c_tag_t tag
, i2c_addr_t addr
, uint8_t cmd
,
217 uint16_t *valp
, int flags
)
220 return (iic_exec(tag
, I2C_OP_READ_WITH_STOP
, addr
, &cmd
, 1,
221 (uint8_t *)valp
, 2, flags
));
225 * iic_smbus_receive_byte:
227 * Perform an SMBus "receive byte" operation.
230 iic_smbus_receive_byte(i2c_tag_t tag
, i2c_addr_t addr
, uint8_t *valp
,
234 return (iic_exec(tag
, I2C_OP_READ_WITH_STOP
, addr
, NULL
, 0,
239 * iic_smbus_send_byte:
241 * Perform an SMBus "send byte" operation.
244 iic_smbus_send_byte(i2c_tag_t tag
, i2c_addr_t addr
, uint8_t val
, int flags
)
247 return (iic_exec(tag
, I2C_OP_WRITE_WITH_STOP
, addr
, NULL
, 0,
252 * iic_smbus_quick_read:
254 * Perform an SMBus "quick read" operation.
257 iic_smbus_quick_read(i2c_tag_t tag
, i2c_addr_t addr
, int flags
)
260 return (iic_exec(tag
, I2C_OP_READ_WITH_STOP
, addr
, NULL
, 0,
265 * iic_smbus_quick_write:
267 * Perform an SMBus "quick write" operation.
270 iic_smbus_quick_write(i2c_tag_t tag
, i2c_addr_t addr
, int flags
)
273 return (iic_exec(tag
, I2C_OP_WRITE_WITH_STOP
, addr
, NULL
, 0,
278 * iic_smbus_block_read:
280 * Perform an SMBus "block read" operation.
283 iic_smbus_block_read(i2c_tag_t tag
, i2c_addr_t addr
, uint8_t cmd
,
284 uint8_t *vbuf
, size_t buflen
, int flags
)
287 return (iic_exec(tag
, I2C_OP_READ_WITH_STOP
, addr
, &cmd
, 1,
288 vbuf
, buflen
, flags
));
292 * iic_smbus_block_write:
294 * Perform an SMBus "block write" operation.
297 iic_smbus_block_write(i2c_tag_t tag
, i2c_addr_t addr
, uint8_t cmd
,
298 uint8_t *vbuf
, size_t buflen
, int flags
)
301 return (iic_exec(tag
, I2C_OP_WRITE_WITH_STOP
, addr
, &cmd
, 1,
302 vbuf
, buflen
, flags
));
308 * Private helper for calculating packet error checksum
311 iic_smbus_crc8(uint16_t data
)
315 for (i
= 0; i
< 8; i
++) {
317 data
= data
^ (0x1070U
<< 3);
321 return (uint8_t)(data
>> 8);
327 * Private function for calculating packet error checking on SMBus
331 iic_smbus_pec(int count
, uint8_t *s
, uint8_t *r
)
336 for (i
= 0; i
< count
; i
++)
337 crc
= iic_smbus_crc8((crc
^ s
[i
]) << 8);
339 for (i
= 0; i
<= r
[0]; i
++)
340 crc
= iic_smbus_crc8((crc
^ r
[i
]) << 8);