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