summaryrefslogtreecommitdiffstats
path: root/sys/dev/iicbus
diff options
context:
space:
mode:
authorimp <imp@FreeBSD.org>2006-07-14 23:15:06 +0000
committerimp <imp@FreeBSD.org>2006-07-14 23:15:06 +0000
commitfd85c7f61c4637d47796575f4873c0a2f57a9932 (patch)
treee0047914863c0b23f720e30549504437b56ebebb /sys/dev/iicbus
parent005f4f7d53a16bf4273219f9cba543e558d546b9 (diff)
downloadFreeBSD-src-fd85c7f61c4637d47796575f4873c0a2f57a9932.zip
FreeBSD-src-fd85c7f61c4637d47796575f4873c0a2f57a9932.tar.gz
Allow iic bridges to support a generalized transfer, rather than
forcing all transfers to do the start read/write stop by hand. Some smart bridges prefer this sort of operation, and this allows us to support their features more easily. When bridges don't support it, we fall back to using the old-style opertaions. Expand the ioctl interface to expose this function. Unlike the old-style interface, this interface is thread safe, even on old bridges.
Diffstat (limited to 'sys/dev/iicbus')
-rw-r--r--sys/dev/iicbus/iic.c29
-rw-r--r--sys/dev/iicbus/iic.h16
-rw-r--r--sys/dev/iicbus/iicbus.c23
-rw-r--r--sys/dev/iicbus/iicbus.h3
-rw-r--r--sys/dev/iicbus/iicbus_if.m10
-rw-r--r--sys/dev/iicbus/iiconf.c42
-rw-r--r--sys/dev/iicbus/iiconf.h6
7 files changed, 117 insertions, 12 deletions
diff --git a/sys/dev/iicbus/iic.c b/sys/dev/iicbus/iic.c
index 28339fa..b50d2e0 100644
--- a/sys/dev/iicbus/iic.c
+++ b/sys/dev/iicbus/iic.c
@@ -240,8 +240,11 @@ iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t
struct iic_softc *sc = IIC_SOFTC(minor(dev));
device_t parent = device_get_parent(iicdev);
struct iiccmd *s = (struct iiccmd *)data;
- int error, count;
+ struct iic_rdwr_data *d = (struct iic_rdwr_data *)data;
+ struct iic_msg *m;
+ int error, count, i;
char *buf = NULL;
+ void **usrbufs = NULL;
if (!sc)
return (EINVAL);
@@ -297,6 +300,30 @@ iicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t
error = copyout(buf, s->buf, s->count);
break;
+ case I2CRDWR:
+ buf = malloc(sizeof(*d->msgs) * d->nmsgs, M_TEMP, M_WAITOK);
+ usrbufs = malloc(sizeof(void *) * d->nmsgs, M_TEMP, M_ZERO | M_WAITOK);
+ error = copyin(d->msgs, buf, sizeof(*d->msgs) * d->nmsgs);
+ if (error)
+ break;
+ /* Allocate kernel buffers for userland data, copyin write data */
+ for (i = 0; i < d->nmsgs; i++) {
+ m = &((struct iic_msg *)buf)[i];
+ usrbufs[i] = m->buf;
+ m->buf = malloc(m->len, M_TEMP, M_WAITOK);
+ if (!(m->flags & IIC_M_RD))
+ copyin(usrbufs[i], m->buf, m->len);
+ }
+ error = iicbus_transfer(parent, (struct iic_msg *)buf, d->nmsgs);
+ /* Copyout all read segments, free up kernel buffers */
+ for (i = 0; i < d->nmsgs; i++) {
+ m = &((struct iic_msg *)buf)[i];
+ if (!(m->flags & IIC_M_RD))
+ copyout(m->buf, usrbufs[i], m->len);
+ free(m->buf, M_TEMP);
+ }
+ free(usrbufs, M_TEMP);
+ break;
default:
error = ENOTTY;
}
diff --git a/sys/dev/iicbus/iic.h b/sys/dev/iicbus/iic.h
index 5d362c1..ed22d85 100644
--- a/sys/dev/iicbus/iic.h
+++ b/sys/dev/iicbus/iic.h
@@ -31,6 +31,16 @@
#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_RD 0x0001 /* read vs write */
+ uint16_t len; /* msg legnth */
+ uint8_t * buf;
+};
+
struct iiccmd {
u_char slave;
int count;
@@ -38,10 +48,16 @@ struct iiccmd {
char *buf;
};
+struct iic_rdwr_data {
+ struct iic_msg *msgs;
+ uint32_t nmsgs;
+};
+
#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 */
#endif
diff --git a/sys/dev/iicbus/iicbus.c b/sys/dev/iicbus/iicbus.c
index 6181698..d2f9ef0 100644
--- a/sys/dev/iicbus/iicbus.c
+++ b/sys/dev/iicbus/iicbus.c
@@ -45,7 +45,7 @@ __FBSDID("$FreeBSD$");
#define DEVTOIICBUS(dev) ((struct iicbus_device*)device_get_ivars(dev))
-static devclass_t iicbus_devclass;
+devclass_t iicbus_devclass;
/* See comments below for why auto-scanning is a bad idea. */
#define SCAN_IICBUS 0
@@ -72,7 +72,7 @@ static device_method_t iicbus_methods[] = {
{ 0, 0 }
};
-static driver_t iicbus_driver = {
+driver_t iicbus_driver = {
"iicbus",
iicbus_methods,
sizeof(struct iicbus_softc),
@@ -81,8 +81,8 @@ static driver_t iicbus_driver = {
static int
iicbus_probe(device_t dev)
{
- device_set_desc(dev, "Philips I2C bus");
+ device_set_desc(dev, "Philips I2C bus");
return (0);
}
@@ -139,51 +139,52 @@ iicbus_attach(device_t dev)
printf("\n");
#endif
- /* attach any known device */
device_add_child(dev, "ic", -1);
- device_add_child(dev, "iic", -1);
device_add_child(dev, "iicsmb", -1);
-
+#if 0
+ /* attach any known device */
+ device_add_child(dev, "iic", -1);
+#endif
bus_generic_attach(dev);
-
return (0);
}
static int
iicbus_detach(device_t dev)
{
+
iicbus_reset(dev, IIC_FASTEST, 0, NULL);
-
bus_generic_detach(dev);
-
return (0);
}
static int
iicbus_add_child(device_t dev, int order, const char *name, int unit)
{
- device_add_child_ordered(dev, order, name, unit);
+ device_add_child_ordered(dev, order, name, unit);
bus_generic_attach(dev);
-
return (0);
}
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);
}
diff --git a/sys/dev/iicbus/iicbus.h b/sys/dev/iicbus/iicbus.h
index fcf758b..f0c061c 100644
--- a/sys/dev/iicbus/iicbus.h
+++ b/sys/dev/iicbus/iicbus.h
@@ -38,4 +38,7 @@ struct iicbus_softc {
extern int iicbus_generic_intr(device_t dev, int event, char *buf);
+extern driver_t iicbus_driver;
+extern devclass_t iicbus_devclass;
+
#endif
diff --git a/sys/dev/iicbus/iicbus_if.m b/sys/dev/iicbus/iicbus_if.m
index 068cf4d..7c1c537 100644
--- a/sys/dev/iicbus/iicbus_if.m
+++ b/sys/dev/iicbus/iicbus_if.m
@@ -27,6 +27,7 @@
#
#include <sys/bus.h>
+#include <dev/iicbus/iic.h>
INTERFACE iicbus;
@@ -105,3 +106,12 @@ METHOD int reset {
u_char addr;
u_char *oldaddr;
};
+
+#
+# Generalized Read/Write interface
+#
+METHOD int transfer {
+ device_t dev;
+ struct iic_msg *msgs;
+ uint32_t nmsgs;
+};
diff --git a/sys/dev/iicbus/iiconf.c b/sys/dev/iicbus/iiconf.c
index 6753db9..da050b0 100644
--- a/sys/dev/iicbus/iiconf.c
+++ b/sys/dev/iicbus/iiconf.c
@@ -331,3 +331,45 @@ iicbus_block_read(device_t bus, u_char slave, char *buf, int len, int *read)
return (error);
}
+
+/*
+ * iicbus_trasnfer()
+ *
+ * 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));
+}
+
+/*
+ * 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 bus, struct iic_msg *msgs, uint32_t nmsgs)
+{
+ int i, error, max, lenread, lenwrote;
+
+ for (i = 0, max = 0; i < nmsgs; i++)
+ if (max < msgs[i].len)
+ max = msgs[i].len;
+ for (i = 0, error = 0; i < nmsgs && error == 0; i++) {
+ if (msgs[i].flags & IIC_M_RD)
+ error = iicbus_block_read(bus, msgs[i].slave,
+ msgs[i].buf, msgs[i].len, &lenread);
+ else
+ error = iicbus_block_write(bus, msgs[i].slave,
+ msgs[i].buf, msgs[i].len, &lenwrote);
+ }
+ return (error);
+}
diff --git a/sys/dev/iicbus/iiconf.h b/sys/dev/iicbus/iiconf.h
index 3a9e7e3..bbf4907 100644
--- a/sys/dev/iicbus/iiconf.h
+++ b/sys/dev/iicbus/iiconf.h
@@ -29,6 +29,8 @@
#define __IICONF_H
#include <sys/queue.h>
+#include <dev/iicbus/iic.h>
+
#define IICPRI (PZERO+8) /* XXX sleep/wakeup queue priority */
@@ -127,6 +129,10 @@ extern int iicbus_block_read(device_t, u_char, char *, int, int *);
extern u_char iicbus_get_addr(device_t);
+/* vectors of iic operations to pass to bridge */
+int iicbus_transfer(device_t bus, struct iic_msg *msgs, uint32_t nmsgs);
+int iicbus_transfer_gen(device_t bus, struct iic_msg *msgs, uint32_t nmsgs);
+
#define IICBUS_MODVER 1
#define IICBUS_MINVER 1
#define IICBUS_MAXVER 1
OpenPOWER on IntegriCloud