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