[PATCH] i2c: cadence: Handling Slave monitor mode

Nava kishore Manne
2015-03-17T16:34:08Z

In slave monitor mode, the I2C interface is set up as a master and
continues to attempt a transfer to a particular slave until the
slave device responds with an ACK.

Added this feature for zero length transfers enable the controller
for slave monitor interrupt and get the status. Disable the slave
monitor mode feature upon successful handling.

Signed-off-by: Nava kishore Manne <navam@xilinx.com>
Acked-by: Harini Katakam <harinik@xilinx.com>
---
drivers/i2c/busses/i2c-cadence.c | 69 ++++++++++++++++++++++++++++++++++++----
1 file changed, 62 insertions(+), 7 deletions(-)

diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
index 7d7a14c..6bac2cb 100644
--- a/drivers/i2c/busses/i2c-cadence.c
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -25,11 +25,13 @@
#define CDNS_I2C_DATA_OFFSET 0x0C /* I2C Data Register, RW */
#define CDNS_I2C_ISR_OFFSET 0x10 /* IRQ Status Register, RW */
#define CDNS_I2C_XFER_SIZE_OFFSET 0x14 /* Transfer Size Register, RW */
+#define CDNS_I2C_SLV_PAUSE_OFFSET 0x18 /* Transfer Size Register, RW */
#define CDNS_I2C_TIME_OUT_OFFSET 0x1C /* Time Out Register, RW */
#define CDNS_I2C_IER_OFFSET 0x24 /* IRQ Enable Register, WO */
#define CDNS_I2C_IDR_OFFSET 0x28 /* IRQ Disable Register, WO */

/* Control Register Bit mask definitions */
+#define CDNS_I2C_CR_SLVMON BIT(5) /* Slave monitor mode bit */
#define CDNS_I2C_CR_HOLD BIT(4) /* Hold Bus bit */
#define CDNS_I2C_CR_ACK_EN BIT(3)
#define CDNS_I2C_CR_NEA BIT(2)
@@ -310,6 +312,23 @@ static irqreturn_t cdns_i2c_isr(int irq, void *ptr)
status = IRQ_HANDLED;
}

+ /* Handling Slave monitor mode interrupt */
+ if (isr_status & CDNS_I2C_IXR_SLV_RDY) {
+ unsigned int ctrl_reg;
+ /* Read control register */
+ ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+
+ /* Disable slave monitor mode */
+ ctrl_reg &= ~CDNS_I2C_CR_SLVMON;
+ cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
+
+ /* Clear interrupt flag for slvmon mode */
+ cdns_i2c_writereg(CDNS_I2C_IXR_SLV_RDY, CDNS_I2C_IDR_OFFSET);
+
+ done_flag = 1;
+ status = IRQ_HANDLED;
+ }
+
/* Update the status for errors */
id->err_status = isr_status & CDNS_I2C_IXR_ERR_INTR_MASK;
if (id->err_status)
@@ -444,6 +463,40 @@ static void cdns_i2c_msend(struct cdns_i2c *id)
}

/**
+ * cdns_i2c_slvmon - Handling Slav monitor mode feature
+ * @id: pointer to the i2c device
+ */
+static void cdns_i2c_slvmon(struct cdns_i2c *id)
+{
+ unsigned int ctrl_reg;
+ unsigned int isr_status;
+
+ id->p_recv_buf = NULL;
+ id->p_send_buf = id->p_msg->buf;
+ id->send_count = id->p_msg->len;
+
+ /* Clear the interrupts in interrupt status register. */
+ isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
+ cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
+
+ /* Enable slvmon control reg */
+ ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+ ctrl_reg |= CDNS_I2C_CR_MS | CDNS_I2C_CR_NEA | CDNS_I2C_CR_SLVMON
+ | CDNS_I2C_CR_CLR_FIFO;
+ ctrl_reg &= ~(CDNS_I2C_CR_RW);
+ cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
+
+ /* Initialize slvmon reg */
+ cdns_i2c_writereg(0xF, CDNS_I2C_SLV_PAUSE_OFFSET);
+
+ /* Set the slave address to start the slave address transmission */
+ cdns_i2c_writereg(id->p_msg->addr, CDNS_I2C_ADDR_OFFSET);
+
+ /* Setup slvmon interrupt flag */
+ cdns_i2c_writereg(CDNS_I2C_IXR_SLV_RDY, CDNS_I2C_IER_OFFSET);
+}
+
+/**
* cdns_i2c_master_reset - Reset the interface
* @adap: pointer to the i2c adapter driver instance
*
@@ -459,7 +512,7 @@ static void cdns_i2c_master_reset(struct i2c_adapter *adap)
cdns_i2c_writereg(CDNS_I2C_IXR_ALL_INTR_MASK, CDNS_I2C_IDR_OFFSET);
/* Clear the hold bit and fifos */
regval = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
- regval &= ~CDNS_I2C_CR_HOLD;
+ regval &= ~(CDNS_I2C_CR_HOLD | CDNS_I2C_CR_SLVMON);
regval |= CDNS_I2C_CR_CLR_FIFO;
cdns_i2c_writereg(regval, CDNS_I2C_CR_OFFSET);
/* Update the transfercount register to zero */
@@ -493,9 +546,11 @@ static int cdns_i2c_process_msg(struct cdns_i2c *id, struct i2c_msg *msg,
cdns_i2c_writereg(reg | CDNS_I2C_CR_NEA,
CDNS_I2C_CR_OFFSET);
}
-
- /* Check for the R/W flag on each msg */
- if (msg->flags & I2C_M_RD)
+ /* Check for zero lenght - Slave monitor mode */
+ if (msg->len == 0)
+ cdns_i2c_slvmon(id);
+ /* Check for the R/W flag on each msg */
+ else if (msg->flags & I2C_M_RD)
cdns_i2c_mrecv(id);
else
cdns_i2c_msend(id);
@@ -535,10 +590,10 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int ret, count;
u32 reg;
struct cdns_i2c *id = adap->algo_data;
-
/* Check if the bus is free */
- if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA)
- return -EAGAIN;
+ if (msgs->len)
+ if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA)
+ return -EAGAIN;

/*
* Set the flag to one when multiple messages are to be
--
2.1.2