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