Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 347060
b: refs/heads/master
c: 23a6129
h: refs/heads/master
v: v3
  • Loading branch information
Shinya Kuribayashi authored and Wolfram Sang committed Nov 16, 2012
1 parent 87a3955 commit 0b9f1ee
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 50 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: 7b0e62920ac314eb819e68b7d2c51994b98b19ca
refs/heads/master: 23a612916a51cc3772ff46c9dc34a86c9c50840e
126 changes: 77 additions & 49 deletions trunk/drivers/i2c/busses/i2c-sh_mobile.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ struct sh_mobile_i2c_data {
unsigned long bus_speed;
struct clk *clk;
u_int8_t icic;
u_int8_t iccl;
u_int8_t icch;
u_int8_t flags;
u_int16_t iccl;
u_int16_t icch;

spinlock_t lock;
wait_queue_head_t wait;
Expand All @@ -135,7 +135,8 @@ struct sh_mobile_i2c_data {

#define IIC_FLAG_HAS_ICIC67 (1 << 0)

#define NORMAL_SPEED 100000 /* FAST_SPEED 400000 */
#define STANDARD_MODE 100000
#define FAST_MODE 400000

/* Register offsets */
#define ICDR 0x00
Expand Down Expand Up @@ -187,55 +188,81 @@ static void iic_set_clr(struct sh_mobile_i2c_data *pd, int offs,
iic_wr(pd, offs, (iic_rd(pd, offs) | set) & ~clr);
}

static u32 sh_mobile_i2c_iccl(unsigned long count_khz, u32 tLOW, u32 tf, int offset)
{
/*
* Conditional expression:
* ICCL >= COUNT_CLK * (tLOW + tf)
*
* SH-Mobile IIC hardware starts counting the LOW period of
* the SCL signal (tLOW) as soon as it pulls the SCL line.
* In order to meet the tLOW timing spec, we need to take into
* account the fall time of SCL signal (tf). Default tf value
* should be 0.3 us, for safety.
*/
return (((count_khz * (tLOW + tf)) + 5000) / 10000) + offset;
}

static u32 sh_mobile_i2c_icch(unsigned long count_khz, u32 tHIGH, u32 tf, int offset)
{
/*
* Conditional expression:
* ICCH >= COUNT_CLK * (tHIGH + tf)
*
* SH-Mobile IIC hardware is aware of SCL transition period 'tr',
* and can ignore it. SH-Mobile IIC controller starts counting
* the HIGH period of the SCL signal (tHIGH) after the SCL input
* voltage increases at VIH.
*
* Afterward it turned out calculating ICCH using only tHIGH spec
* will result in violation of the tHD;STA timing spec. We need
* to take into account the fall time of SDA signal (tf) at START
* condition, in order to meet both tHIGH and tHD;STA specs.
*/
return (((count_khz * (tHIGH + tf)) + 5000) / 10000) + offset;
}

static void sh_mobile_i2c_init(struct sh_mobile_i2c_data *pd)
{
unsigned long i2c_clk;
u_int32_t num;
u_int32_t denom;
u_int32_t tmp;
unsigned long i2c_clk_khz;
u32 tHIGH, tLOW, tf;
int offset;

/* Get clock rate after clock is enabled */
clk_enable(pd->clk);
i2c_clk = clk_get_rate(pd->clk);

/* Calculate the value for iccl. From the data sheet:
* iccl = (p clock / transfer rate) * (L / (L + H))
* where L and H are the SCL low/high ratio (5/4 in this case).
* We also round off the result.
*/
num = i2c_clk * 5;
denom = pd->bus_speed * 9;
tmp = num * 10 / denom;
if (tmp % 10 >= 5)
pd->iccl = (u_int8_t)((num/denom) + 1);
else
pd->iccl = (u_int8_t)(num/denom);

/* one more bit of ICCL in ICIC */
if (pd->flags & IIC_FLAG_HAS_ICIC67) {
if ((num/denom) > 0xff)
pd->icic |= ICIC_ICCLB8;
else
pd->icic &= ~ICIC_ICCLB8;
i2c_clk_khz = clk_get_rate(pd->clk) / 1000;

if (pd->bus_speed == STANDARD_MODE) {
tLOW = 47; /* tLOW = 4.7 us */
tHIGH = 40; /* tHD;STA = tHIGH = 4.0 us */
tf = 3; /* tf = 0.3 us */
offset = 0; /* No offset */
} else if (pd->bus_speed == FAST_MODE) {
tLOW = 13; /* tLOW = 1.3 us */
tHIGH = 6; /* tHD;STA = tHIGH = 0.6 us */
tf = 3; /* tf = 0.3 us */
offset = 0; /* No offset */
} else {
dev_err(pd->dev, "unrecognized bus speed %lu Hz\n",
pd->bus_speed);
goto out;
}

/* Calculate the value for icch. From the data sheet:
icch = (p clock / transfer rate) * (H / (L + H)) */
num = i2c_clk * 4;
tmp = num * 10 / denom;
if (tmp % 10 >= 5)
pd->icch = (u_int8_t)((num/denom) + 1);
pd->iccl = sh_mobile_i2c_iccl(i2c_clk_khz, tLOW, tf, offset);
/* one more bit of ICCL in ICIC */
if ((pd->iccl > 0xff) && (pd->flags & IIC_FLAG_HAS_ICIC67))
pd->icic |= ICIC_ICCLB8;
else
pd->icch = (u_int8_t)(num/denom);
pd->icic &= ~ICIC_ICCLB8;

pd->icch = sh_mobile_i2c_icch(i2c_clk_khz, tHIGH, tf, offset);
/* one more bit of ICCH in ICIC */
if (pd->flags & IIC_FLAG_HAS_ICIC67) {
if ((num/denom) > 0xff)
pd->icic |= ICIC_ICCHB8;
else
pd->icic &= ~ICIC_ICCHB8;
}
if ((pd->icch > 0xff) && (pd->flags & IIC_FLAG_HAS_ICIC67))
pd->icic |= ICIC_ICCHB8;
else
pd->icic &= ~ICIC_ICCHB8;

out:
clk_disable(pd->clk);
}

Expand All @@ -252,8 +279,8 @@ static void activate_ch(struct sh_mobile_i2c_data *pd)
iic_wr(pd, ICIC, 0);

/* Set the clock */
iic_wr(pd, ICCL, pd->iccl);
iic_wr(pd, ICCH, pd->icch);
iic_wr(pd, ICCL, pd->iccl & 0xff);
iic_wr(pd, ICCH, pd->icch & 0xff);
}

static void deactivate_ch(struct sh_mobile_i2c_data *pd)
Expand Down Expand Up @@ -457,8 +484,8 @@ static int start_ch(struct sh_mobile_i2c_data *pd, struct i2c_msg *usr_msg)
iic_set_clr(pd, ICCR, ICCR_ICE, 0);

/* Set the clock */
iic_wr(pd, ICCL, pd->iccl);
iic_wr(pd, ICCH, pd->icch);
iic_wr(pd, ICCL, pd->iccl & 0xff);
iic_wr(pd, ICCH, pd->icch & 0xff);

pd->msg = usr_msg;
pd->pos = -1;
Expand Down Expand Up @@ -627,8 +654,8 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
goto err_irq;
}

/* Use platformd data bus speed or NORMAL_SPEED */
pd->bus_speed = NORMAL_SPEED;
/* Use platform data bus speed or STANDARD_MODE */
pd->bus_speed = STANDARD_MODE;
if (pdata && pdata->bus_speed)
pd->bus_speed = pdata->bus_speed;

Expand Down Expand Up @@ -675,8 +702,9 @@ static int sh_mobile_i2c_probe(struct platform_device *dev)
goto err_all;
}

dev_info(&dev->dev, "I2C adapter %d with bus speed %lu Hz\n",
adap->nr, pd->bus_speed);
dev_info(&dev->dev,
"I2C adapter %d with bus speed %lu Hz (L/H=%x/%x)\n",
adap->nr, pd->bus_speed, pd->iccl, pd->icch);

of_i2c_register_devices(adap);
return 0;
Expand Down

0 comments on commit 0b9f1ee

Please sign in to comment.