diff options
| -rw-r--r-- | drivers/i2c/omap24xx_i2c.c | 465 | ||||
| -rw-r--r-- | drivers/i2c/omap24xx_i2c.h | 2 | 
2 files changed, 172 insertions, 295 deletions
| diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index 80932eff8..a7ffd95d5 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -29,11 +29,10 @@  DECLARE_GLOBAL_DATA_PTR; -#define I2C_STAT_TIMEO	(1 << 31) -#define I2C_TIMEOUT	10 +#define I2C_TIMEOUT	1000 -static u32 wait_for_bb(void); -static u32 wait_for_status_mask(u16 mask); +static void wait_for_bb(void); +static u16 wait_for_pin(void);  static void flush_fifo(void);  /* @@ -51,6 +50,7 @@ void i2c_init(int speed, int slaveadd)  	int psc, fsscll, fssclh;  	int hsscll = 0, hssclh = 0;  	u32 scll, sclh; +	int timeout = I2C_TIMEOUT;  	/* Only handle standard, fast and high speeds */  	if ((speed != OMAP_I2C_STANDARD) && @@ -112,14 +112,24 @@ void i2c_init(int speed, int slaveadd)  		sclh = (unsigned int)fssclh;  	} -	if (gd->flags & GD_FLG_RELOC) -		bus_initialized[current_bus] = 1; -  	if (readw(&i2c_base->con) & I2C_CON_EN) {  		writew(0, &i2c_base->con);  		udelay(50000);  	} +	writew(0x2, &i2c_base->sysc); /* for ES2 after soft reset */ +	udelay(1000); + +	writew(I2C_CON_EN, &i2c_base->con); +	while (!(readw(&i2c_base->syss) & I2C_SYSS_RDONE) && timeout--) { +		if (timeout <= 0) { +			puts("ERROR: Timeout in soft-reset\n"); +			return; +		} +		udelay(1000); +	} + +	writew(0, &i2c_base->con);  	writew(psc, &i2c_base->psc);  	writew(scll, &i2c_base->scll);  	writew(sclh, &i2c_base->sclh); @@ -135,6 +145,81 @@ void i2c_init(int speed, int slaveadd)  	flush_fifo();  	writew(0xFFFF, &i2c_base->stat);  	writew(0, &i2c_base->cnt); + +	if (gd->flags & GD_FLG_RELOC) +		bus_initialized[current_bus] = 1; +} + +static int i2c_read_byte(u8 devaddr, u8 regoffset, u8 *value) +{ +	int i2c_error = 0; +	u16 status; + +	/* wait until bus not busy */ +	wait_for_bb(); + +	/* one byte only */ +	writew(1, &i2c_base->cnt); +	/* set slave address */ +	writew(devaddr, &i2c_base->sa); +	/* no stop bit needed here */ +	writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | +	      I2C_CON_TRX, &i2c_base->con); + +	/* send register offset */ +	while (1) { +		status = wait_for_pin(); +		if (status == 0 || status & I2C_STAT_NACK) { +			i2c_error = 1; +			goto read_exit; +		} +		if (status & I2C_STAT_XRDY) { +			/* Important: have to use byte access */ +			writeb(regoffset, &i2c_base->data); +			writew(I2C_STAT_XRDY, &i2c_base->stat); +		} +		if (status & I2C_STAT_ARDY) { +			writew(I2C_STAT_ARDY, &i2c_base->stat); +			break; +		} +	} + +	/* set slave address */ +	writew(devaddr, &i2c_base->sa); +	/* read one byte from slave */ +	writew(1, &i2c_base->cnt); +	/* need stop bit here */ +	writew(I2C_CON_EN | I2C_CON_MST | +		I2C_CON_STT | I2C_CON_STP, +		&i2c_base->con); + +	/* receive data */ +	while (1) { +		status = wait_for_pin(); +		if (status == 0 || status & I2C_STAT_NACK) { +			i2c_error = 1; +			goto read_exit; +		} +		if (status & I2C_STAT_RRDY) { +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ +	defined(CONFIG_OMAP44XX) +			*value = readb(&i2c_base->data); +#else +			*value = readw(&i2c_base->data); +#endif +			writew(I2C_STAT_RRDY, &i2c_base->stat); +		} +		if (status & I2C_STAT_ARDY) { +			writew(I2C_STAT_ARDY, &i2c_base->stat); +			break; +		} +	} + +read_exit: +	flush_fifo(); +	writew(0xFFFF, &i2c_base->stat); +	writew(0, &i2c_base->cnt); +	return i2c_error;  }  static void flush_fifo(void) @@ -161,42 +246,32 @@ static void flush_fifo(void)  int i2c_probe(uchar chip)  { -	u32 status; +	u16 status;  	int res = 1; /* default = fail */  	if (chip == readw(&i2c_base->oa))  		return res;  	/* wait until bus not busy */ -	status = wait_for_bb(); -	/* exit on BUS busy */ -	if (status & I2C_STAT_TIMEO) -		return res; +	wait_for_bb();  	/* try to write one byte */  	writew(1, &i2c_base->cnt);  	/* set slave address */  	writew(chip, &i2c_base->sa);  	/* stop bit needed here */ -	writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT -			| I2C_CON_STP, &i2c_base->con); -	/* enough delay for the NACK bit set */ -	udelay(9000); +	writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | +	       I2C_CON_STP, &i2c_base->con); + +	status = wait_for_pin(); + +	/* check for ACK (!NAK) */ +	if (!(status & I2C_STAT_NACK)) +		res = 0; + +	/* abort transfer (force idle state) */ +	writew(0, &i2c_base->con); -	if (!(readw(&i2c_base->stat) & I2C_STAT_NACK)) { -		res = 0;      /* success case */ -		flush_fifo(); -		writew(0xFFFF, &i2c_base->stat); -	} else { -		/* failure, clear sources*/ -		writew(0xFFFF, &i2c_base->stat); -		/* finish up xfer */ -		writew(readw(&i2c_base->con) | I2C_CON_STP, &i2c_base->con); -		status = wait_for_bb(); -		/* exit on BUS busy */ -		if (status & I2C_STAT_TIMEO) -			return res; -	}  	flush_fifo();  	/* don't allow any more data in... we don't want it. */  	writew(0, &i2c_base->cnt); @@ -206,309 +281,111 @@ int i2c_probe(uchar chip)  int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)  { -	int i2c_error = 0, i; -	u32 status; +	int i; -	if ((alen > 2) || (alen < 0)) -		return 1; - -	if (alen < 2) { -		if (addr + len > 256) -			return 1; -	} else if (addr + len > 0xFFFF) { +	if (alen > 1) { +		printf("I2C read: addr len %d not supported\n", alen);  		return 1;  	} -	/* wait until bus not busy */ -	status = wait_for_bb(); - -	/* exit on BUS busy */ -	if (status & I2C_STAT_TIMEO) +	if (addr + len > 256) { +		puts("I2C read: address out of range\n");  		return 1; - -	writew((alen & 0xFF), &i2c_base->cnt); -	/* set slave address */ -	writew(chip, &i2c_base->sa); -	/* Clear the Tx & Rx FIFOs */ -	writew((readw(&i2c_base->buf) | I2C_RXFIFO_CLEAR | -		I2C_TXFIFO_CLEAR), &i2c_base->buf); -	/* no stop bit needed here */ -	writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_TRX | -		I2C_CON_STT, &i2c_base->con); - -	/* wait for Transmit ready condition */ -	status = wait_for_status_mask(I2C_STAT_XRDY | I2C_STAT_NACK); - -	if (status & (I2C_STAT_NACK | I2C_STAT_TIMEO)) -		i2c_error = 1; - -	if (!i2c_error) { -		if (status & I2C_STAT_XRDY) { -			switch (alen) { -			case 2: -				/* Send address MSByte */ -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) -				writew(((addr >> 8) & 0xFF), &i2c_base->data); - -				/* Clearing XRDY event */ -				writew((status & I2C_STAT_XRDY), -						&i2c_base->stat); -				/* wait for Transmit ready condition */ -				status = wait_for_status_mask(I2C_STAT_XRDY | -						I2C_STAT_NACK); - -				if (status & (I2C_STAT_NACK | -						I2C_STAT_TIMEO)) { -					i2c_error = 1; -					break; -				} -#endif -			case 1: -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) -				/* Send address LSByte */ -				writew((addr & 0xFF), &i2c_base->data); -#else -				/* Send address Short word */ -				writew((addr & 0xFFFF), &i2c_base->data); -#endif -				/* Clearing XRDY event */ -				writew((status & I2C_STAT_XRDY), -					&i2c_base->stat); -				/*wait for Transmit ready condition */ -				status = wait_for_status_mask(I2C_STAT_ARDY | -						I2C_STAT_NACK); - -				if (status & (I2C_STAT_NACK | -					I2C_STAT_TIMEO)) { -					i2c_error = 1; -					break; -				} -			} -		} else -			i2c_error = 1;  	} -	/* Wait for ARDY to set */ -	status = wait_for_status_mask(I2C_STAT_ARDY | I2C_STAT_NACK -			| I2C_STAT_AL); - -	if (!i2c_error) { -		/* set slave address */ -		writew(chip, &i2c_base->sa); -		writew((len & 0xFF), &i2c_base->cnt); -		/* Clear the Tx & Rx FIFOs */ -		writew((readw(&i2c_base->buf) | I2C_RXFIFO_CLEAR | -			I2C_TXFIFO_CLEAR), &i2c_base->buf); -		/* need stop bit here */ -		writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, -			&i2c_base->con); - -		for (i = 0; i < len; i++) { -			/* wait for Receive condition */ -			status = wait_for_status_mask(I2C_STAT_RRDY | -				I2C_STAT_NACK); -			if (status & (I2C_STAT_NACK | I2C_STAT_TIMEO)) { -				i2c_error = 1; -				break; -			} - -			if (status & I2C_STAT_RRDY) { -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) -				buffer[i] = readb(&i2c_base->data); -#else -				*((u16 *)&buffer[i]) = -					readw(&i2c_base->data) & 0xFFFF; -				i++; -#endif -				writew((status & I2C_STAT_RRDY), -					&i2c_base->stat); -				udelay(1000); -			} else { -				i2c_error = 1; -			} +	for (i = 0; i < len; i++) { +		if (i2c_read_byte(chip, addr + i, &buffer[i])) { +			puts("I2C read: I/O error\n"); +			i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +			return 1;  		}  	} -	/* Wait for ARDY to set */ -	status = wait_for_status_mask(I2C_STAT_ARDY | I2C_STAT_NACK -			| I2C_STAT_AL); - -	if (i2c_error) { -		writew(0, &i2c_base->con); -		return 1; -	} - -	writew(I2C_CON_EN, &i2c_base->con); - -	while (readw(&i2c_base->stat) -		|| (readw(&i2c_base->con) & I2C_CON_MST)) { -		udelay(10000); -		writew(0xFFFF, &i2c_base->stat); -	} - -	writew(I2C_CON_EN, &i2c_base->con); -	flush_fifo(); -	writew(0xFFFF, &i2c_base->stat); -	writew(0, &i2c_base->cnt); -  	return 0;  }  int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)  { +	int i; +	u16 status; +	int i2c_error = 0; -	int i, i2c_error = 0; -	u32 status; -	u16 writelen; - -	if (alen > 2) +	if (alen > 1) { +		printf("I2C write: addr len %d not supported\n", alen);  		return 1; +	} -	if (alen < 2) { -		if (addr + len > 256) -			return 1; -	} else if (addr + len > 0xFFFF) { +	if (addr + len > 256) { +		printf("I2C write: address 0x%x + 0x%x out of range\n", +				addr, len);  		return 1;  	}  	/* wait until bus not busy */ -	status = wait_for_bb(); - -	/* exiting on BUS busy */ -	if (status & I2C_STAT_TIMEO) -		return 1; - -	writelen = (len & 0xFFFF) + alen; +	wait_for_bb(); -	/* two bytes */ -	writew((writelen & 0xFFFF), &i2c_base->cnt); -	/* Clear the Tx & Rx FIFOs */ -	writew((readw(&i2c_base->buf) | I2C_RXFIFO_CLEAR | -			I2C_TXFIFO_CLEAR), &i2c_base->buf); +	/* start address phase - will write regoffset + len bytes data */ +	/* TODO consider case when !CONFIG_OMAP243X/34XX/44XX */ +	writew(alen + len, &i2c_base->cnt);  	/* set slave address */  	writew(chip, &i2c_base->sa);  	/* stop bit needed here */  	writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |  		I2C_CON_STP, &i2c_base->con); -	/* wait for Transmit ready condition */ -	status = wait_for_status_mask(I2C_STAT_XRDY | I2C_STAT_NACK); +	/* Send address byte */ +	status = wait_for_pin(); -	if (status & (I2C_STAT_NACK | I2C_STAT_TIMEO)) +	if (status == 0 || status & I2C_STAT_NACK) {  		i2c_error = 1; - -	if (!i2c_error) { -		if (status & I2C_STAT_XRDY) { -			switch (alen) { -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) -			case 2: -				/* send out MSB byte */ -				writeb(((addr >> 8) & 0xFF), &i2c_base->data); -#else -				writeb((addr  & 0xFFFF), &i2c_base->data); -				break; -#endif -				/* Clearing XRDY event */ -				writew((status & I2C_STAT_XRDY), -					&i2c_base->stat); -				/*waiting for Transmit ready * condition */ -				status = wait_for_status_mask(I2C_STAT_XRDY | -						I2C_STAT_NACK); - -				if (status & (I2C_STAT_NACK | I2C_STAT_TIMEO)) { -					i2c_error = 1; -					break; -				} -			case 1: -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) -				/* send out MSB byte */ -				writeb((addr  & 0xFF), &i2c_base->data); -#else -				writew(((buffer[0] << 8) | (addr & 0xFF)), -					&i2c_base->data); -#endif -			} - -			/* Clearing XRDY event */ -			writew((status & I2C_STAT_XRDY), &i2c_base->stat); -		} - -		/* waiting for Transmit ready condition */ -		status = wait_for_status_mask(I2C_STAT_XRDY | I2C_STAT_NACK); - -		if (status & (I2C_STAT_NACK | I2C_STAT_TIMEO)) -			i2c_error = 1; - -		if (!i2c_error) { -			for (i = ((alen > 1) ? 0 : 1); i < len; i++) { -				if (status & I2C_STAT_XRDY) { -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) -					writeb((buffer[i] & 0xFF), -						&i2c_base->data); -#else -					writew((((buffer[i] << 8) | -					buffer[i + 1]) & 0xFFFF), -						&i2c_base->data); -					i++; -#endif -				} else -					i2c_error = 1; -					/* Clearing XRDY event */ -					writew((status & I2C_STAT_XRDY), -						&i2c_base->stat); -					/* waiting for XRDY condition */ -					status = wait_for_status_mask( -						I2C_STAT_XRDY | -						I2C_STAT_ARDY | -						I2C_STAT_NACK); -					if (status & (I2C_STAT_NACK | -						I2C_STAT_TIMEO)) { -						i2c_error = 1; -						break; -					} -					if (status & I2C_STAT_ARDY) -						break; -			} -		} +		printf("error waiting for i2c address ACK (status=0x%x)\n", +		      status); +		goto write_exit;  	} -	status = wait_for_status_mask(I2C_STAT_ARDY | I2C_STAT_NACK | -				I2C_STAT_AL); - -	if (status & (I2C_STAT_NACK | I2C_STAT_TIMEO)) +	if (status & I2C_STAT_XRDY) { +		writeb(addr & 0xFF, &i2c_base->data); +		writew(I2C_STAT_XRDY, &i2c_base->stat); +	} else {  		i2c_error = 1; - -	if (i2c_error) { -		writew(0, &i2c_base->con); -		return 1; +		printf("i2c bus not ready for transmit (status=0x%x)\n", +		      status); +		goto write_exit;  	} -	if (!i2c_error) { -		int eout = 200; +	/* address phase is over, now write data */ +	for (i = 0; i < len; i++) { +		status = wait_for_pin(); -		writew(I2C_CON_EN, &i2c_base->con); -		while ((status = readw(&i2c_base->stat)) || -				(readw(&i2c_base->con) & I2C_CON_MST)) { -			udelay(1000); -			/* have to read to clear intrrupt */ -			writew(0xFFFF, &i2c_base->stat); -			if (--eout == 0) -				/* better leave with error than hang */ -				break; +		if (status == 0 || status & I2C_STAT_NACK) { +			i2c_error = 1; +			printf("i2c error waiting for data ACK (status=0x%x)\n", +					status); +			goto write_exit; +		} + +		if (status & I2C_STAT_XRDY) { +			writeb(buffer[i], &i2c_base->data); +			writew(I2C_STAT_XRDY, &i2c_base->stat); +		} else { +			i2c_error = 1; +			printf("i2c bus not ready for Tx (i=%d)\n", i); +			goto write_exit;  		}  	} +write_exit:  	flush_fifo();  	writew(0xFFFF, &i2c_base->stat); -	writew(0, &i2c_base->cnt); -	return 0; +	return i2c_error;  } -static u32 wait_for_bb(void) +static void wait_for_bb(void)  {  	int timeout = I2C_TIMEOUT; -	u32 stat; +	u16 stat; +	writew(0xFFFF, &i2c_base->stat);	/* clear current interrupts...*/  	while ((stat = readw(&i2c_base->stat) & I2C_STAT_BB) && timeout--) {  		writew(stat, &i2c_base->stat);  		udelay(1000); @@ -517,28 +394,30 @@ static u32 wait_for_bb(void)  	if (timeout <= 0) {  		printf("timed out in wait_for_bb: I2C_STAT=%x\n",  			readw(&i2c_base->stat)); -		stat |= I2C_STAT_TIMEO;  	}  	writew(0xFFFF, &i2c_base->stat);	 /* clear delayed stuff*/ -	return stat;  } -static u32 wait_for_status_mask(u16 mask) +static u16 wait_for_pin(void)  { -	u32 status; +	u16 status;  	int timeout = I2C_TIMEOUT;  	do {  		udelay(1000);  		status = readw(&i2c_base->stat); -	} while (!(status & mask) && timeout--); +	} while (!(status & +		   (I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY | +		    I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK | +		    I2C_STAT_AL)) && timeout--);  	if (timeout <= 0) { -		printf("timed out in wait_for_status_mask: I2C_STAT=%x\n", +		printf("timed out in wait_for_pin: I2C_STAT=%x\n",  			readw(&i2c_base->stat));  		writew(0xFFFF, &i2c_base->stat); -		status |= I2C_STAT_TIMEO; +		status = 0;  	} +  	return status;  } diff --git a/drivers/i2c/omap24xx_i2c.h b/drivers/i2c/omap24xx_i2c.h index 9a0b126b3..1f38c232d 100644 --- a/drivers/i2c/omap24xx_i2c.h +++ b/drivers/i2c/omap24xx_i2c.h @@ -60,9 +60,7 @@  /* I2C Buffer Configuration Register (I2C_BUF): */  #define I2C_BUF_RDMA_EN		(1 << 15) /* Receive DMA channel enable */ -#define I2C_RXFIFO_CLEAR	(1 << 14) /* RX FIFO Clear */  #define I2C_BUF_XDMA_EN		(1 << 7)  /* Transmit DMA channel enable */ -#define I2C_TXFIFO_CLEAR	(1 << 6)  /* TX FIFO clear */  /* I2C Configuration Register (I2C_CON): */ |