1c3e2dc6bSNicolas Souchu /*- 2c17d4340SNicolas Souchu * Copyright (c) 1998, 2001 Nicolas Souchu 3c3e2dc6bSNicolas Souchu * All rights reserved. 4c3e2dc6bSNicolas Souchu * 5c3e2dc6bSNicolas Souchu * Redistribution and use in source and binary forms, with or without 6c3e2dc6bSNicolas Souchu * modification, are permitted provided that the following conditions 7c3e2dc6bSNicolas Souchu * are met: 8c3e2dc6bSNicolas Souchu * 1. Redistributions of source code must retain the above copyright 9c3e2dc6bSNicolas Souchu * notice, this list of conditions and the following disclaimer. 10c3e2dc6bSNicolas Souchu * 2. Redistributions in binary form must reproduce the above copyright 11c3e2dc6bSNicolas Souchu * notice, this list of conditions and the following disclaimer in the 12c3e2dc6bSNicolas Souchu * documentation and/or other materials provided with the distribution. 13c3e2dc6bSNicolas Souchu * 14c3e2dc6bSNicolas Souchu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15c3e2dc6bSNicolas Souchu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16c3e2dc6bSNicolas Souchu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17c3e2dc6bSNicolas Souchu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18c3e2dc6bSNicolas Souchu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19c3e2dc6bSNicolas Souchu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20c3e2dc6bSNicolas Souchu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21c3e2dc6bSNicolas Souchu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22c3e2dc6bSNicolas Souchu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23c3e2dc6bSNicolas Souchu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24c3e2dc6bSNicolas Souchu * SUCH DAMAGE. 25c3e2dc6bSNicolas Souchu */ 26c3e2dc6bSNicolas Souchu 27*4b7ec270SMarius Strobl #include <sys/cdefs.h> 28*4b7ec270SMarius Strobl __FBSDID("$FreeBSD$"); 29*4b7ec270SMarius Strobl 30c3e2dc6bSNicolas Souchu /* 31c3e2dc6bSNicolas Souchu * I2C to SMB bridge 3204f89a63SNicolas Souchu * 3304f89a63SNicolas Souchu * Example: 3404f89a63SNicolas Souchu * 3504f89a63SNicolas Souchu * smb bttv 3604f89a63SNicolas Souchu * \ / 3704f89a63SNicolas Souchu * smbus 3804f89a63SNicolas Souchu * / \ 3904f89a63SNicolas Souchu * iicsmb bti2c 4004f89a63SNicolas Souchu * | 4104f89a63SNicolas Souchu * iicbus 4204f89a63SNicolas Souchu * / | \ 4304f89a63SNicolas Souchu * iicbb pcf ... 4404f89a63SNicolas Souchu * | 4504f89a63SNicolas Souchu * lpbb 46c3e2dc6bSNicolas Souchu */ 47c3e2dc6bSNicolas Souchu 48c3e2dc6bSNicolas Souchu #include <sys/param.h> 49c3e2dc6bSNicolas Souchu #include <sys/bus.h> 500df8f081SJohn Baldwin #include <sys/kernel.h> 510df8f081SJohn Baldwin #include <sys/lock.h> 520df8f081SJohn Baldwin #include <sys/module.h> 530df8f081SJohn Baldwin #include <sys/mutex.h> 540df8f081SJohn Baldwin #include <sys/systm.h> 55c3e2dc6bSNicolas Souchu #include <sys/uio.h> 56c3e2dc6bSNicolas Souchu 57c3e2dc6bSNicolas Souchu #include <dev/iicbus/iiconf.h> 58c3e2dc6bSNicolas Souchu #include <dev/iicbus/iicbus.h> 59c3e2dc6bSNicolas Souchu 60c3e2dc6bSNicolas Souchu #include <dev/smbus/smbconf.h> 61c3e2dc6bSNicolas Souchu 62c3e2dc6bSNicolas Souchu #include "iicbus_if.h" 63c3e2dc6bSNicolas Souchu #include "smbus_if.h" 64c3e2dc6bSNicolas Souchu 65c3e2dc6bSNicolas Souchu struct iicsmb_softc { 66c3e2dc6bSNicolas Souchu 67c3e2dc6bSNicolas Souchu #define SMB_WAITING_ADDR 0x0 68c3e2dc6bSNicolas Souchu #define SMB_WAITING_LOW 0x1 69c3e2dc6bSNicolas Souchu #define SMB_WAITING_HIGH 0x2 70c3e2dc6bSNicolas Souchu #define SMB_DONE 0x3 71c3e2dc6bSNicolas Souchu int state; 72c3e2dc6bSNicolas Souchu 73c3e2dc6bSNicolas Souchu u_char devaddr; /* slave device address */ 74c3e2dc6bSNicolas Souchu 75c3e2dc6bSNicolas Souchu char low; /* low byte received first */ 76c3e2dc6bSNicolas Souchu char high; /* high byte */ 77c3e2dc6bSNicolas Souchu 78313f8941SJohn Baldwin struct mtx lock; 79c3e2dc6bSNicolas Souchu device_t smbus; 80c3e2dc6bSNicolas Souchu }; 81c3e2dc6bSNicolas Souchu 82c3e2dc6bSNicolas Souchu static int iicsmb_probe(device_t); 83c3e2dc6bSNicolas Souchu static int iicsmb_attach(device_t); 84c17d4340SNicolas Souchu static int iicsmb_detach(device_t); 85c17d4340SNicolas Souchu static void iicsmb_identify(driver_t *driver, device_t parent); 86c3e2dc6bSNicolas Souchu 87b23193a5SWarner Losh static int iicsmb_intr(device_t dev, int event, char *buf); 887048a99cSJohn Baldwin static int iicsmb_callback(device_t dev, int index, void *data); 89c3e2dc6bSNicolas Souchu static int iicsmb_quick(device_t dev, u_char slave, int how); 90c3e2dc6bSNicolas Souchu static int iicsmb_sendb(device_t dev, u_char slave, char byte); 91c3e2dc6bSNicolas Souchu static int iicsmb_recvb(device_t dev, u_char slave, char *byte); 92c3e2dc6bSNicolas Souchu static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte); 93c3e2dc6bSNicolas Souchu static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word); 94c3e2dc6bSNicolas Souchu static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte); 95c3e2dc6bSNicolas Souchu static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word); 96c3e2dc6bSNicolas Souchu static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); 97c3e2dc6bSNicolas Souchu static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); 987048a99cSJohn Baldwin static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf); 99c3e2dc6bSNicolas Souchu 100c3e2dc6bSNicolas Souchu static devclass_t iicsmb_devclass; 101c3e2dc6bSNicolas Souchu 102c3e2dc6bSNicolas Souchu static device_method_t iicsmb_methods[] = { 103c3e2dc6bSNicolas Souchu /* device interface */ 104c17d4340SNicolas Souchu DEVMETHOD(device_identify, iicsmb_identify), 105c3e2dc6bSNicolas Souchu DEVMETHOD(device_probe, iicsmb_probe), 106c3e2dc6bSNicolas Souchu DEVMETHOD(device_attach, iicsmb_attach), 107c17d4340SNicolas Souchu DEVMETHOD(device_detach, iicsmb_detach), 108c3e2dc6bSNicolas Souchu 109c3e2dc6bSNicolas Souchu /* iicbus interface */ 110c3e2dc6bSNicolas Souchu DEVMETHOD(iicbus_intr, iicsmb_intr), 111c3e2dc6bSNicolas Souchu 112c3e2dc6bSNicolas Souchu /* smbus interface */ 11304f89a63SNicolas Souchu DEVMETHOD(smbus_callback, iicsmb_callback), 114c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_quick, iicsmb_quick), 115c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_sendb, iicsmb_sendb), 116c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_recvb, iicsmb_recvb), 117c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_writeb, iicsmb_writeb), 118c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_writew, iicsmb_writew), 119c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_readb, iicsmb_readb), 120c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_readw, iicsmb_readw), 121c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_pcall, iicsmb_pcall), 122c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_bwrite, iicsmb_bwrite), 123c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_bread, iicsmb_bread), 124c3e2dc6bSNicolas Souchu 125*4b7ec270SMarius Strobl DEVMETHOD_END 126c3e2dc6bSNicolas Souchu }; 127c3e2dc6bSNicolas Souchu 128c3e2dc6bSNicolas Souchu static driver_t iicsmb_driver = { 129c3e2dc6bSNicolas Souchu "iicsmb", 130c3e2dc6bSNicolas Souchu iicsmb_methods, 131c3e2dc6bSNicolas Souchu sizeof(struct iicsmb_softc), 132c3e2dc6bSNicolas Souchu }; 133c3e2dc6bSNicolas Souchu 134c17d4340SNicolas Souchu #define IICBUS_TIMEOUT 100 /* us */ 135c17d4340SNicolas Souchu 136c17d4340SNicolas Souchu static void 137c17d4340SNicolas Souchu iicsmb_identify(driver_t *driver, device_t parent) 138c17d4340SNicolas Souchu { 139313f8941SJohn Baldwin 140313f8941SJohn Baldwin if (device_find_child(parent, "iicsmb", -1) == NULL) 141b08a61b0SBernd Walter BUS_ADD_CHILD(parent, 0, "iicsmb", -1); 142c17d4340SNicolas Souchu } 143c17d4340SNicolas Souchu 144c3e2dc6bSNicolas Souchu static int 145c3e2dc6bSNicolas Souchu iicsmb_probe(device_t dev) 146c3e2dc6bSNicolas Souchu { 147c17d4340SNicolas Souchu device_set_desc(dev, "SMBus over I2C bridge"); 148789c4b9dSNathan Whitehorn return (BUS_PROBE_NOWILDCARD); 149c3e2dc6bSNicolas Souchu } 150c3e2dc6bSNicolas Souchu 151c3e2dc6bSNicolas Souchu static int 152c3e2dc6bSNicolas Souchu iicsmb_attach(device_t dev) 153c3e2dc6bSNicolas Souchu { 154c3e2dc6bSNicolas Souchu struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); 155c3e2dc6bSNicolas Souchu 156313f8941SJohn Baldwin mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF); 157313f8941SJohn Baldwin 158c17d4340SNicolas Souchu sc->smbus = device_add_child(dev, "smbus", -1); 159c17d4340SNicolas Souchu 160c3e2dc6bSNicolas Souchu /* probe and attach the smbus */ 161c17d4340SNicolas Souchu bus_generic_attach(dev); 162c17d4340SNicolas Souchu 163c17d4340SNicolas Souchu return (0); 164c17d4340SNicolas Souchu } 165c17d4340SNicolas Souchu 166c17d4340SNicolas Souchu static int 167c17d4340SNicolas Souchu iicsmb_detach(device_t dev) 168c17d4340SNicolas Souchu { 169c17d4340SNicolas Souchu struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); 170c17d4340SNicolas Souchu 171c17d4340SNicolas Souchu bus_generic_detach(dev); 172c17d4340SNicolas Souchu if (sc->smbus) { 173c17d4340SNicolas Souchu device_delete_child(dev, sc->smbus); 174c17d4340SNicolas Souchu } 175313f8941SJohn Baldwin mtx_destroy(&sc->lock); 176c3e2dc6bSNicolas Souchu 177c3e2dc6bSNicolas Souchu return (0); 178c3e2dc6bSNicolas Souchu } 179c3e2dc6bSNicolas Souchu 180c3e2dc6bSNicolas Souchu /* 181c3e2dc6bSNicolas Souchu * iicsmb_intr() 182c3e2dc6bSNicolas Souchu * 183c3e2dc6bSNicolas Souchu * iicbus interrupt handler 184c3e2dc6bSNicolas Souchu */ 185b23193a5SWarner Losh static int 186c3e2dc6bSNicolas Souchu iicsmb_intr(device_t dev, int event, char *buf) 187c3e2dc6bSNicolas Souchu { 188c3e2dc6bSNicolas Souchu struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); 189c3e2dc6bSNicolas Souchu 190313f8941SJohn Baldwin mtx_lock(&sc->lock); 191c3e2dc6bSNicolas Souchu switch (event) { 192c3e2dc6bSNicolas Souchu case INTR_GENERAL: 193c3e2dc6bSNicolas Souchu case INTR_START: 194c3e2dc6bSNicolas Souchu sc->state = SMB_WAITING_ADDR; 195c3e2dc6bSNicolas Souchu break; 196c3e2dc6bSNicolas Souchu 197c3e2dc6bSNicolas Souchu case INTR_STOP: 198c3e2dc6bSNicolas Souchu /* call smbus intr handler */ 199c3e2dc6bSNicolas Souchu smbus_intr(sc->smbus, sc->devaddr, 200c3e2dc6bSNicolas Souchu sc->low, sc->high, SMB_ENOERR); 201c3e2dc6bSNicolas Souchu break; 202c3e2dc6bSNicolas Souchu 203c3e2dc6bSNicolas Souchu case INTR_RECEIVE: 204c3e2dc6bSNicolas Souchu switch (sc->state) { 205c3e2dc6bSNicolas Souchu case SMB_DONE: 206c3e2dc6bSNicolas Souchu /* XXX too much data, discard */ 2076e551fb6SDavid E. O'Brien printf("%s: too much data from 0x%x\n", __func__, 208c3e2dc6bSNicolas Souchu sc->devaddr & 0xff); 209c3e2dc6bSNicolas Souchu goto end; 210c3e2dc6bSNicolas Souchu 211c3e2dc6bSNicolas Souchu case SMB_WAITING_ADDR: 212c3e2dc6bSNicolas Souchu sc->devaddr = (u_char)*buf; 213c3e2dc6bSNicolas Souchu sc->state = SMB_WAITING_LOW; 214c3e2dc6bSNicolas Souchu break; 215c3e2dc6bSNicolas Souchu 216c3e2dc6bSNicolas Souchu case SMB_WAITING_LOW: 217c3e2dc6bSNicolas Souchu sc->low = *buf; 218c3e2dc6bSNicolas Souchu sc->state = SMB_WAITING_HIGH; 219c3e2dc6bSNicolas Souchu break; 220c3e2dc6bSNicolas Souchu 221c3e2dc6bSNicolas Souchu case SMB_WAITING_HIGH: 222c3e2dc6bSNicolas Souchu sc->high = *buf; 223c3e2dc6bSNicolas Souchu sc->state = SMB_DONE; 224c3e2dc6bSNicolas Souchu break; 225c3e2dc6bSNicolas Souchu } 226c3e2dc6bSNicolas Souchu end: 227c3e2dc6bSNicolas Souchu break; 228c3e2dc6bSNicolas Souchu 229c3e2dc6bSNicolas Souchu case INTR_TRANSMIT: 230c3e2dc6bSNicolas Souchu case INTR_NOACK: 231c3e2dc6bSNicolas Souchu break; 232c3e2dc6bSNicolas Souchu 233c3e2dc6bSNicolas Souchu case INTR_ERROR: 234c3e2dc6bSNicolas Souchu switch (*buf) { 235c3e2dc6bSNicolas Souchu case IIC_EBUSERR: 236c3e2dc6bSNicolas Souchu smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR); 237c3e2dc6bSNicolas Souchu break; 238c3e2dc6bSNicolas Souchu 239c3e2dc6bSNicolas Souchu default: 2406e551fb6SDavid E. O'Brien printf("%s unknown error 0x%x!\n", __func__, 241c3e2dc6bSNicolas Souchu (int)*buf); 242c3e2dc6bSNicolas Souchu break; 243c3e2dc6bSNicolas Souchu } 244c3e2dc6bSNicolas Souchu break; 245c3e2dc6bSNicolas Souchu 246c3e2dc6bSNicolas Souchu default: 2476e551fb6SDavid E. O'Brien panic("%s: unknown event (%d)!", __func__, event); 248c3e2dc6bSNicolas Souchu } 249313f8941SJohn Baldwin mtx_unlock(&sc->lock); 250c3e2dc6bSNicolas Souchu 251b23193a5SWarner Losh return (0); 252c3e2dc6bSNicolas Souchu } 253c3e2dc6bSNicolas Souchu 254c3e2dc6bSNicolas Souchu static int 2557048a99cSJohn Baldwin iicsmb_callback(device_t dev, int index, void *data) 25604f89a63SNicolas Souchu { 25704f89a63SNicolas Souchu device_t parent = device_get_parent(dev); 25804f89a63SNicolas Souchu int error = 0; 25904f89a63SNicolas Souchu int how; 26004f89a63SNicolas Souchu 26104f89a63SNicolas Souchu switch (index) { 26204f89a63SNicolas Souchu case SMB_REQUEST_BUS: 26304f89a63SNicolas Souchu /* request underlying iicbus */ 26404f89a63SNicolas Souchu how = *(int *)data; 26504f89a63SNicolas Souchu error = iicbus_request_bus(parent, dev, how); 26604f89a63SNicolas Souchu break; 26704f89a63SNicolas Souchu 26804f89a63SNicolas Souchu case SMB_RELEASE_BUS: 26904f89a63SNicolas Souchu /* release underlying iicbus */ 27004f89a63SNicolas Souchu error = iicbus_release_bus(parent, dev); 27104f89a63SNicolas Souchu break; 27204f89a63SNicolas Souchu 27304f89a63SNicolas Souchu default: 27404f89a63SNicolas Souchu error = EINVAL; 27504f89a63SNicolas Souchu } 27604f89a63SNicolas Souchu 27704f89a63SNicolas Souchu return (error); 27804f89a63SNicolas Souchu } 27904f89a63SNicolas Souchu 28004f89a63SNicolas Souchu static int 281c3e2dc6bSNicolas Souchu iicsmb_quick(device_t dev, u_char slave, int how) 282c3e2dc6bSNicolas Souchu { 283c3e2dc6bSNicolas Souchu device_t parent = device_get_parent(dev); 284c3e2dc6bSNicolas Souchu int error; 285c3e2dc6bSNicolas Souchu 286c3e2dc6bSNicolas Souchu switch (how) { 287c3e2dc6bSNicolas Souchu case SMB_QWRITE: 288c17d4340SNicolas Souchu error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT); 289c3e2dc6bSNicolas Souchu break; 290c3e2dc6bSNicolas Souchu 291c3e2dc6bSNicolas Souchu case SMB_QREAD: 292c17d4340SNicolas Souchu error = iicbus_start(parent, slave | LSB, IICBUS_TIMEOUT); 293c3e2dc6bSNicolas Souchu break; 294c3e2dc6bSNicolas Souchu 295c3e2dc6bSNicolas Souchu default: 296c3e2dc6bSNicolas Souchu error = EINVAL; 297c3e2dc6bSNicolas Souchu break; 298c3e2dc6bSNicolas Souchu } 299c3e2dc6bSNicolas Souchu 300c3e2dc6bSNicolas Souchu if (!error) 301c3e2dc6bSNicolas Souchu error = iicbus_stop(parent); 302c3e2dc6bSNicolas Souchu 303c3e2dc6bSNicolas Souchu return (error); 304c3e2dc6bSNicolas Souchu } 305c3e2dc6bSNicolas Souchu 306c3e2dc6bSNicolas Souchu static int 307c3e2dc6bSNicolas Souchu iicsmb_sendb(device_t dev, u_char slave, char byte) 308c3e2dc6bSNicolas Souchu { 309c3e2dc6bSNicolas Souchu device_t parent = device_get_parent(dev); 310c3e2dc6bSNicolas Souchu int error, sent; 311c3e2dc6bSNicolas Souchu 312c17d4340SNicolas Souchu error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT); 313c3e2dc6bSNicolas Souchu 314c3e2dc6bSNicolas Souchu if (!error) { 315c17d4340SNicolas Souchu error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT); 316c3e2dc6bSNicolas Souchu 317c3e2dc6bSNicolas Souchu iicbus_stop(parent); 318c3e2dc6bSNicolas Souchu } 319c3e2dc6bSNicolas Souchu 320c3e2dc6bSNicolas Souchu return (error); 321c3e2dc6bSNicolas Souchu } 322c3e2dc6bSNicolas Souchu 323c3e2dc6bSNicolas Souchu static int 324c3e2dc6bSNicolas Souchu iicsmb_recvb(device_t dev, u_char slave, char *byte) 325c3e2dc6bSNicolas Souchu { 326c3e2dc6bSNicolas Souchu device_t parent = device_get_parent(dev); 327c3e2dc6bSNicolas Souchu int error, read; 328c3e2dc6bSNicolas Souchu 32904f89a63SNicolas Souchu error = iicbus_start(parent, slave | LSB, 0); 330c3e2dc6bSNicolas Souchu 33104f89a63SNicolas Souchu if (!error) { 332c17d4340SNicolas Souchu error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT); 33304f89a63SNicolas Souchu 33404f89a63SNicolas Souchu iicbus_stop(parent); 33504f89a63SNicolas Souchu } 336c3e2dc6bSNicolas Souchu 337c3e2dc6bSNicolas Souchu return (error); 338c3e2dc6bSNicolas Souchu } 339c3e2dc6bSNicolas Souchu 340c3e2dc6bSNicolas Souchu static int 341c3e2dc6bSNicolas Souchu iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte) 342c3e2dc6bSNicolas Souchu { 343c3e2dc6bSNicolas Souchu device_t parent = device_get_parent(dev); 344c3e2dc6bSNicolas Souchu int error, sent; 345c3e2dc6bSNicolas Souchu 34604f89a63SNicolas Souchu error = iicbus_start(parent, slave & ~LSB, 0); 347c3e2dc6bSNicolas Souchu 348c3e2dc6bSNicolas Souchu if (!error) { 349c17d4340SNicolas Souchu if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) 350c17d4340SNicolas Souchu error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT); 351c3e2dc6bSNicolas Souchu 352c3e2dc6bSNicolas Souchu iicbus_stop(parent); 353c3e2dc6bSNicolas Souchu } 354c3e2dc6bSNicolas Souchu 355c3e2dc6bSNicolas Souchu return (error); 356c3e2dc6bSNicolas Souchu } 357c3e2dc6bSNicolas Souchu 358c3e2dc6bSNicolas Souchu static int 359c3e2dc6bSNicolas Souchu iicsmb_writew(device_t dev, u_char slave, char cmd, short word) 360c3e2dc6bSNicolas Souchu { 361c3e2dc6bSNicolas Souchu device_t parent = device_get_parent(dev); 362c3e2dc6bSNicolas Souchu int error, sent; 363c3e2dc6bSNicolas Souchu 364c3e2dc6bSNicolas Souchu char low = (char)(word & 0xff); 365c3e2dc6bSNicolas Souchu char high = (char)((word & 0xff00) >> 8); 366c3e2dc6bSNicolas Souchu 36704f89a63SNicolas Souchu error = iicbus_start(parent, slave & ~LSB, 0); 368c3e2dc6bSNicolas Souchu 369c3e2dc6bSNicolas Souchu if (!error) { 370c17d4340SNicolas Souchu if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) 371c17d4340SNicolas Souchu if (!(error = iicbus_write(parent, &low, 1, &sent, IICBUS_TIMEOUT))) 372c17d4340SNicolas Souchu error = iicbus_write(parent, &high, 1, &sent, IICBUS_TIMEOUT); 373c3e2dc6bSNicolas Souchu 374c3e2dc6bSNicolas Souchu iicbus_stop(parent); 375c3e2dc6bSNicolas Souchu } 376c3e2dc6bSNicolas Souchu 377c3e2dc6bSNicolas Souchu return (error); 378c3e2dc6bSNicolas Souchu } 379c3e2dc6bSNicolas Souchu 380c3e2dc6bSNicolas Souchu static int 381c3e2dc6bSNicolas Souchu iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte) 382c3e2dc6bSNicolas Souchu { 383c3e2dc6bSNicolas Souchu device_t parent = device_get_parent(dev); 384c3e2dc6bSNicolas Souchu int error, sent, read; 385c3e2dc6bSNicolas Souchu 386313f8941SJohn Baldwin if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) 38704f89a63SNicolas Souchu return (error); 38804f89a63SNicolas Souchu 389c17d4340SNicolas Souchu if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) 390c3e2dc6bSNicolas Souchu goto error; 391c3e2dc6bSNicolas Souchu 392c17d4340SNicolas Souchu if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) 393c3e2dc6bSNicolas Souchu goto error; 394c3e2dc6bSNicolas Souchu 395c17d4340SNicolas Souchu if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT))) 396c3e2dc6bSNicolas Souchu goto error; 397c3e2dc6bSNicolas Souchu 398c3e2dc6bSNicolas Souchu error: 39904f89a63SNicolas Souchu iicbus_stop(parent); 400c3e2dc6bSNicolas Souchu return (error); 401c3e2dc6bSNicolas Souchu } 402c3e2dc6bSNicolas Souchu 403c3e2dc6bSNicolas Souchu #define BUF2SHORT(low,high) \ 404c3e2dc6bSNicolas Souchu ((short)(((high) & 0xff) << 8) | (short)((low) & 0xff)) 405c3e2dc6bSNicolas Souchu 406c3e2dc6bSNicolas Souchu static int 407c3e2dc6bSNicolas Souchu iicsmb_readw(device_t dev, u_char slave, char cmd, short *word) 408c3e2dc6bSNicolas Souchu { 409c3e2dc6bSNicolas Souchu device_t parent = device_get_parent(dev); 410c3e2dc6bSNicolas Souchu int error, sent, read; 411c3e2dc6bSNicolas Souchu char buf[2]; 412c3e2dc6bSNicolas Souchu 413313f8941SJohn Baldwin if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) 41404f89a63SNicolas Souchu return (error); 41504f89a63SNicolas Souchu 416c17d4340SNicolas Souchu if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) 417c3e2dc6bSNicolas Souchu goto error; 418c3e2dc6bSNicolas Souchu 419c17d4340SNicolas Souchu if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) 420c3e2dc6bSNicolas Souchu goto error; 421c3e2dc6bSNicolas Souchu 422c17d4340SNicolas Souchu if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT))) 423c3e2dc6bSNicolas Souchu goto error; 424c3e2dc6bSNicolas Souchu 425c3e2dc6bSNicolas Souchu /* first, receive low, then high byte */ 426c3e2dc6bSNicolas Souchu *word = BUF2SHORT(buf[0], buf[1]); 427c3e2dc6bSNicolas Souchu 428c3e2dc6bSNicolas Souchu error: 42904f89a63SNicolas Souchu iicbus_stop(parent); 430c3e2dc6bSNicolas Souchu return (error); 431c3e2dc6bSNicolas Souchu } 432c3e2dc6bSNicolas Souchu 433c3e2dc6bSNicolas Souchu static int 434c3e2dc6bSNicolas Souchu iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) 435c3e2dc6bSNicolas Souchu { 436c3e2dc6bSNicolas Souchu device_t parent = device_get_parent(dev); 437c3e2dc6bSNicolas Souchu int error, sent, read; 438c3e2dc6bSNicolas Souchu char buf[2]; 439c3e2dc6bSNicolas Souchu 440313f8941SJohn Baldwin if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) 44104f89a63SNicolas Souchu return (error); 442c3e2dc6bSNicolas Souchu 443c17d4340SNicolas Souchu if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) 444c3e2dc6bSNicolas Souchu goto error; 445c3e2dc6bSNicolas Souchu 446c3e2dc6bSNicolas Souchu /* first, send low, then high byte */ 447c3e2dc6bSNicolas Souchu buf[0] = (char)(sdata & 0xff); 448c3e2dc6bSNicolas Souchu buf[1] = (char)((sdata & 0xff00) >> 8); 449c3e2dc6bSNicolas Souchu 450c17d4340SNicolas Souchu if ((error = iicbus_write(parent, buf, 2, &sent, IICBUS_TIMEOUT))) 451c3e2dc6bSNicolas Souchu goto error; 452c3e2dc6bSNicolas Souchu 453c17d4340SNicolas Souchu if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) 454c3e2dc6bSNicolas Souchu goto error; 455c3e2dc6bSNicolas Souchu 456c17d4340SNicolas Souchu if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT))) 457c3e2dc6bSNicolas Souchu goto error; 458c3e2dc6bSNicolas Souchu 459c3e2dc6bSNicolas Souchu /* first, receive low, then high byte */ 460c3e2dc6bSNicolas Souchu *rdata = BUF2SHORT(buf[0], buf[1]); 461c3e2dc6bSNicolas Souchu 462c3e2dc6bSNicolas Souchu error: 46304f89a63SNicolas Souchu iicbus_stop(parent); 464c3e2dc6bSNicolas Souchu return (error); 465c3e2dc6bSNicolas Souchu } 466c3e2dc6bSNicolas Souchu 467c3e2dc6bSNicolas Souchu static int 468c3e2dc6bSNicolas Souchu iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) 469c3e2dc6bSNicolas Souchu { 470c3e2dc6bSNicolas Souchu device_t parent = device_get_parent(dev); 471c3e2dc6bSNicolas Souchu int error, sent; 472c3e2dc6bSNicolas Souchu 473c17d4340SNicolas Souchu if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) 474c3e2dc6bSNicolas Souchu goto error; 475c3e2dc6bSNicolas Souchu 476c17d4340SNicolas Souchu if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) 477c3e2dc6bSNicolas Souchu goto error; 478c3e2dc6bSNicolas Souchu 479c17d4340SNicolas Souchu if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT))) 480c3e2dc6bSNicolas Souchu goto error; 481c3e2dc6bSNicolas Souchu 482c3e2dc6bSNicolas Souchu if ((error = iicbus_stop(parent))) 483c3e2dc6bSNicolas Souchu goto error; 484c3e2dc6bSNicolas Souchu 485c3e2dc6bSNicolas Souchu error: 486c3e2dc6bSNicolas Souchu return (error); 487c3e2dc6bSNicolas Souchu } 488c3e2dc6bSNicolas Souchu 489c3e2dc6bSNicolas Souchu static int 4907048a99cSJohn Baldwin iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf) 491c3e2dc6bSNicolas Souchu { 492c3e2dc6bSNicolas Souchu device_t parent = device_get_parent(dev); 493c3e2dc6bSNicolas Souchu int error, sent, read; 494c3e2dc6bSNicolas Souchu 495313f8941SJohn Baldwin if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) 49604f89a63SNicolas Souchu return (error); 49704f89a63SNicolas Souchu 498c17d4340SNicolas Souchu if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) 499c3e2dc6bSNicolas Souchu goto error; 500c3e2dc6bSNicolas Souchu 501c17d4340SNicolas Souchu if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) 502c3e2dc6bSNicolas Souchu goto error; 503c3e2dc6bSNicolas Souchu 5047048a99cSJohn Baldwin if ((error = iicbus_read(parent, buf, (int)*count, &read, 505c17d4340SNicolas Souchu IIC_LAST_READ, IICBUS_TIMEOUT))) 506c3e2dc6bSNicolas Souchu goto error; 5077048a99cSJohn Baldwin *count = read; 508c3e2dc6bSNicolas Souchu 509c3e2dc6bSNicolas Souchu error: 51004f89a63SNicolas Souchu iicbus_stop(parent); 511c3e2dc6bSNicolas Souchu return (error); 512c3e2dc6bSNicolas Souchu } 513c3e2dc6bSNicolas Souchu 514c3e2dc6bSNicolas Souchu DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0); 5157048a99cSJohn Baldwin DRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0); 516c17d4340SNicolas Souchu MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 517c17d4340SNicolas Souchu MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); 518c17d4340SNicolas Souchu MODULE_VERSION(iicsmb, 1); 519