Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 347069
b: refs/heads/master
c: fe724bf
h: refs/heads/master
i:
  347067: c535bd6
v: v3
  • Loading branch information
Daniel Kurtz authored and Wolfram Sang committed Nov 16, 2012
1 parent 886e75f commit cf59130
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 21 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 0da2e7768b4c2b4dbbb148ebe1606b6b4698fca2
refs/heads/master: fe724bf9f023384eb14431c0e49ec46017ba8e30
67 changes: 47 additions & 20 deletions trunk/drivers/i2c/busses/i2c-s3c2410.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
#define QUIRK_HDMIPHY (1 << 1)
#define QUIRK_NO_GPIO (1 << 2)

/* Max time to wait for bus to become idle after a xfer (in us) */
#define S3C2410_IDLE_TIMEOUT 5000

/* i2c controller state */
enum s3c24xx_i2c_state {
STATE_IDLE,
Expand Down Expand Up @@ -557,6 +560,48 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
return -ETIMEDOUT;
}

/* s3c24xx_i2c_wait_idle
*
* wait for the i2c bus to become idle.
*/

static void s3c24xx_i2c_wait_idle(struct s3c24xx_i2c *i2c)
{
unsigned long iicstat;
ktime_t start, now;
unsigned long delay;

/* ensure the stop has been through the bus */

dev_dbg(i2c->dev, "waiting for bus idle\n");

start = now = ktime_get();

/*
* Most of the time, the bus is already idle within a few usec of the
* end of a transaction. However, really slow i2c devices can stretch
* the clock, delaying STOP generation.
*
* As a compromise between idle detection latency for the normal, fast
* case, and system load in the slow device case, use an exponential
* back off in the polling loop, up to 1/10th of the total timeout,
* then continue to poll at a constant rate up to the timeout.
*/
iicstat = readl(i2c->regs + S3C2410_IICSTAT);
delay = 1;
while ((iicstat & S3C2410_IICSTAT_START) &&
ktime_us_delta(now, start) < S3C2410_IDLE_TIMEOUT) {
usleep_range(delay, 2 * delay);
if (delay < S3C2410_IDLE_TIMEOUT / 10)
delay <<= 1;
now = ktime_get();
iicstat = readl(i2c->regs + S3C2410_IICSTAT);
}

if (iicstat & S3C2410_IICSTAT_START)
dev_warn(i2c->dev, "timeout waiting for bus idle\n");
}

/* s3c24xx_i2c_doxfer
*
* this starts an i2c transfer
Expand All @@ -565,8 +610,7 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
struct i2c_msg *msgs, int num)
{
unsigned long iicstat, timeout;
int spins = 20;
unsigned long timeout;
int ret;

if (i2c->suspended)
Expand Down Expand Up @@ -604,24 +648,7 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
if (i2c->quirks & QUIRK_HDMIPHY)
goto out;

/* ensure the stop has been through the bus */

dev_dbg(i2c->dev, "waiting for bus idle\n");

/* first, try busy waiting briefly */
do {
cpu_relax();
iicstat = readl(i2c->regs + S3C2410_IICSTAT);
} while ((iicstat & S3C2410_IICSTAT_START) && --spins);

/* if that timed out sleep */
if (!spins) {
msleep(1);
iicstat = readl(i2c->regs + S3C2410_IICSTAT);
}

if (iicstat & S3C2410_IICSTAT_START)
dev_warn(i2c->dev, "timeout waiting for bus idle\n");
s3c24xx_i2c_wait_idle(i2c);

out:
return ret;
Expand Down

0 comments on commit cf59130

Please sign in to comment.