From: Daniel Kurtz Date: Tue, 14 Aug 2012 12:52:54 +0000 (+0800) Subject: CHROMIUM: i2c-s3c2410: do not generate STOP for QUIRK_HDMIPHY buses X-Git-Url: http://git.cascardo.eti.br/?a=commitdiff_plain;h=edc19760a277ca1c812f5fd1ebf303f8fd7967fd;p=cascardo%2Flinux.git CHROMIUM: i2c-s3c2410: do not generate STOP for QUIRK_HDMIPHY buses The datasheet says that the STOP sequence should be: 1) I2CSTAT.5 = 0 - Clear BUSY (or 'generate STOP') 2) I2CCON.4 = 0 - Clear IRQPEND 3) Wait until the stop condition takes effect. 4*) I2CSTAT.4 = 0 - Clear TXRXEN Where, step "4*" is only for buses with the "HDMIPHY" quirk. However, after much experimentation, it appears that: a) normal buses automatically clear BUSY and transition from Master->Slave when they complete generating a STOP condition. Therefore, step (3) can be done in doxfer() by polling I2CCON.4 after starting the STOP generation here. b) HDMIPHY bus does neither, so there is no way to do step 3. There is no indication when this bus has finished generating STOP. In fact, we have found that as soon as the IRQPEND bit is cleared in step 2, the HDMIPHY bus generates the STOP condition, and then immediately starts transferring another data byte, even though the bus is supposedly stopped. This is presumably because the bus is still in "Master" mode, and its BUSY bit is still set. To avoid these extra post-STOP transactions on HDMI phy devices, we just disable Serial Output on the bus (I2CSTAT.4 = 0) directly, instead of first generating a proper STOP condition. This should float SDA & SCK terminating the transfer. Subsequent transfers start with a proper START condition, and proceed normally. The HDMIPHY bus is an internal bus that always has exactly two devices, the host as Master and the HDMIPHY device as the slave. Skipping the STOP condition has been tested on this bus and works. Also, since we disable the bus directly from the isr, we can skip the bus idle polling loop at the end of doxfer(). Signed-off-by: Daniel Kurtz BUG=chrome-os-partner:10089 TEST=No "s3c-i2c 12ce0000.i2c: timeout waiting for bus idle" in dmesg TEST=HDMI output works after hot plug 1) EDID displayed in /var/log/Xorg.0.log 2) Video shown on external monitor Change-Id: Ic6518aceba46a058b65e6adff1bc7a5a295eb5b0 Reviewed-on: https://gerrit.chromium.org/gerrit/29876 Reviewed-by: Doug Anderson Commit-Ready: Daniel Kurtz Reviewed-by: Daniel Kurtz Tested-by: Daniel Kurtz --- diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 197f426a06d2..a0080386278c 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -329,8 +329,47 @@ static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) dev_dbg(i2c->dev, "STOP\n"); - /* stop the transfer */ - iicstat &= ~S3C2410_IICSTAT_START; + /* + * The datasheet says that the STOP sequence should be: + * 1) I2CSTAT.5 = 0 - Clear BUSY (or 'generate STOP') + * 2) I2CCON.4 = 0 - Clear IRQPEND + * 3) Wait until the stop condition takes effect. + * 4*) I2CSTAT.4 = 0 - Clear TXRXEN + * + * Where, step "4*" is only for buses with the "HDMIPHY" quirk. + * + * However, after much experimentation, it appears that: + * a) normal buses automatically clear BUSY and transition from + * Master->Slave when they complete generating a STOP condition. + * Therefore, step (3) can be done in doxfer() by polling I2CCON.4 + * after starting the STOP generation here. + * b) HDMIPHY bus does neither, so there is no way to do step 3. + * There is no indication when this bus has finished generating + * STOP. + * + * In fact, we have found that as soon as the IRQPEND bit is cleared in + * step 2, the HDMIPHY bus generates the STOP condition, and then + * immediately starts transferring another data byte, even though the + * bus is supposedly stopped. This is presumably because the bus is + * still in "Master" mode, and its BUSY bit is still set. + * + * To avoid these extra post-STOP transactions on HDMI phy devices, we + * just disable Serial Output on the bus (I2CSTAT.4 = 0) directly, + * instead of first generating a proper STOP condition. This should + * float SDA & SCK terminating the transfer. Subsequent transfers + * start with a proper START condition, and proceed normally. + * + * The HDMIPHY bus is an internal bus that always has exactly two + * 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; + } writel(iicstat, i2c->regs + S3C2410_IICSTAT); i2c->state = STATE_STOP; @@ -667,6 +706,10 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, 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; + /* ensure the stop has been through the bus */ dev_dbg(i2c->dev, "waiting for bus idle\n");