Submitted by: nsouch

Philips I2C bus generic support other new bus architecture.
This commit is contained in:
nsouch 1998-09-03 20:51:50 +00:00
commit 165178e426
8 changed files with 1843 additions and 0 deletions

468
sys/dev/iicbus/if_ic.c Normal file
View File

@ -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);

257
sys/dev/iicbus/iic.c Normal file
View File

@ -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)

215
sys/dev/iicbus/iicbus.c Normal file
View File

@ -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);

42
sys/dev/iicbus/iicbus.h Normal file
View File

@ -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

View File

@ -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;
};

207
sys/dev/iicbus/iiconf.c Normal file
View File

@ -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);
}

115
sys/dev/iicbus/iiconf.h Normal file
View File

@ -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

450
sys/dev/iicbus/iicsmb.c Normal file
View File

@ -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);