i2c: s3c2410: add the new h/w init sequence
authorYoungmin Nam <youngmin.nam@samsung.com>
Thu, 2 Jul 2015 12:11:13 +0000 (21:11 +0900)
committermyung-su.cha <myung-su.cha@samsung.com>
Thu, 10 May 2018 04:40:52 +0000 (13:40 +0900)
To recover from "cannot get bus (error -110)" error,
add the new h/w init sequence. This init sequence initialize
i2c bus (sda and scl), through clearing S3C2410_IICSTAT_TXRXEN.

Change-Id: Id76af5a3c4baa18685b3104095fa3eb36942349f
Signed-off-by: Hyunki Koo <hyunki00.koo@samsung.com>
Signed-off-by: Youngmin Nam <youngmin.nam@samsung.com>
drivers/i2c/busses/i2c-s3c2410.c

index 3e5d67b0a7f4e9acc067e14e83a7feafd236ebc9..e31b73e633989b743aa919f0f287e022d6b5360f 100644 (file)
@@ -93,6 +93,10 @@ static LIST_HEAD(drvdata_list);
 
 #define S3C2440_IICINT_BUSHOLD_CLEAR   (1 << 8)
 
+#define S3C2410_NEED_REG_INIT          (1 << 0)
+#define S3C2410_NEED_BUS_INIT          (2 << 0)
+#define S3C2410_NEED_FULL_INIT         (3 << 0)
+
 /* Treat S3C2410 as baseline hardware, anything else is supported via quirks */
 #define QUIRK_S3C2440          (1 << 0)
 #define QUIRK_HDMIPHY          (1 << 1)
@@ -780,6 +784,7 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
        ret = s3c24xx_i2c_set_master(i2c);
        if (ret != 0) {
                dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
+               i2c->need_hw_init = S3C2410_NEED_FULL_INIT;
                ret = -EAGAIN;
                goto out;
        }
@@ -847,11 +852,12 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
        if (ret)
                return ret;
 
-       if (i2c->need_hw_init)
-               s3c24xx_i2c_init(i2c);
 
        for (retry = 0; retry < adap->retries; retry++) {
 
+               if (i2c->need_hw_init & S3C2410_NEED_FULL_INIT)
+                       s3c24xx_i2c_init(i2c);
+
                ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
 
                if (ret != -EAGAIN) {
@@ -1057,14 +1063,20 @@ static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
 static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
 {
        struct s3c2410_platform_i2c *pdata;
+       unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);
        unsigned int freq;
 
        /* get the plafrom data */
 
        pdata = i2c->pdata;
 
-       /* write slave address */
+       if (i2c->need_hw_init & S3C2410_NEED_BUS_INIT) {
+               /* reset i2c bus to recover from "cannot get bus" */
+               iicstat &= ~S3C2410_IICSTAT_TXRXEN;
+               writel(iicstat, i2c->regs + S3C2410_IICSTAT);
+       }
 
+       /* write slave address */
        writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);
 
        dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);
@@ -1126,7 +1138,7 @@ static int s3c24xx_i2c_notifier(struct notifier_block *self,
        switch (cmd) {
        case LPA_EXIT:
                list_for_each_entry(i2c, &drvdata_list, node)
-                       i2c->need_hw_init = 1;
+                       i2c->need_hw_init = S3C2410_NEED_REG_INIT;
                break;
        }
 
@@ -1225,7 +1237,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       i2c->need_hw_init = 1;
+       i2c->need_hw_init = S3C2410_NEED_REG_INIT;
 
        /* find the IRQ for this unit (note, this relies on the init call to
         * ensure no current IRQs pending
@@ -1318,7 +1330,7 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev)
        int ret;
 
        i2c->suspended = 0;
-       i2c->need_hw_init = 1;
+       i2c->need_hw_init = S3C2410_NEED_REG_INIT;
 
        return 0;
 }
@@ -1331,7 +1343,7 @@ static int s3c24xx_i2c_runtime_resume(struct device *dev)
        struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
 
        if (i2c->quirks & QUIRK_FIMC_I2C)
-               i2c->need_hw_init = 1;
+               i2c->need_hw_init = S3C2410_NEED_REG_INIT;
 
        return 0;
 }