1/* $NetBSD: i2c_exec.c,v 1.6 2007/12/11 12:09:22 lukem 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.6 2007/12/11 12:09:22 lukem 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 50static uint8_t iic_smbus_crc8(uint16_t); 51static 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 */ 63int 64iic_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 memcpy(data, vbuf, sizeof(vbuf)); 94 data[sizeof(vbuf)] = iic_smbus_pec(2, b, data); 95 buflen++; 96 break; 97 } 98 99 return ((*tag->ic_exec)(tag->ic_cookie, op, addr, cmd, 100 cmdlen, data, buflen, flags)); 101 } 102 103 /* 104 * Defer to the controller if it provides an exec function. Use 105 * it if it does. 106 */ 107 if (tag->ic_exec != NULL) 108 return ((*tag->ic_exec)(tag->ic_cookie, op, addr, cmd, 109 cmdlen, buf, buflen, flags)); 110 111 if ((len = cmdlen) != 0) { 112 if ((error = iic_initiate_xfer(tag, addr, flags)) != 0) 113 goto bad; 114 while (len--) { 115 if ((error = iic_write_byte(tag, *cmd++, flags)) != 0) 116 goto bad; 117 } 118 } else if (buflen == 0) { 119 /* 120 * This is a quick_read()/quick_write() command with 121 * neither command nor data bytes 122 */ 123 if (I2C_OP_STOP_P(op)) 124 flags |= I2C_F_STOP; 125 if (I2C_OP_READ_P(op)) 126 flags |= I2C_F_READ; 127 if ((error = iic_initiate_xfer(tag, addr, flags)) != 0) 128 goto bad; 129 } 130 131 if (I2C_OP_READ_P(op)) 132 flags |= I2C_F_READ; 133 134 len = buflen; 135 while (len--) { 136 if (len == 0 && I2C_OP_STOP_P(op)) 137 flags |= I2C_F_STOP; 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) 142 goto bad; 143 /* NACK on last byte. */ 144 if (len == 0) 145 flags |= I2C_F_LAST; 146 if ((error = iic_read_byte(tag, buf++, flags)) != 0) 147 goto bad; 148 } else { 149 /* Maybe send START. */ 150 if ((len + 1) == buflen && cmdlen == 0 && 151 (error = iic_initiate_xfer(tag, addr, flags)) != 0) 152 goto bad; 153 if ((error = iic_write_byte(tag, *buf++, flags)) != 0) 154 goto bad; 155 } 156 } 157 158 return (0); 159 bad: 160 iic_send_stop(tag, flags); 161 return (error); 162} 163 164/* 165 * iic_smbus_write_byte: 166 * 167 * Perform an SMBus "write byte" operation. 168 */ 169int 170iic_smbus_write_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 171 uint8_t val, int flags) 172{ 173 174 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &cmd, 1, 175 &val, 1, flags)); 176} 177 178/* 179 * iic_smbus_write_word: 180 * 181 * Perform an SMBus "write word" operation. 182 */ 183int 184iic_smbus_write_word(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 185 uint16_t val, int flags) 186{ 187 uint8_t vbuf[2]; 188 189 vbuf[0] = val & 0xff; 190 vbuf[1] = (val >> 8) & 0xff; 191 192 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &cmd, 1, 193 vbuf, 2, flags)); 194} 195 196/* 197 * iic_smbus_read_byte: 198 * 199 * Perform an SMBus "read byte" operation. 200 */ 201int 202iic_smbus_read_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 203 uint8_t *valp, int flags) 204{ 205 206 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &cmd, 1, 207 valp, 1, flags)); 208} 209 210/* 211 * iic_smbus_read_word: 212 * 213 * Perform an SMBus "read word" operation. 214 */ 215int 216iic_smbus_read_word(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 217 uint16_t *valp, int flags) 218{ 219 220 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &cmd, 1, 221 (uint8_t *)valp, 2, flags)); 222} 223 224/* 225 * iic_smbus_receive_byte: 226 * 227 * Perform an SMBus "receive byte" operation. 228 */ 229int 230iic_smbus_receive_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t *valp, 231 int flags) 232{ 233 234 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, NULL, 0, 235 valp, 1, flags)); 236} 237 238/* 239 * iic_smbus_send_byte: 240 * 241 * Perform an SMBus "send byte" operation. 242 */ 243int 244iic_smbus_send_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t val, int flags) 245{ 246 247 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, NULL, 0, 248 &val, 1, flags)); 249} 250 251/* 252 * iic_smbus_quick_read: 253 * 254 * Perform an SMBus "quick read" operation. 255 */ 256int 257iic_smbus_quick_read(i2c_tag_t tag, i2c_addr_t addr, int flags) 258{ 259 260 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, NULL, 0, 261 NULL, 0, flags)); 262} 263 264/* 265 * iic_smbus_quick_write: 266 * 267 * Perform an SMBus "quick write" operation. 268 */ 269int 270iic_smbus_quick_write(i2c_tag_t tag, i2c_addr_t addr, int flags) 271{ 272 273 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, NULL, 0, 274 NULL, 0, flags)); 275} 276 277/* 278 * iic_smbus_block_read: 279 * 280 * Perform an SMBus "block read" operation. 281 */ 282int 283iic_smbus_block_read(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_READ_WITH_STOP, addr, &cmd, 1, 288 vbuf, buflen, flags)); 289} 290 291/* 292 * iic_smbus_block_write: 293 * 294 * Perform an SMBus "block write" operation. 295 */ 296int 297iic_smbus_block_write(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 298 uint8_t *vbuf, size_t buflen, int flags) 299{ 300 301 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &cmd, 1, 302 vbuf, buflen, flags)); 303} 304 305/* 306 * iic_smbus_crc8 307 * 308 * Private helper for calculating packet error checksum 309 */ 310static uint8_t 311iic_smbus_crc8(uint16_t data) 312{ 313 int i; 314 315 for (i = 0; i < 8; i++) { 316 if (data & 0x8000) 317 data = data ^ (0x1070U << 3); 318 data = data << 1; 319 } 320 321 return (uint8_t)(data >> 8); 322} 323 324/* 325 * iic_smbus_pec 326 * 327 * Private function for calculating packet error checking on SMBus 328 * packets. 329 */ 330static uint8_t 331iic_smbus_pec(int count, uint8_t *s, uint8_t *r) 332{ 333 int i; 334 uint8_t crc = 0; 335 336 for (i = 0; i < count; i++) 337 crc = iic_smbus_crc8((crc ^ s[i]) << 8); 338 if (r != NULL) 339 for (i = 0; i <= r[0]; i++) 340 crc = iic_smbus_crc8((crc ^ r[i]) << 8); 341 342 return crc; 343} 344