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