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