1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 1998, 2001 Nicolas Souchu 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 /* 31 * I2C to SMB bridge 32 * 33 * Example: 34 * 35 * smb bttv 36 * \ / 37 * smbus 38 * / \ 39 * iicsmb bti2c 40 * | 41 * iicbus 42 * / | \ 43 * iicbb pcf ... 44 * | 45 * lpbb 46 */ 47 48 #include <sys/param.h> 49 #include <sys/bus.h> 50 #include <sys/kernel.h> 51 #include <sys/lock.h> 52 #include <sys/module.h> 53 #include <sys/mutex.h> 54 #include <sys/systm.h> 55 #include <sys/uio.h> 56 57 #include <dev/iicbus/iiconf.h> 58 #include <dev/iicbus/iicbus.h> 59 60 #include <dev/smbus/smb.h> 61 #include <dev/smbus/smbconf.h> 62 63 #include "iicbus_if.h" 64 #include "smbus_if.h" 65 66 struct iicsmb_softc { 67 68 #define SMB_WAITING_ADDR 0x0 69 #define SMB_WAITING_LOW 0x1 70 #define SMB_WAITING_HIGH 0x2 71 #define SMB_DONE 0x3 72 int state; 73 74 u_char devaddr; /* slave device address */ 75 76 char low; /* low byte received first */ 77 char high; /* high byte */ 78 79 struct mtx lock; 80 device_t smbus; 81 }; 82 83 static int iicsmb_probe(device_t); 84 static int iicsmb_attach(device_t); 85 static int iicsmb_detach(device_t); 86 static void iicsmb_identify(driver_t *driver, device_t parent); 87 88 static int iicsmb_intr(device_t dev, int event, char *buf); 89 static int iicsmb_callback(device_t dev, int index, void *data); 90 static int iicsmb_quick(device_t dev, u_char slave, int how); 91 static int iicsmb_sendb(device_t dev, u_char slave, char byte); 92 static int iicsmb_recvb(device_t dev, u_char slave, char *byte); 93 static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 94 static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word); 95 static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 96 static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word); 97 static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 98 static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 99 static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf); 100 101 static device_method_t iicsmb_methods[] = { 102 /* device interface */ 103 DEVMETHOD(device_identify, iicsmb_identify), 104 DEVMETHOD(device_probe, iicsmb_probe), 105 DEVMETHOD(device_attach, iicsmb_attach), 106 DEVMETHOD(device_detach, iicsmb_detach), 107 108 /* iicbus interface */ 109 DEVMETHOD(iicbus_intr, iicsmb_intr), 110 111 /* smbus interface */ 112 DEVMETHOD(smbus_callback, iicsmb_callback), 113 DEVMETHOD(smbus_quick, iicsmb_quick), 114 DEVMETHOD(smbus_sendb, iicsmb_sendb), 115 DEVMETHOD(smbus_recvb, iicsmb_recvb), 116 DEVMETHOD(smbus_writeb, iicsmb_writeb), 117 DEVMETHOD(smbus_writew, iicsmb_writew), 118 DEVMETHOD(smbus_readb, iicsmb_readb), 119 DEVMETHOD(smbus_readw, iicsmb_readw), 120 DEVMETHOD(smbus_pcall, iicsmb_pcall), 121 DEVMETHOD(smbus_bwrite, iicsmb_bwrite), 122 DEVMETHOD(smbus_bread, iicsmb_bread), 123 124 DEVMETHOD_END 125 }; 126 127 static driver_t iicsmb_driver = { 128 "iicsmb", 129 iicsmb_methods, 130 sizeof(struct iicsmb_softc), 131 }; 132 133 static void 134 iicsmb_identify(driver_t *driver, device_t parent) 135 { 136 137 if (device_find_child(parent, "iicsmb", -1) == NULL) 138 BUS_ADD_CHILD(parent, 0, "iicsmb", -1); 139 } 140 141 static int 142 iicsmb_probe(device_t dev) 143 { 144 device_set_desc(dev, "SMBus over I2C bridge"); 145 return (BUS_PROBE_NOWILDCARD); 146 } 147 148 static int 149 iicsmb_attach(device_t dev) 150 { 151 struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); 152 153 mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF); 154 155 sc->smbus = device_add_child(dev, "smbus", -1); 156 157 /* probe and attach the smbus */ 158 bus_generic_attach(dev); 159 160 return (0); 161 } 162 163 static int 164 iicsmb_detach(device_t dev) 165 { 166 struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); 167 168 bus_generic_detach(dev); 169 device_delete_children(dev); 170 mtx_destroy(&sc->lock); 171 172 return (0); 173 } 174 175 /* 176 * iicsmb_intr() 177 * 178 * iicbus interrupt handler 179 */ 180 static int 181 iicsmb_intr(device_t dev, int event, char *buf) 182 { 183 struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); 184 185 mtx_lock(&sc->lock); 186 switch (event) { 187 case INTR_GENERAL: 188 case INTR_START: 189 sc->state = SMB_WAITING_ADDR; 190 break; 191 192 case INTR_STOP: 193 /* call smbus intr handler */ 194 smbus_intr(sc->smbus, sc->devaddr, 195 sc->low, sc->high, SMB_ENOERR); 196 break; 197 198 case INTR_RECEIVE: 199 switch (sc->state) { 200 case SMB_DONE: 201 /* XXX too much data, discard */ 202 printf("%s: too much data from 0x%x\n", __func__, 203 sc->devaddr & 0xff); 204 goto end; 205 206 case SMB_WAITING_ADDR: 207 sc->devaddr = (u_char)*buf; 208 sc->state = SMB_WAITING_LOW; 209 break; 210 211 case SMB_WAITING_LOW: 212 sc->low = *buf; 213 sc->state = SMB_WAITING_HIGH; 214 break; 215 216 case SMB_WAITING_HIGH: 217 sc->high = *buf; 218 sc->state = SMB_DONE; 219 break; 220 } 221 end: 222 break; 223 224 case INTR_TRANSMIT: 225 case INTR_NOACK: 226 break; 227 228 case INTR_ERROR: 229 switch (*buf) { 230 case IIC_EBUSERR: 231 smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR); 232 break; 233 234 default: 235 printf("%s unknown error 0x%x!\n", __func__, 236 (int)*buf); 237 break; 238 } 239 break; 240 241 default: 242 panic("%s: unknown event (%d)!", __func__, event); 243 } 244 mtx_unlock(&sc->lock); 245 246 return (0); 247 } 248 249 static int 250 iicsmb_callback(device_t dev, int index, void *data) 251 { 252 device_t parent = device_get_parent(dev); 253 int error = 0; 254 int how; 255 256 switch (index) { 257 case SMB_REQUEST_BUS: 258 /* request underlying iicbus */ 259 how = *(int *)data; 260 error = iicbus_request_bus(parent, dev, how); 261 break; 262 263 case SMB_RELEASE_BUS: 264 /* release underlying iicbus */ 265 error = iicbus_release_bus(parent, dev); 266 break; 267 268 default: 269 error = EINVAL; 270 } 271 272 return (error); 273 } 274 275 static int 276 iic2smb_error(int error) 277 { 278 switch (error) { 279 case IIC_NOERR: 280 return (SMB_ENOERR); 281 case IIC_EBUSERR: 282 return (SMB_EBUSERR); 283 case IIC_ENOACK: 284 return (SMB_ENOACK); 285 case IIC_ETIMEOUT: 286 return (SMB_ETIMEOUT); 287 case IIC_EBUSBSY: 288 return (SMB_EBUSY); 289 case IIC_ESTATUS: 290 return (SMB_EBUSERR); 291 case IIC_EUNDERFLOW: 292 return (SMB_EBUSERR); 293 case IIC_EOVERFLOW: 294 return (SMB_EBUSERR); 295 case IIC_ENOTSUPP: 296 return (SMB_ENOTSUPP); 297 case IIC_ENOADDR: 298 return (SMB_EBUSERR); 299 case IIC_ERESOURCE: 300 return (SMB_EBUSERR); 301 default: 302 return (SMB_EBUSERR); 303 } 304 } 305 306 #define TRANSFER_MSGS(dev, msgs) iicbus_transfer(dev, msgs, nitems(msgs)) 307 308 static int 309 iicsmb_quick(device_t dev, u_char slave, int how) 310 { 311 struct iic_msg msgs[] = { 312 { slave, how == SMB_QWRITE ? IIC_M_WR : IIC_M_RD, 0, NULL }, 313 }; 314 int error; 315 316 switch (how) { 317 case SMB_QWRITE: 318 case SMB_QREAD: 319 break; 320 default: 321 return (SMB_EINVAL); 322 } 323 324 error = TRANSFER_MSGS(dev, msgs); 325 return (iic2smb_error(error)); 326 } 327 328 static int 329 iicsmb_sendb(device_t dev, u_char slave, char byte) 330 { 331 struct iic_msg msgs[] = { 332 { slave, IIC_M_WR, 1, &byte }, 333 }; 334 int error; 335 336 error = TRANSFER_MSGS(dev, msgs); 337 return (iic2smb_error(error)); 338 } 339 340 static int 341 iicsmb_recvb(device_t dev, u_char slave, char *byte) 342 { 343 struct iic_msg msgs[] = { 344 { slave, IIC_M_RD, 1, byte }, 345 }; 346 int error; 347 348 error = TRANSFER_MSGS(dev, msgs); 349 return (iic2smb_error(error)); 350 } 351 352 static int 353 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 354 { 355 uint8_t bytes[] = { cmd, byte }; 356 struct iic_msg msgs[] = { 357 { slave, IIC_M_WR, nitems(bytes), bytes }, 358 }; 359 int error; 360 361 error = TRANSFER_MSGS(dev, msgs); 362 return (iic2smb_error(error)); 363 } 364 365 static int 366 iicsmb_writew(device_t dev, u_char slave, char cmd, short word) 367 { 368 uint8_t bytes[] = { cmd, word & 0xff, word >> 8 }; 369 struct iic_msg msgs[] = { 370 { slave, IIC_M_WR, nitems(bytes), bytes }, 371 }; 372 int error; 373 374 error = TRANSFER_MSGS(dev, msgs); 375 return (iic2smb_error(error)); 376 } 377 378 static int 379 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 380 { 381 struct iic_msg msgs[] = { 382 { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd }, 383 { slave, IIC_M_RD, 1, byte }, 384 }; 385 int error; 386 387 error = TRANSFER_MSGS(dev, msgs); 388 return (iic2smb_error(error)); 389 } 390 391 static int 392 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word) 393 { 394 uint8_t buf[2]; 395 struct iic_msg msgs[] = { 396 { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd }, 397 { slave, IIC_M_RD, nitems(buf), buf }, 398 }; 399 int error; 400 401 error = TRANSFER_MSGS(dev, msgs); 402 if (error == 0) 403 *word = ((uint16_t)buf[1] << 8) | buf[0]; 404 return (iic2smb_error(error)); 405 } 406 407 static int 408 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 409 { 410 uint8_t in[3] = { cmd, sdata & 0xff, sdata >> 8 }; 411 uint8_t out[2]; 412 struct iic_msg msgs[] = { 413 { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(in), in }, 414 { slave, IIC_M_RD, nitems(out), out }, 415 }; 416 int error; 417 418 error = TRANSFER_MSGS(dev, msgs); 419 if (error == 0) 420 *rdata = ((uint16_t)out[1] << 8) | out[0]; 421 return (iic2smb_error(error)); 422 } 423 424 static int 425 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 426 { 427 uint8_t bytes[2] = { cmd, count }; 428 struct iic_msg msgs[] = { 429 { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes }, 430 { slave, IIC_M_WR | IIC_M_NOSTART, count, buf }, 431 }; 432 int error; 433 434 if (count > SMB_MAXBLOCKSIZE || count == 0) 435 return (SMB_EINVAL); 436 error = TRANSFER_MSGS(dev, msgs); 437 return (iic2smb_error(error)); 438 } 439 440 static int 441 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 442 { 443 struct iic_msg msgs[] = { 444 { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd }, 445 { slave, IIC_M_RD | IIC_M_NOSTOP, 1, count }, 446 }; 447 struct iic_msg block_msg[] = { 448 { slave, IIC_M_RD | IIC_M_NOSTART, 0, buf }, 449 }; 450 device_t parent = device_get_parent(dev); 451 int error; 452 453 /* Have to do this because the command is split in two transfers. */ 454 error = iicbus_request_bus(parent, dev, IIC_WAIT | IIC_RECURSIVE); 455 if (error == 0) 456 error = TRANSFER_MSGS(dev, msgs); 457 if (error == 0) { 458 /* 459 * If the slave offers an empty or a too long reply, 460 * read one byte to generate the stop or abort. 461 */ 462 if (*count > SMB_MAXBLOCKSIZE || *count == 0) 463 block_msg[0].len = 1; 464 else 465 block_msg[0].len = *count; 466 error = TRANSFER_MSGS(dev, block_msg); 467 if (*count > SMB_MAXBLOCKSIZE || *count == 0) 468 error = SMB_EINVAL; 469 } 470 (void)iicbus_release_bus(parent, dev); 471 return (iic2smb_error(error)); 472 } 473 474 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, 0, 0); 475 DRIVER_MODULE(smbus, iicsmb, smbus_driver, 0, 0); 476 MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 477 MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 478 MODULE_VERSION(iicsmb, 1); 479