Commit 4dd39bb1 authored by Sonic Zhang's avatar Sonic Zhang Committed by Jean Delvare

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: default avatarSonic Zhang <sonic.zhang@analog.com>
Signed-off-by: default avatarBryan Wu <cooloney@kernel.org>
Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
parent 4c03f68f
...@@ -39,9 +39,10 @@ ...@@ -39,9 +39,10 @@
#define POLL_TIMEOUT (2 * HZ) #define POLL_TIMEOUT (2 * HZ)
/* SMBus mode*/ /* SMBus mode*/
#define TWI_I2C_MODE_STANDARD 0x01 #define TWI_I2C_MODE_STANDARD 1
#define TWI_I2C_MODE_STANDARDSUB 0x02 #define TWI_I2C_MODE_STANDARDSUB 2
#define TWI_I2C_MODE_COMBINED 0x04 #define TWI_I2C_MODE_COMBINED 3
#define TWI_I2C_MODE_REPEAT 4
struct bfin_twi_iface { struct bfin_twi_iface {
int irq; int irq;
...@@ -58,6 +59,9 @@ struct bfin_twi_iface { ...@@ -58,6 +59,9 @@ struct bfin_twi_iface {
struct timer_list timeout_timer; struct timer_list timeout_timer;
struct i2c_adapter adap; struct i2c_adapter adap;
struct completion complete; struct completion complete;
struct i2c_msg *pmsg;
int msg_num;
int cur_msg;
}; };
static struct bfin_twi_iface twi_iface; static struct bfin_twi_iface twi_iface;
...@@ -76,12 +80,16 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) ...@@ -76,12 +80,16 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
/* start receive immediately after complete sending in /* start receive immediately after complete sending in
* combine mode. * 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() bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
| MDIR | RSTART); | MDIR | RSTART);
} else if (iface->manual_stop) else if (iface->manual_stop)
bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
| STOP); | 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(); SSYNC();
/* Clear status */ /* Clear status */
bfin_write_TWI_INT_STAT(XMTSERV); bfin_write_TWI_INT_STAT(XMTSERV);
...@@ -108,6 +116,11 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) ...@@ -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() bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL()
| STOP); | STOP);
SSYNC(); 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 */ /* Clear interrupt source */
bfin_write_TWI_INT_STAT(RCVSERV); bfin_write_TWI_INT_STAT(RCVSERV);
...@@ -119,7 +132,7 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) ...@@ -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_STAT(0x3e);
bfin_write_TWI_MASTER_CTL(0); bfin_write_TWI_MASTER_CTL(0);
SSYNC(); SSYNC();
iface->result = -1; iface->result = -EIO;
/* if both err and complete int stats are set, return proper /* if both err and complete int stats are set, return proper
* results. * results.
*/ */
...@@ -170,6 +183,42 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) ...@@ -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() | bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() |
MEN | MDIR); MEN | MDIR);
SSYNC(); 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 { } else {
iface->result = 1; iface->result = 1;
bfin_write_TWI_INT_MASK(0); bfin_write_TWI_INT_MASK(0);
...@@ -221,7 +270,6 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap, ...@@ -221,7 +270,6 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap,
{ {
struct bfin_twi_iface *iface = adap->algo_data; struct bfin_twi_iface *iface = adap->algo_data;
struct i2c_msg *pmsg; struct i2c_msg *pmsg;
int i, ret;
int rc = 0; int rc = 0;
if (!(bfin_read_TWI_CONTROL() & TWI_ENA)) if (!(bfin_read_TWI_CONTROL() & TWI_ENA))
...@@ -231,81 +279,76 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap, ...@@ -231,81 +279,76 @@ static int bfin_twi_master_xfer(struct i2c_adapter *adap,
yield(); yield();
} }
ret = 0; iface->pmsg = msgs;
for (i = 0; rc >= 0 && i < num; i++) { iface->msg_num = num;
pmsg = &msgs[i]; iface->cur_msg = 0;
if (pmsg->flags & I2C_M_TEN) {
dev_err(&(adap->dev), "i2c-bfin-twi: 10 bits addr "
"not supported !\n");
rc = -EINVAL;
break;
}
iface->cur_mode = TWI_I2C_MODE_STANDARD; pmsg = &msgs[0];
iface->manual_stop = 0; if (pmsg->flags & I2C_M_TEN) {
iface->transPtr = pmsg->buf; dev_err(&adap->dev, "10 bits addr not supported!\n");
iface->writeNum = iface->readNum = pmsg->len; return -EINVAL;
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->cur_mode = TWI_I2C_MODE_REPEAT;
iface->read_write = I2C_SMBUS_READ; iface->manual_stop = 0;
else { iface->transPtr = pmsg->buf;
iface->read_write = I2C_SMBUS_WRITE; iface->writeNum = iface->readNum = pmsg->len;
/* Transmit first data */ iface->result = 0;
if (iface->writeNum > 0) { iface->timeout_count = 10;
bfin_write_TWI_XMT_DATA8(*(iface->transPtr++)); /* Set Transmit device address */
iface->writeNum--; bfin_write_TWI_MASTER_ADDR(pmsg->addr);
SSYNC();
} /* 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 */ /* clear int stat */
bfin_write_TWI_INT_STAT(MERR|MCOMP|XMTSERV|RCVSERV); bfin_write_TWI_INT_STAT(MERR | MCOMP | XMTSERV | RCVSERV);
/* Interrupt mask . Enable XMT, RCV interrupt */ /* Interrupt mask . Enable XMT, RCV interrupt */
bfin_write_TWI_INT_MASK(MCOMP | MERR | bfin_write_TWI_INT_MASK(MCOMP | MERR | RCVSERV | XMTSERV);
((iface->read_write == I2C_SMBUS_READ)? SSYNC();
RCVSERV : XMTSERV));
SSYNC();
if (pmsg->len > 0 && pmsg->len <= 255) if (pmsg->len <= 255)
bfin_write_TWI_MASTER_CTL(pmsg->len << 6); bfin_write_TWI_MASTER_CTL(pmsg->len << 6);
else if (pmsg->len > 255) { else {
bfin_write_TWI_MASTER_CTL(0xff << 6); bfin_write_TWI_MASTER_CTL(0xff << 6);
iface->manual_stop = 1; iface->manual_stop = 1;
} else }
break;
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT; iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer); add_timer(&iface->timeout_timer);
/* Master enable */ /* Master enable */
bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN | bfin_write_TWI_MASTER_CTL(bfin_read_TWI_MASTER_CTL() | MEN |
((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) | ((iface->read_write == I2C_SMBUS_READ) ? MDIR : 0) |
((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ>100) ? FAST : 0)); ((CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ > 100) ? FAST : 0));
SSYNC(); SSYNC();
wait_for_completion(&iface->complete); wait_for_completion(&iface->complete);
rc = iface->result; rc = iface->result;
if (rc == 1)
ret++;
else if (rc == -1)
break;
}
return ret; if (rc == 1)
return num;
else
return rc;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment