summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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