[RFC 0/1] i2c: omap: Add support for switching to slave mode

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

[RFC 0/1] i2c: omap: Add support for switching to slave mode

Ravikumar Kattekola
I2C controller on most of the omap devices has both master and slave
capability but the i2c framework has been missing support for registering
a bus in slave mode for long.
Recently the i2c slave support has been added to i2c framework, the following
patch adds the required support for omap_i2c driver to register a controller
as a slave device and be deriven by an external/internal master.

The slave interface requires us to add following mandatory events

1. I2C_SLAVE_WRITE_REQUESTED
2. I2C_SLAVE_READ_REQUESTED
3. I2C_SLAVE_WRITE_RECEIVED
4. I2C_SLAVE_READ_PROCESSED

and

5. I2C_SLAVE_STOP

The omap i2c controller (at least on dra7x devices)
doesn't have  start/stop (STT/STP) support for slave mode
so event  #5 is not implemented in the driver.

Refer to Documentation/i2c/slave-interface for more info on
i2c-slave-interface and Documentation/i2c/slave-eeprom-backend for
sample backend driver.

Tested on:
DRA75x EVM Rev G3 and DRA72x EVM Rev B1 connected over i2c3 using DCAN2 lines [JP3]

Ravikumar Kattekola (1):
  drivers: i2c: omap: Add slave support

 drivers/i2c/busses/i2c-omap.c | 144 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 144 insertions(+)

--
2.8.2.396.g5fe494c

Reply | Threaded
Open this post in threaded view
|

[RFC 1/1] drivers: i2c: omap: Add slave support

Ravikumar Kattekola
I2C controller on most of the omap devices has both master and slave
capability but the i2c framework has been missing support for registering
a bus in slave mode for long.

Recently the i2c slave support has been added to i2c framework,
the following patch adds the required support for omap_i2c driver to
register a controller as a slave device and be deriven by
an external/internal master.

The slave interface requires us to add following mandatory events

1. I2C_SLAVE_WRITE_REQUESTED
2. I2C_SLAVE_READ_REQUESTED
3. I2C_SLAVE_WRITE_RECEIVED
4. I2C_SLAVE_READ_PROCESSED

and

5. I2C_SLAVE_STOP

The omap i2c controller (at least on dra7x devices)
doesn't have  start/stop (STT/STP) support for slave mode
so event  #5 is not implemented in the driver.

Signed-off-by: Ravikumar Kattekola <[hidden email]>
---
 drivers/i2c/busses/i2c-omap.c | 144 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 144 insertions(+)

diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index ab1279b..ccfc49f 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -89,6 +89,7 @@ enum {
 /* I2C Interrupt Enable Register (OMAP_I2C_IE): */
 #define OMAP_I2C_IE_XDR (1 << 14) /* TX Buffer drain int enable */
 #define OMAP_I2C_IE_RDR (1 << 13) /* RX Buffer drain int enable */
+#define OMAP_I2C_IE_AAS (1 << 9) /* Addressed as Slave int enable */
 #define OMAP_I2C_IE_XRDY (1 << 4) /* TX data ready int enable */
 #define OMAP_I2C_IE_RRDY (1 << 3) /* RX data ready int enable */
 #define OMAP_I2C_IE_ARDY (1 << 2) /* Access ready int enable */
@@ -202,6 +203,7 @@ struct omap_i2c_dev {
  u8 *regs;
  size_t buf_len;
  struct i2c_adapter adapter;
+ struct i2c_client *slave;
  u8 threshold;
  u8 fifo_size; /* use as flag and value
  * fifo_size==0 implies no fifo
@@ -1003,6 +1005,62 @@ omap_i2c_isr(int irq, void *dev_id)
  return ret;
 }
 
+#ifdef CONFIG_I2C_SLAVE
+static int omap_i2c_slave_irq(struct omap_i2c_dev *omap)
+{
+ u16 stat_raw;
+ u16 stat;
+ u16 bits;
+ u8 value;
+
+ stat_raw = omap_i2c_read_reg(omap, OMAP_I2C_IP_V2_IRQSTATUS_RAW);
+ bits = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
+ stat_raw &= bits;
+
+ if (stat_raw & OMAP_I2C_STAT_AAS) {
+ omap_i2c_ack_stat(omap, OMAP_I2C_STAT_AAS);
+ stat_raw &= ~OMAP_I2C_STAT_AAS;
+ }
+
+ /* Someone's just sayin Hi? okay bye..*/
+ if (!stat_raw)
+ goto out;
+
+ dev_dbg(omap->dev, "IRQ (ISR = 0x%04x)\n", stat_raw);
+
+ if (stat_raw & OMAP_I2C_STAT_RRDY)
+ i2c_slave_event(omap->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
+
+ do {
+ bits = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
+ stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
+ stat &= bits;
+
+ if (stat & OMAP_I2C_STAT_AAS)
+ omap_i2c_ack_stat(omap, OMAP_I2C_STAT_AAS);
+
+ if (stat & OMAP_I2C_STAT_RRDY) {
+ value = omap_i2c_read_reg(omap, OMAP_I2C_DATA_REG);
+ i2c_slave_event(omap->slave, I2C_SLAVE_WRITE_RECEIVED,
+ &value);
+ omap_i2c_ack_stat(omap, OMAP_I2C_STAT_RRDY);
+ }
+
+ if (stat & OMAP_I2C_STAT_XRDY) {
+ i2c_slave_event(omap->slave, I2C_SLAVE_READ_REQUESTED,
+ &value);
+ omap_i2c_write_reg(omap, OMAP_I2C_DATA_REG, value);
+ i2c_slave_event(omap->slave, I2C_SLAVE_READ_PROCESSED,
+ &value);
+ omap_i2c_ack_stat(omap, OMAP_I2C_STAT_XRDY);
+ }
+
+ } while (stat);
+out:
+ return 0;
+}
+#endif
+
 static irqreturn_t
 omap_i2c_isr_thread(int this_irq, void *dev_id)
 {
@@ -1011,6 +1069,13 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
  u16 stat;
  int err = 0, count = 0;
 
+#ifdef CONFIG_I2C_SLAVE
+ if (omap->slave) {
+ /* If a slave is registered pass on the interrupt */
+ err = omap_i2c_slave_irq(omap);
+ goto out;
+ }
+#endif
  do {
  bits = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
  stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
@@ -1139,9 +1204,88 @@ out:
  return IRQ_HANDLED;
 }
 
+#ifdef CONFIG_I2C_SLAVE
+static int omap_i2c_reg_slave(struct i2c_client *slave)
+{
+ struct omap_i2c_dev *omap = i2c_get_adapdata(slave->adapter);
+ u16 reg;
+ int ret = 0;
+
+ dev_info(omap->dev, "Registering as a slave @ %x\n", slave->addr);
+
+ /* Already registered as a slave?
+ * XXX: OMAP I2c controller supports up to four
+ * different OA, can we register as four different
+ * slaves?
+ */
+ if (omap->slave)
+ return -EBUSY;
+
+ ret = pm_runtime_get_sync(omap->dev);
+ if (ret < 0)
+ return ret;
+
+ omap->slave = slave;
+
+ /* Write OA: So that master(s) can talk to us */
+ omap_i2c_write_reg(omap, OMAP_I2C_OA_REG, slave->addr);
+
+ /* Set / Switch to slave mode */
+ reg = omap_i2c_read_reg(omap, OMAP_I2C_CON_REG);
+ reg &= ~OMAP_I2C_CON_MST;
+ omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, reg);
+
+ /* Clear status */
+ omap_i2c_write_reg(omap, OMAP_I2C_SYSS_REG, 0);
+
+ /* As of now, We dont need all interrupts be enabled */
+ omap->iestate = OMAP_I2C_IE_AAS | OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY;
+
+ /* Clear interrupt mask */
+ omap_i2c_write_reg(omap, OMAP_I2C_IP_V2_IRQENABLE_CLR, 0xffff);
+
+ dev_dbg(omap->dev, "omap->iestate 0x%04x\n", omap->iestate);
+
+ /* Enable necessary interrupts */
+ omap_i2c_write_reg(omap, OMAP_I2C_IE_REG, omap->iestate);
+
+ return 0;
+
+}
+
+static int omap_i2c_unreg_slave(struct i2c_client *slave)
+{
+ struct omap_i2c_dev *omap = i2c_get_adapdata(slave->adapter);
+ u16 reg;
+
+ WARN_ON(!omap->slave);
+ omap->slave = NULL;
+
+ /* Switch to master mode */
+ reg = omap_i2c_read_reg(omap, OMAP_I2C_CON_REG);
+ reg |= OMAP_I2C_CON_MST;
+ omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, reg);
+
+ /* clear status */
+ omap_i2c_write_reg(omap, OMAP_I2C_SYSS_REG, 0);
+
+ /* Just to make sure re-init in master mode
+ * Should we do a reset here?
+ */
+ omap_i2c_init(omap);
+
+ pm_runtime_put(omap->dev);
+ return 0;
+}
+#endif
+
 static const struct i2c_algorithm omap_i2c_algo = {
  .master_xfer = omap_i2c_xfer,
  .functionality = omap_i2c_func,
+#ifdef CONFIG_I2C_SLAVE
+ .reg_slave = omap_i2c_reg_slave,
+ .unreg_slave = omap_i2c_unreg_slave,
+#endif /* CONFIG_I2C_SLAVE */
 };
 
 #ifdef CONFIG_OF
--
2.8.2.396.g5fe494c

Reply | Threaded
Open this post in threaded view
|

Re: [RFC 1/1] drivers: i2c: omap: Add slave support

Manish Badarkhe
Hi Ravikumar

Some sanity comments, just good to have.

> +#ifdef CONFIG_I2C_SLAVE
> +static int omap_i2c_slave_irq(struct omap_i2c_dev *omap)
> +{
> +       u16 stat_raw;
> +       u16 stat;
> +       u16 bits;
> +       u8 value;
> +
> +       stat_raw = omap_i2c_read_reg(omap, OMAP_I2C_IP_V2_IRQSTATUS_RAW);
> +       bits = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
> +       stat_raw &= bits;
> +
> +       if (stat_raw & OMAP_I2C_STAT_AAS) {
> +               omap_i2c_ack_stat(omap, OMAP_I2C_STAT_AAS);
> +               stat_raw &= ~OMAP_I2C_STAT_AAS;
> +       }
> +
> +out:
> +       return 0;
> +}
> +#endif

This function always return 0.

> +#ifdef CONFIG_I2C_SLAVE
> +static int omap_i2c_reg_slave(struct i2c_client *slave)
> +{
> +       struct omap_i2c_dev *omap = i2c_get_adapdata(slave->adapter);
> +       u16 reg;
> +       int ret = 0;
> +       /* Enable necessary interrupts */
> +       omap_i2c_write_reg(omap, OMAP_I2C_IE_REG, omap->iestate);
> +
> +       return 0;
> +
> +}

Better to return "ret" here as already been initialized to 0

> +
> +static int omap_i2c_unreg_slave(struct i2c_client *slave)
> +{
> +       struct omap_i2c_dev *omap = i2c_get_adapdata(slave->adapter);
> +       u16 reg;
> +
> +       pm_runtime_put(omap->dev);
> +       return 0;
> +}
> +#endif

This function always return 0


Regards
Manish Badarkhe