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