diff --git a/sys/dev/iicbus/iic.c b/sys/dev/iicbus/iic.c index 9dd1431dbc04..28339fa510ed 100644 --- a/sys/dev/iicbus/iic.c +++ b/sys/dev/iicbus/iic.c @@ -1,319 +1,313 @@ /*- * Copyright (c) 1998, 2001 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #define BUFSIZE 1024 struct iic_softc { u_char sc_addr; /* 7 bit address on iicbus */ int sc_count; /* >0 if device opened */ char sc_buffer[BUFSIZE]; /* output buffer */ char sc_inbuf[BUFSIZE]; /* input buffer */ struct cdev *sc_devnode; }; #define IIC_SOFTC(unit) \ ((struct iic_softc *)devclass_get_softc(iic_devclass, (unit))) #define IIC_DEVICE(unit) \ (devclass_get_device(iic_devclass, (unit))) static int iic_probe(device_t); static int iic_attach(device_t); static int iic_detach(device_t); static void iic_identify(driver_t *driver, device_t parent); static devclass_t iic_devclass; static device_method_t iic_methods[] = { /* device interface */ DEVMETHOD(device_identify, iic_identify), DEVMETHOD(device_probe, iic_probe), DEVMETHOD(device_attach, iic_attach), DEVMETHOD(device_detach, iic_detach), /* iicbus interface */ DEVMETHOD(iicbus_intr, iicbus_generic_intr), { 0, 0 } }; static driver_t iic_driver = { "iic", iic_methods, sizeof(struct iic_softc), }; static d_open_t iicopen; static d_close_t iicclose; static d_write_t iicwrite; static d_read_t iicread; static d_ioctl_t iicioctl; static struct cdevsw iic_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = iicopen, .d_close = iicclose, .d_read = iicread, .d_write = iicwrite, .d_ioctl = iicioctl, .d_name = "iic", }; static void iic_identify(driver_t *driver, device_t parent) { BUS_ADD_CHILD(parent, 0, "iic", -1); } static int iic_probe(device_t dev) { device_set_desc(dev, "I2C generic I/O"); return (0); } static int iic_attach(device_t dev) { struct iic_softc *sc = (struct iic_softc *)device_get_softc(dev); - if (!sc) - return (ENOMEM); - - bzero(sc, sizeof(struct iic_softc)); - sc->sc_devnode = make_dev(&iic_cdevsw, device_get_unit(dev), UID_ROOT, GID_WHEEL, 0600, "iic%d", device_get_unit(dev)); - return (0); } static int iic_detach(device_t dev) { struct iic_softc *sc = (struct iic_softc *)device_get_softc(dev); if (sc->sc_devnode) destroy_dev(sc->sc_devnode); return (0); } static int -iicopen (struct cdev *dev, int flags, int fmt, struct thread *td) +iicopen(struct cdev *dev, int flags, int fmt, struct thread *td) { struct iic_softc *sc = IIC_SOFTC(minor(dev)); if (!sc) return (EINVAL); if (sc->sc_count > 0) return (EBUSY); sc->sc_count++; return (0); } static int iicclose(struct cdev *dev, int flags, int fmt, struct thread *td) { struct iic_softc *sc = IIC_SOFTC(minor(dev)); if (!sc) return (EINVAL); if (!sc->sc_count) return (EINVAL); sc->sc_count--; if (sc->sc_count < 0) panic("%s: iic_count < 0!", __func__); return (0); } static int iicwrite(struct cdev *dev, struct uio * uio, int ioflag) { device_t iicdev = IIC_DEVICE(minor(dev)); struct iic_softc *sc = IIC_SOFTC(minor(dev)); int sent, error, count; if (!sc || !iicdev || !sc->sc_addr) return (EINVAL); if (sc->sc_count == 0) return (EINVAL); if ((error = iicbus_request_bus(device_get_parent(iicdev), iicdev, IIC_DONTWAIT))) return (error); count = min(uio->uio_resid, BUFSIZE); uiomove(sc->sc_buffer, count, uio); error = iicbus_block_write(device_get_parent(iicdev), sc->sc_addr, sc->sc_buffer, count, &sent); iicbus_release_bus(device_get_parent(iicdev), iicdev); return(error); } static int iicread(struct cdev *dev, struct uio * uio, int ioflag) { device_t iicdev = IIC_DEVICE(minor(dev)); struct iic_softc *sc = IIC_SOFTC(minor(dev)); int len, error = 0; int bufsize; if (!sc || !iicdev || !sc->sc_addr) return (EINVAL); if (sc->sc_count == 0) return (EINVAL); if ((error = iicbus_request_bus(device_get_parent(iicdev), iicdev, IIC_DONTWAIT))) return (error); /* max amount of data to read */ len = min(uio->uio_resid, BUFSIZE); if ((error = iicbus_block_read(device_get_parent(iicdev), sc->sc_addr, sc->sc_inbuf, len, &bufsize))) return (error); if (bufsize > uio->uio_resid) panic("%s: too much data read!", __func__); iicbus_release_bus(device_get_parent(iicdev), iicdev); return (uiomove(sc->sc_inbuf, bufsize, uio)); } static int iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td) { device_t iicdev = IIC_DEVICE(minor(dev)); struct iic_softc *sc = IIC_SOFTC(minor(dev)); device_t parent = device_get_parent(iicdev); struct iiccmd *s = (struct iiccmd *)data; int error, count; char *buf = NULL; if (!sc) return (EINVAL); if ((error = iicbus_request_bus(device_get_parent(iicdev), iicdev, (flags & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | IIC_INTR)))) return (error); switch (cmd) { case I2CSTART: error = iicbus_start(parent, s->slave, 0); /* * Implicitly set the chip addr to the slave addr passed as * parameter. Consequently, start/stop shall be called before * the read or the write of a block. */ if (!error) sc->sc_addr = s->slave; break; case I2CSTOP: error = iicbus_stop(parent); break; case I2CRSTCARD: error = iicbus_reset(parent, IIC_UNKNOWN, 0, NULL); break; case I2CWRITE: if (s->count <= 0) { error = EINVAL; break; } buf = malloc((unsigned long)s->count, M_TEMP, M_WAITOK); error = copyin(s->buf, buf, s->count); if (error) break; error = iicbus_write(parent, buf, s->count, &count, 10); break; case I2CREAD: if (s->count <= 0) { error = EINVAL; break; } buf = malloc((unsigned long)s->count, M_TEMP, M_WAITOK); error = iicbus_read(parent, buf, s->count, &count, s->last, 10); if (error) break; error = copyout(buf, s->buf, s->count); break; default: error = ENOTTY; } iicbus_release_bus(device_get_parent(iicdev), iicdev); if (buf != NULL) free(buf, M_TEMP); return (error); } DRIVER_MODULE(iic, iicbus, iic_driver, iic_devclass, 0, 0); MODULE_DEPEND(iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); MODULE_VERSION(iic, 1); diff --git a/sys/dev/iicbus/iicbb.c b/sys/dev/iicbus/iicbb.c index 53ecdceb947f..554f4357380a 100644 --- a/sys/dev/iicbus/iicbb.c +++ b/sys/dev/iicbus/iicbb.c @@ -1,390 +1,386 @@ /*- * Copyright (c) 1998, 2001 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Generic I2C bit-banging code * * Example: * * iicbus * / \ * iicbb pcf * | \ * bti2c lpbb * * From Linux I2C generic interface * (c) 1998 Gerd Knorr * */ #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #include "iicbb_if.h" struct iicbb_softc { device_t iicbus; }; static int iicbb_probe(device_t); static int iicbb_attach(device_t); static int iicbb_detach(device_t); static int iicbb_print_child(device_t, device_t); static int iicbb_callback(device_t, int, caddr_t); static int iicbb_start(device_t, u_char, int); static int iicbb_stop(device_t); static int iicbb_write(device_t, char *, int, int *, int); static int iicbb_read(device_t, char *, int, int *, int, int); static int iicbb_reset(device_t, u_char, u_char, u_char *); static device_method_t iicbb_methods[] = { /* device interface */ DEVMETHOD(device_probe, iicbb_probe), DEVMETHOD(device_attach, iicbb_attach), DEVMETHOD(device_detach, iicbb_detach), /* bus interface */ DEVMETHOD(bus_print_child, iicbb_print_child), /* iicbus interface */ DEVMETHOD(iicbus_callback, iicbb_callback), DEVMETHOD(iicbus_start, iicbb_start), DEVMETHOD(iicbus_repeated_start, iicbb_start), DEVMETHOD(iicbus_stop, iicbb_stop), DEVMETHOD(iicbus_write, iicbb_write), DEVMETHOD(iicbus_read, iicbb_read), DEVMETHOD(iicbus_reset, iicbb_reset), { 0, 0 } }; driver_t iicbb_driver = { "iicbb", iicbb_methods, sizeof(struct iicbb_softc), }; devclass_t iicbb_devclass; static int iicbb_probe(device_t dev) { device_set_desc(dev, "I2C bit-banging driver"); return (0); } static int iicbb_attach(device_t dev) { struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev); - bzero(sc, sizeof(struct iicbb_softc)); - sc->iicbus = device_add_child(dev, "iicbus", -1); - if (!sc->iicbus) return (ENXIO); - bus_generic_attach(dev); return (0); } static int iicbb_detach(device_t dev) { struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev); if (sc->iicbus) { bus_generic_detach(dev); device_delete_child(dev, sc->iicbus); } return (0); } static int iicbb_print_child(device_t bus, device_t dev) { int error; int retval = 0; u_char oldaddr; retval += bus_print_child_header(bus, dev); /* retrieve the interface I2C address */ error = IICBB_RESET(device_get_parent(bus), IIC_FASTEST, 0, &oldaddr); if (error == IIC_ENOADDR) { retval += printf(" on %s master-only\n", device_get_nameunit(bus)); } else { /* restore the address */ IICBB_RESET(device_get_parent(bus), IIC_FASTEST, oldaddr, NULL); retval += printf(" on %s addr 0x%x\n", device_get_nameunit(bus), oldaddr & 0xff); } return (retval); } #define IIC_DELAY 10 #define I2C_SETSDA(dev,val) do { \ IICBB_SETSDA(device_get_parent(dev), val); \ DELAY(IIC_DELAY); \ } while (0) #define I2C_SETSCL(dev,val) do { \ iicbb_setscl(dev, val, 100); \ } while (0) #define I2C_SET(dev,ctrl,data) do { \ I2C_SETSCL(dev, ctrl); \ I2C_SETSDA(dev, data); \ } while (0) #define I2C_GETSDA(dev) (IICBB_GETSDA(device_get_parent(dev))) #define I2C_GETSCL(dev) (IICBB_GETSCL(device_get_parent(dev))) static int i2c_debug = 0; #define I2C_DEBUG(x) do { \ if (i2c_debug) (x); \ } while (0) #define I2C_LOG(format,args...) do { \ printf(format, args); \ } while (0) static void iicbb_setscl(device_t dev, int val, int timeout) { int k = 0; IICBB_SETSCL(device_get_parent(dev), val); DELAY(IIC_DELAY); while (val && !I2C_GETSCL(dev) && k++ < timeout) { IICBB_SETSCL(device_get_parent(dev), val); DELAY(IIC_DELAY); } return; } static void iicbb_one(device_t dev, int timeout) { I2C_SET(dev,0,1); I2C_SET(dev,1,1); I2C_SET(dev,0,1); return; } static void iicbb_zero(device_t dev, int timeout) { I2C_SET(dev,0,0); I2C_SET(dev,1,0); I2C_SET(dev,0,0); return; } /* * Waiting for ACKNOWLEDGE. * * When a chip is being addressed or has received data it will issue an * ACKNOWLEDGE pulse. Therefore the MASTER must release the DATA line * (set it to high level) and then release the CLOCK line. * Now it must wait for the SLAVE to pull the DATA line low. * Actually on the bus this looks like a START condition so nothing happens * because of the fact that the IC's that have not been addressed are doing * nothing. * * When the SLAVE has pulled this line low the MASTER will take the CLOCK * line low and then the SLAVE will release the SDA (data) line. */ static int iicbb_ack(device_t dev, int timeout) { int noack; int k = 0; I2C_SET(dev,0,1); I2C_SET(dev,1,1); do { noack = I2C_GETSDA(dev); if (!noack) break; DELAY(10); k += 10; } while (k < timeout); I2C_SET(dev,0,1); I2C_DEBUG(printf("%c ",noack?'-':'+')); return (noack); } static void iicbb_sendbyte(device_t dev, u_char data, int timeout) { int i; for (i=7; i>=0; i--) { if (data&(1<=0; i--) { I2C_SET(dev,1,1); if (I2C_GETSDA(dev)) data |= (1<")); return (0); } static int iicbb_write(device_t dev, char * buf, int len, int *sent, int timeout) { int bytes, error = 0; bytes = 0; while (len) { /* send byte */ iicbb_sendbyte(dev,(u_char)*buf++, timeout); /* check for ack */ if (iicbb_ack(dev, timeout)) { error = IIC_ENOACK; goto error; } bytes ++; len --; } error: *sent = bytes; return (error); } static int iicbb_read(device_t dev, char * buf, int len, int *read, int last, int delay) { int bytes; bytes = 0; while (len) { /* XXX should insert delay here */ *buf++ = (char)iicbb_readbyte(dev, (len == 1) ? last : 0, delay); bytes ++; len --; } *read = bytes; return (0); } DRIVER_MODULE(iicbb, bktr, iicbb_driver, iicbb_devclass, 0, 0); DRIVER_MODULE(iicbb, lpbb, iicbb_driver, iicbb_devclass, 0, 0); DRIVER_MODULE(iicbb, viapm, iicbb_driver, iicbb_devclass, 0, 0); MODULE_DEPEND(iicbb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); MODULE_VERSION(iicbb, IICBB_MODVER); diff --git a/sys/dev/iicbus/iicsmb.c b/sys/dev/iicbus/iicsmb.c index 966063f2a13b..c2d45d4513e0 100644 --- a/sys/dev/iicbus/iicsmb.c +++ b/sys/dev/iicbus/iicsmb.c @@ -1,513 +1,511 @@ /*- * Copyright (c) 1998, 2001 Nicolas Souchu * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * */ /* * I2C to SMB bridge * * Example: * * smb bttv * \ / * smbus * / \ * iicsmb bti2c * | * iicbus * / | \ * iicbb pcf ... * | * lpbb */ #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" #include "smbus_if.h" struct iicsmb_softc { #define SMB_WAITING_ADDR 0x0 #define SMB_WAITING_LOW 0x1 #define SMB_WAITING_HIGH 0x2 #define SMB_DONE 0x3 int state; u_char devaddr; /* slave device address */ char low; /* low byte received first */ char high; /* high byte */ device_t smbus; }; static int iicsmb_probe(device_t); static int iicsmb_attach(device_t); static int iicsmb_detach(device_t); static void iicsmb_identify(driver_t *driver, device_t parent); static void iicsmb_intr(device_t dev, int event, char *buf); static int iicsmb_callback(device_t dev, int index, caddr_t data); static int iicsmb_quick(device_t dev, u_char slave, int how); static int iicsmb_sendb(device_t dev, u_char slave, char byte); static int iicsmb_recvb(device_t dev, u_char slave, char *byte); static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte); static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word); static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte); static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word); static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata); static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf); static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf); static devclass_t iicsmb_devclass; static device_method_t iicsmb_methods[] = { /* device interface */ DEVMETHOD(device_identify, iicsmb_identify), DEVMETHOD(device_probe, iicsmb_probe), DEVMETHOD(device_attach, iicsmb_attach), DEVMETHOD(device_detach, iicsmb_detach), /* bus interface */ DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD(bus_print_child, bus_generic_print_child), /* iicbus interface */ DEVMETHOD(iicbus_intr, iicsmb_intr), /* smbus interface */ DEVMETHOD(smbus_callback, iicsmb_callback), DEVMETHOD(smbus_quick, iicsmb_quick), DEVMETHOD(smbus_sendb, iicsmb_sendb), DEVMETHOD(smbus_recvb, iicsmb_recvb), DEVMETHOD(smbus_writeb, iicsmb_writeb), DEVMETHOD(smbus_writew, iicsmb_writew), DEVMETHOD(smbus_readb, iicsmb_readb), DEVMETHOD(smbus_readw, iicsmb_readw), DEVMETHOD(smbus_pcall, iicsmb_pcall), DEVMETHOD(smbus_bwrite, iicsmb_bwrite), DEVMETHOD(smbus_bread, iicsmb_bread), { 0, 0 } }; static driver_t iicsmb_driver = { "iicsmb", iicsmb_methods, sizeof(struct iicsmb_softc), }; #define IICBUS_TIMEOUT 100 /* us */ static void iicsmb_identify(driver_t *driver, device_t parent) { BUS_ADD_CHILD(parent, 0, "iicsmb", -1); } static int iicsmb_probe(device_t dev) { device_set_desc(dev, "SMBus over I2C bridge"); return (0); } static int iicsmb_attach(device_t dev) { struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); - bzero(sc, sizeof(*sc)); - sc->smbus = device_add_child(dev, "smbus", -1); /* probe and attach the smbus */ bus_generic_attach(dev); return (0); } static int iicsmb_detach(device_t dev) { struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); bus_generic_detach(dev); if (sc->smbus) { device_delete_child(dev, sc->smbus); } return (0); } /* * iicsmb_intr() * * iicbus interrupt handler */ static void iicsmb_intr(device_t dev, int event, char *buf) { struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev); switch (event) { case INTR_GENERAL: case INTR_START: sc->state = SMB_WAITING_ADDR; break; case INTR_STOP: /* call smbus intr handler */ smbus_intr(sc->smbus, sc->devaddr, sc->low, sc->high, SMB_ENOERR); break; case INTR_RECEIVE: switch (sc->state) { case SMB_DONE: /* XXX too much data, discard */ printf("%s: too much data from 0x%x\n", __func__, sc->devaddr & 0xff); goto end; case SMB_WAITING_ADDR: sc->devaddr = (u_char)*buf; sc->state = SMB_WAITING_LOW; break; case SMB_WAITING_LOW: sc->low = *buf; sc->state = SMB_WAITING_HIGH; break; case SMB_WAITING_HIGH: sc->high = *buf; sc->state = SMB_DONE; break; } end: break; case INTR_TRANSMIT: case INTR_NOACK: break; case INTR_ERROR: switch (*buf) { case IIC_EBUSERR: smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR); break; default: printf("%s unknown error 0x%x!\n", __func__, (int)*buf); break; } break; default: panic("%s: unknown event (%d)!", __func__, event); } return; } static int iicsmb_callback(device_t dev, int index, caddr_t data) { device_t parent = device_get_parent(dev); int error = 0; int how; switch (index) { case SMB_REQUEST_BUS: /* request underlying iicbus */ how = *(int *)data; error = iicbus_request_bus(parent, dev, how); break; case SMB_RELEASE_BUS: /* release underlying iicbus */ error = iicbus_release_bus(parent, dev); break; default: error = EINVAL; } return (error); } static int iicsmb_quick(device_t dev, u_char slave, int how) { device_t parent = device_get_parent(dev); int error; switch (how) { case SMB_QWRITE: error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT); break; case SMB_QREAD: error = iicbus_start(parent, slave | LSB, IICBUS_TIMEOUT); break; default: error = EINVAL; break; } if (!error) error = iicbus_stop(parent); return (error); } static int iicsmb_sendb(device_t dev, u_char slave, char byte) { device_t parent = device_get_parent(dev); int error, sent; error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT); if (!error) { error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT); iicbus_stop(parent); } return (error); } static int iicsmb_recvb(device_t dev, u_char slave, char *byte) { device_t parent = device_get_parent(dev); int error, read; error = iicbus_start(parent, slave | LSB, 0); if (!error) { error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT); iicbus_stop(parent); } return (error); } static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte) { device_t parent = device_get_parent(dev); int error, sent; error = iicbus_start(parent, slave & ~LSB, 0); if (!error) { if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT); iicbus_stop(parent); } return (error); } static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word) { device_t parent = device_get_parent(dev); int error, sent; char low = (char)(word & 0xff); char high = (char)((word & 0xff00) >> 8); error = iicbus_start(parent, slave & ~LSB, 0); if (!error) { if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) if (!(error = iicbus_write(parent, &low, 1, &sent, IICBUS_TIMEOUT))) error = iicbus_write(parent, &high, 1, &sent, IICBUS_TIMEOUT); iicbus_stop(parent); } return (error); } static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte) { device_t parent = device_get_parent(dev); int error, sent, read; if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) return (error); if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT))) goto error; error: iicbus_stop(parent); return (error); } #define BUF2SHORT(low,high) \ ((short)(((high) & 0xff) << 8) | (short)((low) & 0xff)) static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word) { device_t parent = device_get_parent(dev); int error, sent, read; char buf[2]; if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) return (error); if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT))) goto error; /* first, receive low, then high byte */ *word = BUF2SHORT(buf[0], buf[1]); error: iicbus_stop(parent); return (error); } static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata) { device_t parent = device_get_parent(dev); int error, sent, read; char buf[2]; if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) return (error); if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) goto error; /* first, send low, then high byte */ buf[0] = (char)(sdata & 0xff); buf[1] = (char)((sdata & 0xff00) >> 8); if ((error = iicbus_write(parent, buf, 2, &sent, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT))) goto error; /* first, receive low, then high byte */ *rdata = BUF2SHORT(buf[0], buf[1]); error: iicbus_stop(parent); return (error); } static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf) { device_t parent = device_get_parent(dev); int error, sent; if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_stop(parent))) goto error; error: return (error); } static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf) { device_t parent = device_get_parent(dev); int error, sent, read; if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT))) return (error); if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT))) goto error; if ((error = iicbus_read(parent, buf, (int)count, &read, IIC_LAST_READ, IICBUS_TIMEOUT))) goto error; error: iicbus_stop(parent); return (error); } DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0); MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); MODULE_VERSION(iicsmb, 1);