1 /* $NetBSD: i2c_exec.c,v 1.5 2006/03/27 23:59:38 jmcneill 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/param.h> 39 #include <sys/systm.h> 40 #include <sys/device.h> 41 #include <sys/event.h> 42 #include <sys/conf.h> 43 44 #define _I2C_PRIVATE 45 #include <dev/i2c/i2cvar.h> 46 47 static uint8_t iic_smbus_crc8(uint16_t); 48 static uint8_t iic_smbus_pec(int, uint8_t *, uint8_t *); 49 50 /* 51 * iic_exec: 52 * 53 * Simplified I2C client interface engine. 54 * 55 * This and the SMBus routines are the preferred interface for 56 * client access to I2C/SMBus, since many automated controllers 57 * do not provide access to the low-level primitives of the I2C 58 * bus protocol. 59 */ 60 int 61 iic_exec(i2c_tag_t tag, i2c_op_t op, i2c_addr_t addr, const void *vcmd, 62 size_t cmdlen, void *vbuf, size_t buflen, int flags) 63 { 64 const uint8_t *cmd = vcmd; 65 uint8_t *buf = vbuf; 66 int error; 67 size_t len; 68 69 if ((flags & I2C_F_PEC) && cmdlen > 0 && tag->ic_exec != NULL) { 70 uint8_t data[33]; /* XXX */ 71 uint8_t b[3]; 72 73 b[0] = addr << 1; 74 b[1] = cmd[0]; 75 76 switch (buflen) { 77 case 0: 78 data[0] = iic_smbus_pec(2, b, NULL); 79 buflen++; 80 break; 81 case 1: 82 b[2] = buf[0]; 83 data[0] = iic_smbus_pec(3, b, NULL); 84 data[1] = b[2]; 85 buflen++; 86 break; 87 case 2: 88 break; 89 default: 90 memcpy(data, vbuf, sizeof(vbuf)); 91 data[sizeof(vbuf)] = iic_smbus_pec(2, b, data); 92 buflen++; 93 break; 94 } 95 96 return ((*tag->ic_exec)(tag->ic_cookie, op, addr, cmd, 97 cmdlen, data, buflen, flags)); 98 } 99 100 /* 101 * Defer to the controller if it provides an exec function. Use 102 * it if it does. 103 */ 104 if (tag->ic_exec != NULL) 105 return ((*tag->ic_exec)(tag->ic_cookie, op, addr, cmd, 106 cmdlen, buf, buflen, flags)); 107 108 if ((len = cmdlen) != 0) { 109 if ((error = iic_initiate_xfer(tag, addr, flags)) != 0) 110 goto bad; 111 while (len--) { 112 if ((error = iic_write_byte(tag, *cmd++, flags)) != 0) 113 goto bad; 114 } 115 } 116 117 if (I2C_OP_READ_P(op)) 118 flags |= I2C_F_READ; 119 120 len = buflen; 121 while (len--) { 122 if (len == 0 && I2C_OP_STOP_P(op)) 123 flags |= I2C_F_STOP; 124 if (I2C_OP_READ_P(op)) { 125 /* Send REPEATED START. */ 126 if ((len + 1) == buflen && 127 (error = iic_initiate_xfer(tag, addr, flags)) != 0) 128 goto bad; 129 /* NACK on last byte. */ 130 if (len == 0) 131 flags |= I2C_F_LAST; 132 if ((error = iic_read_byte(tag, buf++, flags)) != 0) 133 goto bad; 134 } else { 135 /* Maybe send START. */ 136 if ((len + 1) == buflen && cmdlen == 0 && 137 (error = iic_initiate_xfer(tag, addr, flags)) != 0) 138 goto bad; 139 if ((error = iic_write_byte(tag, *buf++, flags)) != 0) 140 goto bad; 141 } 142 } 143 144 return (0); 145 bad: 146 iic_send_stop(tag, flags); 147 return (error); 148 } 149 150 /* 151 * iic_smbus_write_byte: 152 * 153 * Perform an SMBus "write byte" operation. 154 */ 155 int 156 iic_smbus_write_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 157 uint8_t val, int flags) 158 { 159 160 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &cmd, 1, 161 &val, 1, flags)); 162 } 163 164 /* 165 * iic_smbus_write_word: 166 * 167 * Perform an SMBus "write word" operation. 168 */ 169 int 170 iic_smbus_write_word(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 171 uint16_t val, int flags) 172 { 173 uint8_t vbuf[2]; 174 175 vbuf[0] = val & 0xff; 176 vbuf[1] = (val >> 8) & 0xff; 177 178 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &cmd, 1, 179 vbuf, 2, flags)); 180 } 181 182 /* 183 * iic_smbus_read_byte: 184 * 185 * Perform an SMBus "read byte" operation. 186 */ 187 int 188 iic_smbus_read_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 189 uint8_t *valp, int flags) 190 { 191 192 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &cmd, 1, 193 valp, 1, flags)); 194 } 195 196 /* 197 * iic_smbus_read_word: 198 * 199 * Perform an SMBus "read word" operation. 200 */ 201 int 202 iic_smbus_read_word(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 203 uint16_t *valp, int flags) 204 { 205 206 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &cmd, 1, 207 (uint8_t *)valp, 2, flags)); 208 } 209 210 /* 211 * iic_smbus_receive_byte: 212 * 213 * Perform an SMBus "receive byte" operation. 214 */ 215 int 216 iic_smbus_receive_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t *valp, 217 int flags) 218 { 219 220 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, NULL, 0, 221 valp, 1, flags)); 222 } 223 224 /* 225 * iic_smbus_send_byte: 226 * 227 * Perform an SMBus "send byte" operation. 228 */ 229 int 230 iic_smbus_send_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t val, int flags) 231 { 232 233 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, NULL, 0, 234 &val, 1, flags)); 235 } 236 237 /* 238 * iic_smbus_quick_read: 239 * 240 * Perform an SMBus "quick read" operation. 241 */ 242 int 243 iic_smbus_quick_read(i2c_tag_t tag, i2c_addr_t addr, int flags) 244 { 245 246 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, NULL, 0, 247 NULL, 0, flags)); 248 } 249 250 /* 251 * iic_smbus_quick_write: 252 * 253 * Perform an SMBus "quick write" operation. 254 */ 255 int 256 iic_smbus_quick_write(i2c_tag_t tag, i2c_addr_t addr, int flags) 257 { 258 259 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, NULL, 0, 260 NULL, 0, flags)); 261 } 262 263 /* 264 * iic_smbus_block_read: 265 * 266 * Perform an SMBus "block read" operation. 267 */ 268 int 269 iic_smbus_block_read(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 270 uint8_t *vbuf, size_t buflen, int flags) 271 { 272 273 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &cmd, 1, 274 vbuf, buflen, flags)); 275 } 276 277 /* 278 * iic_smbus_block_write: 279 * 280 * Perform an SMBus "block write" operation. 281 */ 282 int 283 iic_smbus_block_write(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 284 uint8_t *vbuf, size_t buflen, int flags) 285 { 286 287 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &cmd, 1, 288 vbuf, buflen, flags)); 289 } 290 291 /* 292 * iic_smbus_crc8 293 * 294 * Private helper for calculating packet error checksum 295 */ 296 static uint8_t 297 iic_smbus_crc8(uint16_t data) 298 { 299 int i; 300 301 for (i = 0; i < 8; i++) { 302 if (data & 0x8000) 303 data = data ^ (0x1070U << 3); 304 data = data << 1; 305 } 306 307 return (uint8_t)(data >> 8); 308 } 309 310 /* 311 * iic_smbus_pec 312 * 313 * Private function for calculating packet error checking on SMBus 314 * packets. 315 */ 316 static uint8_t 317 iic_smbus_pec(int count, uint8_t *s, uint8_t *r) 318 { 319 int i; 320 uint8_t crc = 0; 321 322 for (i = 0; i < count; i++) 323 crc = iic_smbus_crc8((crc ^ s[i]) << 8); 324 if (r != NULL) 325 for (i = 0; i <= r[0]; i++) 326 crc = iic_smbus_crc8((crc ^ r[i]) << 8); 327 328 return crc; 329 } 330