aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2010-04-02 09:15:09 -0400
committerBen Dooks <ben-linux@fluff.org>2010-05-19 19:18:58 -0400
commit1bc2962e530527de829bf4b1eb99f24dc25d1828 (patch)
treea7332eea2f0bbc00bb1342b813bd73a1e840121f
parent024a6b95181f2df6090975c8a293499d24bf8b28 (diff)
i2c-s3c2410: Remove unconditional 1ms delay on each transfer
The S3C I2C controller indicates completion of I2C transfers before the bus has a stop condition on it. In order to ensure that we do not attempt to start a new transfer before the bus is idle the driver currently inserts a 1ms delay. This is vastly larger than is generally required and has a visible effect on performance under load, such as when bringing up audio CODECs or reading back status information with non-bulk I2C reads. Replace the sleep with a spin on the IIC status register for up to 1ms. This will busy wait but testing on my SMDK6410 system indicates that the overwhelming majority of transactions complete on the first spin, with maximum latencies of less than 10 spins so the absolute overhead of busy waiting should be at worst comprable to msleep(), and the overall system performance is dramatically improved. The main risk is poor interaction with multimaster systems where we may miss the bus going idle before the next transaction. Defend against this by falling back to the original 1ms delay after 20 spins. The overall effect in my testing is an approximately 20% improvement in kernel startup time. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c19
1 files changed, 17 insertions, 2 deletions
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index d27072b2249f..ec3256cce91e 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -482,7 +482,8 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
482static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, 482static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
483 struct i2c_msg *msgs, int num) 483 struct i2c_msg *msgs, int num)
484{ 484{
485 unsigned long timeout; 485 unsigned long iicstat, timeout;
486 int spins = 20;
486 int ret; 487 int ret;
487 488
488 if (i2c->suspended) 489 if (i2c->suspended)
@@ -521,7 +522,21 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
521 522
522 /* ensure the stop has been through the bus */ 523 /* ensure the stop has been through the bus */
523 524
524 msleep(1); 525 dev_dbg(i2c->dev, "waiting for bus idle\n");
526
527 /* first, try busy waiting briefly */
528 do {
529 iicstat = readl(i2c->regs + S3C2410_IICSTAT);
530 } while ((iicstat & S3C2410_IICSTAT_START) && --spins);
531
532 /* if that timed out sleep */
533 if (!spins) {
534 msleep(1);
535 iicstat = readl(i2c->regs + S3C2410_IICSTAT);
536 }
537
538 if (iicstat & S3C2410_IICSTAT_START)
539 dev_warn(i2c->dev, "timeout waiting for bus idle\n");
525 540
526 out: 541 out:
527 return ret; 542 return ret;