aboutsummaryrefslogtreecommitdiff
path: root/drivers/i2c
diff options
context:
space:
mode:
authorSonic Zhang <sonic.zhang@analog.com>2008-04-22 22:16:47 +0200
committerJean Delvare <khali@hyperion.delvare>2008-04-22 22:16:47 +0200
commit4dd39bb12f5b9f0d9a98f29071dc1c51e9306954 (patch)
tree0cab698b36a97a4a73211d6f6370efd2296337ca /drivers/i2c
parent4c03f68fc4ab902353336b6b0c6933617821cf70 (diff)
i2c-bfin-twi: Add repeat start feature to avoid break of a bundle of i2c master xfer operation
- Create a new mode TWI_I2C_MODE_REPEAT. - No change to smbus operation. Signed-off-by: Sonic Zhang <sonic.zhang@analog.com> Signed-off-by: Bryan Wu <cooloney@kernel.org> Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-bfin-twi.c185
1 files changed, 114 insertions, 71 deletions
diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c
index 7dbdaeb707a..86956a8f0ae 100644
--- a/drivers/i2c/busses/i2c-bfin-twi.c
+++ b/drivers/i2c/busses/i2c-bfin-twi.c
@@ -39,9 +39,10 @@
#define POLL_TIMEOUT (2 * HZ)
/* SMBus mode*/
-#define TWI_I2C_MODE_STANDARD 0x01
-#define TWI_I2C_MODE_STANDARDSUB 0x02
-#define TWI_I2C_MODE_COMBINED 0x04
+#define TWI_I2C_MODE_STANDARD 1
+#define TWI_I2C_MODE_STANDARDSUB 2
+#define TWI_I2C_MODE_COMBINED 3
+#define TWI_I2C_MODE_REPEAT 4
struct bfin_twi_iface {
int irq;
@@ -58,6 +59,9 @@ struct bfin_twi_iface {
struct timer_list timeout_timer;
struct i2c_adapter adap;
struct completion complete;
+ struct i2c_msg *pmsg;
+ int msg_num;
+ int cur_msg;
};
static struct bfin_twi_iface twi_iface;
@@ -76,12 +80,16 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
/* start receive immediately after complete sending in
* combine mode.
*/
- else if (iface->cur_mode == TWI_I2C_MODE_COMBINED) {
+ else if (iface->cur_mode == TWI_I2C_MODE_COMBINED)
bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
| MDIR | RSTART);
- } else if (iface->manual_stop)
+ else if (iface->manual_stop)
bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
| STOP);
+ else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
+ iface->cur_msg+1 < iface->msg_num)
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
+ | RSTART);
SSYNC();
/* Clear status */
bfin_write_TWI_INT_STAT(XMTSERV);
@@ -108,6 +116,11 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
| STOP);
SSYNC();
+ } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
+ iface->cur_msg+1 < iface->msg_num) {
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
+ | RSTART);
+ SSYNC();
}
/* Clear interrupt source */
bfin_write_TWI_INT_STAT(RCVSERV);
@@ -119,7 +132,7 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
bfin_write_TWI_MASTER_STAT(0x3e);
bfin_write_TWI_MASTER_CTL(0);
SSYNC();
- iface->result = -1;
+ iface->result = -EIO;
/* if both err and complete int stats are set, return proper
* results.
*/
@@ -170,6 +183,42 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() |
MEN | MDIR);
SSYNC();
+ } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
+ iface->cur_msg+1 < iface->msg_num) {
+ iface->cur_msg++;
+ iface->transPtr = iface->pmsg[iface->cur_msg].buf;
+ iface->writeNum = iface->readNum =
+ iface->pmsg[iface->cur_msg].len;
+ /* Set Transmit device address */
+ bfin_write_TWI_MASTER_ADDR(
+ iface->pmsg[iface->cur_msg].addr);
+ if (iface->pmsg[iface->cur_msg].flags & I2C_M_RD)
+ iface->read_write = I2C_SMBUS_READ;
+ else {
+ iface->read_write = I2C_SMBUS_WRITE;
+ /* Transmit first data */
+ if (iface->writeNum > 0) {
+ bfin_write_TWI_XMT_DATA8(
+ *(iface->transPtr++));
+ iface->writeNum--;
+ SSYNC();
+ }
+ }
+
+ if (iface->pmsg[iface->cur_msg].len <= 255)
+ bfin_write_TWI_MASTER_CTL(
+ iface->pmsg[iface->cur_msg].len << 6);
+ else {
+ bfin_write_TWI_MASTER_CTL(0xff << 6);
+ iface->manual_stop = 1;
+ }
+ /* remove restart bit and enable master receive */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() &
+ ~RSTART);
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() |
+ MEN | ((iface->read_write == I2C_SMBUS_READ) ?
+ MDIR : 0));
+ SSYNC();
} else {
iface->result = 1;
bfin_write_TWI_INT_MASK(0);
@@ -221,7 +270,6 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap,
{
struct bfin_twi_iface *iface = adap->algo_data;
struct i2c_msg *pmsg;
- int i, ret;
int rc = 0;
if (!(bfin_read_TWI_CONTROL() & TWI_ENA))
@@ -231,81 +279,76 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap,
yield();
}
- ret = 0;
- for (i = 0; rc >= 0 && i < num; i++) {
- pmsg = &msgs[i];
- if (pmsg->flags & I2C_M_TEN) {
- dev_err(&(adap->dev), "i2c-bfin-twi: 10 bits addr "
- "not supported !\n");
- rc = -EINVAL;
- break;
- }
+ iface->pmsg = msgs;
+ iface->msg_num = num;
+ iface->cur_msg = 0;
- iface->cur_mode = TWI_I2C_MODE_STANDARD;
- iface->manual_stop = 0;
- iface->transPtr = pmsg->buf;
- iface->writeNum = iface->readNum = pmsg->len;
- iface->result = 0;
- iface->timeout_count = 10;
- /* Set Transmit device address */
- bfin_write_TWI_MASTER_ADDR(pmsg->addr);
-
- /* FIFO Initiation. Data in FIFO should be
- * discarded before start a new operation.
- */
- bfin_write_TWI_FIFO_CTL(0x3);
- SSYNC();
- bfin_write_TWI_FIFO_CTL(0);
- SSYNC();
+ pmsg = &msgs[0];
+ if (pmsg->flags & I2C_M_TEN) {
+ dev_err(&adap->dev, "10 bits addr not supported!\n");
+ return -EINVAL;
+ }
- if (pmsg->flags & I2C_M_RD)
- iface->read_write = I2C_SMBUS_READ;
- else {
- iface->read_write = I2C_SMBUS_WRITE;
- /* Transmit first data */
- if (iface->writeNum > 0) {
- bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
- iface->writeNum--;
- SSYNC();
- }
+ iface->cur_mode = TWI_I2C_MODE_REPEAT;
+ iface->manual_stop = 0;
+ iface->transPtr = pmsg->buf;
+ iface->writeNum = iface->readNum = pmsg->len;
+ iface->result = 0;
+ iface->timeout_count = 10;
+ /* Set Transmit device address */
+ bfin_write_TWI_MASTER_ADDR(pmsg->addr);
+
+ /* FIFO Initiation. Data in FIFO should be
+ * discarded before start a new operation.
+ */
+ bfin_write_TWI_FIFO_CTL(0x3);
+ SSYNC();
+ bfin_write_TWI_FIFO_CTL(0);
+ SSYNC();
+
+ if (pmsg->flags & I2C_M_RD)
+ iface->read_write = I2C_SMBUS_READ;
+ else {
+ iface->read_write = I2C_SMBUS_WRITE;
+ /* Transmit first data */
+ if (iface->writeNum > 0) {
+ bfin_write_TWI_XMT_DATA8(*(iface->transPtr++));
+ iface->writeNum--;
+ SSYNC();
}
+ }
- /* clear int stat */
- bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV);
+ /* 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 |
- ((iface->read_write == I2C_SMBUS_READ)?
- RCVSERV : XMTSERV));
- SSYNC();
+ /* Interrupt mask . Enable XMT, RCV interrupt */
+ bfin_write_TWI_INT_MASK(MCOMP | MERR | RCVSERV | XMTSERV);
+ SSYNC();
- if (pmsg->len > 0 && pmsg->len <= 255)
- bfin_write_TWI_MASTER_CTL(pmsg->len << 6);
- else if (pmsg->len > 255) {
- bfin_write_TWI_MASTER_CTL(0xff << 6);
- iface->manual_stop = 1;
- } else
- break;
+ if (pmsg->len <= 255)
+ bfin_write_TWI_MASTER_CTL(pmsg->len << 6);
+ else {
+ bfin_write_TWI_MASTER_CTL(0xff << 6);
+ iface->manual_stop = 1;
+ }
- iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
- add_timer(&iface->timeout_timer);
+ iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+ add_timer(&iface->timeout_timer);
- /* Master enable */
- bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
- ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
- ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0));
- SSYNC();
+ /* Master enable */
+ bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
+ ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
+ ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
+ SSYNC();
- wait_for_completion(&iface->complete);
+ wait_for_completion(&iface->complete);
- rc = iface->result;
- if (rc == 1)
- ret++;
- else if (rc == -1)
- break;
- }
+ rc = iface->result;
- return ret;
+ if (rc == 1)
+ return num;
+ else
+ return rc;
}
/*