public inbox for linux-omap@vger.kernel.org
 help / color / mirror / Atom feed
* [pacth] I2C bug fixes for L-O and L-Z
@ 2009-02-14  2:39 Woodruff, Richard
  2009-03-05 13:49 ` Aaro Koskinen
  0 siblings, 1 reply; 8+ messages in thread
From: Woodruff, Richard @ 2009-02-14  2:39 UTC (permalink / raw)
  To: linux-omap@vger.kernel.org

[-- Attachment #1: Type: text/plain, Size: 1397 bytes --]

Hi,

Recently one custom board was having some I2C issues. In looking at it a couple things stood out.  I've attached a patches for l-o and l-z for anyone to comment on which cares.

Bug Fixes:

        -1- OMAP_I2C_WE_STC_WE should never be enabled for wake up unless I+F clk is cut.  Having it set like code does can mess up I2C FSM.

        -2- STT/STP bits are best written together.  There was a 2430-es1'ish errata which might have existed.  This is not the case in 3430.  There should be no need to use b_hw = 1.
                -a- Any write to I2C_CON is bad before ARDY.  An untimely write can mess up I2C FSM for next write.

        -3- There is a possible issue which a double clear of ARDY status in irq handler can make sure doesn't happen.

Note: When using I2C with HS mode there is a target bus capacitance for proper operation.  Depending on your board the optimal pull up resister level might very.  On OMAP3 there seems to be a few options which can allow you to play with effective resistance with out a hardware mod (and potential get better quality).
        - CONTROL block has pull-up control for special internal pulls
        - Padconf has normal pull-ups
        - T2 has internal controllable pull-ups (default on)
        - Your board might have an external pull up.

Signed-off-by: Richard Woodruff <r-woodruff2@ti.com>

Regards,
Richard W.

[-- Attachment #2: i2c_lo.diff --]
[-- Type: application/octet-stream, Size: 3954 bytes --]

diff --git a/arch/arm/mach-integrator/clock.h b/arch/arm/mach-integrator/clock.h
deleted file mode 100644
index e69de29..0000000
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index c95368c..ce39e7f 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -91,9 +91,13 @@
 #define OMAP_I2C_STAT_NACK	(1 << 1)	/* No ack interrupt enable */
 #define OMAP_I2C_STAT_AL	(1 << 0)	/* Arbitration lost int ena */
 
-/* I2C WE wakeup enable register */
+/* I2C WE wakeup enable register 
+ * Don't use WE_STC unless I+F clocks are OFF else bad FSM
+ */
 #define OMAP_I2C_WE_XDR_WE	(1 << 14)	/* TX drain wakup */
 #define OMAP_I2C_WE_RDR_WE	(1 << 13)	/* RX drain wakeup */
+#define OMAP_I2C_WE_ROVR_WE	(1 << 11)	/* RX overflow wakeup */
+#define OMAP_I2C_WE_XUDF_WE	(1 << 10)	/* TX underflow wakeup */
 #define OMAP_I2C_WE_AAS_WE	(1 << 9)	/* Address as slave wakeup*/
 #define OMAP_I2C_WE_BF_WE	(1 << 8)	/* Bus free wakeup */
 #define OMAP_I2C_WE_STC_WE	(1 << 6)	/* Start condition wakeup */
@@ -104,10 +108,11 @@
 #define OMAP_I2C_WE_AL_WE	(1 << 0)	/* Arbitration lost wakeup */
 
 #define OMAP_I2C_WE_ALL		(OMAP_I2C_WE_XDR_WE | OMAP_I2C_WE_RDR_WE | \
+				OMAP_I2C_WE_ROVR_WE | OMAP_I2C_WE_ROVR_WE | \
 				OMAP_I2C_WE_AAS_WE | OMAP_I2C_WE_BF_WE | \
-				OMAP_I2C_WE_STC_WE | OMAP_I2C_WE_GC_WE | \
-				OMAP_I2C_WE_DRDY_WE | OMAP_I2C_WE_ARDY_WE | \
-				OMAP_I2C_WE_NACK_WE | OMAP_I2C_WE_AL_WE)
+				OMAP_I2C_WE_GC_WE | OMAP_I2C_WE_DRDY_WE | \
+				OMAP_I2C_WE_ARDY_WE | OMAP_I2C_WE_NACK_WE | \
+				OMAP_I2C_WE_AL_WE)
 
 /* I2C Buffer Configuration Register (OMAP_I2C_BUF): */
 #define OMAP_I2C_BUF_RDMA_EN	(1 << 15)	/* RX DMA channel enable */
@@ -265,6 +270,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
 	unsigned long fclk_rate = 12000000;
 	unsigned long timeout;
 	unsigned long internal_clk = 0;
+	int delay_count = 100;
 
 	if (dev->rev >= OMAP_I2C_REV_2) {
 		omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, SYSC_SOFTRESET_MASK);
@@ -279,7 +285,10 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
 						"for controller reset\n");
 				return -ETIMEDOUT;
 			}
-			msleep(1);
+			if( --delay_count > 0 )
+				udelay(1);
+			else    
+				msleep(1);
 		}
 
 		/* SYSC register is cleared by the reset; rewrite it */
@@ -408,14 +417,18 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
 static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
 {
 	unsigned long timeout;
-
+	int delay_count = 100;
+	
 	timeout = jiffies + OMAP_I2C_TIMEOUT;
 	while (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
 		if (time_after(jiffies, timeout)) {
 			dev_warn(dev->dev, "timeout waiting for bus ready\n");
 			return -ETIMEDOUT;
 		}
-		msleep(1);
+		if( --delay_count > 0 )
+			udelay(1);
+		else    
+			msleep(1);
 	}
 
 	return 0;
@@ -486,7 +499,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 			}
 			cpu_relax();
 		}
-
+		/* FIXME: should consider ARDY value before writing to I2C_CON */
 		w |= OMAP_I2C_CON_STP;
 		w &= ~OMAP_I2C_CON_STT;
 		omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
@@ -673,8 +686,11 @@ omap_i2c_isr(int this_irq, void *dev_id)
 			err |= OMAP_I2C_STAT_AL;
 		}
 		if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
-					OMAP_I2C_STAT_AL))
-			omap_i2c_complete_cmd(dev, err);
+					OMAP_I2C_STAT_AL)){
+				/* errata: ARDY needs double clear for some hardware */
+				omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, OMAP_I2C_STAT_ARDY);	
+				omap_i2c_complete_cmd(dev, err);
+		}
 		if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) {
 			u8 num_bytes = 1;
 			if (dev->fifo_size) {
@@ -846,9 +862,11 @@ omap_i2c_probe(struct platform_device *pdev)
 		 * call back latencies.
 		 */
 		dev->fifo_size = (dev->fifo_size / 2);
-		dev->b_hw = 1; /* Enable hardware fixes */
 	}
 
+	if (cpu_is_omap2430())
+		dev->b_hw = 1; /* Enable hardware fix (might be es1 only) */
+
 	/* reset ASAP, clearing any IRQs */
 	omap_i2c_init(dev);
 

[-- Attachment #3: i2c_lz.diff --]
[-- Type: application/octet-stream, Size: 4044 bytes --]

diff --git a/arch/arm/mach-omap2/board-ldp.c b/arch/arm/mach-omap2/board-ldp.c
index 722d1bc..c799695 100755
--- a/arch/arm/mach-omap2/board-ldp.c
+++ b/arch/arm/mach-omap2/board-ldp.c
@@ -805,7 +805,7 @@ static struct i2c_board_info __initdata ldp_i2c_boardinfo_2[] = {
 
 static int __init omap_i2c_init(void)
 {
-	omap_register_i2c_bus(1, 400, ldp_i2c_boardinfo,
+	omap_register_i2c_bus(1, 2600, ldp_i2c_boardinfo,
 			ARRAY_SIZE(ldp_i2c_boardinfo));
 	omap_register_i2c_bus(2, 100, ldp_i2c_boardinfo_2,
 			ARRAY_SIZE(ldp_i2c_boardinfo_2));
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 9a05419..0bd8332 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -91,7 +91,9 @@
 #define OMAP_I2C_STAT_NACK	(1 << 1)	/* No ack interrupt enable */
 #define OMAP_I2C_STAT_AL	(1 << 0)	/* Arbitration lost int ena */
 
-/* I2C WE wakeup enable register */
+/* I2C WE wakeup enable register 
+ * Don't use WE_STC unless I+F clocks are OFF else bad FSM
+ */
 #define OMAP_I2C_WE_XDR_WE	(1 << 14)	/* TX drain wakup */
 #define OMAP_I2C_WE_RDR_WE	(1 << 13)	/* RX drain wakeup */
 #define OMAP_I2C_WE_ROVR_WE	(1 << 11)	/* RX overflow wakeup */
@@ -108,9 +110,9 @@
 #define OMAP_I2C_WE_ALL		(OMAP_I2C_WE_XDR_WE | OMAP_I2C_WE_RDR_WE | \
 				OMAP_I2C_WE_ROVR_WE | OMAP_I2C_WE_ROVR_WE | \
 				OMAP_I2C_WE_AAS_WE | OMAP_I2C_WE_BF_WE | \
-				OMAP_I2C_WE_STC_WE | OMAP_I2C_WE_GC_WE | \
-				OMAP_I2C_WE_DRDY_WE | OMAP_I2C_WE_ARDY_WE | \
-				OMAP_I2C_WE_NACK_WE | OMAP_I2C_WE_AL_WE)
+				OMAP_I2C_WE_GC_WE | OMAP_I2C_WE_DRDY_WE | \
+				OMAP_I2C_WE_ARDY_WE | OMAP_I2C_WE_NACK_WE | \
+				OMAP_I2C_WE_AL_WE)
 
 /* I2C Buffer Configuration Register (OMAP_I2C_BUF): */
 #define OMAP_I2C_BUF_RDMA_EN	(1 << 15)	/* RX DMA channel enable */
@@ -284,6 +286,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
 	unsigned long fclk_rate = 12000000;
 	unsigned long timeout;
 	unsigned long internal_clk = 0;
+	int delay_count = 100;
 
 	if (dev->rev >= OMAP_I2C_REV_2) {
 		omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, SYSC_SOFTRESET_MASK);
@@ -298,7 +301,10 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
 						"for controller reset\n");
 				return -ETIMEDOUT;
 			}
-			msleep(1);
+			if( --delay_count > 0 )
+				udelay(1);
+			else    
+				msleep(1);
 		}
 
 		/* SYSC register is cleared by the reset; rewrite it */
@@ -428,14 +434,18 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
 static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
 {
 	unsigned long timeout;
-
+	int delay_count = 100;
+	
 	timeout = jiffies + OMAP_I2C_TIMEOUT;
 	while (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
 		if (time_after(jiffies, timeout)) {
 			dev_warn(dev->dev, "timeout waiting for bus ready\n");
 			return -ETIMEDOUT;
 		}
-		msleep(1);
+		if( --delay_count > 0 )
+			udelay(1);
+		else    
+			msleep(1);
 	}
 
 	return 0;
@@ -506,7 +516,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 			}
 			cpu_relax();
 		}
-
+		/* FIXME: should consider ARDY value before writing to I2C_CON */
 		w |= OMAP_I2C_CON_STP;
 		w &= ~OMAP_I2C_CON_STT;
 		omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
@@ -693,8 +703,11 @@ omap_i2c_isr(int this_irq, void *dev_id)
 			err |= OMAP_I2C_STAT_AL;
 		}
 		if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
-					OMAP_I2C_STAT_AL))
-			omap_i2c_complete_cmd(dev, err);
+					OMAP_I2C_STAT_AL)){
+				/* errata: ARDY needs double clear for some hardware */
+				omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, OMAP_I2C_STAT_ARDY);	
+				omap_i2c_complete_cmd(dev, err);
+		}
 		if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) {
 			u8 num_bytes = 1;
 			if (dev->fifo_size) {
@@ -866,9 +879,11 @@ omap_i2c_probe(struct platform_device *pdev)
 		 * call back latencies.
 		 */
 		dev->fifo_size = (dev->fifo_size / 2);
-		dev->b_hw = 1; /* Enable hardware fixes */
 	}
 
+	if (cpu_is_omap2430())
+		dev->b_hw = 1; /* Enable hardware fix (might be es1 only) */
+
 	/* reset ASAP, clearing any IRQs */
 	omap_i2c_init(dev);
 

^ permalink raw reply related	[flat|nested] 8+ messages in thread
* RE: [pacth] I2C bug fixes for L-O and L-Z
@ 2009-02-16  7:02 Eero Nurkkala
  2009-02-16 14:19 ` Woodruff, Richard
  0 siblings, 1 reply; 8+ messages in thread
From: Eero Nurkkala @ 2009-02-16  7:02 UTC (permalink / raw)
  To: linux-omap

Could you please also address the question of the load on the SCL line?

In my understanding, many boards are using the experimental values
from TRM for SCLL, SCLH, HSSCLL and HSSCLH. I have notices that those
"default" values fail to provide a standard I2C clock for, at least,
boards that I have tested. Should those values be board specific and
taken off from the I2C driver? Or should a note be added there saying
"These values are board specific and they depend on the load on the SCL
line. Please measure and validate correct values for your board".



^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2009-03-05 13:49 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-02-14  2:39 [pacth] I2C bug fixes for L-O and L-Z Woodruff, Richard
2009-03-05 13:49 ` Aaro Koskinen
  -- strict thread matches above, loose matches on Subject: below --
2009-02-16  7:02 Eero Nurkkala
2009-02-16 14:19 ` Woodruff, Richard
2009-02-17  6:02   ` Eero Nurkkala
2009-02-17  6:22     ` Woodruff, Richard
2009-02-20 20:59       ` Woodruff, Richard
2009-02-23 13:37         ` Eero Nurkkala

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox