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