summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authornsouch <nsouch@FreeBSD.org>1998-09-03 20:51:50 +0000
committernsouch <nsouch@FreeBSD.org>1998-09-03 20:51:50 +0000
commit165178e4262e62b57512165963e7c081da08cf8b (patch)
treebff1057fa9517b8ef557656e717e05c5a8b9d719 /sys
downloadFreeBSD-src-165178e4262e62b57512165963e7c081da08cf8b.zip
FreeBSD-src-165178e4262e62b57512165963e7c081da08cf8b.tar.gz
Submitted by: nsouch
Philips I2C bus generic support other new bus architecture.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/iicbus/if_ic.c468
-rw-r--r--sys/dev/iicbus/iic.c257
-rw-r--r--sys/dev/iicbus/iicbus.c215
-rw-r--r--sys/dev/iicbus/iicbus.h42
-rw-r--r--sys/dev/iicbus/iicbus_if.m89
-rw-r--r--sys/dev/iicbus/iiconf.c207
-rw-r--r--sys/dev/iicbus/iiconf.h115
-rw-r--r--sys/dev/iicbus/iicsmb.c450
8 files changed, 1843 insertions, 0 deletions
diff --git a/sys/dev/iicbus/if_ic.c b/sys/dev/iicbus/if_ic.c
new file mode 100644
index 0000000..52626e0
--- /dev/null
+++ b/sys/dev/iicbus/if_ic.c
@@ -0,0 +1,468 @@
+/*-
+ * 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: if_ic.c,v 1.1.1.11 1998/08/13 17:10:42 son Exp $
+ */
+
+/*
+ * I2C bus IP driver
+ */
+
+#ifdef KERNEL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/time.h>
+#include <sys/malloc.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/netisr.h>
+
+#endif /* KERNEL */
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+
+#include "bpfilter.h"
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+
+#include "iicbus_if.h"
+
+#define ICHDRLEN sizeof(u_int)
+#define ICMTU 1500 /* default mtu */
+
+struct ic_softc {
+ struct ifnet ic_if;
+
+ u_char ic_addr; /* peer I2C address */
+
+ int ic_sending;
+
+ char *ic_obuf;
+ char *ic_ifbuf;
+ char *ic_cp;
+
+ int ic_xfercnt;
+
+ int ic_iferrs;
+};
+
+static devclass_t ic_devclass;
+
+static int icprobe(device_t);
+static int icattach(device_t);
+
+static int icioctl(struct ifnet *, u_long, caddr_t);
+static int icoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
+ struct rtentry *);
+
+static void icintr(device_t, int, char *);
+
+static device_method_t ic_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, icprobe),
+ DEVMETHOD(device_attach, icattach),
+
+ /* iicbus interface */
+ DEVMETHOD(iicbus_intr, icintr),
+
+ { 0, 0 }
+};
+
+static driver_t ic_driver = {
+ "ic",
+ ic_methods,
+ DRIVER_TYPE_MISC,
+ sizeof(struct ic_softc),
+};
+
+/*
+ * icprobe()
+ */
+static int
+icprobe(device_t dev)
+{
+ return (0);
+}
+
+/*
+ * icattach()
+ */
+static int
+icattach(device_t dev)
+{
+ struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
+ struct ifnet *ifp = &sc->ic_if;
+
+ sc->ic_addr = iicbus_get_addr(dev);
+
+ ifp->if_softc = sc;
+ ifp->if_name = "ic";
+ ifp->if_unit = device_get_unit(dev);
+ ifp->if_mtu = ICMTU;
+ ifp->if_flags = IFF_SIMPLEX | IFF_POINTOPOINT | IFF_MULTICAST;
+ ifp->if_ioctl = icioctl;
+ ifp->if_output = icoutput;
+ ifp->if_type = IFT_PARA;
+ ifp->if_hdrlen = 0;
+ ifp->if_addrlen = 0;
+ ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+ if_attach(ifp);
+
+#if NBPFILTER > 0
+ bpfattach(ifp, DLT_NULL, ICHDRLEN);
+#endif
+
+ return (0);
+}
+
+/*
+ * iciotcl()
+ */
+static int
+icioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ device_t icdev = devclass_get_device(ic_devclass, ifp->if_unit);
+ device_t parent = device_get_parent(icdev);
+ struct ic_softc *sc = (struct ic_softc *)device_get_softc(icdev);
+
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ifreq *ifr = (struct ifreq *)data;
+
+ u_char *iptr, *optr;
+ int error;
+
+ switch (cmd) {
+
+ case SIOCSIFDSTADDR:
+ case SIOCAIFADDR:
+ case SIOCSIFADDR:
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ return EAFNOSUPPORT;
+ ifp->if_flags |= IFF_UP;
+ /* FALLTHROUGH */
+ case SIOCSIFFLAGS:
+ if ((!(ifp->if_flags & IFF_UP)) && (ifp->if_flags & IFF_RUNNING)) {
+
+ /* XXX disable PCF */
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /* IFF_UP is not set, try to release the bus anyway */
+ iicbus_release_bus(parent, icdev);
+ break;
+ }
+ if (((ifp->if_flags & IFF_UP)) && (!(ifp->if_flags & IFF_RUNNING))) {
+
+ if ((error = iicbus_request_bus(parent, icdev, IIC_WAIT|IIC_INTR)))
+ return (error);
+
+ sc->ic_obuf = malloc(sc->ic_if.if_mtu + ICHDRLEN,
+ M_DEVBUF, M_WAITOK);
+ if (!sc->ic_obuf) {
+ iicbus_release_bus(parent, icdev);
+ return ENOBUFS;
+ }
+
+ sc->ic_ifbuf = malloc(sc->ic_if.if_mtu + ICHDRLEN,
+ M_DEVBUF, M_WAITOK);
+ if (!sc->ic_ifbuf) {
+ iicbus_release_bus(parent, icdev);
+ return ENOBUFS;
+ }
+
+ iicbus_reset(parent, IIC_FASTEST);
+
+ ifp->if_flags |= IFF_RUNNING;
+ }
+ break;
+
+ case SIOCSIFMTU:
+ /* save previous buffers */
+ iptr = sc->ic_ifbuf;
+ optr = sc->ic_obuf;
+
+ /* allocate input buffer */
+ sc->ic_ifbuf = malloc(ifr->ifr_mtu+ICHDRLEN, M_DEVBUF, M_NOWAIT);
+ if (!sc->ic_ifbuf) {
+
+ sc->ic_ifbuf = iptr;
+ sc->ic_obuf = optr;
+
+ return ENOBUFS;
+ }
+
+ /* allocate output buffer */
+ sc->ic_ifbuf = malloc(ifr->ifr_mtu+ICHDRLEN, M_DEVBUF, M_NOWAIT);
+ if (!sc->ic_obuf) {
+
+ free(sc->ic_ifbuf,M_DEVBUF);
+
+ sc->ic_ifbuf = iptr;
+ sc->ic_obuf = optr;
+
+ return ENOBUFS;
+ }
+
+ if (iptr)
+ free(iptr,M_DEVBUF);
+
+ if (optr)
+ free(optr,M_DEVBUF);
+
+ sc->ic_if.if_mtu = ifr->ifr_mtu;
+ break;
+
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = sc->ic_if.if_mtu;
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (ifr == 0) {
+ return EAFNOSUPPORT; /* XXX */
+ }
+ switch (ifr->ifr_addr.sa_family) {
+
+ case AF_INET:
+ break;
+
+ default:
+ return EAFNOSUPPORT;
+ }
+ break;
+
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * icintr()
+ */
+static void
+icintr (device_t dev, int event, char *ptr)
+{
+ struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
+ int unit = device_get_unit(dev);
+ int s, len;
+ struct mbuf *top;
+ int i;
+
+ s = splhigh();
+
+ switch (event) {
+
+ case INTR_GENERAL:
+ case INTR_START:
+ sc->ic_cp = sc->ic_ifbuf;
+ sc->ic_xfercnt = 0;
+ break;
+
+ case INTR_STOP:
+
+ /* if any error occured during transfert,
+ * drop the packet */
+ if (sc->ic_iferrs)
+ goto err;
+
+ if ((len = sc->ic_xfercnt) == 0)
+ break; /* ignore */
+
+ if (len <= ICHDRLEN)
+ goto err;
+
+ if (IF_QFULL(&ipintrq)) {
+ IF_DROP(&ipintrq);
+ break;
+ }
+
+ len -= ICHDRLEN;
+ sc->ic_if.if_ipackets ++;
+ sc->ic_if.if_ibytes += len;
+
+#if NBPFILTER > 0
+ if (sc->ic_if.if_bpf)
+ bpf_tap(&sc->ic_if, sc->ic_ifbuf, len + ICHDRLEN);
+#endif
+
+ top = m_devget(sc->ic_ifbuf + ICHDRLEN, len, 0, &sc->ic_if, 0);
+
+ if (top) {
+ IF_ENQUEUE(&ipintrq, top);
+ schednetisr(NETISR_IP);
+ }
+ break;
+
+ err:
+ printf("ic%d: errors (%d)!\n", unit, sc->ic_iferrs);
+
+ sc->ic_iferrs = 0; /* reset error count */
+ sc->ic_if.if_ierrors ++;
+
+ break;
+
+ case INTR_RECEIVE:
+ if (sc->ic_xfercnt >= sc->ic_if.if_mtu+ICHDRLEN) {
+ sc->ic_iferrs ++;
+
+ } else {
+ *sc->ic_cp++ = *ptr;
+ sc->ic_xfercnt ++;
+ }
+ break;
+
+ case INTR_NOACK: /* xfer terminated by master */
+ break;
+
+ case INTR_TRANSMIT:
+ *ptr = 0xff; /* XXX */
+ break;
+
+ case INTR_ERROR:
+ sc->ic_iferrs ++;
+ break;
+
+ default:
+ panic("%s: unknown event (%d)!", __FUNCTION__, event);
+ }
+
+ splx(s);
+ return;
+}
+
+/*
+ * icoutput()
+ */
+static int
+icoutput(struct ifnet *ifp, struct mbuf *m,
+ struct sockaddr *dst, struct rtentry *rt)
+{
+ device_t icdev = devclass_get_device(ic_devclass, ifp->if_unit);
+ device_t parent = device_get_parent(icdev);
+ struct ic_softc *sc = (struct ic_softc *)device_get_softc(icdev);
+
+ int s, len, sent;
+ struct mbuf *mm;
+ u_char *cp;
+ u_int hdr = dst->sa_family;
+
+ ifp->if_flags |= IFF_RUNNING;
+
+ s = splhigh();
+
+ /* already sending? */
+ if (sc->ic_sending) {
+ ifp->if_oerrors ++;
+ goto error;
+ }
+
+ /* insert header */
+ bcopy ((char *)&hdr, sc->ic_obuf, ICHDRLEN);
+
+ cp = sc->ic_obuf + ICHDRLEN;
+ len = 0;
+ mm = m;
+ do {
+ if (len + mm->m_len > sc->ic_if.if_mtu) {
+ /* packet to large */
+ ifp->if_oerrors ++;
+ goto error;
+ }
+
+ bcopy(mtod(mm,char *), cp, mm->m_len);
+ cp += mm->m_len;
+ len += mm->m_len;
+
+ } while ((mm = mm->m_next));
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf) {
+ struct mbuf m0, *n = m;
+
+ /*
+ * We need to prepend the address family as
+ * a four byte field. Cons up a dummy header
+ * to pacify bpf. This is safe because bpf
+ * will only read from the mbuf (i.e., it won't
+ * try to free it or keep a pointer a to it).
+ */
+ m0.m_next = m;
+ m0.m_len = sizeof(u_int);
+ m0.m_data = (char *)&hdr;
+ n = &m0;
+
+ bpf_mtap(ifp, n);
+ }
+#endif
+
+ sc->ic_sending = 1;
+
+ m_freem(m);
+ splx(s);
+
+ /* send the packet */
+ if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf,
+ len + ICHDRLEN, &sent))
+
+ ifp->if_oerrors ++;
+ else {
+ ifp->if_opackets ++;
+ ifp->if_obytes += len;
+ }
+
+ sc->ic_sending = 0;
+
+ return (0);
+
+error:
+ m_freem(m);
+ splx(s);
+
+ return(0);
+}
+
+DRIVER_MODULE(ic, iicbus, ic_driver, ic_devclass, 0, 0);
diff --git a/sys/dev/iicbus/iic.c b/sys/dev/iicbus/iic.c
new file mode 100644
index 0000000..c22515d
--- /dev/null
+++ b/sys/dev/iicbus/iic.c
@@ -0,0 +1,257 @@
+/*-
+ * 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: iic.c,v 1.1.2.9 1998/08/13 17:10:42 son Exp $
+ *
+ */
+#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 <machine/iic.h>
+
+#include "iicbus_if.h"
+
+#define BUFSIZE 1024
+
+struct iic_softc {
+
+ u_char sc_addr; /* address on iicbus */
+ int sc_count; /* >0 if device opened */
+
+ char sc_buffer[BUFSIZE]; /* output buffer */
+ char sc_inbuf[BUFSIZE]; /* input buffer */
+};
+
+#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 devclass_t iic_devclass;
+
+static device_method_t iic_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, iic_probe),
+ DEVMETHOD(device_attach, iic_attach),
+
+ /* iicbus interface */
+ DEVMETHOD(iicbus_intr, iicbus_generic_intr),
+
+ { 0, 0 }
+};
+
+static driver_t iic_driver = {
+ "iic",
+ iic_methods,
+ DRIVER_TYPE_MISC,
+ 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;
+
+#define CDEV_MAJOR 15
+static struct cdevsw iic_cdevsw =
+ { iicopen, iicclose, iicread, iicwrite, /*15*/
+ iicioctl, nullstop, nullreset, nodevtotty, /*iic*/
+ seltrue, nommap, nostrat, "iic", NULL, -1 };
+
+/*
+ * iicprobe()
+ */
+static int
+iic_probe(device_t dev)
+{
+ struct iic_softc *sc = (struct iic_softc *)device_get_softc(dev);
+
+ sc->sc_addr = iicbus_get_addr(dev);
+
+ /* XXX detect chip with start/stop conditions */
+
+ return (0);
+}
+
+/*
+ * iicattach()
+ */
+static int
+iic_attach(device_t dev)
+{
+ struct iic_softc *sc = (struct iic_softc *)device_get_softc(dev);
+
+ return (0);
+}
+
+static int
+iicopen (dev_t dev, int flags, int fmt, struct proc *p)
+{
+ 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(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ 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!", __FUNCTION__);
+
+ return (0);
+}
+
+static int
+iicwrite(dev_t 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)
+ return (EINVAL);
+
+ if (sc->sc_count == 0)
+ return (EINVAL);
+
+ 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);
+
+ return(error);
+}
+
+static int
+iicread(dev_t 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)
+ return (EINVAL);
+
+ if (sc->sc_count == 0)
+ return (EINVAL);
+
+ /* 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!", __FUNCTION__);
+
+ return (uiomove(sc->sc_inbuf, bufsize, uio));
+}
+
+static int
+iicioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
+{
+ device_t iicdev = IIC_DEVICE(minor(dev));
+ struct iic_softc *sc = IIC_SOFTC(minor(dev));
+ int error;
+ device_t parent = device_get_parent(iicdev);
+
+ if (!sc)
+ return (EINVAL);
+
+ switch (cmd) {
+ case I2CSTART:
+ error = iicbus_start(parent, sc->sc_addr);
+ break;
+
+ case I2CSTOP:
+ error = iicbus_stop(parent);
+ break;
+
+ case I2CRSTCARD:
+ error = iicbus_reset(parent, 0);
+ break;
+
+ default:
+ error = ENODEV;
+ }
+
+ return (error);
+}
+
+static int iic_devsw_installed = 0;
+
+static void
+iic_drvinit(void *unused)
+{
+ dev_t dev;
+
+ if( ! iic_devsw_installed ) {
+ dev = makedev(CDEV_MAJOR,0);
+ cdevsw_add(&dev,&iic_cdevsw,NULL);
+ iic_devsw_installed = 1;
+ }
+}
+
+CDEV_DRIVER_MODULE(iic, iicbus, iic_driver, iic_devclass, CDEV_MAJOR,
+ iic_cdevsw, 0, 0);
+
+SYSINIT(iicdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,iic_drvinit,NULL)
diff --git a/sys/dev/iicbus/iicbus.c b/sys/dev/iicbus/iicbus.c
new file mode 100644
index 0000000..41a0cea
--- /dev/null
+++ b/sys/dev/iicbus/iicbus.c
@@ -0,0 +1,215 @@
+/*-
+ * 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: iicbus.c,v 1.1.2.7 1998/08/29 16:54:16 son Exp $
+ *
+ */
+
+/*
+ * Autoconfiguration and support routines for the Philips serial I2C bus
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <machine/clock.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+
+#include "iicbus_if.h"
+
+#define DEVTOIICBUS(dev) ((struct iicbus_device*)device_get_ivars(dev))
+
+/*
+ * structure used to attach devices to the I2C bus
+ */
+struct iicbus_device {
+ const char *iicd_name; /* device name */
+ int iicd_class; /* driver or slave device class */
+ const char *iicd_desc; /* device descriptor */
+ u_char iicd_addr; /* address of the device */
+ int iicd_alive; /* 1 if device found */
+};
+
+/*
+ * Common I2C addresses
+ */
+#define I2C_GENERAL_CALL 0x0
+#define I2C_MASTER_ADDRESS 0xaa
+#define I2C_INET_ADDRESS 0xaa
+
+#define MAXSLAVE 256
+
+#define IICBUS_UNKNOWN_CLASS 0
+#define IICBUS_DEVICE_CLASS 1
+#define IICBUS_DRIVER_CLASS 2
+
+/*
+ * list of known devices
+ */
+struct iicbus_device iicbus_children[] = {
+ { "iic", IICBUS_DRIVER_CLASS, "General Call", I2C_GENERAL_CALL },
+ { "iicsmb", IICBUS_DRIVER_CLASS, "I2C to SMB bridge" },
+ { "iic", IICBUS_DEVICE_CLASS, "PCF8574 I2C to 8 bits parallel i/o", 64},
+ { "iic", IICBUS_DEVICE_CLASS, "PCF8584 as slave", I2C_MASTER_ADDRESS },
+ { "ic", IICBUS_DEVICE_CLASS, "network interface", I2C_INET_ADDRESS },
+ { NULL, 0 }
+};
+
+static devclass_t iicbus_devclass;
+
+/*
+ * Device methods
+ */
+static int iicbus_probe(device_t);
+static int iicbus_attach(device_t);
+static void iicbus_print_child(device_t, device_t);
+static int iicbus_read_ivar(device_t , device_t, int, u_long *);
+
+static device_method_t iicbus_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, iicbus_probe),
+ DEVMETHOD(device_attach, iicbus_attach),
+ DEVMETHOD(device_detach, bus_generic_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, iicbus_print_child),
+ DEVMETHOD(bus_read_ivar, iicbus_read_ivar),
+ DEVMETHOD(bus_write_ivar, bus_generic_write_ivar),
+ DEVMETHOD(bus_create_intr, bus_generic_create_intr),
+ DEVMETHOD(bus_connect_intr, bus_generic_connect_intr),
+
+ { 0, 0 }
+};
+
+static driver_t iicbus_driver = {
+ "iicbus",
+ iicbus_methods,
+ DRIVER_TYPE_MISC,
+ sizeof(struct iicbus_softc),
+};
+
+/*
+ * At 'probe' time, we add all the devices which we know about to the
+ * bus. The generic attach routine will probe and attach them if they
+ * are alive.
+ */
+static int
+iicbus_probe(device_t dev)
+{
+ struct iicbus_softc *sc = device_get_softc(dev);
+ struct iicbus_device *iicdev;
+ device_t child;
+
+ /* XXX should query parent */
+ sc->ownaddr = I2C_MASTER_ADDRESS;
+
+ iicbus_reset(dev, IIC_FASTEST);
+
+ for (iicdev = iicbus_children; iicdev->iicd_name; iicdev++) {
+
+ /* probe devices, not drivers */
+ switch (iicdev->iicd_class) {
+ case IICBUS_DEVICE_CLASS:
+ if (!iicbus_start(dev, iicdev->iicd_addr)) {
+ iicbus_stop(dev);
+ iicdev->iicd_alive = 1;
+ }
+ break;
+ case IICBUS_DRIVER_CLASS:
+ iicdev->iicd_addr = sc->ownaddr;
+ break;
+ default:
+ panic("%s: unknown class!", __FUNCTION__);
+ }
+
+ child = device_add_child(dev, iicdev->iicd_name, -1, iicdev);
+ device_set_desc(child, iicdev->iicd_desc);
+ }
+
+ return (0);
+}
+
+static int
+iicbus_attach(device_t dev)
+{
+ bus_generic_attach(dev);
+
+ return (0);
+}
+
+int
+iicbus_generic_intr(device_t dev, int event, char *buf)
+{
+ return (0);
+}
+
+static void
+iicbus_print_child(device_t bus, device_t dev)
+{
+ struct iicbus_device* iicdev = DEVTOIICBUS(dev);
+
+ switch (iicdev->iicd_class) {
+ case IICBUS_DEVICE_CLASS:
+ printf(" on %s%d addr %d %s", device_get_name(bus),
+ device_get_unit(bus), iicdev->iicd_addr,
+ (iicdev->iicd_alive) ? "found" : "not found");
+ break;
+
+ case IICBUS_DRIVER_CLASS:
+ printf(" on %s%d", device_get_name(bus),
+ device_get_unit(bus));
+ break;
+
+ default:
+ panic("%s: unknown class!", __FUNCTION__);
+ }
+
+ return;
+}
+
+static int
+iicbus_read_ivar(device_t bus, device_t dev, int index, u_long* result)
+{
+ struct iicbus_device* iicdev = DEVTOIICBUS(dev);
+
+ switch (index) {
+ case IICBUS_IVAR_ADDR:
+ *result = (u_long)iicdev->iicd_addr;
+ break;
+
+ default:
+ return (ENOENT);
+ }
+
+ return (0);
+}
+
+DRIVER_MODULE(iicbus, pcf, iicbus_driver, iicbus_devclass, 0, 0);
diff --git a/sys/dev/iicbus/iicbus.h b/sys/dev/iicbus/iicbus.h
new file mode 100644
index 0000000..e845252
--- /dev/null
+++ b/sys/dev/iicbus/iicbus.h
@@ -0,0 +1,42 @@
+/*-
+ * 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: iicbus.h,v 1.1.2.3 1998/08/13 17:10:43 son Exp $
+ *
+ */
+#ifndef __IICBUS_H
+#define __IICBUS_H
+
+struct iicbus_softc {
+
+ u_char ownaddr; /* address of the adapter */
+ device_t owner; /* iicbus owner device structure */
+};
+
+extern devclass_t iicbus_devclass;
+
+extern int iicbus_generic_intr(device_t dev, int event, char *buf);
+
+#endif
diff --git a/sys/dev/iicbus/iicbus_if.m b/sys/dev/iicbus/iicbus_if.m
new file mode 100644
index 0000000..bf7efeb
--- /dev/null
+++ b/sys/dev/iicbus/iicbus_if.m
@@ -0,0 +1,89 @@
+#
+# 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: iicbus_if.m,v 1.1.2.4 1998/08/13 17:10:43 son Exp $
+#
+
+INTERFACE iicbus
+
+#
+# Interprete interrupt
+#
+METHOD int intr {
+ device_t dev;
+ int event;
+ char *buf;
+};
+
+#
+# Send REPEATED_START condition
+#
+METHOD int repeated_start {
+ device_t dev;
+ u_char slave;
+};
+
+#
+# Send START condition
+#
+METHOD int start {
+ device_t dev;
+ u_char slave;
+};
+
+#
+# Send STOP condition
+#
+METHOD int stop {
+ device_t dev;
+};
+
+#
+# Read from I2C bus
+#
+METHOD int read {
+ device_t dev;
+ char *buf;
+ int len;
+ int *bytes;
+};
+
+#
+# Write to the I2C bus
+#
+METHOD int write {
+ device_t dev;
+ char *buf;
+ int len;
+ int *bytes;
+};
+
+#
+# Reset I2C bus
+#
+METHOD int reset {
+ device_t dev;
+ u_char speed;
+};
diff --git a/sys/dev/iicbus/iiconf.c b/sys/dev/iicbus/iiconf.c
new file mode 100644
index 0000000..b037b7c
--- /dev/null
+++ b/sys/dev/iicbus/iiconf.c
@@ -0,0 +1,207 @@
+/*-
+ * 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: iiconf.c,v 1.1.1.11 1998/08/29 17:02:05 son Exp $
+ *
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+#include "iicbus_if.h"
+
+/*
+ * iicbus_intr()
+ */
+void
+iicbus_intr(device_t bus, int event, char *buf)
+{
+ struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
+
+ /* call owner's intr routine */
+ if (sc->owner)
+ IICBUS_INTR(sc->owner, event, buf);
+
+ return;
+}
+
+/*
+ * iicbus_alloc_bus()
+ *
+ * Allocate a new bus connected to the given parent device
+ */
+device_t
+iicbus_alloc_bus(device_t parent)
+{
+ device_t child;
+
+ /* add the bus to the parent */
+ child = device_add_child(parent, "iicbus", -1, NULL);
+
+ if (child)
+ device_set_desc(child, "Philips I2C bus");
+
+ return (child);
+}
+
+/*
+ * iicbus_request_bus()
+ *
+ * Allocate the device to perform transfers.
+ *
+ * how : IIC_WAIT or IIC_DONTWAIT
+ */
+int
+iicbus_request_bus(device_t bus, device_t dev, int how)
+{
+ struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
+ int s, error = 0;
+
+ while (!error) {
+ s = splhigh();
+ if (sc->owner) {
+ splx(s);
+
+ switch (how) {
+ case (IIC_WAIT | IIC_INTR):
+ error = tsleep(sc, IICPRI|PCATCH, "iicreq", 0);
+ break;
+
+ case (IIC_WAIT | IIC_NOINTR):
+ error = tsleep(sc, IICPRI, "iicreq", 0);
+ break;
+
+ default:
+ return (EWOULDBLOCK);
+ break;
+ }
+
+ } else {
+ sc->owner = dev;
+
+ splx(s);
+ return (0);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * iicbus_release_bus()
+ *
+ * Release the device allocated with iicbus_request_dev()
+ */
+int
+iicbus_release_bus(device_t bus, device_t dev)
+{
+ struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
+ int s;
+
+ s = splhigh();
+ if (sc->owner != dev) {
+ splx(s);
+ return (EACCES);
+ }
+
+ sc->owner = 0;
+ splx(s);
+
+ /* wakeup waiting processes */
+ wakeup(sc);
+
+ return (0);
+}
+
+/*
+ * iicbus_block_write()
+ *
+ * Write a block of data to slave ; start/stop protocol managed
+ */
+int
+iicbus_block_write(device_t bus, u_char slave, char *buf, int len, int *sent)
+{
+ u_char addr = slave & ~LSB;
+ int error;
+
+ if ((error = iicbus_start(bus, addr)))
+ return (error);
+
+ error = iicbus_write(bus, buf, len, sent);
+
+ iicbus_stop(bus);
+
+ return (error);
+}
+
+/*
+ * iicbus_block_read()
+ *
+ * Read a block of data from slave ; start/stop protocol managed
+ */
+int
+iicbus_block_read(device_t bus, u_char slave, char *buf, int len, int *read)
+{
+ u_char addr = slave | LSB;
+ int error;
+
+ if ((error = iicbus_start(bus, addr)))
+ return (error);
+
+ error = iicbus_read(bus, buf, len, read);
+
+ /* STOP condition sent at adapter level */
+
+ return (error);
+}
+
+/*
+ * iicbus_get_addr()
+ *
+ * Get the I2C 7 bits address of the device
+ */
+u_char
+iicbus_get_addr(device_t dev)
+{
+ u_long addr;
+ device_t parent = device_get_parent(dev);
+
+ BUS_READ_IVAR(parent, dev, IICBUS_IVAR_ADDR, &addr);
+
+ return ((u_char)addr);
+}
+
+u_char
+iicbus_get_own_address(device_t bus)
+{
+ struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
+
+ return (sc->ownaddr);
+}
diff --git a/sys/dev/iicbus/iiconf.h b/sys/dev/iicbus/iiconf.h
new file mode 100644
index 0000000..9bf0ed3
--- /dev/null
+++ b/sys/dev/iicbus/iiconf.h
@@ -0,0 +1,115 @@
+/*-
+ * 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: iiconf.h,v 1.1.1.10 1998/08/13 17:10:43 son Exp $
+ */
+#ifndef __IICONF_H
+#define __IICONF_H
+
+#include <sys/queue.h>
+
+#define IICPRI PZERO+8 /* XXX sleep/wakeup queue priority */
+
+#define n(flags) (~(flags) & (flags))
+
+#define LSB 0x1
+
+/*
+ * How tsleep() is called in iic_request_bus().
+ */
+#define IIC_DONTWAIT 0
+#define IIC_NOINTR 0
+#define IIC_WAIT 0x1
+#define IIC_INTR 0x2
+
+/*
+ * i2c modes
+ */
+#define IIC_MASTER 0x1
+#define IIC_SLAVE 0x2
+#define IIC_POLLED 0x4
+
+/*
+ * i2c speed
+ */
+#define IIC_UNKNOWN 0x0
+#define IIC_SLOW 0x1
+#define IIC_FAST 0x2
+#define IIC_FASTEST 0x3
+
+/*
+ * interrupt events
+ */
+#define INTR_GENERAL 0x1 /* general call received */
+#define INTR_START 0x2 /* the I2C interface is addressed */
+#define INTR_STOP 0x3 /* stop condition received */
+#define INTR_RECEIVE 0x4 /* character received */
+#define INTR_TRANSMIT 0x5 /* character to transmit */
+#define INTR_ERROR 0x6 /* error */
+#define INTR_NOACK 0x7 /* no ack from master receiver */
+
+/*
+ * adapter layer errors
+ */
+#define IIC_NOERR 0x0 /* no error occured */
+#define IIC_EBUSERR 0x1 /* bus error */
+#define IIC_ENOACK 0x2 /* ack not received until timeout */
+#define IIC_ETIMEOUT 0x3 /* timeout */
+#define IIC_EBUSBSY 0x4 /* bus busy */
+#define IIC_ESTATUS 0x5 /* status error */
+#define IIC_EUNDERFLOW 0x6 /* slave ready for more data */
+#define IIC_EOVERFLOW 0x7 /* too much data */
+
+/*
+ * ivars codes
+ */
+#define IICBUS_IVAR_ADDR 0x1 /* I2C address of the device */
+
+extern int iicbus_request_bus(device_t, device_t, int);
+extern int iicbus_release_bus(device_t, device_t);
+extern device_t iicbus_alloc_bus(device_t);
+
+extern void iicbus_intr(device_t, int, char *);
+
+#define iicbus_repeated_start(bus,slave) \
+ (IICBUS_REPEATED_START(device_get_parent(bus), slave))
+#define iicbus_start(bus,slave) \
+ (IICBUS_START(device_get_parent(bus), slave))
+#define iicbus_stop(bus) \
+ (IICBUS_STOP(device_get_parent(bus)))
+#define iicbus_reset(bus,speed) \
+ (IICBUS_RESET(device_get_parent(bus), speed))
+#define iicbus_write(bus,buf,len,sent) \
+ (IICBUS_WRITE(device_get_parent(bus), buf, len, sent))
+#define iicbus_read(bus,buf,len,sent) \
+ (IICBUS_READ(device_get_parent(bus), buf, len, sent))
+
+extern int iicbus_block_write(device_t, u_char, char *, int, int *);
+extern int iicbus_block_read(device_t, u_char, char *, int, int *);
+
+extern u_char iicbus_get_addr(device_t);
+extern u_char iicbus_get_own_address(device_t);
+
+#endif
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