[PATCH] I2C: EXYNOS: Add slave support to i2c
Tushar Behera
tushar.behera at linaro.org
Fri Dec 7 07:03:17 EST 2012
On 12/03/2012 05:46 PM, Giridhar Maruthy wrote:
> 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 <giridhar.maruthy at linaro.org>
> ---
> drivers/i2c/busses/i2c-s3c2410.c | 100
> ++++++++++++++++++++++++++------------
> 1 file changed, 68 insertions(+), 32 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-s3c2410.c
> b/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(struct
> s3c24xx_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(struct
> s3c24xx_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(struct
> s3c24xx_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.
> + /* 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, struct
> s3c24xx_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 at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
--
Tushar Behera
More information about the linux-arm-kernel
mailing list