[PATCH] i2c-omap: Update driver

Sascha Hauer s.hauer at pengutronix.de
Mon Sep 30 04:16:42 EDT 2013


On Thu, Sep 26, 2013 at 01:40:24PM +0200, Jan Weitzel wrote:
> 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>

Applied, thanks

Sascha

> ---
>  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
> 
> 
> _______________________________________________
> barebox mailing list
> barebox at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



More information about the barebox mailing list