1 /* $NetBSD: i2c_exec.c,v 1.21 2025/09/23 12:37:43 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.21 2025/09/23 12:37:43 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 tag->ic_channel = I2C_CHANNEL_DEFAULT; 77 } 78 79 /* 80 * iic_tag_fini: 81 * 82 * Teardown of the i2c controller tag. 83 */ 84 void 85 iic_tag_fini(i2c_tag_t tag) 86 { 87 88 mutex_destroy(&tag->ic_bus_lock); 89 } 90 91 /* 92 * iic_acquire_bus_lock -- 93 * 94 * Acquire an i2c bus lock. Used by iic_acquire_bus() and other 95 * places that need to acquire an i2c-related lock with the same 96 * logic. 97 */ 98 int 99 iic_acquire_bus_lock(kmutex_t *lock, int flags) 100 { 101 if (flags & I2C_F_POLL) { 102 /* 103 * Polling should only be used in rare and/or 104 * extreme circumstances; most i2c clients should 105 * be allowed to sleep. 106 * 107 * Really, the ONLY user of I2C_F_POLL should be 108 * "when cold", i.e. during early autoconfiguration 109 * when there is only proc0, and we might have to 110 * read SEEPROMs, etc. There should be no other 111 * users interfering with our access of the i2c bus 112 * in that case. 113 */ 114 if (mutex_tryenter(lock) == 0) { 115 return EBUSY; 116 } 117 } else { 118 /* 119 * N.B. We implement this as a mutex that we hold across 120 * across a series of requests beause we'd like to get the 121 * priority boost if a higher-priority process wants the 122 * i2c bus while we're asleep waiting for the controller 123 * to perform the I/O. 124 * 125 * XXXJRT Disable preemption here? We'd like to keep the 126 * CPU while holding this resource, unless we release it 127 * voluntarily (which should only happen while waiting for 128 * a controller to complete I/O). 129 */ 130 mutex_enter(lock); 131 } 132 133 return 0; 134 } 135 136 /* 137 * iic_release_bus_lock -- 138 * 139 * Release a previously-acquired i2c-related bus lock. 140 */ 141 void 142 iic_release_bus_lock(kmutex_t *lock) 143 { 144 mutex_exit(lock); 145 } 146 147 /* 148 * iic_acquire_bus: 149 * 150 * Acquire the I2C bus for use by a client. 151 */ 152 int 153 iic_acquire_bus(i2c_tag_t tag, int flags) 154 { 155 #if 0 /* XXX Not quite ready for this yet. */ 156 KASSERT(!cpu_intr_p()); 157 #endif 158 159 flags = iic_op_flags(flags); 160 161 int error = iic_acquire_bus_lock(&tag->ic_bus_lock, flags); 162 if (error) { 163 return error; 164 } 165 166 if (tag->ic_acquire_bus) { 167 error = (*tag->ic_acquire_bus)(tag->ic_cookie, flags); 168 } 169 170 if (__predict_false(error)) { 171 iic_release_bus_lock(&tag->ic_bus_lock); 172 } 173 174 return error; 175 } 176 177 /* 178 * iic_release_bus: 179 * 180 * Release the I2C bus, allowing another client to use it. 181 */ 182 void 183 iic_release_bus(i2c_tag_t tag, int flags) 184 { 185 #if 0 /* XXX Not quite ready for this yet. */ 186 KASSERT(!cpu_intr_p()); 187 #endif 188 189 flags = iic_op_flags(flags); 190 191 if (tag->ic_release_bus) { 192 (*tag->ic_release_bus)(tag->ic_cookie, flags); 193 } 194 195 iic_release_bus_lock(&tag->ic_bus_lock); 196 } 197 198 /* 199 * iic_exec: 200 * 201 * Simplified I2C client interface engine. 202 * 203 * This and the SMBus routines are the preferred interface for 204 * client access to I2C/SMBus, since many automated controllers 205 * do not provide access to the low-level primitives of the I2C 206 * bus protocol. 207 */ 208 int 209 iic_exec(i2c_tag_t tag, i2c_op_t op, i2c_addr_t addr, const void *vcmd, 210 size_t cmdlen, void *vbuf, size_t buflen, int flags) 211 { 212 const uint8_t *cmd = vcmd; 213 uint8_t *buf = vbuf; 214 int error; 215 size_t len; 216 217 #if 0 /* XXX Not quite ready for this yet. */ 218 KASSERT(!cpu_intr_p()); 219 #endif 220 221 flags = iic_op_flags(flags); 222 223 if ((flags & I2C_F_PEC) && cmdlen > 0 && tag->ic_exec != NULL) { 224 uint8_t data[33]; /* XXX */ 225 uint8_t b[3]; 226 227 b[0] = addr << 1; 228 b[1] = cmd[0]; 229 230 switch (buflen) { 231 case 0: 232 data[0] = iic_smbus_pec(2, b, NULL); 233 buflen++; 234 break; 235 case 1: 236 b[2] = buf[0]; 237 data[0] = iic_smbus_pec(3, b, NULL); 238 data[1] = b[2]; 239 buflen++; 240 break; 241 case 2: 242 break; 243 default: 244 KASSERT(buflen+1 < sizeof(data)); 245 memcpy(data, vbuf, buflen); 246 data[buflen] = iic_smbus_pec(2, b, data); 247 buflen++; 248 break; 249 } 250 251 return ((*tag->ic_exec)(tag->ic_cookie, op, addr, cmd, 252 cmdlen, data, buflen, flags)); 253 } 254 255 /* 256 * Defer to the controller if it provides an exec function. Use 257 * it if it does. 258 */ 259 if (tag->ic_exec != NULL) 260 return ((*tag->ic_exec)(tag->ic_cookie, op, addr, cmd, 261 cmdlen, buf, buflen, flags)); 262 263 if ((len = cmdlen) != 0) { 264 if ((error = iic_initiate_xfer(tag, addr, flags)) != 0) 265 goto bad; 266 while (len--) { 267 if ((error = iic_write_byte(tag, *cmd++, flags)) != 0) 268 goto bad; 269 } 270 } else if (buflen == 0) { 271 /* 272 * This is a quick_read()/quick_write() command with 273 * neither command nor data bytes 274 */ 275 if (I2C_OP_STOP_P(op)) 276 flags |= I2C_F_STOP; 277 if (I2C_OP_READ_P(op)) 278 flags |= I2C_F_READ; 279 if ((error = iic_initiate_xfer(tag, addr, flags)) != 0) 280 goto bad; 281 } 282 283 if (I2C_OP_READ_P(op)) 284 flags |= I2C_F_READ; 285 286 len = buflen; 287 while (len--) { 288 if (len == 0 && I2C_OP_STOP_P(op)) 289 flags |= I2C_F_STOP; 290 if (I2C_OP_READ_P(op)) { 291 /* Send REPEATED START. */ 292 if ((len + 1) == buflen && 293 (error = iic_initiate_xfer(tag, addr, flags)) != 0) 294 goto bad; 295 /* NACK on last byte. */ 296 if (len == 0) 297 flags |= I2C_F_LAST; 298 if ((error = iic_read_byte(tag, buf++, flags)) != 0) 299 goto bad; 300 } else { 301 /* Maybe send START. */ 302 if ((len + 1) == buflen && cmdlen == 0 && 303 (error = iic_initiate_xfer(tag, addr, flags)) != 0) 304 goto bad; 305 if ((error = iic_write_byte(tag, *buf++, flags)) != 0) 306 goto bad; 307 } 308 } 309 310 return (0); 311 bad: 312 iic_send_stop(tag, flags); 313 return (error); 314 } 315 316 /* 317 * iic_smbus_write_byte: 318 * 319 * Perform an SMBus "write byte" operation. 320 */ 321 int 322 iic_smbus_write_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 323 uint8_t val, int flags) 324 { 325 326 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &cmd, 1, 327 &val, 1, flags)); 328 } 329 330 /* 331 * iic_smbus_write_word: 332 * 333 * Perform an SMBus "write word" operation. 334 */ 335 int 336 iic_smbus_write_word(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 337 uint16_t val, int flags) 338 { 339 uint8_t vbuf[2]; 340 341 vbuf[0] = val & 0xff; 342 vbuf[1] = (val >> 8) & 0xff; 343 344 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &cmd, 1, 345 vbuf, 2, flags)); 346 } 347 348 /* 349 * iic_smbus_read_byte: 350 * 351 * Perform an SMBus "read byte" operation. 352 */ 353 int 354 iic_smbus_read_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 355 uint8_t *valp, int flags) 356 { 357 358 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &cmd, 1, 359 valp, 1, flags)); 360 } 361 362 /* 363 * iic_smbus_read_word: 364 * 365 * Perform an SMBus "read word" operation. 366 */ 367 int 368 iic_smbus_read_word(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 369 uint16_t *valp, int flags) 370 { 371 372 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &cmd, 1, 373 (uint8_t *)valp, 2, flags)); 374 } 375 376 /* 377 * iic_smbus_receive_byte: 378 * 379 * Perform an SMBus "receive byte" operation. 380 */ 381 int 382 iic_smbus_receive_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t *valp, 383 int flags) 384 { 385 386 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, NULL, 0, 387 valp, 1, flags)); 388 } 389 390 /* 391 * iic_smbus_send_byte: 392 * 393 * Perform an SMBus "send byte" operation. 394 */ 395 int 396 iic_smbus_send_byte(i2c_tag_t tag, i2c_addr_t addr, uint8_t val, int flags) 397 { 398 399 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, NULL, 0, 400 &val, 1, flags)); 401 } 402 403 /* 404 * iic_smbus_quick_read: 405 * 406 * Perform an SMBus "quick read" operation. 407 */ 408 int 409 iic_smbus_quick_read(i2c_tag_t tag, i2c_addr_t addr, int flags) 410 { 411 412 return (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, NULL, 0, 413 NULL, 0, flags)); 414 } 415 416 /* 417 * iic_smbus_quick_write: 418 * 419 * Perform an SMBus "quick write" operation. 420 */ 421 int 422 iic_smbus_quick_write(i2c_tag_t tag, i2c_addr_t addr, int flags) 423 { 424 425 return (iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, NULL, 0, 426 NULL, 0, flags)); 427 } 428 429 /* 430 * iic_smbus_block_read: 431 * 432 * Perform an SMBus "block read" operation. 433 */ 434 int 435 iic_smbus_block_read(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 436 uint8_t *vbuf, size_t buflen, int flags) 437 { 438 439 return (iic_exec(tag, I2C_OP_READ_BLOCK, addr, &cmd, 1, 440 vbuf, buflen, flags)); 441 } 442 443 /* 444 * iic_smbus_block_write: 445 * 446 * Perform an SMBus "block write" operation. 447 */ 448 int 449 iic_smbus_block_write(i2c_tag_t tag, i2c_addr_t addr, uint8_t cmd, 450 uint8_t *vbuf, size_t buflen, int flags) 451 { 452 453 return (iic_exec(tag, I2C_OP_WRITE_BLOCK, addr, &cmd, 1, 454 vbuf, buflen, flags)); 455 } 456 457 /* 458 * iic_smbus_crc8 459 * 460 * Private helper for calculating packet error checksum 461 */ 462 static uint8_t 463 iic_smbus_crc8(uint16_t data) 464 { 465 int i; 466 467 for (i = 0; i < 8; i++) { 468 if (data & 0x8000) 469 data = data ^ (0x1070U << 3); 470 data = data << 1; 471 } 472 473 return (uint8_t)(data >> 8); 474 } 475 476 /* 477 * iic_smbus_pec 478 * 479 * Private function for calculating packet error checking on SMBus 480 * packets. 481 */ 482 static uint8_t 483 iic_smbus_pec(int count, uint8_t *s, uint8_t *r) 484 { 485 int i; 486 uint8_t crc = 0; 487 488 for (i = 0; i < count; i++) 489 crc = iic_smbus_crc8((crc ^ s[i]) << 8); 490 if (r != NULL) 491 for (i = 0; i <= r[0]; i++) 492 crc = iic_smbus_crc8((crc ^ r[i]) << 8); 493 494 return crc; 495 } 496 497 MODULE(MODULE_CLASS_MISC, i2cexec, NULL); 498 499 static int 500 i2cexec_modcmd(modcmd_t cmd, void *opaque) 501 { 502 switch (cmd) { 503 case MODULE_CMD_INIT: 504 case MODULE_CMD_FINI: 505 return 0; 506 break; 507 default: 508 return ENOTTY; 509 } 510 } 511