[PATCH] i2c-omap: Update driver

Jan Weitzel j.weitzel at phytec.de
Thu Sep 26 07:40:24 EDT 2013


The driver didn't work well with at24 driver. NACKS are lost.
Errors are lost in isr due to the local variable err. Also we didn't wait for
bus free in omap_i2c_xfer_msg.

Fix issues and get other improvements from linux kernel

Tested on OMAP4 and AM335x

Signed-off-by: Jan Weitzel <j.weitzel at phytec.de>
---
 arch/arm/mach-omap/include/mach/generic.h |    9 +-
 drivers/i2c/busses/i2c-omap.c             |  635 ++++++++++++++++++++---------
 include/i2c/i2c.h                         |    1 +
 3 files changed, 454 insertions(+), 191 deletions(-)

diff --git a/arch/arm/mach-omap/include/mach/generic.h b/arch/arm/mach-omap/include/mach/generic.h
index ece8c2b..31ab100 100644
--- a/arch/arm/mach-omap/include/mach/generic.h
+++ b/arch/arm/mach-omap/include/mach/generic.h
@@ -2,12 +2,13 @@
 #define _MACH_GENERIC_H
 
 /* I2C controller revisions */
-#define OMAP_I2C_REV_2			0x20
+#define OMAP_I2C_OMAP1_REV_2            0x20
 
 /* I2C controller revisions present on specific hardware */
-#define OMAP_I2C_REV_ON_2430		0x36
-#define OMAP_I2C_REV_ON_3430		0x3C
-#define OMAP_I2C_REV_ON_4430		0x40
+#define OMAP_I2C_REV_ON_2430		0x00000036
+#define OMAP_I2C_REV_ON_3430_3530       0x0000003C
+#define OMAP_I2C_REV_ON_3630            0x00000040
+#define OMAP_I2C_REV_ON_4430_PLUS       0x50400002
 
 #ifdef CONFIG_ARCH_OMAP
 #define cpu_is_omap2430()	(1)
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 2eb5133..bec3b29 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -131,29 +131,41 @@
 #define SYSC_IDLEMODE_SMART		0x2
 #define SYSC_CLOCKACTIVITY_FCLK		0x2
 
+/* Errata definitions */
+#define I2C_OMAP_ERRATA_I207		(1 << 0)
+#define I2C_OMAP_ERRATA_I462		(1 << 1)
+
 /* i2c driver flags from kernel */
-#define OMAP_I2C_FLAG_RESET_REGS_POSTIDLE	BIT(3)
+#define OMAP_I2C_FLAG_NO_FIFO			BIT(0)
+#define OMAP_I2C_FLAG_16BIT_DATA_REG		BIT(2)
 #define OMAP_I2C_FLAG_BUS_SHIFT_NONE		0
 #define OMAP_I2C_FLAG_BUS_SHIFT_1		BIT(7)
 #define OMAP_I2C_FLAG_BUS_SHIFT_2		BIT(8)
 #define OMAP_I2C_FLAG_BUS_SHIFT__SHIFT		7
 
+/* timeout waiting for the controller to respond */
+#define OMAP_I2C_TIMEOUT		(1000 * MSECOND)	/* ms */
+
 struct omap_i2c_struct {
 	void 			*base;
 	u8			reg_shift;
 	struct omap_i2c_driver_data	*data;
 	struct resource		*ioarea;
 	u32			speed;		/* Speed of bus in Khz */
+	u16			scheme;
 	u16			cmd_err;
 	u8			*buf;
+	u8			*regs;
 	size_t			buf_len;
 	struct i2c_adapter	adapter;
+	u8			threshold;
 	u8			fifo_size;	/* use as flag and value
 						 * fifo_size==0 implies no fifo
 						 * if set, should be trsh+1
 						 */
-	u8			rev;
+	u32			rev;
 	unsigned		b_hw:1;		/* bad h/w fixes */
+	unsigned		receiver:1;	/* true for receiver mode */
 	u16			iestate;	/* Saved interrupt register */
 	u16			pscstate;
 	u16			scllstate;
@@ -161,6 +173,7 @@ struct omap_i2c_struct {
 	u16			bufstate;
 	u16			syscstate;
 	u16			westate;
+	u16			errata;
 };
 #define to_omap_i2c_struct(a)	container_of(a, struct omap_i2c_struct, adapter)
 
@@ -183,14 +196,15 @@ enum {
 	OMAP_I2C_SCLH_REG,
 	OMAP_I2C_SYSTEST_REG,
 	OMAP_I2C_BUFSTAT_REG,
-	OMAP_I2C_REVNB_LO,
-	OMAP_I2C_REVNB_HI,
-	OMAP_I2C_IRQSTATUS_RAW,
-	OMAP_I2C_IRQENABLE_SET,
-	OMAP_I2C_IRQENABLE_CLR,
+	/* only on OMAP4430 */
+	OMAP_I2C_IP_V2_REVNB_LO,
+	OMAP_I2C_IP_V2_REVNB_HI,
+	OMAP_I2C_IP_V2_IRQSTATUS_RAW,
+	OMAP_I2C_IP_V2_IRQENABLE_SET,
+	OMAP_I2C_IP_V2_IRQENABLE_CLR,
 };
 
-static const u8 reg_map[] = {
+static const u8 reg_map_ip_v1[] = {
 	[OMAP_I2C_REV_REG] = 0x00,
 	[OMAP_I2C_IE_REG] = 0x01,
 	[OMAP_I2C_STAT_REG] = 0x02,
@@ -211,7 +225,7 @@ static const u8 reg_map[] = {
 	[OMAP_I2C_BUFSTAT_REG] = 0x10,
 };
 
-static const u8 omap4_reg_map[] = {
+static const u8 reg_map_ip_v2[] = {
 	[OMAP_I2C_REV_REG] = 0x04,
 	[OMAP_I2C_IE_REG] = 0x2c,
 	[OMAP_I2C_STAT_REG] = 0x28,
@@ -230,92 +244,104 @@ static const u8 omap4_reg_map[] = {
 	[OMAP_I2C_SCLH_REG] = 0xb8,
 	[OMAP_I2C_SYSTEST_REG] = 0xbc,
 	[OMAP_I2C_BUFSTAT_REG] = 0xc0,
-	[OMAP_I2C_REVNB_LO] = 0x00,
-	[OMAP_I2C_REVNB_HI] = 0x04,
-	[OMAP_I2C_IRQSTATUS_RAW] = 0x24,
-	[OMAP_I2C_IRQENABLE_SET] = 0x2c,
-	[OMAP_I2C_IRQENABLE_CLR] = 0x30,
+	[OMAP_I2C_IP_V2_REVNB_LO] = 0x00,
+	[OMAP_I2C_IP_V2_REVNB_HI] = 0x04,
+	[OMAP_I2C_IP_V2_IRQSTATUS_RAW] = 0x24,
+	[OMAP_I2C_IP_V2_IRQENABLE_SET] = 0x2c,
+	[OMAP_I2C_IP_V2_IRQENABLE_CLR] = 0x30,
 };
 
 struct omap_i2c_driver_data {
 	u32		flags;
 	u32		fclk_rate;
-	u8		*regs;
 };
 
 static struct omap_i2c_driver_data omap3_data = {
-	.flags =	OMAP_I2C_FLAG_RESET_REGS_POSTIDLE |
-			OMAP_I2C_FLAG_BUS_SHIFT_2,
+	.flags =	OMAP_I2C_FLAG_BUS_SHIFT_2,
 	.fclk_rate =	96000,
-	.regs =		(u8 *) reg_map,
 };
 
 static struct omap_i2c_driver_data omap4_data = {
 	.flags =	OMAP_I2C_FLAG_BUS_SHIFT_NONE,
 	.fclk_rate =	96000,
-	.regs =		(u8 *) omap4_reg_map,
 };
 
 static struct omap_i2c_driver_data am33xx_data = {
-	.flags =	OMAP_I2C_FLAG_RESET_REGS_POSTIDLE |
-			OMAP_I2C_FLAG_BUS_SHIFT_NONE,
+	.flags =	OMAP_I2C_FLAG_BUS_SHIFT_NONE,
 	.fclk_rate =	48000,
-	.regs =		(u8 *) omap4_reg_map,
 };
 
 static inline void omap_i2c_write_reg(struct omap_i2c_struct *i2c_omap,
 				      int reg, u16 val)
 {
 	__raw_writew(val, i2c_omap->base +
-			(i2c_omap->data->regs[reg] << i2c_omap->reg_shift));
+			(i2c_omap->regs[reg] << i2c_omap->reg_shift));
 }
 
 static inline u16 omap_i2c_read_reg(struct omap_i2c_struct *i2c_omap, int reg)
 {
 	return __raw_readw(i2c_omap->base +
-			(i2c_omap->data->regs[reg] << i2c_omap->reg_shift));
+			(i2c_omap->regs[reg] << i2c_omap->reg_shift));
 }
 
-static void omap_i2c_unidle(struct omap_i2c_struct *i2c_omap)
+static void __omap_i2c_init(struct omap_i2c_struct *dev)
 {
-	struct omap_i2c_driver_data *i2c_data = i2c_omap->data;
 
-	if (i2c_data->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) {
-		omap_i2c_write_reg(i2c_omap, OMAP_I2C_CON_REG, 0);
-		omap_i2c_write_reg(i2c_omap, OMAP_I2C_PSC_REG, i2c_omap->pscstate);
-		omap_i2c_write_reg(i2c_omap, OMAP_I2C_SCLL_REG, i2c_omap->scllstate);
-		omap_i2c_write_reg(i2c_omap, OMAP_I2C_SCLH_REG, i2c_omap->sclhstate);
-		omap_i2c_write_reg(i2c_omap, OMAP_I2C_BUF_REG, i2c_omap->bufstate);
-		omap_i2c_write_reg(i2c_omap, OMAP_I2C_SYSC_REG, i2c_omap->syscstate);
-		omap_i2c_write_reg(i2c_omap, OMAP_I2C_WE_REG, i2c_omap->westate);
-		omap_i2c_write_reg(i2c_omap, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
-	}
+	omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+
+	/* Setup clock prescaler to obtain approx 12MHz I2C module clock: */
+	omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, dev->pscstate);
+
+	/* SCL low and high time values */
+	omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, dev->scllstate);
+	omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, dev->sclhstate);
+	if (dev->rev >= OMAP_I2C_REV_ON_3430_3530)
+		omap_i2c_write_reg(dev, OMAP_I2C_WE_REG, dev->westate);
+
+	/* Take the I2C module out of reset: */
+	omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
 
 	/*
 	 * Don't write to this register if the IE state is 0 as it can
 	 * cause deadlock.
 	 */
-	if (i2c_omap->iestate)
-		omap_i2c_write_reg(i2c_omap, OMAP_I2C_IE_REG, i2c_omap->iestate);
+	if (dev->iestate)
+		omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, dev->iestate);
 }
 
-static void omap_i2c_idle(struct omap_i2c_struct *i2c_omap)
+static int omap_i2c_reset(struct omap_i2c_struct *dev)
 {
-	u16 iv;
+	uint64_t start;
+	u16 sysc;
 
-	i2c_omap->iestate = omap_i2c_read_reg(i2c_omap, OMAP_I2C_IE_REG);
+	if (dev->rev >= OMAP_I2C_OMAP1_REV_2) {
+		sysc = omap_i2c_read_reg(dev, OMAP_I2C_SYSC_REG);
 
-	/* Barebox driver don't need to clear interrupts here */
+		/* Disable I2C controller before soft reset */
+		omap_i2c_write_reg(dev, OMAP_I2C_CON_REG,
+			omap_i2c_read_reg(dev, OMAP_I2C_CON_REG) &
+				~(OMAP_I2C_CON_EN));
 
-	/* omap_i2c_write_reg(i2c_omap, OMAP_I2C_IE_REG, 0); */
-	if (i2c_omap->rev < OMAP_I2C_REV_2) {
-		iv = omap_i2c_read_reg(i2c_omap, OMAP_I2C_IV_REG); /* Read clears */
-	} else {
-		omap_i2c_write_reg(i2c_omap, OMAP_I2C_STAT_REG, i2c_omap->iestate);
+		omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, SYSC_SOFTRESET_MASK);
+		/* For some reason we need to set the EN bit before the
+		 * reset done bit gets set. */
+		omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
+		start = get_time_ns();
+		while (!(omap_i2c_read_reg(dev, OMAP_I2C_SYSS_REG) &
+			 SYSS_RESETDONE_MASK)) {
+			if (is_timeout(start, OMAP_I2C_TIMEOUT)) {
+				dev_warn(&dev->adapter.dev, "timeout waiting "
+						"for controller reset\n");
+				return -ETIMEDOUT;
+			}
+			mdelay(1000);
+		}
+
+		/* SYSC register is cleared by the reset; rewrite it */
+		omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, sysc);
 
-		/* Flush posted write before the i2c_omap->idle store occurs */
-		omap_i2c_read_reg(i2c_omap, OMAP_I2C_STAT_REG);
 	}
+	return 0;
 }
 
 static int omap_i2c_init(struct omap_i2c_struct *i2c_omap)
@@ -326,7 +352,7 @@ static int omap_i2c_init(struct omap_i2c_struct *i2c_omap)
 	unsigned long internal_clk = 0;
 	struct omap_i2c_driver_data *i2c_data = i2c_omap->data;
 
-	if (i2c_omap->rev >= OMAP_I2C_REV_2) {
+	if (i2c_omap->rev >= OMAP_I2C_OMAP1_REV_2) {
 		/* Disable I2C controller before soft reset */
 		omap_i2c_write_reg(i2c_omap, OMAP_I2C_CON_REG,
 			omap_i2c_read_reg(i2c_omap, OMAP_I2C_CON_REG) &
@@ -354,7 +380,7 @@ static int omap_i2c_init(struct omap_i2c_struct *i2c_omap)
 			omap_i2c_write_reg(i2c_omap, OMAP_I2C_SYSC_REG,
 					   SYSC_AUTOIDLE_MASK);
 
-		} else if (i2c_omap->rev >= OMAP_I2C_REV_ON_3430) {
+		} else if (i2c_omap->rev >= OMAP_I2C_REV_ON_3430_3530) {
 			i2c_omap->syscstate = SYSC_AUTOIDLE_MASK;
 			i2c_omap->syscstate |= SYSC_ENAWAKEUP_MASK;
 			i2c_omap->syscstate |= (SYSC_IDLEMODE_SMART <<
@@ -443,12 +469,14 @@ static int omap_i2c_init(struct omap_i2c_struct *i2c_omap)
 			OMAP_I2C_IE_AL)  | ((i2c_omap->fifo_size) ?
 				(OMAP_I2C_IE_RDR | OMAP_I2C_IE_XDR) : 0);
 	omap_i2c_write_reg(i2c_omap, OMAP_I2C_IE_REG, i2c_omap->iestate);
-	if (i2c_data->flags & OMAP_I2C_FLAG_RESET_REGS_POSTIDLE) {
-		i2c_omap->pscstate = psc;
-		i2c_omap->scllstate = scll;
-		i2c_omap->sclhstate = sclh;
-		i2c_omap->bufstate = buf;
-	}
+
+	i2c_omap->pscstate = psc;
+	i2c_omap->scllstate = scll;
+	i2c_omap->sclhstate = sclh;
+	i2c_omap->bufstate = buf;
+
+	__omap_i2c_init(i2c_omap);
+
 	return 0;
 }
 
@@ -472,151 +500,301 @@ static int omap_i2c_wait_for_bb(struct i2c_adapter *adapter)
 }
 
 static inline void
+omap_i2c_complete_cmd(struct omap_i2c_struct *dev, u16 err)
+{
+	dev->cmd_err |= err;
+}
+
+static inline void
 omap_i2c_ack_stat(struct omap_i2c_struct *i2c_omap, u16 stat)
 {
 	omap_i2c_write_reg(i2c_omap, OMAP_I2C_STAT_REG, stat);
 }
 
+static int errata_omap3_i462(struct omap_i2c_struct *dev)
+{
+	unsigned long timeout = 10000;
+	u16 stat;
+
+	do {
+		stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
+		if (stat & OMAP_I2C_STAT_XUDF)
+			break;
+
+		if (stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) {
+			omap_i2c_ack_stat(dev, (OMAP_I2C_STAT_XRDY |
+							OMAP_I2C_STAT_XDR));
+			if (stat & OMAP_I2C_STAT_NACK) {
+				dev->cmd_err |= OMAP_I2C_STAT_NACK;
+				omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK);
+			}
+
+			if (stat & OMAP_I2C_STAT_AL) {
+				dev_err(&dev->adapter.dev, "Arbitration lost\n");
+				dev->cmd_err |= OMAP_I2C_STAT_AL;
+				omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL);
+			}
+
+			return -EIO;
+		}
+	} while (--timeout);
+
+	if (!timeout) {
+		dev_err(&dev->adapter.dev, "timeout waiting on XUDF bit\n");
+		return 0;
+	}
+
+	return 0;
+}
+
+static void omap_i2c_receive_data(struct omap_i2c_struct *dev, u8 num_bytes,
+		bool is_rdr)
+{
+	u16		w;
+
+	while (num_bytes--) {
+		w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
+		*dev->buf++ = w;
+		dev->buf_len--;
+
+		/*
+		 * Data reg in 2430, omap3 and
+		 * omap4 is 8 bit wide
+		 */
+		if (dev->data->flags & OMAP_I2C_FLAG_16BIT_DATA_REG) {
+			*dev->buf++ = w >> 8;
+			dev->buf_len--;
+		}
+	}
+}
+
+static inline void i2c_omap_errata_i207(struct omap_i2c_struct *dev, u16 stat)
+{
+	/*
+	 * I2C Errata(Errata Nos. OMAP2: 1.67, OMAP3: 1.8)
+	 * Not applicable for OMAP4.
+	 * Under certain rare conditions, RDR could be set again
+	 * when the bus is busy, then ignore the interrupt and
+	 * clear the interrupt.
+	 */
+	if (stat & OMAP_I2C_STAT_RDR) {
+		/* Step 1: If RDR is set, clear it */
+		omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR);
+
+		/* Step 2: */
+		if (!(omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG)
+						& OMAP_I2C_STAT_BB)) {
+
+			/* Step 3: */
+			if (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG)
+						& OMAP_I2C_STAT_RDR) {
+				omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR);
+				dev_dbg(&dev->adapter.dev, "RDR when bus is busy.\n");
+			}
+
+		}
+	}
+}
+
+static int omap_i2c_transmit_data(struct omap_i2c_struct *dev, u8 num_bytes,
+		bool is_xdr)
+{
+	u16		w;
+
+	while (num_bytes--) {
+		w = *dev->buf++;
+		dev->buf_len--;
+
+		/*
+		 * Data reg in 2430, omap3 and
+		 * omap4 is 8 bit wide
+		 */
+		if (dev->data->flags & OMAP_I2C_FLAG_16BIT_DATA_REG) {
+			w |= *dev->buf++ << 8;
+			dev->buf_len--;
+		}
+
+		if (dev->errata & I2C_OMAP_ERRATA_I462) {
+			int ret;
+
+			ret = errata_omap3_i462(dev);
+			if (ret < 0)
+				return ret;
+		}
+
+		omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
+	}
+
+	return 0;
+}
+
 static int
 omap_i2c_isr(struct omap_i2c_struct *dev)
 {
 	u16 bits;
-	u16 stat, w;
-	int err, count = 0;
+	u16 stat;
+	int err = 0, count = 0;
+
+	do {
+		bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
+		stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
+		stat &= bits;
+
+		/* If we're in receiver mode, ignore XDR/XRDY */
+		if (dev->receiver)
+			stat &= ~(OMAP_I2C_STAT_XDR | OMAP_I2C_STAT_XRDY);
+		else
+			stat &= ~(OMAP_I2C_STAT_RDR | OMAP_I2C_STAT_RRDY);
+
+		if (!stat) {
+			/* my work here is done */
+			goto out;
+		}
 
-	bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
-	while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
 		dev_dbg(&dev->adapter.dev, "IRQ (ISR = 0x%04x)\n", stat);
 		if (count++ == 100) {
 			dev_warn(&dev->adapter.dev, "Too much work in one IRQ\n");
 			break;
 		}
 
-		err = 0;
-complete:
-		/*
-		 * Ack the stat in one go, but [R/X]DR and [R/X]RDY should be
-		 * acked after the data operation is complete.
-		 * Ref: TRM SWPU114Q Figure 18-31
-		 */
-		omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat &
-				~(OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR |
-				OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
-
 		if (stat & OMAP_I2C_STAT_NACK) {
 			err |= OMAP_I2C_STAT_NACK;
-			omap_i2c_write_reg(dev, OMAP_I2C_CON_REG,
-					   OMAP_I2C_CON_STP);
+			omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK);
+			break;
 		}
+
 		if (stat & OMAP_I2C_STAT_AL) {
 			dev_err(&dev->adapter.dev, "Arbitration lost\n");
 			err |= OMAP_I2C_STAT_AL;
+			omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL);
+			break;
 		}
+
+		/*
+		 * ProDB0017052: Clear ARDY bit twice
+		 */
 		if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
 					OMAP_I2C_STAT_AL)) {
-			omap_i2c_ack_stat(dev, stat &
-				(OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR |
-				OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
-			return 0;
+			omap_i2c_ack_stat(dev, (OMAP_I2C_STAT_RRDY |
+						OMAP_I2C_STAT_RDR |
+						OMAP_I2C_STAT_XRDY |
+						OMAP_I2C_STAT_XDR |
+						OMAP_I2C_STAT_ARDY));
+			break;
 		}
-		if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) {
+
+		if (stat & OMAP_I2C_STAT_RDR) {
 			u8 num_bytes = 1;
-			if (dev->fifo_size) {
-				if (stat & OMAP_I2C_STAT_RRDY)
-					num_bytes = dev->fifo_size;
-				else    /* read RXSTAT on RDR interrupt */
-					num_bytes = (omap_i2c_read_reg(dev,
-							OMAP_I2C_BUFSTAT_REG)
-							>> 8) & 0x3F;
-			}
-			while (num_bytes) {
-				num_bytes--;
-				w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
-				if (dev->buf_len) {
-					*dev->buf++ = w;
-					dev->buf_len--;
-				} else {
-					if (stat & OMAP_I2C_STAT_RRDY)
-						dev_err(&dev->adapter.dev,
-							"RRDY IRQ while no data"
-								" requested\n");
-					if (stat & OMAP_I2C_STAT_RDR)
-						dev_err(&dev->adapter.dev,
-							"RDR IRQ while no data"
-								" requested\n");
-					break;
-				}
-			}
-			omap_i2c_ack_stat(dev,
-				stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR));
+
+			if (dev->fifo_size)
+				num_bytes = dev->buf_len;
+
+			omap_i2c_receive_data(dev, num_bytes, true);
+
+			if (dev->errata & I2C_OMAP_ERRATA_I207)
+				i2c_omap_errata_i207(dev, stat);
+
+			omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR);
 			continue;
 		}
-		if (stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)) {
+
+		if (stat & OMAP_I2C_STAT_RRDY) {
 			u8 num_bytes = 1;
-			if (dev->fifo_size) {
-				if (stat & OMAP_I2C_STAT_XRDY)
-					num_bytes = dev->fifo_size;
-				else    /* read TXSTAT on XDR interrupt */
-					num_bytes = omap_i2c_read_reg(dev,
-							OMAP_I2C_BUFSTAT_REG)
-							& 0x3F;
-			}
-			while (num_bytes) {
-				num_bytes--;
-				w = 0;
-				if (dev->buf_len) {
-					w = *dev->buf++;
-					dev->buf_len--;
-				} else {
-					if (stat & OMAP_I2C_STAT_XRDY)
-						dev_err(&dev->adapter.dev,
-							"XRDY IRQ while no "
-							"data to send\n");
-					if (stat & OMAP_I2C_STAT_XDR)
-						dev_err(&dev->adapter.dev,
-							"XDR IRQ while no "
-							"data to send\n");
-					break;
-				}
-
-				/*
-				 * OMAP3430 Errata 1.153: When an XRDY/XDR
-				 * is hit, wait for XUDF before writing data
-				 * to DATA_REG. Otherwise some data bytes can
-				 * be lost while transferring them from the
-				 * memory to the I2C interface.
-				 */
-
-				if (dev->rev <= OMAP_I2C_REV_ON_3430) {
-						while (!(stat & OMAP_I2C_STAT_XUDF)) {
-							if (stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) {
-								omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
-								err |= OMAP_I2C_STAT_XUDF;
-								goto complete;
-							}
-							stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
-						}
-				}
-
-				omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
-			}
-			omap_i2c_ack_stat(dev,
-				stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
+
+			if (dev->threshold)
+				num_bytes = dev->threshold;
+
+			omap_i2c_receive_data(dev, num_bytes, false);
+			omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RRDY);
+			continue;
+		}
+
+		if (stat & OMAP_I2C_STAT_XDR) {
+			u8 num_bytes = 1;
+			int ret;
+
+			if (dev->fifo_size)
+				num_bytes = dev->buf_len;
+
+			ret = omap_i2c_transmit_data(dev, num_bytes, true);
+			if (ret < 0)
+				break;
+
+			omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XDR);
 			continue;
 		}
+
+		if (stat & OMAP_I2C_STAT_XRDY) {
+			u8 num_bytes = 1;
+			int ret;
+
+			if (dev->threshold)
+				num_bytes = dev->threshold;
+
+			ret = omap_i2c_transmit_data(dev, num_bytes, false);
+			if (ret < 0)
+				break;
+
+			omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XRDY);
+			continue;
+		}
+
 		if (stat & OMAP_I2C_STAT_ROVR) {
 			dev_err(&dev->adapter.dev, "Receive overrun\n");
-			dev->cmd_err |= OMAP_I2C_STAT_ROVR;
+			err |= OMAP_I2C_STAT_ROVR;
+			omap_i2c_ack_stat(dev, OMAP_I2C_STAT_ROVR);
+			break;
 		}
+
 		if (stat & OMAP_I2C_STAT_XUDF) {
 			dev_err(&dev->adapter.dev, "Transmit underflow\n");
-			dev->cmd_err |= OMAP_I2C_STAT_XUDF;
+			err |= OMAP_I2C_STAT_XUDF;
+			omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XUDF);
+			break;
 		}
-	}
+	} while (stat);
 
+	omap_i2c_complete_cmd(dev, err);
+	return 0;
+
+out:
 	return -EBUSY;
 }
 
+static void omap_i2c_resize_fifo(struct omap_i2c_struct *dev, u8 size,
+								bool is_rx)
+{
+	u16		buf;
+
+	if (dev->data->flags & OMAP_I2C_FLAG_NO_FIFO)
+		return;
+
+	/*
+	 * Set up notification threshold based on message size. We're doing
+	 * this to try and avoid draining feature as much as possible. Whenever
+	 * we have big messages to transfer (bigger than our total fifo size)
+	 * then we might use draining feature to transfer the remaining bytes.
+	 */
+
+	dev->threshold = clamp(size, (u8) 1, dev->fifo_size);
+
+	buf = omap_i2c_read_reg(dev, OMAP_I2C_BUF_REG);
+
+	if (is_rx) {
+		/* Clear RX Threshold */
+		buf &= ~(0x3f << 8);
+		buf |= ((dev->threshold - 1) << 8) | OMAP_I2C_BUF_RXFIF_CLR;
+	} else {
+		/* Clear TX Threshold */
+		buf &= ~0x3f;
+		buf |= (dev->threshold - 1) | OMAP_I2C_BUF_TXFIF_CLR;
+	}
+
+	omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, buf);
+
+	if (dev->rev < OMAP_I2C_REV_ON_3630)
+		dev->b_hw = 1; /* Enable hardware fixes */
+}
 
 /*
  * Low level master read/write transaction.
@@ -637,12 +815,18 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adapter,
 	if (msg->len == 0)
 		return -EINVAL;
 
+	i2c_omap->receiver = !!(msg->flags & I2C_M_RD);
+	omap_i2c_resize_fifo(i2c_omap, msg->len, i2c_omap->receiver);
+
 	omap_i2c_write_reg(i2c_omap, OMAP_I2C_SA_REG, msg->addr);
 
 	/* REVISIT: Could the STB bit of I2C_CON be used with probing? */
 	i2c_omap->buf = msg->buf;
 	i2c_omap->buf_len = msg->len;
 
+	/* make sure writes to dev->buf_len are ordered */
+	barrier();
+
 	omap_i2c_write_reg(i2c_omap, OMAP_I2C_CNT_REG, i2c_omap->buf_len);
 
 	/* Clear the FIFO Buffers */
@@ -658,6 +842,8 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adapter,
 	if (i2c_omap->speed > 400)
 		w |= OMAP_I2C_CON_OPMODE_HS;
 
+	if (msg->flags & I2C_M_STOP)
+		stop = 1;
 	if (msg->flags & I2C_M_TEN)
 		w |= OMAP_I2C_CON_XA;
 	if (!(msg->flags & I2C_M_RD))
@@ -698,38 +884,64 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adapter,
 	ret = omap_i2c_isr(i2c_omap);
 	while (ret){
 		ret = omap_i2c_isr(i2c_omap);
-		if (is_timeout(start, 50 * MSECOND)) {
+		if (is_timeout(start, OMAP_I2C_TIMEOUT)) {
 				dev_err(&adapter->dev,
 				"timed out on polling for "
 				"open i2c message handling\n");
+				omap_i2c_reset(i2c_omap);
+				__omap_i2c_init(i2c_omap);
 				return -ETIMEDOUT;
 			}
 	}
 
-	i2c_omap->buf_len = 0;
 	if (likely(!i2c_omap->cmd_err))
 		return 0;
 
 	/* We have an error */
 	if (i2c_omap->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR |
 			    OMAP_I2C_STAT_XUDF)) {
-		omap_i2c_init(i2c_omap);
+		omap_i2c_reset(i2c_omap);
+		__omap_i2c_init(i2c_omap);
 		return -EIO;
 	}
 
 	if (i2c_omap->cmd_err & OMAP_I2C_STAT_NACK) {
 		if (msg->flags & I2C_M_IGNORE_NAK)
 			return 0;
-		if (stop) {
-			w = omap_i2c_read_reg(i2c_omap, OMAP_I2C_CON_REG);
-			w |= OMAP_I2C_CON_STP;
-			omap_i2c_write_reg(i2c_omap, OMAP_I2C_CON_REG, w);
-		}
+
+		w = omap_i2c_read_reg(i2c_omap, OMAP_I2C_CON_REG);
+		w |= OMAP_I2C_CON_STP;
+		omap_i2c_write_reg(i2c_omap, OMAP_I2C_CON_REG, w);
 		return -EREMOTEIO;
 	}
 	return -EIO;
 }
 
+static void omap_i2c_unidle(struct omap_i2c_struct *i2c_omap)
+{
+		__omap_i2c_init(i2c_omap);
+}
+
+static void omap_i2c_idle(struct omap_i2c_struct *i2c_omap)
+{
+	u16 iv;
+
+	i2c_omap->iestate = omap_i2c_read_reg(i2c_omap, OMAP_I2C_IE_REG);
+
+	/* Barebox driver don't need to clear interrupts here */
+
+	/* omap_i2c_write_reg(i2c_omap, OMAP_I2C_IE_REG, 0); */
+	if (i2c_omap->rev < OMAP_I2C_OMAP1_REV_2) {
+		/* Read clears */
+		iv = omap_i2c_read_reg(i2c_omap, OMAP_I2C_IV_REG);
+	} else {
+		omap_i2c_write_reg(i2c_omap, OMAP_I2C_STAT_REG,
+							i2c_omap->iestate);
+
+		/* Flush posted write before the i2c_omap->idle store occurs */
+		omap_i2c_read_reg(i2c_omap, OMAP_I2C_STAT_REG);
+	}
+}
 
 /*
  * Prepare controller for a transaction and call omap_i2c_xfer_msg
@@ -756,11 +968,24 @@ omap_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], int num)
 
 	if (r == 0)
 		r = num;
+
+	omap_i2c_wait_for_bb(adapter);
+
 out:
 	omap_i2c_idle(i2c_omap);
 	return r;
 }
 
+#define OMAP_I2C_SCHEME(rev)		((rev & 0xc000) >> 14)
+
+#define OMAP_I2C_REV_SCHEME_0_MAJOR(rev) (rev >> 4)
+#define OMAP_I2C_REV_SCHEME_0_MINOR(rev) (rev & 0xf)
+
+#define OMAP_I2C_REV_SCHEME_1_MAJOR(rev) ((rev & 0x0700) >> 7)
+#define OMAP_I2C_REV_SCHEME_1_MINOR(rev) (rev & 0x1f)
+#define OMAP_I2C_SCHEME_0		0
+#define OMAP_I2C_SCHEME_1		1
+
 static int __init
 i2c_omap_probe(struct device_d *pdev)
 {
@@ -768,7 +993,8 @@ i2c_omap_probe(struct device_d *pdev)
 	struct omap_i2c_driver_data *i2c_data;
 	int r;
 	u32 speed = 0;
-	u16 s;
+	u32 rev;
+	u16 minor, major;
 
 	i2c_omap = kzalloc(sizeof(struct omap_i2c_struct), GFP_KERNEL);
 	if (!i2c_omap) {
@@ -787,31 +1013,67 @@ i2c_omap_probe(struct device_d *pdev)
 	if (pdev->platform_data != NULL)
 		speed = *(u32 *)pdev->platform_data;
 	else
-		speed = 100;	/* Defualt speed */
+		speed = 100;	/* Default speed */
 
 	i2c_omap->speed = speed;
 	i2c_omap->base = dev_request_mem_region(pdev, 0);
 	printf ("I2C probe\n");
-	omap_i2c_unidle(i2c_omap);
-
-	i2c_omap->rev = omap_i2c_read_reg(i2c_omap, OMAP_I2C_REV_REG) & 0xff;
-
-	/* Set up the fifo size - Get total size */
-	s = (omap_i2c_read_reg(i2c_omap, OMAP_I2C_BUFSTAT_REG) >> 14) & 0x3;
-	i2c_omap->fifo_size = 0x8 << s;
 
 	/*
-	 * Set up notification threshold as half the total available
-	 * size. This is to ensure that we can handle the status on int
-	 * call back latencies.
+	 * Read the Rev hi bit-[15:14] ie scheme this is 1 indicates ver2.
+	 * On omap1/3/2 Offset 4 is IE Reg the bit [15:14] is 0 at reset.
+	 * Also since the omap_i2c_read_reg uses reg_map_ip_* a
+	 * raw_readw is done.
 	 */
+	rev = __raw_readw(i2c_omap->base + 0x04);
+
+	i2c_omap->scheme = OMAP_I2C_SCHEME(rev);
+	switch (i2c_omap->scheme) {
+	case OMAP_I2C_SCHEME_0:
+		i2c_omap->regs = (u8 *)reg_map_ip_v1;
+		i2c_omap->rev = omap_i2c_read_reg(i2c_omap, OMAP_I2C_REV_REG);
+		minor = OMAP_I2C_REV_SCHEME_0_MAJOR(i2c_omap->rev);
+		major = OMAP_I2C_REV_SCHEME_0_MAJOR(i2c_omap->rev);
+		break;
+	case OMAP_I2C_SCHEME_1:
+		/* FALLTHROUGH */
+	default:
+		i2c_omap->regs = (u8 *)reg_map_ip_v2;
+		rev = (rev << 16) |
+			omap_i2c_read_reg(i2c_omap, OMAP_I2C_IP_V2_REVNB_LO);
+		minor = OMAP_I2C_REV_SCHEME_1_MINOR(rev);
+		major = OMAP_I2C_REV_SCHEME_1_MAJOR(rev);
+		i2c_omap->rev = rev;
+	}
 
-	i2c_omap->fifo_size = (i2c_omap->fifo_size / 2);
+	i2c_omap->errata = 0;
 
-	if (i2c_omap->rev >= OMAP_I2C_REV_ON_4430)
-		i2c_omap->b_hw = 0; /* Disable hardware fixes */
-	else
-		i2c_omap->b_hw = 1; /* Enable hardware fixes */
+	if (i2c_omap->rev >= OMAP_I2C_REV_ON_2430 &&
+			i2c_omap->rev < OMAP_I2C_REV_ON_4430_PLUS)
+		i2c_omap->errata |= I2C_OMAP_ERRATA_I207;
+
+	if (i2c_omap->rev <= OMAP_I2C_REV_ON_3430_3530)
+		i2c_omap->errata |= I2C_OMAP_ERRATA_I462;
+
+	if (!(i2c_data->flags & OMAP_I2C_FLAG_NO_FIFO)) {
+		u16 s;
+
+		/* Set up the fifo size - Get total size */
+		s = (omap_i2c_read_reg(i2c_omap, OMAP_I2C_BUFSTAT_REG) >> 14)
+									 & 0x3;
+		i2c_omap->fifo_size = 0x8 << s;
+
+		/*
+		 * Set up notification threshold as half the total available
+		 * size. This is to ensure that we can handle the status on int
+		 * call back latencies.
+		 */
+
+		i2c_omap->fifo_size = (i2c_omap->fifo_size / 2);
+
+		if (i2c_omap->rev < OMAP_I2C_REV_ON_3630)
+			i2c_omap->b_hw = 1; /* Enable hardware fixes */
+	}
 
 	/* reset ASAP, clearing any IRQs */
 	omap_i2c_init(i2c_omap);
@@ -821,7 +1083,7 @@ i2c_omap_probe(struct device_d *pdev)
 
 	omap_i2c_idle(i2c_omap);
 
-	i2c_omap->adapter.master_xfer	= omap_i2c_xfer,
+	i2c_omap->adapter.master_xfer = omap_i2c_xfer,
 	i2c_omap->adapter.nr = pdev->id;
 	i2c_omap->adapter.dev.parent = pdev;
 
@@ -837,7 +1099,6 @@ i2c_omap_probe(struct device_d *pdev)
 err_unuse_clocks:
 	omap_i2c_write_reg(i2c_omap, OMAP_I2C_CON_REG, 0);
 	omap_i2c_idle(i2c_omap);
-
 err_free_mem:
 	kfree(i2c_omap);
 
diff --git a/include/i2c/i2c.h b/include/i2c/i2c.h
index 46185ac..81e5daa 100644
--- a/include/i2c/i2c.h
+++ b/include/i2c/i2c.h
@@ -36,6 +36,7 @@ struct i2c_platform_data {
 #define I2C_M_DATA_ONLY		0x0002	/* transfer data bytes only */
 #define I2C_M_TEN               0x0010  /* this is a ten bit chip address */
 #define I2C_M_IGNORE_NAK        0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
+#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */
 
 /**
  * struct i2c_msg - an I2C transaction segment beginning with START
-- 
1.7.0.4




More information about the barebox mailing list