diff options
Diffstat (limited to 'drivers/i2c/omap1510_i2c.c')
| -rw-r--r-- | drivers/i2c/omap1510_i2c.c | 281 | 
1 files changed, 281 insertions, 0 deletions
| diff --git a/drivers/i2c/omap1510_i2c.c b/drivers/i2c/omap1510_i2c.c new file mode 100644 index 000000000..04400fbcd --- /dev/null +++ b/drivers/i2c/omap1510_i2c.c @@ -0,0 +1,281 @@ +/* + * Basic I2C functions + * + * Copyright (c) 2003 Texas Instruments + * + * This package is free software;  you can redistribute it and/or + * modify it under the terms of the license found in the file + * named COPYING that should have accompanied this file. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Jian Zhang jzhang@ti.com, Texas Instruments + * + * Copyright (c) 2003 Wolfgang Denk, wd@denx.de + * Rewritten to fit into the current U-Boot framework + * + */ + +#include <common.h> + +#ifdef CONFIG_DRIVER_OMAP1510_I2C + +static void wait_for_bb (void); +static u16 wait_for_pin (void); + +void i2c_init (int speed, int slaveadd) +{ +	u16 scl; + +	if (inw (I2C_CON) & I2C_CON_EN) { +		outw (0, I2C_CON); +		udelay (5000); +	} + +	/* 12Mhz I2C module clock */ +	outw (0, I2C_PSC); +	outw (I2C_CON_EN, I2C_CON); +	outw (0, I2C_SYSTEST); +	/* have to enable intrrupts or OMAP i2c module doesn't work */ +	outw (I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | +	      I2C_IE_NACK_IE | I2C_IE_AL_IE, I2C_IE); +	scl = (12000000 / 2) / speed - 6; +	outw (scl, I2C_SCLL); +	outw (scl, I2C_SCLH); +	/* own address */ +	outw (slaveadd, I2C_OA); +	outw (0, I2C_CNT); +	udelay (1000); +} + +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 */ +	outw (1, I2C_CNT); +	/* set slave address */ +	outw (devaddr, I2C_SA); +	/* no stop bit needed here */ +	outw (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX, I2C_CON); + +	status = wait_for_pin (); + +	if (status & I2C_STAT_XRDY) { +		/* Important: have to use byte access */ +		*(volatile u8 *) (I2C_DATA) = regoffset; +		udelay (20000); +		if (inw (I2C_STAT) & I2C_STAT_NACK) { +			i2c_error = 1; +		} +	} else { +		i2c_error = 1; +	} + +	if (!i2c_error) { +		/* free bus, otherwise we can't use a combined transction */ +		outw (0, I2C_CON); +		while (inw (I2C_STAT) || (inw (I2C_CON) & I2C_CON_MST)) { +			udelay (10000); +			/* Have to clear pending interrupt to clear I2C_STAT */ +			inw (I2C_IV); +		} + +		wait_for_bb (); +		/* set slave address */ +		outw (devaddr, I2C_SA); +		/* read one byte from slave */ +		outw (1, I2C_CNT); +		/* need stop bit here */ +		outw (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, +		      I2C_CON); + +		status = wait_for_pin (); +		if (status & I2C_STAT_RRDY) { +			*value = inw (I2C_DATA); +			udelay (20000); +		} else { +			i2c_error = 1; +		} + +		if (!i2c_error) { +			outw (I2C_CON_EN, I2C_CON); +			while (inw (I2C_STAT) +			       || (inw (I2C_CON) & I2C_CON_MST)) { +				udelay (10000); +				inw (I2C_IV); +			} +		} +	} + +	return i2c_error; +} + +static int i2c_write_byte (u8 devaddr, u8 regoffset, u8 value) +{ +	int i2c_error = 0; +	u16 status; + +	/* wait until bus not busy */ +	wait_for_bb (); + +	/* two bytes */ +	outw (2, I2C_CNT); +	/* set slave address */ +	outw (devaddr, I2C_SA); +	/* stop bit needed here */ +	outw (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | +	      I2C_CON_STP, I2C_CON); + +	/* wait until state change */ +	status = wait_for_pin (); + +	if (status & I2C_STAT_XRDY) { +		/* send out two bytes */ +		outw ((value << 8) + regoffset, I2C_DATA); +		/* must have enough delay to allow BB bit to go low */ +		udelay (30000); +		if (inw (I2C_STAT) & I2C_STAT_NACK) { +			i2c_error = 1; +		} +	} else { +		i2c_error = 1; +	} + +	if (!i2c_error) { +		outw (I2C_CON_EN, I2C_CON); +		while (inw (I2C_STAT) || (inw (I2C_CON) & I2C_CON_MST)) { +			udelay (1000); +			/* have to read to clear intrrupt */ +			inw (I2C_IV); +		} +	} + +	return i2c_error; +} + +int i2c_probe (uchar chip) +{ +	int res = 1; + +	if (chip == inw (I2C_OA)) { +		return res; +	} + +	/* wait until bus not busy */ +	wait_for_bb (); + +	/* try to read one byte */ +	outw (1, I2C_CNT); +	/* set slave address */ +	outw (chip, I2C_SA); +	/* stop bit needed here */ +	outw (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, I2C_CON); +	/* enough delay for the NACK bit set */ +	udelay (2000); +	if (!(inw (I2C_STAT) & I2C_STAT_NACK)) { +		res = 0; +	} else { +		outw (inw (I2C_CON) | I2C_CON_STP, I2C_CON); +		udelay (20); +		wait_for_bb (); +	} + +	return res; +} + +int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ +	int i; + +	if (alen > 1) { +		printf ("I2C read: addr len %d not supported\n", alen); +		return 1; +	} + +	if (addr + len > 256) { +		printf ("I2C read: address out of range\n"); +		return 1; +	} + +	for (i = 0; i < len; i++) { +		if (i2c_read_byte (chip, addr + i, &buffer[i])) { +			printf ("I2C read: I/O error\n"); +			i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE); +			return 1; +		} +	} + +	return 0; +} + +int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ +	int i; + +	if (alen > 1) { +		printf ("I2C read: addr len %d not supported\n", alen); +		return 1; +	} + +	if (addr + len > 256) { +		printf ("I2C read: address out of range\n"); +		return 1; +	} + +	for (i = 0; i < len; i++) { +		if (i2c_write_byte (chip, addr + i, buffer[i])) { +			printf ("I2C read: I/O error\n"); +			i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE); +			return 1; +		} +	} + +	return 0; +} + +static void wait_for_bb (void) +{ +	int timeout = 10; + +	while ((inw (I2C_STAT) & I2C_STAT_BB) && timeout--) { +		inw (I2C_IV); +		udelay (1000); +	} + +	if (timeout <= 0) { +		printf ("timed out in wait_for_bb: I2C_STAT=%x\n", +			inw (I2C_STAT)); +	} +} + +static u16 wait_for_pin (void) +{ +	u16 status, iv; +	int timeout = 10; + +	do { +		udelay (1000); +		status = inw (I2C_STAT); +		iv = inw (I2C_IV); +	} while (!iv && +		 !(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_pin: I2C_STAT=%x\n", +			inw (I2C_STAT)); +	} + +	return status; +} + +#endif /* CONFIG_DRIVER_OMAP1510_I2C */ |