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