<div class="gmail_quote">This patch adds slave support to i2c. The dt entry i2c-mode<br>decides at probe time if the controller needs to work in<br>slave mode and the controller is accordingly programmed.<br><br>Signed-off-by: Giridhar Maruthy <<a href="mailto:giridhar.maruthy@linaro.org" target="_blank">giridhar.maruthy@linaro.org</a>><br>
---<br> drivers/i2c/busses/i2c-s3c2410.c |  100 ++++++++++++++++++++++++++------------<br> 1 file changed, 68 insertions(+), 32 deletions(-)<br><br>diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c<br>
index e93e7d6..d83a6d7 100644<br>--- a/drivers/i2c/busses/i2c-s3c2410.c<br>+++ b/drivers/i2c/busses/i2c-s3c2410.c<br>@@ -53,6 +53,9 @@<br> /* Max time to wait for bus to become idle after a xfer (in us) */<br> #define S3C2410_IDLE_TIMEOUT   5000<br>
<br>+/* To find the master/slave mode of current controller */<br>+#define is_master(i2c) (!i2c->i2c_mode)<br>+<br> /* i2c controller state */<br> enum s3c24xx_i2c_state {<br>        STATE_IDLE,<br>@@ -89,6 +92,8 @@ struct s3c24xx_i2c {<br>
 #ifdef CONFIG_CPU_FREQ<br>        struct notifier_block   freq_transition;<br> #endif<br>+       /* i2c_mode: 0 is for master; and 1 is for slave */<br>+       unsigned int            i2c_mode;<br> };<br><br> static struct platform_device_id s3c24xx_driver_ids[] = {<br>
@@ -202,11 +207,21 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,<br>        stat = 0;<br>        stat |=  S3C2410_IICSTAT_TXRXEN;<br><br>-       if (msg->flags & I2C_M_RD) {<br>-               stat |= S3C2410_IICSTAT_MASTER_RX;<br>
-               addr |= 1;<br>-       } else<br>-               stat |= S3C2410_IICSTAT_MASTER_TX;<br>+       if (is_master(i2c)) {<br>+               /* Master mode */<br>+               if (msg->flags & I2C_M_RD) {<br>
+                       stat |= S3C2410_IICSTAT_MASTER_RX;<br>+                       addr |= 1;<br>+               } else<br>+                       stat |= S3C2410_IICSTAT_MASTER_TX;<br>+       } else {<br>+               /* Slave mode */<br>
+               if (msg->flags & I2C_M_RD) {<br>+                       stat |= S3C2410_IICSTAT_SLAVE_RX;<br>+                       addr |= 1;<br>+               } else<br>+                       stat |= S3C2410_IICSTAT_SLAVE_TX;<br>
+       }<br><br>        if (msg->flags & I2C_M_REV_DIR_ADDR)<br>                addr ^= 1;<br>@@ -228,8 +243,10 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,<br>        dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);<br>
        writel(iiccon, i2c->regs + S3C2410_IICCON);<br><br>-       stat |= S3C2410_IICSTAT_START;<br>-       writel(stat, i2c->regs + S3C2410_IICSTAT);<br>+       if (is_master(i2c)) {<br>+               stat |= S3C2410_IICSTAT_START;<br>
+               writel(stat, i2c->regs + S3C2410_IICSTAT);<br>+       }<br> }<br><br> static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)<br>@@ -272,14 +289,19 @@ static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)<br>
         * devices, the host as Master and the HDMIPHY device as the slave.<br>         * Skipping the STOP condition has been tested on this bus and works.<br>         */<br>-       if (i2c->quirks & QUIRK_HDMIPHY) {<br>
-               /* Stop driving the I2C pins */<br>-               iicstat &= ~S3C2410_IICSTAT_TXRXEN;<br>-       } else {<br>-               /* stop the transfer */<br>-               iicstat &= ~S3C2410_IICSTAT_START;<br>
+       if (is_master(i2c)) {<br>+               if (i2c->quirks & QUIRK_HDMIPHY) {<br>+                       /* Stop driving the I2C pins */<br>+                       iicstat &= ~S3C2410_IICSTAT_TXRXEN;<br>+               } else {<br>
+                       /* stop the transfer */<br>+                       if (is_master(i2c)) {<br>+                               /* Start/Stop required only for master */<br>+                               iicstat &= ~S3C2410_IICSTAT_START;<br>
+                       }<br>+               }<br>+               writel(iicstat, i2c->regs + S3C2410_IICSTAT);<br>        }<br>-       writel(iicstat, i2c->regs + S3C2410_IICSTAT);<br><br>        i2c->state = STATE_STOP;<br>
<br>@@ -348,7 +370,8 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)<br>                 */<br><br>                if (iicstat & S3C2410_IICSTAT_LASTBIT &&<br>-                   !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {<br>
+                   !(i2c->msg->flags & I2C_M_IGNORE_NAK) &&<br>+                   is_master(i2c)) {<br>                        /* ack was not received... */<br><br>                        dev_dbg(i2c->dev, "ack was not received\n");<br>
@@ -380,7 +403,7 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)<br>                 * end of the message, and if so, work out what to do<br>                 */<br><br>-               if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {<br>
+               if (!(i2c->msg->flags & I2C_M_IGNORE_NAK) && is_master(i2c)) {<br>                        if (iicstat & S3C2410_IICSTAT_LASTBIT) {<br>                                dev_dbg(i2c->dev, "WRITE: No Ack\n");<br>
<br>@@ -432,7 +455,6 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)<br><br>                } else {<br>                        /* send stop */<br>-<br>                        s3c24xx_i2c_stop(i2c, 0);<br>
                }<br>                break;<br>@@ -447,7 +469,7 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)<br>                i2c->msg->buf[i2c->msg_ptr++] = byte;<br><br>  prepare_read:<br>
-               if (is_msglast(i2c)) {<br>+               if (is_msglast(i2c) && is_master(i2c)) {<br>                        /* last byte of buffer */<br><br>                        if (is_lastmsg(i2c))<br>@@ -612,11 +634,13 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,<br>
        if (i2c->suspended)<br>                return -EIO;<br><br>-       ret = s3c24xx_i2c_set_master(i2c);<br>-       if (ret != 0) {<br>-               dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);<br>
-               ret = -EAGAIN;<br>-               goto out;<br>+       if (is_master(i2c)) {<br>+               ret = s3c24xx_i2c_set_master(i2c);<br>+               if (ret != 0) {<br>+                       dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);<br>
+                       ret = -EAGAIN;<br>+                       goto out;<br>+               }<br>        }<br><br>        i2c->msg     = msgs;<br>@@ -628,23 +652,29 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,<br>
        s3c24xx_i2c_enable_irq(i2c);<br>        s3c24xx_i2c_message_start(i2c, msgs);<br><br>-       timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);<br>+       if (is_master(i2c))<br>+               timeout = wait_event_timeout(i2c->wait,\<br>
+                               i2c->msg_num == 0, HZ * 5);<br>+       else<br>+               wait_event_interruptible(i2c->wait, i2c->msg_num == 0);<br><br>        ret = i2c->msg_idx;<br><br>        /* having these next two as dev_err() makes life very<br>
         * noisy when doing an i2cdetect */<br><br>-       if (timeout == 0)<br>-               dev_dbg(i2c->dev, "timeout\n");<br>-       else if (ret != num)<br>-               dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);<br>
+       if (is_master(i2c)) {<br>+               if (timeout == 0)<br>+                       dev_dbg(i2c->dev, "timeout\n");<br>+               else if (ret != num)<br>+                       dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);<br>
<br>-       /* For QUIRK_HDMIPHY, bus is already disabled */<br>-       if (i2c->quirks & QUIRK_HDMIPHY)<br>-               goto out;<br>+               /* For QUIRK_HDMIPHY, bus is already disabled */<br>+               if (i2c->quirks & QUIRK_HDMIPHY)<br>
+                       goto out;<br><br>-       s3c24xx_i2c_wait_idle(i2c);<br>+               s3c24xx_i2c_wait_idle(i2c);<br>+       }<br><br>  out:<br>        return ret;<br>@@ -963,6 +993,7 @@ s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)<br>
        of_property_read_u32(np, "samsung,i2c-slave-addr", &pdata->slave_addr);<br>        of_property_read_u32(np, "samsung,i2c-max-bus-freq",<br>                                (u32 *)&pdata->frequency);<br>
+       of_property_read_u32(np, "samsung,i2c-mode", &i2c->i2c_mode);<br> }<br> #else<br> static void<br>@@ -1004,6 +1035,10 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)<br>                goto err_noclk;<br>
        }<br><br>+       /* By default, i2c works in master mode */<br>+       /* This currently will be updated using DT */<br>+       i2c->i2c_mode   = 0;<br>+<br>        i2c->quirks = s3c24xx_get_device_quirks(pdev);<br>
        if (pdata)<br>                memcpy(i2c->pdata, pdata, sizeof(*pdata));<br>@@ -1017,6 +1052,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)<br>        i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;<br>
        i2c->tx_setup     = 50;<br><br>+<br>        init_waitqueue_head(&i2c->wait);<br><br>        /* find the clock and enable it */<br><span><font color="#888888">--<br>1.7.9.5<br><br></font></span></div><br>