summaryrefslogtreecommitdiff
path: root/sys/dev/i2c
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2013-04-20 14:27:10 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2013-04-20 14:27:10 +0000
commita0a8d5f2e7371d2bd388fa6ae71cd31ecfade412 (patch)
tree01de5056cd5188c8c1b039dd6978ad79baf54f23 /sys/dev/i2c
parent5591268b32dee28ab372cd2ad7ed51a744e64646 (diff)
Implement support for "clock stretching" where a slave device pulls down SCL
to slow down the master device. This makes the i2c bit-banging code work with slow slave device such as SDVO chips and DDC eeproms found on various graphics hardware supported by inteldrm(4). Mostly from NetBSD.
Diffstat (limited to 'sys/dev/i2c')
-rw-r--r--sys/dev/i2c/i2c_bitbang.c33
1 files changed, 32 insertions, 1 deletions
diff --git a/sys/dev/i2c/i2c_bitbang.c b/sys/dev/i2c/i2c_bitbang.c
index 5a865b0af9c..b63a3682e71 100644
--- a/sys/dev/i2c/i2c_bitbang.c
+++ b/sys/dev/i2c/i2c_bitbang.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: i2c_bitbang.c,v 1.3 2006/01/13 23:56:46 grange Exp $ */
+/* $OpenBSD: i2c_bitbang.c,v 1.4 2013/04/20 14:27:09 kettenis Exp $ */
/* $NetBSD: i2c_bitbang.c,v 1.1 2003/09/30 00:35:31 thorpej Exp $ */
/*
@@ -54,6 +54,27 @@
#define OUTPUT ops->ibo_bits[I2C_BIT_OUTPUT] /* SDA is output */
#define INPUT ops->ibo_bits[I2C_BIT_INPUT] /* SDA is input */
+#define SCL_BAIL_COUNT 1000
+
+int i2c_wait_for_scl(void *, i2c_bitbang_ops_t);
+
+int
+i2c_wait_for_scl(void *v, i2c_bitbang_ops_t ops)
+{
+ int bail = 0;
+
+ while(((BB_READ & SCL) == 0) && bail < SCL_BAIL_COUNT) {
+ delay(1);
+ bail++;
+ }
+ if (bail == SCL_BAIL_COUNT) {
+ i2c_bitbang_send_stop(v, 0, ops);
+ return (EIO);
+ }
+
+ return (0);
+}
+
/*ARGSUSED*/
int
i2c_bitbang_send_start(void *v, int flags, i2c_bitbang_ops_t ops)
@@ -64,6 +85,8 @@ i2c_bitbang_send_start(void *v, int flags, i2c_bitbang_ops_t ops)
BB_SET(SDA | SCL);
delay(5); /* bus free time (4.7 uS) */
BB_SET( SCL);
+ if (i2c_wait_for_scl(v, ops) != 0)
+ return (EIO);
delay(4); /* start hold time (4.0 uS) */
BB_SET( 0);
delay(5); /* clock low time (4.7 uS) */
@@ -115,6 +138,8 @@ i2c_bitbang_read_byte(void *v, uint8_t *valp, int flags,
for (i = 0; i < 8; i++) {
val <<= 1;
BB_SET(SDA | SCL);
+ if (i2c_wait_for_scl(v, ops) != 0)
+ return (EIO);
delay(4); /* clock high time (4.0 uS) */
if (BB_READ & SDA)
val |= 1;
@@ -127,6 +152,8 @@ i2c_bitbang_read_byte(void *v, uint8_t *valp, int flags,
BB_SET(bit );
delay(1); /* data setup time (250 nS) */
BB_SET(bit | SCL);
+ if (i2c_wait_for_scl(v, ops) != 0)
+ return (EIO);
delay(4); /* clock high time (4.0 uS) */
BB_SET(bit );
delay(5); /* clock low time (4.7 uS) */
@@ -157,6 +184,8 @@ i2c_bitbang_write_byte(void *v, uint8_t val, int flags,
BB_SET(bit );
delay(1); /* data setup time (250 nS) */
BB_SET(bit | SCL);
+ if (i2c_wait_for_scl(v, ops) != 0)
+ return (EIO);
delay(4); /* clock high time (4.0 uS) */
BB_SET(bit );
delay(5); /* clock low time (4.7 uS) */
@@ -167,6 +196,8 @@ i2c_bitbang_write_byte(void *v, uint8_t val, int flags,
BB_SET(SDA );
delay(5);
BB_SET(SDA | SCL);
+ if (i2c_wait_for_scl(v, ops) != 0)
+ return (EIO);
delay(4);
error = (BB_READ & SDA) ? EIO : 0;
BB_SET(SDA );