Commit 23a61291 authored by Shinya Kuribayashi's avatar Shinya Kuribayashi Committed by Wolfram Sang

i2c: i2c-sh_mobile: optimize ICCH/ICCL values according to I2C bus speed

ICCH/ICCL values is supposed to be calculated/optimized to strictly meet
the timing specs required by the I2C standard. The resulting I2C bus
speed does not matter at all, if it's less than 100 or 400 kHz.

With this change, sh_mobile_i2c_icch() is virtually identical to
sh_mobile_i2c_iccl(), but they're providing good descriptions of
SH-/R-Mobile I2C hardware spec, and I'd leave them as separated.

Also fix a typo in the comment, print icch/iccl values at probe, etc.
Signed-off-by: default avatarShinya Kuribayashi <shinya.kuribayashi.px@renesas.com>

[wsa: squashed two patches for bisectability]
Signed-off-by: default avatarWolfram Sang <w.sang@pengutronix.de>
parent 7b0e6292
......@@ -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;
......@@ -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
......@@ -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);
}
......@@ -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)
......@@ -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;
......@@ -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;
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment