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", DEVICE_UNIT_ANY); 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", DEVICE_UNIT_ANY); 156 157 /* probe and attach the smbus */ 158 bus_attach_children(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 mtx_destroy(&sc->lock); 170 171 return (0); 172 } 173 174 /* 175 * iicsmb_intr() 176 * 177 * iicbus interrupt handler 178 */ 179 static int 180 iicsmb_intr(device_t dev, int event, char *buf) 181 { 182 struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); 183 184 mtx_lock(&sc->lock); 185 switch (event) { 186 case INTR_GENERAL: 187 case INTR_START: 188 sc->state = SMB_WAITING_ADDR; 189 break; 190 191 case INTR_STOP: 192 /* call smbus intr handler */ 193 smbus_intr(sc->smbus, sc->devaddr, 194 sc->low, sc->high, SMB_ENOERR); 195 break; 196 197 case INTR_RECEIVE: 198 switch (sc->state) { 199 case SMB_DONE: 200 /* XXX too much data, discard */ 201 printf("%s: too much data from 0x%x\n", __func__, 202 sc->devaddr & 0xff); 203 goto end; 204 205 case SMB_WAITING_ADDR: 206 sc->devaddr = (u_char)*buf; 207 sc->state = SMB_WAITING_LOW; 208 break; 209 210 case SMB_WAITING_LOW: 211 sc->low = *buf; 212 sc->state = SMB_WAITING_HIGH; 213 break; 214 215 case SMB_WAITING_HIGH: 216 sc->high = *buf; 217 sc->state = SMB_DONE; 218 break; 219 } 220 end: 221 break; 222 223 case INTR_TRANSMIT: 224 case INTR_NOACK: 225 break; 226 227 case INTR_ERROR: 228 switch (*buf) { 229 case IIC_EBUSERR: 230 smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR); 231 break; 232 233 default: 234 printf("%s unknown error 0x%x!\n", __func__, 235 (int)*buf); 236 break; 237 } 238 break; 239 240 default: 241 panic("%s: unknown event (%d)!", __func__, event); 242 } 243 mtx_unlock(&sc->lock); 244 245 return (0); 246 } 247 248 static int 249 iicsmb_callback(device_t dev, int index, void *data) 250 { 251 device_t parent = device_get_parent(dev); 252 int error = 0; 253 int how; 254 255 switch (index) { 256 case SMB_REQUEST_BUS: 257 /* request underlying iicbus */ 258 how = *(int *)data; 259 error = iicbus_request_bus(parent, dev, how); 260 break; 261 262 case SMB_RELEASE_BUS: 263 /* release underlying iicbus */ 264 error = iicbus_release_bus(parent, dev); 265 break; 266 267 default: 268 error = EINVAL; 269 } 270 271 return (error); 272 } 273 274 static int 275 iic2smb_error(int error) 276 { 277 switch (error) { 278 case IIC_NOERR: 279 return (SMB_ENOERR); 280 case IIC_EBUSERR: 281 return (SMB_EBUSERR); 282 case IIC_ENOACK: 283 return (SMB_ENOACK); 284 case IIC_ETIMEOUT: 285 return (SMB_ETIMEOUT); 286 case IIC_EBUSBSY: 287 return (SMB_EBUSY); 288 case IIC_ESTATUS: 289 return (SMB_EBUSERR); 290 case IIC_EUNDERFLOW: 291 return (SMB_EBUSERR); 292 case IIC_EOVERFLOW: 293 return (SMB_EBUSERR); 294 case IIC_ENOTSUPP: 295 return (SMB_ENOTSUPP); 296 case IIC_ENOADDR: 297 return (SMB_EBUSERR); 298 case IIC_ERESOURCE: 299 return (SMB_EBUSERR); 300 default: 301 return (SMB_EBUSERR); 302 } 303 } 304 305 #define TRANSFER_MSGS(dev, msgs) iicbus_transfer(dev, msgs, nitems(msgs)) 306 307 static int 308 iicsmb_quick(device_t dev, u_char slave, int how) 309 { 310 struct iic_msg msgs[] = { 311 { slave, how == SMB_QWRITE ? IIC_M_WR : IIC_M_RD, 0, NULL }, 312 }; 313 int error; 314 315 switch (how) { 316 case SMB_QWRITE: 317 case SMB_QREAD: 318 break; 319 default: 320 return (SMB_EINVAL); 321 } 322 323 error = TRANSFER_MSGS(dev, msgs); 324 return (iic2smb_error(error)); 325 } 326 327 static int 328 iicsmb_sendb(device_t dev, u_char slave, char byte) 329 { 330 struct iic_msg msgs[] = { 331 { slave, IIC_M_WR, 1, &byte }, 332 }; 333 int error; 334 335 error = TRANSFER_MSGS(dev, msgs); 336 return (iic2smb_error(error)); 337 } 338 339 static int 340 iicsmb_recvb(device_t dev, u_char slave, char *byte) 341 { 342 struct iic_msg msgs[] = { 343 { slave, IIC_M_RD, 1, byte }, 344 }; 345 int error; 346 347 error = TRANSFER_MSGS(dev, msgs); 348 return (iic2smb_error(error)); 349 } 350 351 static int 352 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 353 { 354 uint8_t bytes[] = { cmd, byte }; 355 struct iic_msg msgs[] = { 356 { slave, IIC_M_WR, nitems(bytes), bytes }, 357 }; 358 int error; 359 360 error = TRANSFER_MSGS(dev, msgs); 361 return (iic2smb_error(error)); 362 } 363 364 static int 365 iicsmb_writew(device_t dev, u_char slave, char cmd, short word) 366 { 367 uint8_t bytes[] = { cmd, word & 0xff, word >> 8 }; 368 struct iic_msg msgs[] = { 369 { slave, IIC_M_WR, nitems(bytes), bytes }, 370 }; 371 int error; 372 373 error = TRANSFER_MSGS(dev, msgs); 374 return (iic2smb_error(error)); 375 } 376 377 static int 378 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 379 { 380 struct iic_msg msgs[] = { 381 { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd }, 382 { slave, IIC_M_RD, 1, byte }, 383 }; 384 int error; 385 386 error = TRANSFER_MSGS(dev, msgs); 387 return (iic2smb_error(error)); 388 } 389 390 static int 391 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word) 392 { 393 uint8_t buf[2]; 394 struct iic_msg msgs[] = { 395 { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd }, 396 { slave, IIC_M_RD, nitems(buf), buf }, 397 }; 398 int error; 399 400 error = TRANSFER_MSGS(dev, msgs); 401 if (error == 0) 402 *word = ((uint16_t)buf[1] << 8) | buf[0]; 403 return (iic2smb_error(error)); 404 } 405 406 static int 407 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 408 { 409 uint8_t in[3] = { cmd, sdata & 0xff, sdata >> 8 }; 410 uint8_t out[2]; 411 struct iic_msg msgs[] = { 412 { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(in), in }, 413 { slave, IIC_M_RD, nitems(out), out }, 414 }; 415 int error; 416 417 error = TRANSFER_MSGS(dev, msgs); 418 if (error == 0) 419 *rdata = ((uint16_t)out[1] << 8) | out[0]; 420 return (iic2smb_error(error)); 421 } 422 423 static int 424 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 425 { 426 uint8_t bytes[2] = { cmd, count }; 427 struct iic_msg msgs[] = { 428 { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes }, 429 { slave, IIC_M_WR | IIC_M_NOSTART, count, buf }, 430 }; 431 int error; 432 433 if (count > SMB_MAXBLOCKSIZE || count == 0) 434 return (SMB_EINVAL); 435 error = TRANSFER_MSGS(dev, msgs); 436 return (iic2smb_error(error)); 437 } 438 439 static int 440 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 441 { 442 struct iic_msg msgs[] = { 443 { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd }, 444 { slave, IIC_M_RD | IIC_M_NOSTOP, 1, count }, 445 }; 446 struct iic_msg block_msg[] = { 447 { slave, IIC_M_RD | IIC_M_NOSTART, 0, buf }, 448 }; 449 device_t parent = device_get_parent(dev); 450 int error; 451 452 /* Have to do this because the command is split in two transfers. */ 453 error = iicbus_request_bus(parent, dev, IIC_WAIT | IIC_RECURSIVE); 454 if (error == 0) 455 error = TRANSFER_MSGS(dev, msgs); 456 if (error == 0) { 457 /* 458 * If the slave offers an empty or a too long reply, 459 * read one byte to generate the stop or abort. 460 */ 461 if (*count > SMB_MAXBLOCKSIZE || *count == 0) 462 block_msg[0].len = 1; 463 else 464 block_msg[0].len = *count; 465 error = TRANSFER_MSGS(dev, block_msg); 466 if (*count > SMB_MAXBLOCKSIZE || *count == 0) 467 error = SMB_EINVAL; 468 } 469 (void)iicbus_release_bus(parent, dev); 470 return (iic2smb_error(error)); 471 } 472 473 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, 0, 0); 474 DRIVER_MODULE(smbus, iicsmb, smbus_driver, 0, 0); 475 MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 476 MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 477 MODULE_VERSION(iicsmb, 1); 478