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