123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- /****************************************************************
- * $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>
- DECLARE_GLOBAL_DATA_PTR;
- #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 { \
- 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 */
|