|
@@ -58,11 +58,13 @@
|
|
|
#define IE_M_RX_FIFO_FULL_SHIFT 31
|
|
|
#define IE_M_RX_THLD_SHIFT 30
|
|
|
#define IE_M_START_BUSY_SHIFT 28
|
|
|
+#define IE_M_TX_UNDERRUN_SHIFT 27
|
|
|
|
|
|
#define IS_OFFSET 0x3c
|
|
|
#define IS_M_RX_FIFO_FULL_SHIFT 31
|
|
|
#define IS_M_RX_THLD_SHIFT 30
|
|
|
#define IS_M_START_BUSY_SHIFT 28
|
|
|
+#define IS_M_TX_UNDERRUN_SHIFT 27
|
|
|
|
|
|
#define M_TX_OFFSET 0x40
|
|
|
#define M_TX_WR_STATUS_SHIFT 31
|
|
@@ -76,7 +78,7 @@
|
|
|
#define M_RX_DATA_SHIFT 0
|
|
|
#define M_RX_DATA_MASK 0xff
|
|
|
|
|
|
-#define I2C_TIMEOUT_MSEC 100
|
|
|
+#define I2C_TIMEOUT_MSEC 50000
|
|
|
#define M_TX_RX_FIFO_SIZE 64
|
|
|
|
|
|
enum bus_speed_index {
|
|
@@ -95,12 +97,17 @@ struct bcm_iproc_i2c_dev {
|
|
|
|
|
|
struct completion done;
|
|
|
int xfer_is_done;
|
|
|
+
|
|
|
+ struct i2c_msg *msg;
|
|
|
+
|
|
|
+ /* bytes that have been transferred */
|
|
|
+ unsigned int tx_bytes;
|
|
|
};
|
|
|
|
|
|
/*
|
|
|
* Can be expanded in the future if more interrupt status bits are utilized
|
|
|
*/
|
|
|
-#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT)
|
|
|
+#define ISR_MASK (BIT(IS_M_START_BUSY_SHIFT) | BIT(IS_M_TX_UNDERRUN_SHIFT))
|
|
|
|
|
|
static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
|
|
|
{
|
|
@@ -112,9 +119,49 @@ static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
|
|
|
if (!status)
|
|
|
return IRQ_NONE;
|
|
|
|
|
|
+ /* TX FIFO is empty and we have more data to send */
|
|
|
+ if (status & BIT(IS_M_TX_UNDERRUN_SHIFT)) {
|
|
|
+ struct i2c_msg *msg = iproc_i2c->msg;
|
|
|
+ unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes;
|
|
|
+ unsigned int i;
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ /* can only fill up to the FIFO size */
|
|
|
+ tx_bytes = min_t(unsigned int, tx_bytes, M_TX_RX_FIFO_SIZE);
|
|
|
+ for (i = 0; i < tx_bytes; i++) {
|
|
|
+ /* start from where we left over */
|
|
|
+ unsigned int idx = iproc_i2c->tx_bytes + i;
|
|
|
+
|
|
|
+ val = msg->buf[idx];
|
|
|
+
|
|
|
+ /* mark the last byte */
|
|
|
+ if (idx == msg->len - 1) {
|
|
|
+ u32 tmp;
|
|
|
+
|
|
|
+ val |= BIT(M_TX_WR_STATUS_SHIFT);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Since this is the last byte, we should
|
|
|
+ * now disable TX FIFO underrun interrupt
|
|
|
+ */
|
|
|
+ tmp = readl(iproc_i2c->base + IE_OFFSET);
|
|
|
+ tmp &= ~BIT(IE_M_TX_UNDERRUN_SHIFT);
|
|
|
+ writel(tmp, iproc_i2c->base + IE_OFFSET);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* load data into TX FIFO */
|
|
|
+ writel(val, iproc_i2c->base + M_TX_OFFSET);
|
|
|
+ }
|
|
|
+ /* update number of transferred bytes */
|
|
|
+ iproc_i2c->tx_bytes += tx_bytes;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (status & BIT(IS_M_START_BUSY_SHIFT)) {
|
|
|
+ iproc_i2c->xfer_is_done = 1;
|
|
|
+ complete_all(&iproc_i2c->done);
|
|
|
+ }
|
|
|
+
|
|
|
writel(status, iproc_i2c->base + IS_OFFSET);
|
|
|
- iproc_i2c->xfer_is_done = 1;
|
|
|
- complete_all(&iproc_i2c->done);
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
@@ -207,6 +254,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
|
|
int ret, i;
|
|
|
u8 addr;
|
|
|
u32 val;
|
|
|
+ unsigned int tx_bytes;
|
|
|
unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC);
|
|
|
|
|
|
/* check if bus is busy */
|
|
@@ -216,13 +264,20 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
|
|
return -EBUSY;
|
|
|
}
|
|
|
|
|
|
+ iproc_i2c->msg = msg;
|
|
|
+
|
|
|
/* format and load slave address into the TX FIFO */
|
|
|
addr = msg->addr << 1 | (msg->flags & I2C_M_RD ? 1 : 0);
|
|
|
writel(addr, iproc_i2c->base + M_TX_OFFSET);
|
|
|
|
|
|
- /* for a write transaction, load data into the TX FIFO */
|
|
|
+ /*
|
|
|
+ * For a write transaction, load data into the TX FIFO. Only allow
|
|
|
+ * loading up to TX FIFO size - 1 bytes of data since the first byte
|
|
|
+ * has been used up by the slave address
|
|
|
+ */
|
|
|
+ tx_bytes = min_t(unsigned int, msg->len, M_TX_RX_FIFO_SIZE - 1);
|
|
|
if (!(msg->flags & I2C_M_RD)) {
|
|
|
- for (i = 0; i < msg->len; i++) {
|
|
|
+ for (i = 0; i < tx_bytes; i++) {
|
|
|
val = msg->buf[i];
|
|
|
|
|
|
/* mark the last byte */
|
|
@@ -231,6 +286,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
|
|
|
|
|
writel(val, iproc_i2c->base + M_TX_OFFSET);
|
|
|
}
|
|
|
+ iproc_i2c->tx_bytes = tx_bytes;
|
|
|
}
|
|
|
|
|
|
/* mark as incomplete before starting the transaction */
|
|
@@ -242,13 +298,24 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
|
|
* transaction is done, i.e., the internal start_busy bit, transitions
|
|
|
* from 1 to 0.
|
|
|
*/
|
|
|
- writel(1 << IE_M_START_BUSY_SHIFT, iproc_i2c->base + IE_OFFSET);
|
|
|
+ val = BIT(IE_M_START_BUSY_SHIFT);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If TX data size is larger than the TX FIFO, need to enable TX
|
|
|
+ * underrun interrupt, which will be triggerred when the TX FIFO is
|
|
|
+ * empty. When that happens we can then pump more data into the FIFO
|
|
|
+ */
|
|
|
+ if (!(msg->flags & I2C_M_RD) &&
|
|
|
+ msg->len > iproc_i2c->tx_bytes)
|
|
|
+ val |= BIT(IE_M_TX_UNDERRUN_SHIFT);
|
|
|
+
|
|
|
+ writel(val, iproc_i2c->base + IE_OFFSET);
|
|
|
|
|
|
/*
|
|
|
* Now we can activate the transfer. For a read operation, specify the
|
|
|
* number of bytes to read
|
|
|
*/
|
|
|
- val = 1 << M_CMD_START_BUSY_SHIFT;
|
|
|
+ val = BIT(M_CMD_START_BUSY_SHIFT);
|
|
|
if (msg->flags & I2C_M_RD) {
|
|
|
val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) |
|
|
|
(msg->len << M_CMD_RD_CNT_SHIFT);
|
|
@@ -331,7 +398,6 @@ static const struct i2c_algorithm bcm_iproc_algo = {
|
|
|
static struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
|
|
|
/* need to reserve one byte in the FIFO for the slave address */
|
|
|
.max_read_len = M_TX_RX_FIFO_SIZE - 1,
|
|
|
- .max_write_len = M_TX_RX_FIFO_SIZE - 1,
|
|
|
};
|
|
|
|
|
|
static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c)
|