|
@@ -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;
|