<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>