Page MenuHomeFreeBSD

D22109.id63540.diff
No OneTemporary

D22109.id63540.diff

Index: sys/dev/iicbus/iicbb.c
===================================================================
--- sys/dev/iicbus/iicbb.c
+++ sys/dev/iicbus/iicbb.c
@@ -52,6 +52,7 @@
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/bus.h>
+#include <sys/sysctl.h>
#include <sys/uio.h>
#ifdef FDT
@@ -68,9 +69,13 @@
#include "iicbus_if.h"
#include "iicbb_if.h"
+/* Based on the SMBus specification. */
+#define DEFAULT_SCL_LOW_TIMEOUT (25 * 1000)
+
struct iicbb_softc {
device_t iicbus;
- int udelay; /* signal toggle delay in usec */
+ u_int udelay; /* signal toggle delay in usec */
+ u_int scl_low_timeout;
};
static int iicbb_attach(device_t);
@@ -86,6 +91,7 @@
static int iicbb_read(device_t, char *, int, int *, int, int);
static int iicbb_reset(device_t, u_char, u_char, u_char *);
static int iicbb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs);
+static void iicbb_set_speed(struct iicbb_softc *sc, u_char);
#ifdef FDT
static phandle_t iicbb_get_node(device_t, device_t);
#endif
@@ -142,9 +148,20 @@
sc->iicbus = device_add_child(dev, "iicbus", -1);
if (!sc->iicbus)
return (ENXIO);
- sc->udelay = 10; /* 10 uS default */
- bus_generic_attach(dev);
+ sc->scl_low_timeout = DEFAULT_SCL_LOW_TIMEOUT;
+ iicbb_set_speed(sc, IIC_UNKNOWN);
+
+ SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "delay", CTLFLAG_RD, &sc->udelay,
+ 0, "Signal change delay controlled by bus frequency, microseconds");
+
+ SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "scl_low_timeout", CTLFLAG_RWTUN, &sc->scl_low_timeout,
+ 0, "SCL low timeout, microseconds");
+ bus_generic_attach(dev);
return (0);
}
@@ -201,24 +218,17 @@
return (retval);
}
-#define I2C_SETSDA(sc,dev,val) do { \
- IICBB_SETSDA(device_get_parent(dev), val); \
+#define I2C_GETSDA(dev) (IICBB_GETSDA(device_get_parent(dev)))
+#define I2C_SETSDA(dev, x) (IICBB_SETSDA(device_get_parent(dev), x))
+#define I2C_GETSCL(dev) (IICBB_GETSCL(device_get_parent(dev)))
+#define I2C_SETSCL(dev, x) (IICBB_SETSCL(device_get_parent(dev), x))
+
+#define I2C_SET(sc, dev, ctrl, val) do { \
+ iicbb_setscl(dev, ctrl); \
+ I2C_SETSDA(dev, val); \
DELAY(sc->udelay); \
} while (0)
-#define I2C_SETSCL(dev,val) do { \
- iicbb_setscl(dev, val, 100); \
- } while (0)
-
-#define I2C_SET(sc,dev,ctrl,data) do { \
- I2C_SETSCL(dev, ctrl); \
- I2C_SETSDA(sc, dev, data); \
- } while (0)
-
-#define I2C_GETSDA(dev) (IICBB_GETSDA(device_get_parent(dev)))
-
-#define I2C_GETSCL(dev) (IICBB_GETSCL(device_get_parent(dev)))
-
static int i2c_debug = 0;
#define I2C_DEBUG(x) do { \
if (i2c_debug) (x); \
@@ -229,20 +239,37 @@
} while (0)
static void
-iicbb_setscl(device_t dev, int val, int timeout)
+iicbb_setscl(device_t dev, int val)
{
struct iicbb_softc *sc = device_get_softc(dev);
- int k = 0;
+ sbintime_t now, end;
+ int fast_timeout;
- IICBB_SETSCL(device_get_parent(dev), val);
+ I2C_SETSCL(dev, val);
DELAY(sc->udelay);
- while (val && !I2C_GETSCL(dev) && k++ < timeout) {
- IICBB_SETSCL(device_get_parent(dev), val);
+ /* Pulling low cannot fail. */
+ if (!val)
+ return;
+
+ /* Use DELAY for up to 1 ms, then switch to pause. */
+ end = sbinuptime() + sc->scl_low_timeout * SBT_1US;
+ fast_timeout = MIN(sc->scl_low_timeout, 1000);
+ while (fast_timeout > 0) {
+ if (I2C_GETSCL(dev))
+ return;
+ I2C_SETSCL(dev, 1); /* redundant ? */
DELAY(sc->udelay);
+ fast_timeout -= sc->udelay;
}
- return;
+ while (!I2C_GETSCL(dev)) {
+ now = sbinuptime();
+ if (now >= end)
+ break;
+ pause_sbt("iicbb-scl-low", SBT_1MS, C_PREL(8), 0);
+ }
+
}
static void
@@ -290,6 +317,11 @@
I2C_SET(sc,dev,0,1);
I2C_SET(sc,dev,1,1);
+
+ /* SCL must be high now. */
+ if (!I2C_GETSCL(dev))
+ return (IIC_ETIMEOUT);
+
do {
noack = I2C_GETSDA(dev);
if (!noack)
@@ -301,14 +333,14 @@
I2C_SET(sc,dev,0,1);
I2C_DEBUG(printf("%c ",noack?'-':'+'));
- return (noack);
+ return (noack ? IIC_ENOACK : 0);
}
static void
iicbb_sendbyte(device_t dev, u_char data, int timeout)
{
int i;
-
+
for (i=7; i>=0; i--) {
if (data&(1<<i)) {
iicbb_one(dev, timeout);
@@ -341,7 +373,7 @@
iicbb_zero(dev, timeout);
}
I2C_DEBUG(printf("r%02x%c ",(int)data,last?'-':'+'));
- return data;
+ return (data);
}
static int
@@ -353,6 +385,7 @@
static int
iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
{
+ iicbb_set_speed(device_get_softc(dev), speed);
return (IICBB_RESET(device_get_parent(dev), speed, addr, oldaddr));
}
@@ -365,6 +398,11 @@
I2C_DEBUG(printf("<"));
I2C_SET(sc,dev,1,1);
+
+ /* SCL must be high now. */
+ if (!I2C_GETSCL(dev))
+ return (IIC_ETIMEOUT);
+
I2C_SET(sc,dev,1,0);
I2C_SET(sc,dev,0,0);
@@ -372,14 +410,10 @@
iicbb_sendbyte(dev, slave, timeout);
/* check for ack */
- if (iicbb_ack(dev, timeout)) {
- error = IIC_ENOACK;
- goto error;
- }
+ error = iicbb_ack(dev, timeout);
+ if (error == 0)
+ return (0);
- return(0);
-
-error:
iicbb_stop(dev);
return (error);
}
@@ -394,6 +428,10 @@
I2C_SET(sc,dev,1,1);
I2C_DEBUG(printf(">"));
I2C_DEBUG(printf("\n"));
+
+ /* SCL must be high now. */
+ if (!I2C_GETSCL(dev))
+ return (IIC_ETIMEOUT);
return (0);
}
@@ -408,15 +446,13 @@
iicbb_sendbyte(dev,(u_char)*buf++, timeout);
/* check for ack */
- if (iicbb_ack(dev, timeout)) {
- error = IIC_ENOACK;
- goto error;
- }
- bytes ++;
- len --;
+ error = iicbb_ack(dev, timeout);
+ if (error != 0)
+ break;
+ bytes++;
+ len--;
}
-error:
*sent = bytes;
return (error);
}
@@ -452,6 +488,24 @@
IICBB_POST_XFER(device_get_parent(dev));
return (error);
+}
+
+static void
+iicbb_set_speed(struct iicbb_softc *sc, u_char speed)
+{
+ u_int busfreq, period;
+
+ /*
+ * NB: the resulting frequency will be a quarter (even less) of the
+ * configured bus frequency. This is for historic reasons. The default
+ * bus frequency is 100 kHz. And the historic default udelay is 10
+ * microseconds. The cycle of sending a bit takes four udelay-s plus
+ * SCL is kept low for extra two udelay-s. The actual I/O toggling also
+ * has an overhead.
+ */
+ busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed);
+ period = 1000000 / busfreq; /* Hz -> uS */
+ sc->udelay = MAX(period, 1);
}
DRIVER_MODULE(iicbus, iicbb, iicbus_driver, iicbus_devclass, 0, 0);

File Metadata

Mime Type
text/plain
Expires
Fri, Dec 27, 11:06 AM (10 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15613652
Default Alt Text
D22109.id63540.diff (6 KB)

Event Timeline