diff options
Diffstat (limited to 'cpu/bf537/i2c.c')
| -rw-r--r-- | cpu/bf537/i2c.c | 460 | 
1 files changed, 460 insertions, 0 deletions
| diff --git a/cpu/bf537/i2c.c b/cpu/bf537/i2c.c new file mode 100644 index 000000000..3b0d026e0 --- /dev/null +++ b/cpu/bf537/i2c.c @@ -0,0 +1,460 @@ +/**************************************************************** + * $ID: i2c.c	24 Oct 2006 12:00:00 +0800 $ 			* + *								* + * Description:							* + *								* + * Maintainer:  sonicz  <sonic.zhang@analog.com>		* + *								* + * CopyRight (c)  2006  Analog Device				* + * All rights reserved.						* + *								* + * This file is free software;					* + *	you are free to modify and/or redistribute it		* + *	under the terms of the GNU General Public Licence (GPL).* + *								* + ****************************************************************/ + +#include <common.h> + +#ifdef CONFIG_HARD_I2C + +#include <asm/blackfin.h> +#include <i2c.h> +#include <asm/io.h> + +#define bfin_read16(addr) ({ unsigned __v; \ +			__asm__ __volatile__ (\ +			"%0 = w[%1] (z);\n\t"\ +			: "=d"(__v) : "a"(addr)); (unsigned short)__v; }) + +#define bfin_write16(addr,val) ({\ +			__asm__ __volatile__ (\ +			"w[%0] = %1;\n\t"\ +			: : "a"(addr) , "d"(val) : "memory");}) + +/* Two-Wire Interface		(0xFFC01400 - 0xFFC014FF) */ +#define bfin_read_TWI_CLKDIV()		bfin_read16(TWI_CLKDIV) +#define bfin_write_TWI_CLKDIV(val)	bfin_write16(TWI_CLKDIV,val) +#define bfin_read_TWI_CONTROL()		bfin_read16(TWI_CONTROL) +#define bfin_write_TWI_CONTROL(val)	bfin_write16(TWI_CONTROL,val) +#define bfin_read_TWI_SLAVE_CTL()	bfin_read16(TWI_SLAVE_CTL) +#define bfin_write_TWI_SLAVE_CTL(val)	bfin_write16(TWI_SLAVE_CTL,val) +#define bfin_read_TWI_SLAVE_STAT()	bfin_read16(TWI_SLAVE_STAT) +#define bfin_write_TWI_SLAVE_STAT(val)	bfin_write16(TWI_SLAVE_STAT,val) +#define bfin_read_TWI_SLAVE_ADDR()	bfin_read16(TWI_SLAVE_ADDR) +#define bfin_write_TWI_SLAVE_ADDR(val)	bfin_write16(TWI_SLAVE_ADDR,val) +#define bfin_read_TWI_MASTER_CTL()	bfin_read16(TWI_MASTER_CTL) +#define bfin_write_TWI_MASTER_CTL(val)	bfin_write16(TWI_MASTER_CTL,val) +#define bfin_read_TWI_MASTER_STAT()	bfin_read16(TWI_MASTER_STAT) +#define bfin_write_TWI_MASTER_STAT(val)	bfin_write16(TWI_MASTER_STAT,val) +#define bfin_read_TWI_MASTER_ADDR()	bfin_read16(TWI_MASTER_ADDR) +#define bfin_write_TWI_MASTER_ADDR(val)	bfin_write16(TWI_MASTER_ADDR,val) +#define bfin_read_TWI_INT_STAT()	bfin_read16(TWI_INT_STAT) +#define bfin_write_TWI_INT_STAT(val)	bfin_write16(TWI_INT_STAT,val) +#define bfin_read_TWI_INT_MASK()	bfin_read16(TWI_INT_MASK) +#define bfin_write_TWI_INT_MASK(val)	bfin_write16(TWI_INT_MASK,val) +#define bfin_read_TWI_FIFO_CTL()	bfin_read16(TWI_FIFO_CTL) +#define bfin_write_TWI_FIFO_CTL(val)	bfin_write16(TWI_FIFO_CTL,val) +#define bfin_read_TWI_FIFO_STAT()	bfin_read16(TWI_FIFO_STAT) +#define bfin_write_TWI_FIFO_STAT(val)	bfin_write16(TWI_FIFO_STAT,val) +#define bfin_read_TWI_XMT_DATA8()	bfin_read16(TWI_XMT_DATA8) +#define bfin_write_TWI_XMT_DATA8(val)	bfin_write16(TWI_XMT_DATA8,val) +#define bfin_read_TWI_XMT_DATA16()	bfin_read16(TWI_XMT_DATA16) +#define bfin_write_TWI_XMT_DATA16(val)	bfin_write16(TWI_XMT_DATA16,val) +#define bfin_read_TWI_RCV_DATA8()	bfin_read16(TWI_RCV_DATA8) +#define bfin_write_TWI_RCV_DATA8(val)	bfin_write16(TWI_RCV_DATA8,val) +#define bfin_read_TWI_RCV_DATA16()	bfin_read16(TWI_RCV_DATA16) +#define bfin_write_TWI_RCV_DATA16(val)	bfin_write16(TWI_RCV_DATA16,val) + +#ifdef DEBUG_I2C +#define PRINTD(fmt,args...)	do {	\ +	DECLARE_GLOBAL_DATA_PTR;	\ +	if (gd->have_console)		\ +		printf(fmt ,##args);	\ +	} while (0) +#else +#define PRINTD(fmt,args...) +#endif + +#ifndef CONFIG_TWICLK_KHZ +#define CONFIG_TWICLK_KHZ	50 +#endif + +/* All transfers are described by this data structure */ +struct i2c_msg { +	u16 addr;		/* slave address */ +	u16 flags; +#define I2C_M_STOP		0x2 +#define I2C_M_RD		0x1 +	u16 len;		/* msg length */ +	u8 *buf;		/* pointer to msg data */ +}; + +/** + * i2c_reset: - reset the host controller + * + */ + +static void i2c_reset(void) +{ +	/* Disable TWI */ +	bfin_write_TWI_CONTROL(0); +	sync(); + +	/* Set TWI internal clock as 10MHz */ +	bfin_write_TWI_CONTROL(((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F); + +	/* Set Twi interface clock as specified */ +	if (CONFIG_TWICLK_KHZ > 400) +		bfin_write_TWI_CLKDIV(((5 * 1024 / 400) << 8) | ((5 * 1024 / +						400) & 0xFF)); +	else +		bfin_write_TWI_CLKDIV(((5 * 1024 / +					CONFIG_TWICLK_KHZ) << 8) | ((5 * 1024 / +						CONFIG_TWICLK_KHZ) +						& 0xFF)); + +	/* Enable TWI */ +	bfin_write_TWI_CONTROL(bfin_read_TWI_CONTROL() | TWI_ENA); +	sync(); +} + +int wait_for_completion(struct i2c_msg *msg, int timeout_count) +{ +	unsigned short twi_int_stat; +	unsigned short mast_stat; +	int i; + +	for (i = 0; i < timeout_count; i++) { +		twi_int_stat = bfin_read_TWI_INT_STAT(); +		mast_stat = bfin_read_TWI_MASTER_STAT(); + +		if (XMTSERV & twi_int_stat) { +			/* Transmit next data */ +			if (msg->len > 0) { +				bfin_write_TWI_XMT_DATA8(*(msg->buf++)); +				msg->len--; +			} else if (msg->flags & I2C_M_STOP) +				bfin_write_TWI_MASTER_CTL +				    (bfin_read_TWI_MASTER_CTL() | STOP); +			sync(); +			/* Clear status */ +			bfin_write_TWI_INT_STAT(XMTSERV); +			sync(); +			i = 0; +		} +		if (RCVSERV & twi_int_stat) { +			if (msg->len > 0) { +				/* Receive next data */ +				*(msg->buf++) = bfin_read_TWI_RCV_DATA8(); +				msg->len--; +			} else if (msg->flags & I2C_M_STOP) { +				bfin_write_TWI_MASTER_CTL +				    (bfin_read_TWI_MASTER_CTL() | STOP); +				sync(); +			} +			/* Clear interrupt source */ +			bfin_write_TWI_INT_STAT(RCVSERV); +			sync(); +			i = 0; +		} +		if (MERR & twi_int_stat) { +			bfin_write_TWI_INT_STAT(MERR); +			bfin_write_TWI_INT_MASK(0); +			bfin_write_TWI_MASTER_STAT(0x3e); +			bfin_write_TWI_MASTER_CTL(0); +			sync(); +			/* +			 * if both err and complete int stats are set, +			 * return proper results. +			 */ +			if (MCOMP & twi_int_stat) { +				bfin_write_TWI_INT_STAT(MCOMP); +				bfin_write_TWI_INT_MASK(0); +				bfin_write_TWI_MASTER_CTL(0); +				sync(); +				/* +				 * If it is a quick transfer, +				 * only address bug no data, not an err. +				 */ +				if (msg->len == 0 && mast_stat & BUFRDERR) +					return 0; +				/* +				 * If address not acknowledged return -3, +				 * else return 0. +				 */ +				else if (!(mast_stat & ANAK)) +					return 0; +				else +					return -3; +			} +			return -1; +		} +		if (MCOMP & twi_int_stat) { +			bfin_write_TWI_INT_STAT(MCOMP); +			sync(); +			bfin_write_TWI_INT_MASK(0); +			bfin_write_TWI_MASTER_CTL(0); +			sync(); +			return 0; +		} +	} +	if (msg->flags & I2C_M_RD) +		return -4; +	else +		return -2; +} + +/** + * i2c_transfer: - Transfer one byte over the i2c bus + * + * This function can tranfer a byte over the i2c bus in both directions. + * It is used by the public API functions. + * + * @return:	 0: transfer successful + *		-1: transfer fail + *		-2: transmit timeout + *		-3: ACK missing + *		-4: receive timeout + *		-5: controller not ready + */ +int i2c_transfer(struct i2c_msg *msg) +{ +	int ret = 0; +	int timeout_count = 10000; +	int len = msg->len; + +	if (!(bfin_read_TWI_CONTROL() & TWI_ENA)) { +		ret = -5; +		goto transfer_error; +	} + +	while (bfin_read_TWI_MASTER_STAT() & BUSBUSY) ; + +	/* Set Transmit device address */ +	bfin_write_TWI_MASTER_ADDR(msg->addr); + +	/* +	 * FIFO Initiation. +	 * Data in FIFO should be discarded before start a new operation. +	 */ +	bfin_write_TWI_FIFO_CTL(0x3); +	sync(); +	bfin_write_TWI_FIFO_CTL(0); +	sync(); + +	if (!(msg->flags & I2C_M_RD)) { +		/* Transmit first data */ +		if (msg->len > 0) { +			PRINTD("1 in i2c_transfer: buf=%d, len=%d\n", *msg->buf, +			       len); +			bfin_write_TWI_XMT_DATA8(*(msg->buf++)); +			msg->len--; +			sync(); +		} +	} + +	/* clear int stat */ +	bfin_write_TWI_INT_STAT(MERR | MCOMP | XMTSERV | RCVSERV); + +	/* Interrupt mask . Enable XMT, RCV interrupt */ +	bfin_write_TWI_INT_MASK(MCOMP | MERR | +			((msg->flags & I2C_M_RD) ? RCVSERV : XMTSERV)); +	sync(); + +	if (len > 0 && len <= 255) +		bfin_write_TWI_MASTER_CTL((len << 6)); +	else if (msg->len > 255) { +		bfin_write_TWI_MASTER_CTL((0xff << 6)); +		msg->flags &= I2C_M_STOP; +	} else +		bfin_write_TWI_MASTER_CTL(0); + +	/* Master enable */ +	bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN | +			((msg->flags & I2C_M_RD) +			 ? MDIR : 0) | ((CONFIG_TWICLK_KHZ > +					 100) ? FAST : 0)); +	sync(); + +	ret = wait_for_completion(msg, timeout_count); +	PRINTD("3 in i2c_transfer: ret=%d\n", ret); + +transfer_error: +	switch (ret) { +	case 1: +		PRINTD(("i2c_transfer: error: transfer fail\n")); +		break; +	case 2: +		PRINTD(("i2c_transfer: error: transmit timeout\n")); +		break; +	case 3: +		PRINTD(("i2c_transfer: error: ACK missing\n")); +		break; +	case 4: +		PRINTD(("i2c_transfer: error: receive timeout\n")); +		break; +	case 5: +		PRINTD(("i2c_transfer: error: controller not ready\n")); +		i2c_reset(); +		break; +	default: +		break; +	} +	return ret; + +} + +/* ---------------------------------------------------------------------*/ +/* API Functions							*/ +/* ---------------------------------------------------------------------*/ + +void i2c_init(int speed, int slaveaddr) +{ +	i2c_reset(); +} + +/** + * i2c_probe: - Test if a chip answers for a given i2c address + * + * @chip:	address of the chip which is searched for + * @return: 	0 if a chip was found, -1 otherwhise + */ + +int i2c_probe(uchar chip) +{ +	struct i2c_msg msg; +	u8 probebuf; + +	i2c_reset(); + +	probebuf = 0; +	msg.addr = chip; +	msg.flags = 0; +	msg.len = 1; +	msg.buf = &probebuf; +	if (i2c_transfer(&msg)) +		return -1; + +	msg.addr = chip; +	msg.flags = I2C_M_RD; +	msg.len = 1; +	msg.buf = &probebuf; +	if (i2c_transfer(&msg)) +		return -1; + +	return 0; +} + +/** + *   i2c_read: - Read multiple bytes from an i2c device + * + *   chip:    I2C chip address, range 0..127 + *   addr:    Memory (register) address within the chip + *   alen:    Number of bytes to use for addr (typically 1, 2 for larger + *		memories, 0 for register type devices with only one + *		register) + *   buffer:  Where to read/write the data + *   len:     How many bytes to read/write + * + *   Returns: 0 on success, not 0 on failure + */ + +int i2c_read(uchar chip, uint addr, int alen, uchar * buffer, int len) +{ +	struct i2c_msg msg; +	u8 addr_bytes[3];	/* lowest...highest byte of data address */ + +	PRINTD("i2c_read: chip=0x%x, addr=0x%x, alen=0x%x, len=0x%x\n", chip, +			addr, alen, len); + +	if (alen > 0) { +		addr_bytes[0] = (u8) ((addr >> 0) & 0x000000FF); +		addr_bytes[1] = (u8) ((addr >> 8) & 0x000000FF); +		addr_bytes[2] = (u8) ((addr >> 16) & 0x000000FF); +		msg.addr = chip; +		msg.flags = 0; +		msg.len = alen; +		msg.buf = addr_bytes; +		if (i2c_transfer(&msg)) +			return -1; +	} + +	/* start read sequence */ +	PRINTD(("i2c_read: start read sequence\n")); +	msg.addr = chip; +	msg.flags = I2C_M_RD; +	msg.len = len; +	msg.buf = buffer; +	if (i2c_transfer(&msg)) +		return -1; + +	return 0; +} + +/** + *   i2c_write: -  Write multiple bytes to an i2c device + * + *   chip:    I2C chip address, range 0..127 + *   addr:    Memory (register) address within the chip + *   alen:    Number of bytes to use for addr (typically 1, 2 for larger + *		memories, 0 for register type devices with only one + *		register) + *   buffer:  Where to read/write the data + *   len:     How many bytes to read/write + * + *   Returns: 0 on success, not 0 on failure + */ + +int i2c_write(uchar chip, uint addr, int alen, uchar * buffer, int len) +{ +	struct i2c_msg msg; +	u8 addr_bytes[3];	/* lowest...highest byte of data address */ + +	PRINTD +		("i2c_write: chip=0x%x, addr=0x%x, alen=0x%x, len=0x%x, buf0=0x%x\n", +		 chip, addr, alen, len, buffer[0]); + +	/* chip address write */ +	if (alen > 0) { +		addr_bytes[0] = (u8) ((addr >> 0) & 0x000000FF); +		addr_bytes[1] = (u8) ((addr >> 8) & 0x000000FF); +		addr_bytes[2] = (u8) ((addr >> 16) & 0x000000FF); +		msg.addr = chip; +		msg.flags = 0; +		msg.len = alen; +		msg.buf = addr_bytes; +		if (i2c_transfer(&msg)) +			return -1; +	} + +	/* start read sequence */ +	PRINTD(("i2c_write: start write sequence\n")); +	msg.addr = chip; +	msg.flags = 0; +	msg.len = len; +	msg.buf = buffer; +	if (i2c_transfer(&msg)) +		return -1; + +	return 0; + +} + +uchar i2c_reg_read(uchar chip, uchar reg) +{ +	uchar buf; + +	PRINTD("i2c_reg_read: chip=0x%02x, reg=0x%02x\n", chip, reg); +	i2c_read(chip, reg, 0, &buf, 1); +	return (buf); +} + +void i2c_reg_write(uchar chip, uchar reg, uchar val) +{ +	PRINTD("i2c_reg_write: chip=0x%02x, reg=0x%02x, val=0x%02x\n", chip, +			reg, val); +	i2c_write(chip, reg, 0, &val, 1); +} + +#endif				/* CONFIG_HARD_I2C */ |