Hello Vijay, the patch set works fine on the Beagle (except for the i2c tool - but as discussed that will work after the next libbsd update). The patches look OK for me.
Best regards Christian On 08/06/2019 14:49, Vijay Kumar Banerjee wrote: > --- > freebsd/sys/dev/iicbus/iic.c | 506 ++++++++++++++++++++++++++ > freebsd/sys/dev/iicbus/iic.h | 72 ++++ > freebsd/sys/dev/iicbus/iicbus.c | 378 ++++++++++++++++++++ > freebsd/sys/dev/iicbus/iicbus.h | 83 +++++ > freebsd/sys/dev/iicbus/iiconf.c | 527 ++++++++++++++++++++++++++++ > freebsd/sys/dev/iicbus/iiconf.h | 162 +++++++++ > freebsd/sys/dev/iicbus/ofw_iicbus.c | 242 +++++++++++++ > 7 files changed, 1970 insertions(+) > create mode 100644 freebsd/sys/dev/iicbus/iic.c > create mode 100644 freebsd/sys/dev/iicbus/iic.h > create mode 100644 freebsd/sys/dev/iicbus/iicbus.c > create mode 100644 freebsd/sys/dev/iicbus/iicbus.h > create mode 100644 freebsd/sys/dev/iicbus/iiconf.c > create mode 100644 freebsd/sys/dev/iicbus/iiconf.h > create mode 100644 freebsd/sys/dev/iicbus/ofw_iicbus.c > > diff --git a/freebsd/sys/dev/iicbus/iic.c b/freebsd/sys/dev/iicbus/iic.c > new file mode 100644 > index 00000000..dc9c06f9 > --- /dev/null > +++ b/freebsd/sys/dev/iicbus/iic.c > @@ -0,0 +1,506 @@ > +#include <machine/rtems-bsd-kernel-space.h> > + > +/*- > + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD > + * > + * Copyright (c) 1998, 2001 Nicolas Souchu > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + * > + * $FreeBSD$ > + * > + */ > +#include <sys/param.h> > +#include <sys/bus.h> > +#include <sys/conf.h> > +#include <sys/fcntl.h> > +#include <sys/lock.h> > +#include <sys/kernel.h> > +#include <sys/malloc.h> > +#include <sys/module.h> > +#include <sys/sx.h> > +#include <sys/systm.h> > +#include <sys/uio.h> > +#include <sys/errno.h> > + > +#include <dev/iicbus/iiconf.h> > +#include <dev/iicbus/iicbus.h> > +#include <dev/iicbus/iic.h> > + > +#include <rtems/bsd/local/iicbus_if.h> > + > +struct iic_softc { > + device_t sc_dev; > + struct cdev *sc_devnode; > +}; > + > +struct iic_cdevpriv { > + struct sx lock; > + struct iic_softc *sc; > + bool started; > + uint8_t addr; > +}; > + > + > +#define IIC_LOCK(cdp) sx_xlock(&(cdp)->lock) > +#define IIC_UNLOCK(cdp) sx_xunlock(&(cdp)->lock) > + > +static MALLOC_DEFINE(M_IIC, "iic", "I2C device data"); > + > +static int iic_probe(device_t); > +static int iic_attach(device_t); > +static int iic_detach(device_t); > +static void iic_identify(driver_t *driver, device_t parent); > +static void iicdtor(void *data); > +static int iicuio_move(struct iic_cdevpriv *priv, struct uio *uio, int last); > +static int iicuio(struct cdev *dev, struct uio *uio, int ioflag); > +static int iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int > flags); > + > +static devclass_t iic_devclass; > + > +static device_method_t iic_methods[] = { > + /* device interface */ > + DEVMETHOD(device_identify, iic_identify), > + DEVMETHOD(device_probe, iic_probe), > + DEVMETHOD(device_attach, iic_attach), > + DEVMETHOD(device_detach, iic_detach), > + > + /* iicbus interface */ > + DEVMETHOD(iicbus_intr, iicbus_generic_intr), > + > + { 0, 0 } > +}; > + > +static driver_t iic_driver = { > + "iic", > + iic_methods, > + sizeof(struct iic_softc), > +}; > + > +static d_open_t iicopen; > +static d_ioctl_t iicioctl; > + > +static struct cdevsw iic_cdevsw = { > + .d_version = D_VERSION, > + .d_open = iicopen, > + .d_read = iicuio, > + .d_write = iicuio, > + .d_ioctl = iicioctl, > + .d_name = "iic", > +}; > + > +static void > +iic_identify(driver_t *driver, device_t parent) > +{ > + > + if (device_find_child(parent, "iic", -1) == NULL) > + BUS_ADD_CHILD(parent, 0, "iic", -1); > +} > + > +static int > +iic_probe(device_t dev) > +{ > + if (iicbus_get_addr(dev) > 0) > + return (ENXIO); > + > + device_set_desc(dev, "I2C generic I/O"); > + > + return (0); > +} > + > +static int > +iic_attach(device_t dev) > +{ > + struct iic_softc *sc; > + > + sc = device_get_softc(dev); > + sc->sc_dev = dev; > + sc->sc_devnode = make_dev(&iic_cdevsw, device_get_unit(dev), > + UID_ROOT, GID_WHEEL, > + 0600, "iic%d", device_get_unit(dev)); > + if (sc->sc_devnode == NULL) { > + device_printf(dev, "failed to create character device\n"); > + return (ENXIO); > + } > + sc->sc_devnode->si_drv1 = sc; > + > + return (0); > +} > + > +static int > +iic_detach(device_t dev) > +{ > + struct iic_softc *sc; > + > + sc = device_get_softc(dev); > + > + if (sc->sc_devnode) > + destroy_dev(sc->sc_devnode); > + > + return (0); > +} > + > +static int > +iicopen(struct cdev *dev, int flags, int fmt, struct thread *td) > +{ > + struct iic_cdevpriv *priv; > + int error; > + > + priv = malloc(sizeof(*priv), M_IIC, M_WAITOK | M_ZERO); > + > + sx_init(&priv->lock, "iic"); > + priv->sc = dev->si_drv1; > + > + error = devfs_set_cdevpriv(priv, iicdtor); > + if (error != 0) > + free(priv, M_IIC); > + > + return (error); > +} > + > +static void > +iicdtor(void *data) > +{ > + device_t iicdev, parent; > + struct iic_cdevpriv *priv; > + > + priv = data; > + KASSERT(priv != NULL, ("iic cdevpriv should not be NULL!")); > + > + iicdev = priv->sc->sc_dev; > + parent = device_get_parent(iicdev); > + > + if (priv->started) { > + iicbus_stop(parent); > + iicbus_reset(parent, IIC_UNKNOWN, 0, NULL); > + iicbus_release_bus(parent, iicdev); > + } > + > + sx_destroy(&priv->lock); > + free(priv, M_IIC); > +} > + > +static int > +iicuio_move(struct iic_cdevpriv *priv, struct uio *uio, int last) > +{ > + device_t parent; > + int error, num_bytes, transferred_bytes, written_bytes; > + char buffer[128]; > + > + parent = device_get_parent(priv->sc->sc_dev); > + error = 0; > + > + /* > + * We can only transfer up to sizeof(buffer) bytes in 1 shot, so loop > until > + * everything has been transferred. > + */ > + while ((error == 0) && (uio->uio_resid > 0)) { > + > + num_bytes = MIN(uio->uio_resid, sizeof(buffer)); > + transferred_bytes = 0; > + > + if (uio->uio_rw == UIO_WRITE) { > + error = uiomove(buffer, num_bytes, uio); > + > + while ((error == 0) && (transferred_bytes < num_bytes)) > { > + written_bytes = 0; > + error = iicbus_write(parent, > &buffer[transferred_bytes], > + num_bytes - transferred_bytes, > &written_bytes, 0); > + transferred_bytes += written_bytes; > + } > + > + } else if (uio->uio_rw == UIO_READ) { > + error = iicbus_read(parent, buffer, > + num_bytes, &transferred_bytes, > + ((uio->uio_resid <= sizeof(buffer)) ? last : 0), 0); > + if (error == 0) > + error = uiomove(buffer, transferred_bytes, uio); > + } > + } > + > + return (error); > +} > + > +static int > +iicuio(struct cdev *dev, struct uio *uio, int ioflag) > +{ > + device_t parent; > + struct iic_cdevpriv *priv; > + int error; > + uint8_t addr; > + > + priv = NULL; > + error = devfs_get_cdevpriv((void**)&priv); > + > + if (error != 0) > + return (error); > + KASSERT(priv != NULL, ("iic cdevpriv should not be NULL!")); > + > + IIC_LOCK(priv); > + if (priv->started || (priv->addr == 0)) { > + IIC_UNLOCK(priv); > + return (ENXIO); > + } > + parent = device_get_parent(priv->sc->sc_dev); > + > + error = iicbus_request_bus(parent, priv->sc->sc_dev, > + (ioflag & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | IIC_INTR)); > + if (error != 0) { > + IIC_UNLOCK(priv); > + return (error); > + } > + > + if (uio->uio_rw == UIO_READ) > + addr = priv->addr | LSB; > + else > + addr = priv->addr & ~LSB; > + > + error = iicbus_start(parent, addr, 0); > + if (error != 0) > + { > + iicbus_release_bus(parent, priv->sc->sc_dev); > + IIC_UNLOCK(priv); > + return (error); > + } > + > + error = iicuio_move(priv, uio, IIC_LAST_READ); > + > + iicbus_stop(parent); > + iicbus_release_bus(parent, priv->sc->sc_dev); > + IIC_UNLOCK(priv); > + return (error); > +} > + > +static int > +iicrdwr(struct iic_cdevpriv *priv, struct iic_rdwr_data *d, int flags) > +{ > + struct iic_msg *buf, *m; > + void **usrbufs; > + device_t iicdev, parent; > + int error; > + uint32_t i; > + > + iicdev = priv->sc->sc_dev; > + parent = device_get_parent(iicdev); > + error = 0; > + > + if (d->nmsgs > IIC_RDRW_MAX_MSGS) > + return (EINVAL); > + > + buf = malloc(sizeof(*d->msgs) * d->nmsgs, M_IIC, M_WAITOK); > + > + error = copyin(d->msgs, buf, sizeof(*d->msgs) * d->nmsgs); > + if (error != 0) { > + free(buf, M_IIC); > + return (error); > + } > + > + /* Alloc kernel buffers for userland data, copyin write data */ > + usrbufs = malloc(sizeof(void *) * d->nmsgs, M_IIC, M_WAITOK | M_ZERO); > + > + for (i = 0; i < d->nmsgs; i++) { > + m = &(buf[i]); > + usrbufs[i] = m->buf; > + > + /* > + * At least init the buffer to NULL so we can safely free() it > later. > + * If the copyin() to buf failed, don't try to malloc bogus > m->len. > + */ > + m->buf = NULL; > + if (error != 0) > + continue; > + > + /* m->len is uint16_t, so allocation size is capped at 64K. */ > + m->buf = malloc(m->len, M_IIC, M_WAITOK); > + if (!(m->flags & IIC_M_RD)) > + error = copyin(usrbufs[i], m->buf, m->len); > + } > + > + if (error == 0) > + error = iicbus_request_bus(parent, iicdev, > + (flags & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | > IIC_INTR)); > + > + if (error == 0) { > + error = iicbus_transfer(iicdev, buf, d->nmsgs); > + iicbus_release_bus(parent, iicdev); > + } > + > + /* Copyout all read segments, free up kernel buffers */ > + for (i = 0; i < d->nmsgs; i++) { > + m = &(buf[i]); > + if ((error == 0) && (m->flags & IIC_M_RD)) > + error = copyout(m->buf, usrbufs[i], m->len); > + free(m->buf, M_IIC); > + } > + > + free(usrbufs, M_IIC); > + free(buf, M_IIC); > + return (error); > +} > + > +static int > +iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct > thread *td) > +{ > + device_t parent, iicdev; > + struct iiccmd *s; > + struct uio ubuf; > + struct iovec uvec; > + struct iic_cdevpriv *priv; > + int error; > + > + s = (struct iiccmd *)data; > + error = devfs_get_cdevpriv((void**)&priv); > + if (error != 0) > + return (error); > + > + KASSERT(priv != NULL, ("iic cdevpriv should not be NULL!")); > + > + iicdev = priv->sc->sc_dev; > + parent = device_get_parent(iicdev); > + IIC_LOCK(priv); > + > + > + switch (cmd) { > + case I2CSTART: > + if (priv->started) { > + error = EINVAL; > + break; > + } > + error = iicbus_request_bus(parent, iicdev, > + (flags & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | > IIC_INTR)); > + > + if (error == 0) > + error = iicbus_start(parent, s->slave, 0); > + > + if (error == 0) { > + priv->addr = s->slave; > + priv->started = true; > + } else > + iicbus_release_bus(parent, iicdev); > + > + break; > + > + case I2CSTOP: > + if (priv->started) { > + error = iicbus_stop(parent); > + iicbus_release_bus(parent, iicdev); > + priv->started = false; > + } > + > + break; > + > + case I2CRSTCARD: > + /* > + * Bus should be owned before we reset it. > + * We allow the bus to be already owned as the result of an > in-progress > + * sequence; however, bus reset will always be followed by > release > + * (a new start is presumably needed for I/O anyway). */ > + if (!priv->started) > + error = iicbus_request_bus(parent, iicdev, > + (flags & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | > IIC_INTR)); > + > + if (error == 0) { > + error = iicbus_reset(parent, IIC_UNKNOWN, 0, NULL); > + /* > + * Ignore IIC_ENOADDR as it only means we have a > master-only > + * controller. > + */ > + if (error == IIC_ENOADDR) > + error = 0; > + > + iicbus_release_bus(parent, iicdev); > + priv->started = false; > + } > + break; > + > + case I2CWRITE: > + if (!priv->started) { > + error = EINVAL; > + break; > + } > + uvec.iov_base = s->buf; > + uvec.iov_len = s->count; > + ubuf.uio_iov = &uvec; > + ubuf.uio_iovcnt = 1; > + ubuf.uio_segflg = UIO_USERSPACE; > + ubuf.uio_td = td; > + ubuf.uio_resid = s->count; > + ubuf.uio_offset = 0; > + ubuf.uio_rw = UIO_WRITE; > + error = iicuio_move(priv, &ubuf, 0); > + break; > + > + case I2CREAD: > + if (!priv->started) { > + error = EINVAL; > + break; > + } > + uvec.iov_base = s->buf; > + uvec.iov_len = s->count; > + ubuf.uio_iov = &uvec; > + ubuf.uio_iovcnt = 1; > + ubuf.uio_segflg = UIO_USERSPACE; > + ubuf.uio_td = td; > + ubuf.uio_resid = s->count; > + ubuf.uio_offset = 0; > + ubuf.uio_rw = UIO_READ; > + error = iicuio_move(priv, &ubuf, s->last); > + break; > + > + case I2CRDWR: > + /* > + * The rdwr list should be a self-contained set of > + * transactions. Fail if another transaction is in progress. > + */ > + if (priv->started) { > + error = EINVAL; > + break; > + } > + > + error = iicrdwr(priv, (struct iic_rdwr_data *)data, flags); > + > + break; > + > + case I2CRPTSTART: > + if (!priv->started) { > + error = EINVAL; > + break; > + } > + error = iicbus_repeated_start(parent, s->slave, 0); > + break; > + > + case I2CSADDR: > + priv->addr = *((uint8_t*)data); > + break; > + > + default: > + error = ENOTTY; > + } > + > + IIC_UNLOCK(priv); > + return (error); > +} > + > +DRIVER_MODULE(iic, iicbus, iic_driver, iic_devclass, 0, 0); > +MODULE_DEPEND(iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); > +MODULE_VERSION(iic, 1); > diff --git a/freebsd/sys/dev/iicbus/iic.h b/freebsd/sys/dev/iicbus/iic.h > new file mode 100644 > index 00000000..5ebf56a9 > --- /dev/null > +++ b/freebsd/sys/dev/iicbus/iic.h > @@ -0,0 +1,72 @@ > +/*- > + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD > + * > + * 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. > + * > + * $FreeBSD$ > + * > + */ > +#ifndef __IIC_H > +#define __IIC_H > + > +#include <sys/ioccom.h> > + > +/* Designed to be compatible with linux's struct i2c_msg */ > +struct iic_msg > +{ > + uint16_t slave; > + uint16_t flags; > +#define IIC_M_WR 0 /* Fake flag for write */ > +#define IIC_M_RD 0x0001 /* read vs write */ > +#define IIC_M_NOSTOP 0x0002 /* do not send a I2C stop after message > */ > +#define IIC_M_NOSTART 0x0004 /* do not send a I2C start before > message */ > + uint16_t len; /* msg length */ > + uint8_t * buf; > +}; > + > +struct iiccmd { > + u_char slave; > + int count; > + int last; > + char *buf; > +}; > + > +struct iic_rdwr_data { > + struct iic_msg *msgs; > + uint32_t nmsgs; > +}; > + > +#define IIC_RDRW_MAX_MSGS 42 > + > +#define I2CSTART _IOW('i', 1, struct iiccmd) /* start condition */ > +#define I2CSTOP _IO('i', 2) /* stop > condition */ > +#define I2CRSTCARD _IOW('i', 3, struct iiccmd) /* reset the card */ > +#define I2CWRITE _IOW('i', 4, struct iiccmd) /* send data */ > +#define I2CREAD _IOW('i', 5, struct iiccmd) /* receive data > */ > +#define I2CRDWR _IOW('i', 6, struct iic_rdwr_data) /* > General read/write interface */ > +#define I2CRPTSTART _IOW('i', 7, struct iiccmd) /* repeated start */ > +#define I2CSADDR _IOW('i', 8, uint8_t) /* set slave address > for future I/O */ > + > +#endif > diff --git a/freebsd/sys/dev/iicbus/iicbus.c b/freebsd/sys/dev/iicbus/iicbus.c > new file mode 100644 > index 00000000..d1e55483 > --- /dev/null > +++ b/freebsd/sys/dev/iicbus/iicbus.c > @@ -0,0 +1,378 @@ > +#include <machine/rtems-bsd-kernel-space.h> > + > +/*- > + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD > + * > + * Copyright (c) 1998, 2001 Nicolas Souchu > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + */ > + > +#include <sys/cdefs.h> > +__FBSDID("$FreeBSD$"); > + > +/* > + * Autoconfiguration and support routines for the Philips serial I2C bus > + */ > + > +#include <sys/param.h> > +#include <sys/systm.h> > +#include <sys/kernel.h> > +#include <sys/lock.h> > +#include <sys/malloc.h> > +#include <sys/module.h> > +#include <sys/mutex.h> > +#include <sys/rman.h> > +#include <sys/sysctl.h> > +#include <sys/bus.h> > + > +#include <dev/iicbus/iiconf.h> > +#include <dev/iicbus/iicbus.h> > + > +#include <rtems/bsd/local/iicbus_if.h> > + > +/* See comments below for why auto-scanning is a bad idea. */ > +#define SCAN_IICBUS 0 > + > +static int > +iicbus_probe(device_t dev) > +{ > + > + device_set_desc(dev, "Philips I2C bus"); > + > + /* Allow other subclasses to override this driver. */ > + return (BUS_PROBE_GENERIC); > +} > + > +#if SCAN_IICBUS > +static int > +iic_probe_device(device_t dev, u_char addr) > +{ > + int count; > + char byte; > + > + if ((addr & 1) == 0) { > + /* is device writable? */ > + if (!iicbus_start(dev, (u_char)addr, 0)) { > + iicbus_stop(dev); > + return (1); > + } > + } else { > + /* is device readable? */ > + if (!iicbus_block_read(dev, (u_char)addr, &byte, 1, &count)) > + return (1); > + } > + > + return (0); > +} > +#endif > + > +/* > + * We add all the devices which we know about. > + * The generic attach routine will attach them if they are alive. > + */ > +static int > +iicbus_attach(device_t dev) > +{ > +#if SCAN_IICBUS > + unsigned char addr; > +#endif > + struct iicbus_softc *sc = IICBUS_SOFTC(dev); > + int strict; > + > + sc->dev = dev; > + mtx_init(&sc->lock, "iicbus", NULL, MTX_DEF); > + iicbus_init_frequency(dev, 0); > + iicbus_reset(dev, IIC_FASTEST, 0, NULL); > + if (resource_int_value(device_get_name(dev), > + device_get_unit(dev), "strict", &strict) == 0) > + sc->strict = strict; > + else > + sc->strict = 1; > + > + /* device probing is meaningless since the bus is supposed to be > + * hot-plug. Moreover, some I2C chips do not appreciate random > + * accesses like stop after start to fast, reads for less than > + * x bytes... > + */ > +#if SCAN_IICBUS > + printf("Probing for devices on iicbus%d:", device_get_unit(dev)); > + > + /* probe any devices */ > + for (addr = 16; addr < 240; addr++) { > + if (iic_probe_device(dev, (u_char)addr)) { > + printf(" <%x>", addr); > + } > + } > + printf("\n"); > +#endif > + bus_generic_probe(dev); > + bus_enumerate_hinted_children(dev); > + bus_generic_attach(dev); > + return (0); > +} > + > +static int > +iicbus_detach(device_t dev) > +{ > + struct iicbus_softc *sc = IICBUS_SOFTC(dev); > + > + iicbus_reset(dev, IIC_FASTEST, 0, NULL); > + bus_generic_detach(dev); > + device_delete_children(dev); > + mtx_destroy(&sc->lock); > + return (0); > +} > + > +static int > +iicbus_print_child(device_t dev, device_t child) > +{ > + struct iicbus_ivar *devi = IICBUS_IVAR(child); > + int retval = 0; > + > + retval += bus_print_child_header(dev, child); > + if (devi->addr != 0) > + retval += printf(" at addr %#x", devi->addr); > + resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%jd"); > + retval += bus_print_child_footer(dev, child); > + > + return (retval); > +} > + > +static void > +iicbus_probe_nomatch(device_t bus, device_t child) > +{ > + struct iicbus_ivar *devi = IICBUS_IVAR(child); > + > + device_printf(bus, "<unknown card> at addr %#x\n", devi->addr); > +} > + > +static int > +iicbus_child_location_str(device_t bus, device_t child, char *buf, > + size_t buflen) > +{ > + struct iicbus_ivar *devi = IICBUS_IVAR(child); > + > + snprintf(buf, buflen, "addr=%#x", devi->addr); > + return (0); > +} > + > +static int > +iicbus_child_pnpinfo_str(device_t bus, device_t child, char *buf, > + size_t buflen) > +{ > + *buf = '\0'; > + return (0); > +} > + > +static int > +iicbus_read_ivar(device_t bus, device_t child, int which, uintptr_t *result) > +{ > + struct iicbus_ivar *devi = IICBUS_IVAR(child); > + > + switch (which) { > + default: > + return (EINVAL); > + case IICBUS_IVAR_ADDR: > + *result = devi->addr; > + break; > + case IICBUS_IVAR_NOSTOP: > + *result = devi->nostop; > + break; > + } > + return (0); > +} > + > +static int > +iicbus_write_ivar(device_t bus, device_t child, int which, uintptr_t value) > +{ > + struct iicbus_ivar *devi = IICBUS_IVAR(child); > + > + switch (which) { > + default: > + return (EINVAL); > + case IICBUS_IVAR_ADDR: > + if (devi->addr != 0) > + return (EINVAL); > + devi->addr = value; > + case IICBUS_IVAR_NOSTOP: > + devi->nostop = value; > + break; > + } > + return (0); > +} > + > +static device_t > +iicbus_add_child(device_t dev, u_int order, const char *name, int unit) > +{ > + device_t child; > + struct iicbus_ivar *devi; > + > + child = device_add_child_ordered(dev, order, name, unit); > + if (child == NULL) > + return (child); > + devi = malloc(sizeof(struct iicbus_ivar), M_DEVBUF, M_NOWAIT | M_ZERO); > + if (devi == NULL) { > + device_delete_child(dev, child); > + return (0); > + } > + resource_list_init(&devi->rl); > + device_set_ivars(child, devi); > + return (child); > +} > + > +static void > +iicbus_hinted_child(device_t bus, const char *dname, int dunit) > +{ > + device_t child; > + int irq; > + struct iicbus_ivar *devi; > + > + child = BUS_ADD_CHILD(bus, 0, dname, dunit); > + devi = IICBUS_IVAR(child); > + resource_int_value(dname, dunit, "addr", &devi->addr); > + if (resource_int_value(dname, dunit, "irq", &irq) == 0) { > + if (bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1) != 0) > + device_printf(bus, > + "warning: bus_set_resource() failed\n"); > + } > +} > + > +static struct resource_list * > +iicbus_get_resource_list(device_t bus __unused, device_t child) > +{ > + struct iicbus_ivar *devi; > + > + devi = IICBUS_IVAR(child); > + return (&devi->rl); > +} > + > +int > +iicbus_generic_intr(device_t dev, int event, char *buf) > +{ > + > + return (0); > +} > + > +int > +iicbus_null_callback(device_t dev, int index, caddr_t data) > +{ > + > + return (0); > +} > + > +int > +iicbus_null_repeated_start(device_t dev, u_char addr) > +{ > + > + return (IIC_ENOTSUPP); > +} > + > +void > +iicbus_init_frequency(device_t dev, u_int bus_freq) > +{ > + struct iicbus_softc *sc = IICBUS_SOFTC(dev); > + > + /* > + * If a bus frequency value was passed in, use it. Otherwise initialize > + * it first to the standard i2c 100KHz frequency, then override that > + * from a hint if one exists. > + */ > + if (bus_freq > 0) > + sc->bus_freq = bus_freq; > + else { > + sc->bus_freq = 100000; > + resource_int_value(device_get_name(dev), device_get_unit(dev), > + "frequency", (int *)&sc->bus_freq); > + } > + /* > + * Set up the sysctl that allows the bus frequency to be changed. > + * It is flagged as a tunable so that the user can set the value in > + * loader(8), and that will override any other setting from any source. > + * The sysctl tunable/value is the one most directly controlled by the > + * user and thus the one that always takes precedence. > + */ > + SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), > + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), > + OID_AUTO, "frequency", CTLFLAG_RW | CTLFLAG_TUN, &sc->bus_freq, > + sc->bus_freq, "Bus frequency in Hz"); > +} > + > +static u_int > +iicbus_get_frequency(device_t dev, u_char speed) > +{ > + struct iicbus_softc *sc = IICBUS_SOFTC(dev); > + > + /* > + * If the frequency has not been configured for the bus, or the request > + * is specifically for SLOW speed, use the standard 100KHz rate, else > + * use the configured bus speed. > + */ > + if (sc->bus_freq == 0 || speed == IIC_SLOW) > + return (100000); > + return (sc->bus_freq); > +} > + > +static device_method_t iicbus_methods[] = { > + /* device interface */ > + DEVMETHOD(device_probe, iicbus_probe), > + DEVMETHOD(device_attach, iicbus_attach), > + DEVMETHOD(device_detach, iicbus_detach), > + > + /* bus interface */ > + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), > + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), > + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), > + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), > + DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), > + DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), > + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), > + DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), > + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), > + DEVMETHOD(bus_get_resource_list, iicbus_get_resource_list), > + DEVMETHOD(bus_add_child, iicbus_add_child), > + DEVMETHOD(bus_print_child, iicbus_print_child), > + DEVMETHOD(bus_probe_nomatch, iicbus_probe_nomatch), > + DEVMETHOD(bus_read_ivar, iicbus_read_ivar), > + DEVMETHOD(bus_write_ivar, iicbus_write_ivar), > + DEVMETHOD(bus_child_pnpinfo_str, iicbus_child_pnpinfo_str), > + DEVMETHOD(bus_child_location_str, iicbus_child_location_str), > + DEVMETHOD(bus_hinted_child, iicbus_hinted_child), > + > + /* iicbus interface */ > + DEVMETHOD(iicbus_transfer, iicbus_transfer), > + DEVMETHOD(iicbus_get_frequency, iicbus_get_frequency), > + > + DEVMETHOD_END > +}; > + > +driver_t iicbus_driver = { > + "iicbus", > + iicbus_methods, > + sizeof(struct iicbus_softc), > +}; > + > +devclass_t iicbus_devclass; > + > +MODULE_VERSION(iicbus, IICBUS_MODVER); > +DRIVER_MODULE(iicbus, iichb, iicbus_driver, iicbus_devclass, 0, 0); > diff --git a/freebsd/sys/dev/iicbus/iicbus.h b/freebsd/sys/dev/iicbus/iicbus.h > new file mode 100644 > index 00000000..503305c7 > --- /dev/null > +++ b/freebsd/sys/dev/iicbus/iicbus.h > @@ -0,0 +1,83 @@ > +/*- > + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD > + * > + * 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. > + * > + * $FreeBSD$ > + * > + */ > +#ifndef __IICBUS_H > +#define __IICBUS_H > + > +#include <sys/_lock.h> > +#include <sys/_mutex.h> > + > +#define IICBUS_IVAR(d) (struct iicbus_ivar *) device_get_ivars(d) > +#define IICBUS_SOFTC(d) (struct iicbus_softc *) device_get_softc(d) > + > +struct iicbus_softc > +{ > + device_t dev; /* Myself */ > + device_t owner; /* iicbus owner device structure */ > + u_int owncount; /* iicbus ownership nesting count */ > + u_char started; /* address of the 'started' slave > + * 0 if no start condition succeeded */ > + u_char strict; /* deny operations that violate the > + * I2C protocol */ > + struct mtx lock; > + u_int bus_freq; /* Configured bus Hz. */ > +}; > + > +struct iicbus_ivar > +{ > + uint32_t addr; > + struct resource_list rl; > + bool nostop; > +}; > + > +enum { > + IICBUS_IVAR_ADDR, /* Address or base address */ > + IICBUS_IVAR_NOSTOP, /* nostop defaults */ > +}; > + > +#define IICBUS_ACCESSOR(A, B, T) \ > + __BUS_ACCESSOR(iicbus, A, IICBUS, B, T) > + > +IICBUS_ACCESSOR(addr, ADDR, uint32_t) > +IICBUS_ACCESSOR(nostop, NOSTOP, bool) > + > +#define IICBUS_LOCK(sc) mtx_lock(&(sc)->lock) > +#define IICBUS_UNLOCK(sc) mtx_unlock(&(sc)->lock) > +#define IICBUS_ASSERT_LOCKED(sc) mtx_assert(&(sc)->lock, > MA_OWNED) > + > +int iicbus_generic_intr(device_t dev, int event, char *buf); > +void iicbus_init_frequency(device_t dev, u_int bus_freq); > + > +extern driver_t iicbus_driver; > +extern devclass_t iicbus_devclass; > +extern driver_t ofw_iicbus_driver; > +extern devclass_t ofw_iicbus_devclass; > + > +#endif > diff --git a/freebsd/sys/dev/iicbus/iiconf.c b/freebsd/sys/dev/iicbus/iiconf.c > new file mode 100644 > index 00000000..afdce118 > --- /dev/null > +++ b/freebsd/sys/dev/iicbus/iiconf.c > @@ -0,0 +1,527 @@ > +#include <machine/rtems-bsd-kernel-space.h> > + > +/*- > + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD > + * > + * 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. > + */ > + > +#include <sys/cdefs.h> > +__FBSDID("$FreeBSD$"); > + > +#include <sys/param.h> > +#include <sys/systm.h> > +#include <sys/lock.h> > +#include <sys/malloc.h> > +#include <sys/module.h> > +#include <sys/mutex.h> > +#include <sys/bus.h> > + > +#include <dev/iicbus/iiconf.h> > +#include <dev/iicbus/iicbus.h> > +#include <rtems/bsd/local/iicbus_if.h> > + > +/* > + * Translate IIC_Exxxxx status values to vaguely-equivelent errno values. > + */ > +int > +iic2errno(int iic_status) > +{ > + switch (iic_status) { > + case IIC_NOERR: return (0); > + case IIC_EBUSERR: return (EALREADY); > + case IIC_ENOACK: return (EIO); > + case IIC_ETIMEOUT: return (ETIMEDOUT); > + case IIC_EBUSBSY: return (EWOULDBLOCK); > + case IIC_ESTATUS: return (EPROTO); > + case IIC_EUNDERFLOW: return (EIO); > + case IIC_EOVERFLOW: return (EOVERFLOW); > + case IIC_ENOTSUPP: return (EOPNOTSUPP); > + case IIC_ENOADDR: return (EADDRNOTAVAIL); > + case IIC_ERESOURCE: return (ENOMEM); > + default: return (EIO); > + } > +} > + > +/* > + * 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; > +} > + > +static int > +iicbus_poll(struct iicbus_softc *sc, int how) > +{ > + int error; > + > + IICBUS_ASSERT_LOCKED(sc); > + switch (how & IIC_INTRWAIT) { > + case IIC_WAIT | IIC_INTR: > + error = mtx_sleep(sc, &sc->lock, IICPRI|PCATCH, "iicreq", 0); > + break; > + > + case IIC_WAIT | IIC_NOINTR: > + error = mtx_sleep(sc, &sc->lock, IICPRI, "iicreq", 0); > + break; > + > + default: > + return (IIC_EBUSBSY); > + } > + > + return (error); > +} > + > +/* > + * 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 error = 0; > + > + IICBUS_LOCK(sc); > + > + for (;;) { > + if (sc->owner == NULL) > + break; > + if ((how & IIC_RECURSIVE) && sc->owner == dev) > + break; > + if ((error = iicbus_poll(sc, how)) != 0) > + break; > + } > + > + if (error == 0) { > + ++sc->owncount; > + if (sc->owner == NULL) { > + sc->owner = dev; > + /* > + * Drop the lock around the call to the bus driver, it > + * should be allowed to sleep in the IIC_WAIT case. > + * Drivers might also need to grab locks that would > + * cause a LOR if our lock is held. > + */ > + IICBUS_UNLOCK(sc); > + /* Ask the underlying layers if the request is ok */ > + error = IICBUS_CALLBACK(device_get_parent(bus), > + IIC_REQUEST_BUS, (caddr_t)&how); > + IICBUS_LOCK(sc); > + > + if (error != 0) { > + sc->owner = NULL; > + sc->owncount = 0; > + wakeup_one(sc); > + } > + } > + } > + > + IICBUS_UNLOCK(sc); > + > + 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); > + > + IICBUS_LOCK(sc); > + > + if (sc->owner != dev) { > + IICBUS_UNLOCK(sc); > + return (IIC_EBUSBSY); > + } > + > + if (--sc->owncount == 0) { > + /* Drop the lock while informing the low-level driver. */ > + IICBUS_UNLOCK(sc); > + IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS, NULL); > + IICBUS_LOCK(sc); > + sc->owner = NULL; > + wakeup_one(sc); > + } > + IICBUS_UNLOCK(sc); > + return (0); > +} > + > +/* > + * iicbus_started() > + * > + * Test if the iicbus is started by the controller > + */ > +int > +iicbus_started(device_t bus) > +{ > + struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); > + > + return (sc->started); > +} > + > +/* > + * iicbus_start() > + * > + * Send start condition to the slave addressed by 'slave' > + */ > +int > +iicbus_start(device_t bus, u_char slave, int timeout) > +{ > + struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); > + int error = 0; > + > + if (sc->started) > + return (IIC_ESTATUS); /* protocol error, bus already started */ > + > + if (!(error = IICBUS_START(device_get_parent(bus), slave, timeout))) > + sc->started = slave; > + else > + sc->started = 0; > + > + return (error); > +} > + > +/* > + * iicbus_repeated_start() > + * > + * Send start condition to the slave addressed by 'slave' > + */ > +int > +iicbus_repeated_start(device_t bus, u_char slave, int timeout) > +{ > + struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); > + int error = 0; > + > + if (!sc->started) > + return (IIC_ESTATUS); /* protocol error, bus not started */ > + > + if (!(error = IICBUS_REPEATED_START(device_get_parent(bus), slave, > timeout))) > + sc->started = slave; > + else > + sc->started = 0; > + > + return (error); > +} > + > +/* > + * iicbus_stop() > + * > + * Send stop condition to the bus > + */ > +int > +iicbus_stop(device_t bus) > +{ > + struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); > + int error = 0; > + > + if (!sc->started) > + return (IIC_ESTATUS); /* protocol error, bus not started */ > + > + error = IICBUS_STOP(device_get_parent(bus)); > + > + /* refuse any further access */ > + sc->started = 0; > + > + return (error); > +} > + > +/* > + * iicbus_write() > + * > + * Write a block of data to the slave previously started by > + * iicbus_start() call > + */ > +int > +iicbus_write(device_t bus, const char *buf, int len, int *sent, int timeout) > +{ > + struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); > + > + /* a slave must have been started for writing */ > + if (sc->started == 0 || (sc->strict != 0 && (sc->started & LSB) != 0)) > + return (IIC_ESTATUS); > + > + return (IICBUS_WRITE(device_get_parent(bus), buf, len, sent, timeout)); > +} > + > +/* > + * iicbus_read() > + * > + * Read a block of data from the slave previously started by > + * iicbus_read() call > + */ > +int > +iicbus_read(device_t bus, char *buf, int len, int *read, int last, int delay) > +{ > + struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus); > + > + /* a slave must have been started for reading */ > + if (sc->started == 0 || (sc->strict != 0 && (sc->started & LSB) == 0)) > + return (IIC_ESTATUS); > + > + return (IICBUS_READ(device_get_parent(bus), buf, len, read, last, > delay)); > +} > + > +/* > + * iicbus_write_byte() > + * > + * Write a byte to the slave previously started by iicbus_start() call > + */ > +int > +iicbus_write_byte(device_t bus, char byte, int timeout) > +{ > + struct iicbus_softc *sc = device_get_softc(bus); > + char data = byte; > + int sent; > + > + /* a slave must have been started for writing */ > + if (sc->started == 0 || (sc->strict != 0 && (sc->started & LSB) != 0)) > + return (IIC_ESTATUS); > + > + return (iicbus_write(bus, &data, 1, &sent, timeout)); > +} > + > +/* > + * iicbus_read_byte() > + * > + * Read a byte from the slave previously started by iicbus_start() call > + */ > +int > +iicbus_read_byte(device_t bus, char *byte, int timeout) > +{ > + struct iicbus_softc *sc = device_get_softc(bus); > + int read; > + > + /* a slave must have been started for reading */ > + if (sc->started == 0 || (sc->strict != 0 && (sc->started & LSB) == 0)) > + return (IIC_ESTATUS); > + > + return (iicbus_read(bus, byte, 1, &read, IIC_LAST_READ, timeout)); > +} > + > +/* > + * 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, 0))) > + return (error); > + > + error = iicbus_write(bus, buf, len, sent, 0); > + > + 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, 0))) > + return (error); > + > + error = iicbus_read(bus, buf, len, read, IIC_LAST_READ, 0); > + > + iicbus_stop(bus); > + > + return (error); > +} > + > +/* > + * iicbus_transfer() > + * > + * Do an aribtrary number of transfers on the iicbus. We pass these > + * raw requests to the bridge driver. If the bridge driver supports > + * them directly, then it manages all the details. If not, it can use > + * the helper function iicbus_transfer_gen() which will do the > + * transfers at a low level. > + * > + * Pointers passed in as part of iic_msg must be kernel pointers. > + * Callers that have user addresses to manage must do so on their own. > + */ > +int > +iicbus_transfer(device_t bus, struct iic_msg *msgs, uint32_t nmsgs) > +{ > + > + return (IICBUS_TRANSFER(device_get_parent(bus), msgs, nmsgs)); > +} > + > +int > +iicbus_transfer_excl(device_t dev, struct iic_msg *msgs, uint32_t nmsgs, > + int how) > +{ > + device_t bus; > + int error; > + > + bus = device_get_parent(dev); > + error = iicbus_request_bus(bus, dev, how); > + if (error == 0) > + error = IICBUS_TRANSFER(bus, msgs, nmsgs); > + iicbus_release_bus(bus, dev); > + return (error); > +} > + > +/* > + * Generic version of iicbus_transfer that calls the appropriate > + * routines to accomplish this. See note above about acceptable > + * buffer addresses. > + */ > +int > +iicbus_transfer_gen(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) > +{ > + int i, error, lenread, lenwrote, nkid, rpstart, addr; > + device_t *children, bus; > + bool nostop, started; > + > + if ((error = device_get_children(dev, &children, &nkid)) != 0) > + return (IIC_ERESOURCE); > + if (nkid != 1) { > + free(children, M_TEMP); > + return (IIC_ENOTSUPP); > + } > + bus = children[0]; > + rpstart = 0; > + free(children, M_TEMP); > + nostop = iicbus_get_nostop(dev); > + started = false; > + for (i = 0, error = 0; i < nmsgs && error == 0; i++) { > + addr = msgs[i].slave; > + if (msgs[i].flags & IIC_M_RD) > + addr |= LSB; > + else > + addr &= ~LSB; > + > + if (!(msgs[i].flags & IIC_M_NOSTART)) { > + if (rpstart) > + error = iicbus_repeated_start(bus, addr, 0); > + else > + error = iicbus_start(bus, addr, 0); > + if (error != 0) > + break; > + started = true; > + } > + > + if (msgs[i].flags & IIC_M_RD) > + error = iicbus_read(bus, msgs[i].buf, msgs[i].len, > + &lenread, IIC_LAST_READ, 0); > + else > + error = iicbus_write(bus, msgs[i].buf, msgs[i].len, > + &lenwrote, 0); > + if (error != 0) > + break; > + > + if ((msgs[i].flags & IIC_M_NOSTOP) != 0 || > + (nostop && i + 1 < nmsgs)) { > + rpstart = 1; /* Next message gets repeated start */ > + } else { > + rpstart = 0; > + iicbus_stop(bus); > + } > + } > + if (error != 0 && started) > + iicbus_stop(bus); > + return (error); > +} > + > +int > +iicdev_readfrom(device_t slavedev, uint8_t regaddr, void *buffer, > + uint16_t buflen, int waithow) > +{ > + struct iic_msg msgs[2]; > + uint8_t slaveaddr; > + > + /* > + * Two transfers back to back with a repeat-start between them; first we > + * write the address-within-device, then we read from the device. > + */ > + slaveaddr = iicbus_get_addr(slavedev); > + > + msgs[0].slave = slaveaddr; > + msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP; > + msgs[0].len = 1; > + msgs[0].buf = ®addr; > + > + msgs[1].slave = slaveaddr; > + msgs[1].flags = IIC_M_RD; > + msgs[1].len = buflen; > + msgs[1].buf = buffer; > + > + return (iicbus_transfer_excl(slavedev, msgs, nitems(msgs), waithow)); > +} > + > +int iicdev_writeto(device_t slavedev, uint8_t regaddr, void *buffer, > + uint16_t buflen, int waithow) > +{ > + struct iic_msg msgs[2]; > + uint8_t slaveaddr; > + > + /* > + * Two transfers back to back with no stop or start between them; first > + * we write the address then we write the data to that address, all in a > + * single transfer from two scattered buffers. > + */ > + slaveaddr = iicbus_get_addr(slavedev); > + > + msgs[0].slave = slaveaddr; > + msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP; > + msgs[0].len = 1; > + msgs[0].buf = ®addr; > + > + msgs[1].slave = slaveaddr; > + msgs[1].flags = IIC_M_WR | IIC_M_NOSTART; > + msgs[1].len = buflen; > + msgs[1].buf = buffer; > + > + return (iicbus_transfer_excl(slavedev, msgs, nitems(msgs), waithow)); > +} > diff --git a/freebsd/sys/dev/iicbus/iiconf.h b/freebsd/sys/dev/iicbus/iiconf.h > new file mode 100644 > index 00000000..c264183e > --- /dev/null > +++ b/freebsd/sys/dev/iicbus/iiconf.h > @@ -0,0 +1,162 @@ > +/*- > + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD > + * > + * Copyright (c) 1998, 2001 Nicolas Souchu > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * 1. Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * 2. Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in the > + * documentation and/or other materials provided with the distribution. > + * > + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND > + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE > + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE > + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE > + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL > + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS > + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) > + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT > + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY > + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF > + * SUCH DAMAGE. > + * > + * $FreeBSD$ > + */ > +#ifndef __IICONF_H > +#define __IICONF_H > + > +#include <sys/queue.h> > +#include <dev/iicbus/iic.h> > + > + > +#define IICPRI (PZERO+8) /* XXX sleep/wakeup queue priority */ > + > +#define LSB 0x1 > + > +/* > + * Options affecting iicbus_request_bus() > + */ > +#define IIC_DONTWAIT 0 > +#define IIC_NOINTR 0 > +#define IIC_WAIT 0x1 > +#define IIC_INTR 0x2 > +#define IIC_INTRWAIT (IIC_INTR | IIC_WAIT) > +#define IIC_RECURSIVE 0x4 > + > +/* > + * 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 > + > +#define IIC_LAST_READ 0x1 > + > +/* > + * callback index > + */ > +#define IIC_REQUEST_BUS 0x1 > +#define IIC_RELEASE_BUS 0x2 > + > +/* > + * 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 occurred */ > +#define IIC_EBUSERR 0x1 /* bus error (hardware not in expected state) */ > +#define IIC_ENOACK 0x2 /* ack not received until timeout */ > +#define IIC_ETIMEOUT 0x3 /* timeout */ > +#define IIC_EBUSBSY 0x4 /* bus busy (reserved by another client) */ > +#define IIC_ESTATUS 0x5 /* status error */ > +#define IIC_EUNDERFLOW 0x6 /* slave ready for more data */ > +#define IIC_EOVERFLOW 0x7 /* too much data */ > +#define IIC_ENOTSUPP 0x8 /* request not supported */ > +#define IIC_ENOADDR 0x9 /* no address assigned to the interface */ > +#define IIC_ERESOURCE 0xa /* resources (memory, whatever) > unavailable */ > + > +/* > + * Note that all iicbus functions return IIC_Exxxxx status values, > + * except iic2errno() (obviously) and iicbus_started() (returns bool). > + */ > +extern int iic2errno(int); > +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 *); > + > +extern int iicbus_null_repeated_start(device_t, u_char); > +extern int iicbus_null_callback(device_t, int, caddr_t); > + > +#define iicbus_reset(bus,speed,addr,oldaddr) \ > + (IICBUS_RESET(device_get_parent(bus), speed, addr, oldaddr)) > + > +/* basic I2C operations */ > +extern int iicbus_started(device_t); > +extern int iicbus_start(device_t, u_char, int); > +extern int iicbus_stop(device_t); > +extern int iicbus_repeated_start(device_t, u_char, int); > +extern int iicbus_write(device_t, const char *, int, int *, int); > +extern int iicbus_read(device_t, char *, int, int *, int, int); > + > +/* single byte read/write functions, start/stop not managed */ > +extern int iicbus_write_byte(device_t, char, int); > +extern int iicbus_read_byte(device_t, char *, int); > + > +/* Read/write operations with start/stop conditions managed */ > +extern int iicbus_block_write(device_t, u_char, char *, int, int *); > +extern int iicbus_block_read(device_t, u_char, char *, int, int *); > + > +/* vectors of iic operations to pass to bridge */ > +int iicbus_transfer(device_t bus, struct iic_msg *msgs, uint32_t nmsgs); > +int iicbus_transfer_excl(device_t bus, struct iic_msg *msgs, uint32_t nmsgs, > + int how); > +int iicbus_transfer_gen(device_t bus, struct iic_msg *msgs, uint32_t nmsgs); > + > +/* > + * Simple register read/write routines, but the "register" can be any size. > + * The transfers are done with iicbus_transfer_excl(). Reads use a > repeat-start > + * between sending the address and reading; writes use a single start/stop. > + */ > +int iicdev_readfrom(device_t _slavedev, uint8_t _regaddr, void *_buffer, > + uint16_t _buflen, int _waithow); > +int iicdev_writeto(device_t _slavedev, uint8_t _regaddr, void *_buffer, > + uint16_t _buflen, int _waithow); > + > +#define IICBUS_MODVER 1 > +#define IICBUS_MINVER 1 > +#define IICBUS_MAXVER 1 > +#define IICBUS_PREFVER IICBUS_MODVER > + > +extern driver_t iicbb_driver; > +extern devclass_t iicbb_devclass; > + > +#define IICBB_MODVER 1 > +#define IICBB_MINVER 1 > +#define IICBB_MAXVER 1 > +#define IICBB_PREFVER IICBB_MODVER > + > +#endif > diff --git a/freebsd/sys/dev/iicbus/ofw_iicbus.c > b/freebsd/sys/dev/iicbus/ofw_iicbus.c > new file mode 100644 > index 00000000..cb31459e > --- /dev/null > +++ b/freebsd/sys/dev/iicbus/ofw_iicbus.c > @@ -0,0 +1,242 @@ > +#include <machine/rtems-bsd-kernel-space.h> > + > +/*- > + * Copyright (c) 2009, Nathan Whitehorn <nwhiteh...@freebsd.org> > + * 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 unmodified, 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, > + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT > + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF > + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > + */ > + > +#include <sys/cdefs.h> > +__FBSDID("$FreeBSD$"); > + > +#include <sys/param.h> > +#include <sys/bus.h> > +#include <sys/kernel.h> > +#include <sys/libkern.h> > +#include <sys/lock.h> > +#include <sys/module.h> > +#include <sys/mutex.h> > + > +#include <dev/iicbus/iicbus.h> > +#include <dev/iicbus/iiconf.h> > +#include <dev/ofw/ofw_bus.h> > +#include <dev/ofw/ofw_bus_subr.h> > +#include <dev/ofw/openfirm.h> > + > +#include <rtems/bsd/local/iicbus_if.h> > + > +/* Methods */ > +static device_probe_t ofw_iicbus_probe; > +static device_attach_t ofw_iicbus_attach; > +static device_t ofw_iicbus_add_child(device_t dev, u_int order, > + const char *name, int unit); > +static const struct ofw_bus_devinfo *ofw_iicbus_get_devinfo(device_t bus, > + device_t dev); > + > +static device_method_t ofw_iicbus_methods[] = { > + /* Device interface */ > + DEVMETHOD(device_probe, ofw_iicbus_probe), > + DEVMETHOD(device_attach, ofw_iicbus_attach), > + > + /* Bus interface */ > + DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), > + DEVMETHOD(bus_add_child, ofw_iicbus_add_child), > + > + /* ofw_bus interface */ > + DEVMETHOD(ofw_bus_get_devinfo, ofw_iicbus_get_devinfo), > + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), > + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), > + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), > + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), > + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), > + > + DEVMETHOD_END > +}; > + > +struct ofw_iicbus_devinfo { > + struct iicbus_ivar opd_dinfo; /* Must be the first. */ > + struct ofw_bus_devinfo opd_obdinfo; > +}; > + > +devclass_t ofw_iicbus_devclass; > + > +DEFINE_CLASS_1(iicbus, ofw_iicbus_driver, ofw_iicbus_methods, > + sizeof(struct iicbus_softc), iicbus_driver); > +EARLY_DRIVER_MODULE(ofw_iicbus, iicbb, ofw_iicbus_driver, > ofw_iicbus_devclass, > + 0, 0, BUS_PASS_BUS); > +EARLY_DRIVER_MODULE(ofw_iicbus, iichb, ofw_iicbus_driver, > ofw_iicbus_devclass, > + 0, 0, BUS_PASS_BUS); > +EARLY_DRIVER_MODULE(ofw_iicbus, twsi, ofw_iicbus_driver, ofw_iicbus_devclass, > + 0, 0, BUS_PASS_BUS); > +MODULE_VERSION(ofw_iicbus, 1); > +MODULE_DEPEND(ofw_iicbus, iicbus, 1, 1, 1); > + > +static int > +ofw_iicbus_probe(device_t dev) > +{ > + > + if (ofw_bus_get_node(dev) == -1) > + return (ENXIO); > + device_set_desc(dev, "OFW I2C bus"); > + > + return (0); > +} > + > +static int > +ofw_iicbus_attach(device_t dev) > +{ > + struct iicbus_softc *sc = IICBUS_SOFTC(dev); > + struct ofw_iicbus_devinfo *dinfo; > + phandle_t child, node, root; > + pcell_t freq, paddr; > + device_t childdev; > + ssize_t compatlen; > + char compat[255]; > + char *curstr; > + u_int iic_addr_8bit = 0; > + > + sc->dev = dev; > + mtx_init(&sc->lock, "iicbus", NULL, MTX_DEF); > + > + /* > + * If there is a clock-frequency property for the device node, use it as > + * the starting value for the bus frequency. Then call the common > + * routine that handles the tunable/sysctl which allows the FDT value to > + * be overridden by the user. > + */ > + node = ofw_bus_get_node(dev); > + freq = 0; > + OF_getencprop(node, "clock-frequency", &freq, sizeof(freq)); > + iicbus_init_frequency(dev, freq); > + > + iicbus_reset(dev, IIC_FASTEST, 0, NULL); > + > + bus_generic_probe(dev); > + bus_enumerate_hinted_children(dev); > + > + /* > + * Check if we're running on a PowerMac, needed for the I2C > + * address below. > + */ > + root = OF_peer(0); > + compatlen = OF_getprop(root, "compatible", compat, > + sizeof(compat)); > + if (compatlen != -1) { > + for (curstr = compat; curstr < compat + compatlen; > + curstr += strlen(curstr) + 1) { > + if (strncmp(curstr, "MacRISC", 7) == 0) > + iic_addr_8bit = 1; > + } > + } > + > + /* > + * Attach those children represented in the device tree. > + */ > + for (child = OF_child(node); child != 0; child = OF_peer(child)) { > + /* > + * Try to get the I2C address first from the i2c-address > + * property, then try the reg property. It moves around > + * on different systems. > + */ > + if (OF_getencprop(child, "i2c-address", &paddr, > + sizeof(paddr)) == -1) > + if (OF_getencprop(child, "reg", &paddr, > + sizeof(paddr)) == -1) > + continue; > + > + /* > + * Now set up the I2C and OFW bus layer devinfo and add it > + * to the bus. > + */ > + dinfo = malloc(sizeof(struct ofw_iicbus_devinfo), M_DEVBUF, > + M_NOWAIT | M_ZERO); > + if (dinfo == NULL) > + continue; > + /* > + * FreeBSD drivers expect I2C addresses to be expressed as > + * 8-bit values. Apple OFW data contains 8-bit values, but > + * Linux FDT data contains 7-bit values, so shift them up to > + * 8-bit format. > + */ > + if (iic_addr_8bit) > + dinfo->opd_dinfo.addr = paddr; > + else > + dinfo->opd_dinfo.addr = paddr << 1; > + > + if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) != > + 0) { > + free(dinfo, M_DEVBUF); > + continue; > + } > + > + childdev = device_add_child(dev, NULL, -1); > + resource_list_init(&dinfo->opd_dinfo.rl); > + ofw_bus_intr_to_rl(childdev, child, > + &dinfo->opd_dinfo.rl, NULL); > + device_set_ivars(childdev, dinfo); > + } > + > + /* Register bus */ > + OF_device_register_xref(OF_xref_from_node(node), dev); > + return (bus_generic_attach(dev)); > +} > + > +static device_t > +ofw_iicbus_add_child(device_t dev, u_int order, const char *name, int unit) > +{ > + device_t child; > + struct ofw_iicbus_devinfo *devi; > + > + child = device_add_child_ordered(dev, order, name, unit); > + if (child == NULL) > + return (child); > + devi = malloc(sizeof(struct ofw_iicbus_devinfo), M_DEVBUF, > + M_NOWAIT | M_ZERO); > + if (devi == NULL) { > + device_delete_child(dev, child); > + return (0); > + } > + > + /* > + * NULL all the OFW-related parts of the ivars for non-OFW > + * children. > + */ > + devi->opd_obdinfo.obd_node = -1; > + devi->opd_obdinfo.obd_name = NULL; > + devi->opd_obdinfo.obd_compat = NULL; > + devi->opd_obdinfo.obd_type = NULL; > + devi->opd_obdinfo.obd_model = NULL; > + > + device_set_ivars(child, devi); > + > + return (child); > +} > + > +static const struct ofw_bus_devinfo * > +ofw_iicbus_get_devinfo(device_t bus, device_t dev) > +{ > + struct ofw_iicbus_devinfo *dinfo; > + > + dinfo = device_get_ivars(dev); > + return (&dinfo->opd_obdinfo); > +} > _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel