i2c: iproc: Add i2c repeated start capability
Enable handling of i2c repeated start. The current code handles a multi msg i2c transfer as separate i2c bus transactions. This change will now handle this case using the i2c repeated start protocol. The number of msgs in a transfer is limited to two, and must be a write followed by a read. Signed-off-by: Lori Hikichi <lori.hikichi@broadcom.com> Signed-off-by: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com> Signed-off-by: Icarus Chau <icarus.chau@broadcom.com> Signed-off-by: Ray Jui <ray.jui@broadcom.com> Signed-off-by: Shivaraj Shetty <sshetty1@broadcom.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
parent
9af433840b
commit
5a5e277b8d
|
@ -81,6 +81,7 @@
|
||||||
#define M_CMD_PROTOCOL_MASK 0xf
|
#define M_CMD_PROTOCOL_MASK 0xf
|
||||||
#define M_CMD_PROTOCOL_BLK_WR 0x7
|
#define M_CMD_PROTOCOL_BLK_WR 0x7
|
||||||
#define M_CMD_PROTOCOL_BLK_RD 0x8
|
#define M_CMD_PROTOCOL_BLK_RD 0x8
|
||||||
|
#define M_CMD_PROTOCOL_PROCESS 0xa
|
||||||
#define M_CMD_PEC_SHIFT 8
|
#define M_CMD_PEC_SHIFT 8
|
||||||
#define M_CMD_RD_CNT_SHIFT 0
|
#define M_CMD_RD_CNT_SHIFT 0
|
||||||
#define M_CMD_RD_CNT_MASK 0xff
|
#define M_CMD_RD_CNT_MASK 0xff
|
||||||
|
@ -675,13 +676,20 @@ static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
/*
|
||||||
struct i2c_msg *msg)
|
* If 'process_call' is true, then this is a multi-msg transfer that requires
|
||||||
|
* a repeated start between the messages.
|
||||||
|
* More specifically, it must be a write (reg) followed by a read (data).
|
||||||
|
* The i2c quirks are set to enforce this rule.
|
||||||
|
*/
|
||||||
|
static int bcm_iproc_i2c_xfer_internal(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||||
|
struct i2c_msg *msgs, bool process_call)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
u8 addr;
|
u8 addr;
|
||||||
u32 val, tmp, val_intr_en;
|
u32 val, tmp, val_intr_en;
|
||||||
unsigned int tx_bytes;
|
unsigned int tx_bytes;
|
||||||
|
struct i2c_msg *msg = &msgs[0];
|
||||||
|
|
||||||
/* check if bus is busy */
|
/* check if bus is busy */
|
||||||
if (!!(iproc_i2c_rd_reg(iproc_i2c,
|
if (!!(iproc_i2c_rd_reg(iproc_i2c,
|
||||||
|
@ -707,14 +715,29 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||||
val = msg->buf[i];
|
val = msg->buf[i];
|
||||||
|
|
||||||
/* mark the last byte */
|
/* mark the last byte */
|
||||||
if (i == msg->len - 1)
|
if (!process_call && (i == msg->len - 1))
|
||||||
val |= BIT(M_TX_WR_STATUS_SHIFT);
|
val |= 1 << M_TX_WR_STATUS_SHIFT;
|
||||||
|
|
||||||
iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
|
iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
|
||||||
}
|
}
|
||||||
iproc_i2c->tx_bytes = tx_bytes;
|
iproc_i2c->tx_bytes = tx_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Process the read message if this is process call */
|
||||||
|
if (process_call) {
|
||||||
|
msg++;
|
||||||
|
iproc_i2c->msg = msg; /* point to second msg */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The last byte to be sent out should be a slave
|
||||||
|
* address with read operation
|
||||||
|
*/
|
||||||
|
addr = i2c_8bit_addr_from_msg(msg);
|
||||||
|
/* mark it the last byte out */
|
||||||
|
val = addr | (1 << M_TX_WR_STATUS_SHIFT);
|
||||||
|
iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
|
||||||
|
}
|
||||||
|
|
||||||
/* mark as incomplete before starting the transaction */
|
/* mark as incomplete before starting the transaction */
|
||||||
if (iproc_i2c->irq)
|
if (iproc_i2c->irq)
|
||||||
reinit_completion(&iproc_i2c->done);
|
reinit_completion(&iproc_i2c->done);
|
||||||
|
@ -733,7 +756,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||||
* underrun interrupt, which will be triggerred when the TX FIFO is
|
* underrun interrupt, which will be triggerred when the TX FIFO is
|
||||||
* empty. When that happens we can then pump more data into the FIFO
|
* empty. When that happens we can then pump more data into the FIFO
|
||||||
*/
|
*/
|
||||||
if (!(msg->flags & I2C_M_RD) &&
|
if (!process_call && !(msg->flags & I2C_M_RD) &&
|
||||||
msg->len > iproc_i2c->tx_bytes)
|
msg->len > iproc_i2c->tx_bytes)
|
||||||
val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT);
|
val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT);
|
||||||
|
|
||||||
|
@ -743,6 +766,8 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||||
*/
|
*/
|
||||||
val = BIT(M_CMD_START_BUSY_SHIFT);
|
val = BIT(M_CMD_START_BUSY_SHIFT);
|
||||||
if (msg->flags & I2C_M_RD) {
|
if (msg->flags & I2C_M_RD) {
|
||||||
|
u32 protocol;
|
||||||
|
|
||||||
iproc_i2c->rx_bytes = 0;
|
iproc_i2c->rx_bytes = 0;
|
||||||
if (msg->len > M_RX_FIFO_MAX_THLD_VALUE)
|
if (msg->len > M_RX_FIFO_MAX_THLD_VALUE)
|
||||||
iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE;
|
iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE;
|
||||||
|
@ -758,7 +783,10 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
||||||
/* enable the RX threshold interrupt */
|
/* enable the RX threshold interrupt */
|
||||||
val_intr_en |= BIT(IE_M_RX_THLD_SHIFT);
|
val_intr_en |= BIT(IE_M_RX_THLD_SHIFT);
|
||||||
|
|
||||||
val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) |
|
protocol = process_call ?
|
||||||
|
M_CMD_PROTOCOL_PROCESS : M_CMD_PROTOCOL_BLK_RD;
|
||||||
|
|
||||||
|
val |= (protocol << M_CMD_PROTOCOL_SHIFT) |
|
||||||
(msg->len << M_CMD_RD_CNT_SHIFT);
|
(msg->len << M_CMD_RD_CNT_SHIFT);
|
||||||
} else {
|
} else {
|
||||||
val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
|
val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
|
||||||
|
@ -774,17 +802,24 @@ static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
|
||||||
struct i2c_msg msgs[], int num)
|
struct i2c_msg msgs[], int num)
|
||||||
{
|
{
|
||||||
struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter);
|
struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter);
|
||||||
int ret, i;
|
bool process_call = false;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* go through all messages */
|
if (num == 2) {
|
||||||
for (i = 0; i < num; i++) {
|
/* Repeated start, use process call */
|
||||||
ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]);
|
process_call = true;
|
||||||
if (ret) {
|
if (msgs[1].flags & I2C_M_NOSTART) {
|
||||||
dev_dbg(iproc_i2c->device, "xfer failed\n");
|
dev_err(iproc_i2c->device, "Invalid repeated start\n");
|
||||||
return ret;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = bcm_iproc_i2c_xfer_internal(iproc_i2c, msgs, process_call);
|
||||||
|
if (ret) {
|
||||||
|
dev_dbg(iproc_i2c->device, "xfer failed\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -809,6 +844,8 @@ static struct i2c_algorithm bcm_iproc_algo = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
|
static const struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
|
||||||
|
.flags = I2C_AQ_COMB_WRITE_THEN_READ,
|
||||||
|
.max_comb_1st_msg_len = M_TX_RX_FIFO_SIZE,
|
||||||
.max_read_len = M_RX_MAX_READ_LEN,
|
.max_read_len = M_RX_MAX_READ_LEN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue