summaryrefslogtreecommitdiffstats
path: root/sys/dev/iicbus/iicsmb.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/iicbus/iicsmb.c')
-rw-r--r--sys/dev/iicbus/iicsmb.c450
1 files changed, 450 insertions, 0 deletions
diff --git a/sys/dev/iicbus/iicsmb.c b/sys/dev/iicbus/iicsmb.c
new file mode 100644
index 0000000..8f3a8bb
--- /dev/null
+++ b/sys/dev/iicbus/iicsmb.c
@@ -0,0 +1,450 @@
+/*-
+ * Copyright (c) 1998 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.
+ *
+ * $Id: iicsmb.c,v 1.1.2.2 1998/08/13 17:10:44 son Exp $
+ *
+ */
+
+/*
+ * I2C to SMB bridge
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+
+#include <machine/clock.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+
+#include <dev/smbus/smbconf.h>
+
+#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 void iicsmb_print_child(device_t, device_t);
+
+static void iicsmb_intr(device_t dev, int event, char *buf);
+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_probe, iicsmb_probe),
+ DEVMETHOD(device_attach, iicsmb_attach),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, iicsmb_print_child),
+
+ /* iicbus interface */
+ DEVMETHOD(iicbus_intr, iicsmb_intr),
+
+ /* smbus interface */
+ 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,
+ DRIVER_TYPE_MISC,
+ sizeof(struct iicsmb_softc),
+};
+
+static int
+iicsmb_probe(device_t dev)
+{
+ struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
+
+ sc->smbus = smbus_alloc_bus(dev);
+
+ if (!sc->smbus)
+ return (EINVAL); /* XXX don't know what to return else */
+
+ return (0);
+}
+
+static int
+iicsmb_attach(device_t dev)
+{
+ struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
+
+ /* probe and attach the smbus */
+ device_probe_and_attach(sc->smbus);
+
+ return (0);
+}
+
+static void
+iicsmb_print_child(device_t bus, device_t dev)
+{
+ printf(" on %s%d", device_get_name(bus), device_get_unit(bus));
+
+ return;
+}
+
+/*
+ * 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", __FUNCTION__,
+ 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", __FUNCTION__,
+ (int)*buf);
+ break;
+ }
+ break;
+
+ default:
+ panic("%s: unknown event (%d)!", __FUNCTION__, event);
+ }
+
+ return;
+}
+
+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);
+ break;
+
+ case SMB_QREAD:
+ error = iicbus_start(parent, slave | LSB);
+ 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);
+
+ if (!error) {
+ error = iicbus_write(parent, &byte, 1, &sent);
+
+ 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);
+
+ if (!error)
+ error = iicbus_read(parent, byte, 1, &read);
+
+ 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);
+
+ if (!error) {
+ if (!(error = iicbus_write(parent, &cmd, 1, &sent)))
+ error = iicbus_write(parent, &byte, 1, &sent);
+
+ 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);
+
+ if (!error) {
+ if (!(error = iicbus_write(parent, &cmd, 1, &sent)))
+ if (!(error = iicbus_write(parent, &low, 1, &sent)))
+ error = iicbus_write(parent, &high, 1, &sent);
+
+ 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)))
+ goto error;
+
+ if ((error = iicbus_write(parent, &cmd, 1, &sent)))
+ goto error;
+
+ if ((error = iicbus_repeated_start(parent, slave | LSB)))
+ goto error;
+
+ if ((error = iicbus_read(parent, byte, 1, &read)))
+ goto error;
+
+error:
+ 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)))
+ goto error;
+
+ if ((error = iicbus_write(parent, &cmd, 1, &sent)))
+ goto error;
+
+ if ((error = iicbus_repeated_start(parent, slave | LSB)))
+ goto error;
+
+ if ((error = iicbus_read(parent, buf, 2, &read)))
+ goto error;
+
+ /* first, receive low, then high byte */
+ *word = BUF2SHORT(buf[0], buf[1]);
+
+error:
+ 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)))
+ goto error;
+
+ if ((error = iicbus_write(parent, &cmd, 1, &sent)))
+ 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)))
+ goto error;
+
+ if ((error = iicbus_repeated_start(parent, slave | LSB)))
+ goto error;
+
+ if ((error = iicbus_read(parent, buf, 2, &read)))
+ goto error;
+
+ /* first, receive low, then high byte */
+ *rdata = BUF2SHORT(buf[0], buf[1]);
+
+error:
+ 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)))
+ goto error;
+
+ if ((error = iicbus_write(parent, &cmd, 1, &sent)))
+ goto error;
+
+ if ((error = iicbus_write(parent, buf, (int)count, &sent)))
+ 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)))
+ goto error;
+
+ if ((error = iicbus_write(parent, &cmd, 1, &sent)))
+ goto error;
+
+ if ((error = iicbus_repeated_start(parent, slave | LSB)))
+ goto error;
+
+ if ((error = iicbus_read(parent, buf, (int)count, &read)))
+ goto error;
+
+error:
+ return (error);
+}
+
+DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
OpenPOWER on IntegriCloud