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>
This commit is contained in:
parent
4c03f68fc4
commit
4dd39bb12f
1 changed files with 122 additions and 79 deletions
|
@ -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();
|
||||
|
||||
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);
|
||||
|
||||
/* Interrupt mask . Enable XMT, RCV interrupt */
|
||||
bfin_write_TWI_INT_MASK(MCOMP | MERR |
|
||||
((iface->read_write == I2C_SMBUS_READ)?
|
||||
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;
|
||||
|
||||
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();
|
||||
|
||||
wait_for_completion(&iface->complete);
|
||||
|
||||
rc = iface->result;
|
||||
if (rc == 1)
|
||||
ret++;
|
||||
else if (rc == -1)
|
||||
break;
|
||||
pmsg = &msgs[0];
|
||||
if (pmsg->flags & I2C_M_TEN) {
|
||||
dev_err(&adap->dev, "10 bits addr not supported!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
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);
|
||||
|
||||
/* Interrupt mask . Enable XMT, RCV interrupt */
|
||||
bfin_write_TWI_INT_MASK(MCOMP | MERR | RCVSERV | XMTSERV);
|
||||
SSYNC();
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
|
||||
rc = iface->result;
|
||||
|
||||
if (rc == 1)
|
||||
return num;
|
||||
else
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in a new issue