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