i2c: exynos5: Migration from 3.18 based kernel
authorYoungmin Nam <youngmin.nam@samsung.com>
Thu, 6 Apr 2017 06:02:25 +0000 (15:02 +0900)
committermyung-su.cha <myung-su.cha@samsung.com>
Wed, 9 May 2018 12:14:45 +0000 (21:14 +0900)
Change-Id: Ibfe5c46e2c7f383db94f973298e0c507140596f9
Signed-off-by: Youngmin Nam <youngmin.nam@samsung.com>
drivers/i2c/busses/i2c-exynos5.c

index 3855e0b11877144b0ff717df480969f571fba3fd..84b986c19ddb6de0960a17cbc513059098d534ed 100644 (file)
 #include <linux/module.h>
 
 #include <linux/i2c.h>
+#include <linux/init.h>
 #include <linux/time.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/clk.h>
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/of_irq.h>
-#include <linux/spinlock.h>
+#include <linux/of_gpio.h>
+#include "../../pinctrl/core.h"
+
+#include <soc/samsung/exynos-powermode.h>
+#ifdef CONFIG_CPU_IDLE
+#include <soc/samsung/exynos-pm.h>
+#endif
+
+#if defined(CONFIG_CPU_IDLE)
+static LIST_HEAD(drvdata_list);
+#endif
 
 /*
  * HSI2C controller from Samsung supports 2 modes of operation
 #define HSI2C_MASTER                           (1u << 3)
 #define HSI2C_RXCHON                           (1u << 6)
 #define HSI2C_TXCHON                           (1u << 7)
+#define HSI2C_EXT_MSB                          (1u << 29)
+#define HSI2C_EXT_ADDR                         (1u << 30)
 #define HSI2C_SW_RST                           (1u << 31)
 
 /* I2C_FIFO_CTL Register bits */
 #define HSI2C_RXFIFO_EN                                (1u << 0)
 #define HSI2C_TXFIFO_EN                                (1u << 1)
-#define HSI2C_RXFIFO_TRIGGER_LEVEL(x)          ((x) << 4)
-#define HSI2C_TXFIFO_TRIGGER_LEVEL(x)          ((x) << 16)
-
+#define HSI2C_FIFO_MAX                         (0x40)
+#define HSI2C_RXFIFO_TRIGGER_LEVEL             (0x8 << 4)
+#define HSI2C_TXFIFO_TRIGGER_LEVEL             (0x8 << 16)
 /* I2C_TRAILING_CTL Register bits */
 #define HSI2C_TRAILING_COUNT                   (0xf)
 
@@ -84,6 +98,9 @@
 #define HSI2C_INT_TX_ALMOSTEMPTY_EN            (1u << 0)
 #define HSI2C_INT_RX_ALMOSTFULL_EN             (1u << 1)
 #define HSI2C_INT_TRAILING_EN                  (1u << 6)
+#define HSI2C_INT_TRANSFER_DONE                        (1u << 7)
+#define HSI2C_INT_I2C_EN                       (1u << 9)
+#define HSI2C_INT_CHK_TRANS_STATE              (0xf << 8)
 
 /* I2C_INT_STAT Register bits */
 #define HSI2C_INT_TX_ALMOSTEMPTY               (1u << 0)
 #define HSI2C_INT_RX_OVERRUN                   (1u << 5)
 #define HSI2C_INT_TRAILING                     (1u << 6)
 #define HSI2C_INT_I2C                          (1u << 9)
-
-#define HSI2C_INT_TRANS_DONE                   (1u << 7)
-#define HSI2C_INT_TRANS_ABORT                  (1u << 8)
-#define HSI2C_INT_NO_DEV_ACK                   (1u << 9)
-#define HSI2C_INT_NO_DEV                       (1u << 10)
-#define HSI2C_INT_TIMEOUT                      (1u << 11)
-#define HSI2C_INT_I2C_TRANS                    (HSI2C_INT_TRANS_DONE | \
-                                               HSI2C_INT_TRANS_ABORT | \
-                                               HSI2C_INT_NO_DEV_ACK |  \
-                                               HSI2C_INT_NO_DEV |      \
-                                               HSI2C_INT_TIMEOUT)
+#define HSI2C_RX_INT                           (HSI2C_INT_RX_ALMOSTFULL | \
+                                                HSI2C_INT_RX_UNDERRUN | \
+                                                HSI2C_INT_RX_OVERRUN | \
+                                                HSI2C_INT_TRAILING)
 
 /* I2C_FIFO_STAT Register bits */
 #define HSI2C_RX_FIFO_EMPTY                    (1u << 24)
 #define HSI2C_RX_FIFO_FULL                     (1u << 23)
 #define HSI2C_RX_FIFO_LVL(x)                   ((x >> 16) & 0x7f)
+#define HSI2C_RX_FIFO_LVL_MASK                 (0x7F << 16)
 #define HSI2C_TX_FIFO_EMPTY                    (1u << 8)
 #define HSI2C_TX_FIFO_FULL                     (1u << 7)
 #define HSI2C_TX_FIFO_LVL(x)                   ((x >> 0) & 0x7f)
+#define HSI2C_TX_FIFO_LVL_MASK                 (0x7F << 0)
+#define HSI2C_FIFO_EMPTY                       (HSI2C_RX_FIFO_EMPTY |  \
+                                               HSI2C_TX_FIFO_EMPTY)
 
 /* I2C_CONF Register bits */
 #define HSI2C_AUTO_MODE                                (1u << 31)
 #define HSI2C_10BIT_ADDR_MODE                  (1u << 30)
 #define HSI2C_HS_MODE                          (1u << 29)
+#define HSI2C_FILTER_EN_SCL                    (1u << 28)
+#define HSI2C_FILTER_EN_SDA                    (1u << 27)
+#define HSI2C_FTL_CYCLE_SCL_MASK               (0x7 << 16)
+#define HSI2C_FTL_CYCLE_SDA_MASK               (0x7 << 13)
 
 /* I2C_AUTO_CONF Register bits */
 #define HSI2C_READ_WRITE                       (1u << 16)
 
 /* I2C_TIMEOUT Register bits */
 #define HSI2C_TIMEOUT_EN                       (1u << 31)
-#define HSI2C_TIMEOUT_MASK                     0xff
 
 /* I2C_TRANS_STATUS register bits */
 #define HSI2C_MASTER_BUSY                      (1u << 17)
 #define HSI2C_NO_DEV_ACK                       (1u << 2)
 #define HSI2C_TRANS_ABORT                      (1u << 1)
 #define HSI2C_TRANS_DONE                       (1u << 0)
+#define HSI2C_MAST_ST_MASK                     (0xf << 0)
 
 /* I2C_TRANS_STATUS register bits for Exynos7 variant */
 #define HSI2C_MASTER_ST_MASK                   0xf
  * Controller operating frequency, timing values for operation
  * are calculated against this frequency
  */
-#define HSI2C_HS_TX_CLOCK      1000000
-#define HSI2C_FS_TX_CLOCK      100000
+#define HSI2C_HS_TX_CLOCK      2500000
+#define HSI2C_FS_TX_CLOCK      400000
+#define HSI2C_HIGH_SPD         1
+#define HSI2C_FAST_SPD         0
+
+#define HSI2C_POLLING 0
+#define HSI2C_INTERRUPT 1
 
 #define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(1000))
+#define EXYNOS5_FIFO_SIZE              16
 
-#define HSI2C_EXYNOS7  BIT(0)
+#define EXYNOS5_HSI2C_RUNTIME_PM_DELAY (100)
 
 struct exynos5_i2c {
+       struct list_head        node;
        struct i2c_adapter      adap;
+       unsigned int            need_hw_init;
        unsigned int            suspended:1;
 
        struct i2c_msg          *msg;
        struct completion       msg_complete;
        unsigned int            msg_ptr;
+       unsigned int            msg_len;
 
        unsigned int            irq;
 
        void __iomem            *regs;
        struct clk              *clk;
+       struct clk              *rate_clk;
        struct device           *dev;
        int                     state;
 
-       spinlock_t              lock;           /* IRQ synchronization */
-
        /*
         * Since the TRANS_DONE bit is cleared on read, and we may read it
         * either during an IRQ or after a transaction, keep track of its
@@ -199,54 +225,162 @@ struct exynos5_i2c {
        int                     trans_done;
 
        /* Controller operating frequency */
-       unsigned int            op_clock;
+       unsigned int            fs_clock;
+       unsigned int            hs_clock;
 
-       /* Version of HS-I2C Hardware */
-       const struct exynos_hsi2c_variant *variant;
+       /*
+        * HSI2C Controller can operate in
+        * 1. High speed upto 3.4Mbps
+        * 2. Fast speed upto 1Mbps
+        */
+       int                     speed_mode;
+       int                     operation_mode;
+       int                     bus_id;
+       int                     scl_clk_stretch;
+       int                     stop_after_trans;
+       unsigned int            transfer_delay;
+       int                     idle_ip_index;
 };
 
-/**
- * struct exynos_hsi2c_variant - platform specific HSI2C driver data
- * @fifo_depth: the fifo depth supported by the HSI2C module
- *
- * Specifies platform specific configuration of HSI2C module.
- * Note: A structure for driver specific platform data is used for future
- * expansion of its usage.
- */
-struct exynos_hsi2c_variant {
-       unsigned int    fifo_depth;
-       unsigned int    hw;
+static const struct of_device_id exynos5_i2c_match[] = {
+       { .compatible = "samsung,exynos5-hsi2c" },
+       {},
 };
+MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
 
-static const struct exynos_hsi2c_variant exynos5250_hsi2c_data = {
-       .fifo_depth     = 64,
-};
+#ifdef CONFIG_GPIOLIB
+static void change_i2c_gpio(struct exynos5_i2c *i2c)
+{
+       struct pinctrl_state *default_i2c_pins;
+       struct pinctrl *default_i2c_pinctrl;
+       int status = 0;
+
+       default_i2c_pinctrl = devm_pinctrl_get(i2c->dev);
+       if (IS_ERR(default_i2c_pinctrl)) {
+               dev_err(i2c->dev, "Can't get i2c pinctrl!!!\n");
+               return ;
+       }
 
-static const struct exynos_hsi2c_variant exynos5260_hsi2c_data = {
-       .fifo_depth     = 16,
-};
+       default_i2c_pins = pinctrl_lookup_state(default_i2c_pinctrl,
+                               "default");
+       if (!IS_ERR(default_i2c_pins)) {
+               default_i2c_pinctrl->state = NULL;
+               status = pinctrl_select_state(default_i2c_pinctrl, default_i2c_pins);
+               if (status)
+                       dev_err(i2c->dev, "Can't set default i2c pins!!!\n");
+       } else {
+               dev_err(i2c->dev, "Can't get default pinstate!!!\n");
+       }
+}
 
-static const struct exynos_hsi2c_variant exynos7_hsi2c_data = {
-       .fifo_depth     = 16,
-       .hw             = HSI2C_EXYNOS7,
-};
+static void recover_gpio_pins(struct exynos5_i2c *i2c)
+{
+       int gpio_sda, gpio_scl;
+       int sda_val, scl_val, clk_cnt;
+       unsigned long timeout;
+       struct device_node *np = i2c->adap.dev.of_node;
 
-static const struct of_device_id exynos5_i2c_match[] = {
-       {
-               .compatible = "samsung,exynos5-hsi2c",
-               .data = &exynos5250_hsi2c_data
-       }, {
-               .compatible = "samsung,exynos5250-hsi2c",
-               .data = &exynos5250_hsi2c_data
-       }, {
-               .compatible = "samsung,exynos5260-hsi2c",
-               .data = &exynos5260_hsi2c_data
-       }, {
-               .compatible = "samsung,exynos7-hsi2c",
-               .data = &exynos7_hsi2c_data
-       }, {},
-};
-MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
+       dev_err(i2c->dev, "Recover GPIO pins\n");
+
+       gpio_sda = of_get_named_gpio(np, "gpio_sda", 0);
+       if (!gpio_is_valid(gpio_sda)) {
+               dev_err(i2c->dev, "Can't get gpio_sda!!!\n");
+               return ;
+       }
+       gpio_scl = of_get_named_gpio(np, "gpio_scl", 0);
+       if (!gpio_is_valid(gpio_scl)) {
+               dev_err(i2c->dev, "Can't get gpio_scl!!!\n");
+               return ;
+       }
+
+       sda_val = gpio_get_value(gpio_sda);
+       scl_val = gpio_get_value(gpio_scl);
+
+       dev_err(i2c->dev, "SDA line : %s, SCL line : %s\n",
+                       sda_val ? "HIGH" : "LOW", scl_val ? "HIGH" : "LOW");
+
+       if (sda_val == 1)
+               return ;
+
+       /* Wait for SCL as high for 500msec */
+       if (scl_val == 0) {
+               timeout = jiffies + msecs_to_jiffies(500);
+               while (time_before(jiffies, timeout)) {
+                       if (gpio_get_value(gpio_scl) != 0) {
+                               timeout = 0;
+                               break;
+                       }
+                       msleep(10);
+               }
+               if (timeout)
+                       dev_err(i2c->dev, "SCL line is still LOW!!!\n");
+       }
+
+       sda_val = gpio_get_value(gpio_sda);
+
+       if (sda_val == 0) {
+               gpio_direction_output(gpio_scl, 1);
+
+               for (clk_cnt = 0; clk_cnt < 100; clk_cnt++) {
+                       /* Make clock for slave */
+                       gpio_set_value(gpio_scl, 0);
+                       udelay(5);
+                       gpio_set_value(gpio_scl, 1);
+                       udelay(5);
+                       if (gpio_get_value(gpio_sda) == 1) {
+                               dev_err(i2c->dev, "SDA line is recovered.\n");
+                               break;
+                       }
+               }
+               if (clk_cnt == 100)
+                       dev_err(i2c->dev, "SDA line is not recovered!!!\n");
+       }
+
+       /* Change I2C GPIO as default function */
+       change_i2c_gpio(i2c);
+}
+#endif
+
+static inline void dump_i2c_register(struct exynos5_i2c *i2c)
+{
+       dev_err(i2c->dev, "Register dump(suspended : %d)\n", i2c->suspended);
+       dev_err(i2c->dev, ": CTL        0x%08x\n"
+                       , readl(i2c->regs + HSI2C_CTL));
+       dev_err(i2c->dev, ": FIFO_CTL   0x%08x\n"
+                       , readl(i2c->regs + HSI2C_FIFO_CTL));
+       dev_err(i2c->dev, ": INT_EN     0x%08x\n"
+                       , readl(i2c->regs + HSI2C_INT_ENABLE));
+       dev_err(i2c->dev, ": INT_STAT   0x%08x\n"
+                       , readl(i2c->regs + HSI2C_INT_STATUS));
+       dev_err(i2c->dev, ": FIFO_STAT  0x%08x\n"
+                       , readl(i2c->regs + HSI2C_FIFO_STATUS));
+       dev_err(i2c->dev, ": CONF       0x%08x\n"
+                       , readl(i2c->regs + HSI2C_CONF));
+       dev_err(i2c->dev, ": AUTO_CONF  0x%08x\n"
+                       , readl(i2c->regs + HSI2C_AUTO_CONF));
+       dev_err(i2c->dev, ": TRANS_STAT 0x%08x\n"
+                       , readl(i2c->regs + HSI2C_TRANS_STATUS));
+       dev_err(i2c->dev, ": TIMING_HS1 0x%08x\n"
+                       , readl(i2c->regs + HSI2C_TIMING_HS1));
+       dev_err(i2c->dev, ": TIMING_HS2 0x%08x\n"
+                       , readl(i2c->regs + HSI2C_TIMING_HS2));
+       dev_err(i2c->dev, ": TIMING_HS3 0x%08x\n"
+                       , readl(i2c->regs + HSI2C_TIMING_HS3));
+       dev_err(i2c->dev, ": TIMING_FS1 0x%08x\n"
+                       , readl(i2c->regs + HSI2C_TIMING_FS1));
+       dev_err(i2c->dev, ": TIMING_FS2 0x%08x\n"
+                       , readl(i2c->regs + HSI2C_TIMING_FS2));
+       dev_err(i2c->dev, ": TIMING_FS3 0x%08x\n"
+                       , readl(i2c->regs + HSI2C_TIMING_FS3));
+       dev_err(i2c->dev, ": TIMING_SLA 0x%08x\n"
+                       , readl(i2c->regs + HSI2C_TIMING_SLA));
+       dev_err(i2c->dev, ": ADDR       0x%08x\n"
+                       , readl(i2c->regs + HSI2C_ADDR));
+
+#ifdef CONFIG_GPIOLIB
+       recover_gpio_pins(i2c);
+#endif
+}
 
 static void exynos5_i2c_clr_pend_irq(struct exynos5_i2c *i2c)
 {
@@ -273,39 +407,31 @@ static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, bool hs_timings)
        unsigned int t_scl_l, t_scl_h;
        unsigned int t_sr_release;
        unsigned int t_ftl_cycle;
-       unsigned int clkin = clk_get_rate(i2c->clk);
-       unsigned int op_clk = hs_timings ? i2c->op_clock :
-               (i2c->op_clock >= HSI2C_HS_TX_CLOCK) ? HSI2C_FS_TX_CLOCK :
-               i2c->op_clock;
-       int div, clk_cycle, temp;
+       unsigned int clkin = clk_get_rate(i2c->rate_clk);
+       unsigned int div, utemp0 = 0, utemp1 = 0, clk_cycle = 0;
+       unsigned int op_clk = (mode == HSI2C_HIGH_SPD) ?
+                               i2c->hs_clock : i2c->fs_clock;
 
        /*
-        * In case of HSI2C controller in Exynos5 series
         * FPCLK / FI2C =
-        * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE
-        *
-        * In case of HSI2C controllers in Exynos7 series
-        * FPCLK / FI2C =
-        * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + FLT_CYCLE
-        *
-        * clk_cycle := TSCLK_L + TSCLK_H
-        * temp := (CLK_DIV + 1) * (clk_cycle + 2)
-        *
-        * Constraints: 4 <= temp, 0 <= CLK_DIV < 256, 2 <= clk_cycle <= 510
-        *
+        * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + {(FLT_CYCLE + 3) %
+        * (CLK_DIV + 1)} * 2
         */
        t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7;
-       temp = clkin / op_clk - 8 - t_ftl_cycle;
-       if (i2c->variant->hw != HSI2C_EXYNOS7)
-               temp -= t_ftl_cycle;
-       div = temp / 512;
-       clk_cycle = temp / (div + 1) - 2;
-       if (temp < 4 || div >= 256 || clk_cycle < 2) {
-               dev_err(i2c->dev, "%s clock set-up failed\n",
-                       hs_timings ? "HS" : "FS");
-               return -EINVAL;
+       utemp0 = (clkin / op_clk) - (t_ftl_cycle + 3) * 2;
+
+       /* CLK_DIV max is 256 */
+       for (div = 0; div < 256; div++) {
+               utemp1 = (utemp0 + ((t_ftl_cycle + 3) % (div + 1)) * 2) / (div + 1);
+
+               if ((utemp1 < 512) && (utemp1 > 4)) {
+                       clk_cycle = utemp1 - 2;
+                       break;
+               } else if (div == 255) {
+                       dev_warn(i2c->dev, "Failed to calculate divisor");
+                       return -EINVAL;
+               }
        }
-
        t_scl_l = clk_cycle / 2;
        t_scl_h = clk_cycle / 2;
        t_start_su = t_scl_l;
@@ -360,23 +486,19 @@ static int exynos5_hsi2c_clock_setup(struct exynos5_i2c *i2c)
 static void exynos5_i2c_init(struct exynos5_i2c *i2c)
 {
        u32 i2c_conf = readl(i2c->regs + HSI2C_CONF);
-       u32 i2c_timeout = readl(i2c->regs + HSI2C_TIMEOUT);
-
-       /* Clear to disable Timeout */
-       i2c_timeout &= ~HSI2C_TIMEOUT_EN;
-       writel(i2c_timeout, i2c->regs + HSI2C_TIMEOUT);
 
-       writel((HSI2C_FUNC_MODE_I2C | HSI2C_MASTER),
-                                       i2c->regs + HSI2C_CTL);
+       writel(HSI2C_MASTER, i2c->regs + HSI2C_CTL);
        writel(HSI2C_TRAILING_COUNT, i2c->regs + HSI2C_TRAILIG_CTL);
 
-       if (i2c->op_clock >= HSI2C_HS_TX_CLOCK) {
-               writel(HSI2C_MASTER_ID(MASTER_ID(i2c->adap.nr)),
+       if (i2c->speed_mode == HSI2C_HIGH_SPD) {
+               writel(HSI2C_MASTER_ID(MASTER_ID(i2c->bus_id)),
                                        i2c->regs + HSI2C_ADDR);
                i2c_conf |= HSI2C_HS_MODE;
        }
 
        writel(i2c_conf | HSI2C_AUTO_MODE, i2c->regs + HSI2C_CONF);
+
+       i2c->need_hw_init = 0;
 }
 
 static void exynos5_i2c_reset(struct exynos5_i2c *i2c)
@@ -398,6 +520,13 @@ static void exynos5_i2c_reset(struct exynos5_i2c *i2c)
        exynos5_i2c_init(i2c);
 }
 
+static inline void exynos5_i2c_stop(struct exynos5_i2c *i2c)
+{
+       writel(0, i2c->regs + HSI2C_INT_ENABLE);
+
+       complete(&i2c->msg_complete);
+}
+
 /*
  * exynos5_i2c_irq: top level IRQ servicing routine
  *
@@ -405,291 +534,371 @@ static void exynos5_i2c_reset(struct exynos5_i2c *i2c)
  * FIFO_STATUS or TRANS_STATUS registers are to be check for detailed
  * state of the bus.
  */
+
 static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
 {
        struct exynos5_i2c *i2c = dev_id;
-       u32 fifo_level, int_status, fifo_status, trans_status;
+       unsigned long reg_val;
+       unsigned long trans_status;
        unsigned char byte;
-       int len = 0;
-
-       i2c->state = -EINVAL;
-
-       spin_lock(&i2c->lock);
-
-       int_status = readl(i2c->regs + HSI2C_INT_STATUS);
-       writel(int_status, i2c->regs + HSI2C_INT_STATUS);
-
-       /* handle interrupt related to the transfer status */
-       if (i2c->variant->hw == HSI2C_EXYNOS7) {
-               if (int_status & HSI2C_INT_TRANS_DONE) {
-                       i2c->trans_done = 1;
-                       i2c->state = 0;
-               } else if (int_status & HSI2C_INT_TRANS_ABORT) {
-                       dev_dbg(i2c->dev, "Deal with arbitration lose\n");
-                       i2c->state = -EAGAIN;
-                       goto stop;
-               } else if (int_status & HSI2C_INT_NO_DEV_ACK) {
-                       dev_dbg(i2c->dev, "No ACK from device\n");
-                       i2c->state = -ENXIO;
-                       goto stop;
-               } else if (int_status & HSI2C_INT_NO_DEV) {
-                       dev_dbg(i2c->dev, "No device\n");
-                       i2c->state = -ENXIO;
-                       goto stop;
-               } else if (int_status & HSI2C_INT_TIMEOUT) {
-                       dev_dbg(i2c->dev, "Accessing device timed out\n");
-                       i2c->state = -ETIMEDOUT;
-                       goto stop;
-               }
-
-               trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
-               if ((trans_status & HSI2C_MASTER_ST_MASK) == HSI2C_MASTER_ST_LOSE) {
-                       i2c->state = -EAGAIN;
-                       goto stop;
-               }
-       } else if (int_status & HSI2C_INT_I2C) {
-               trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
-               if (trans_status & HSI2C_NO_DEV_ACK) {
-                       dev_dbg(i2c->dev, "No ACK from device\n");
-                       i2c->state = -ENXIO;
-                       goto stop;
-               } else if (trans_status & HSI2C_NO_DEV) {
-                       dev_dbg(i2c->dev, "No device\n");
-                       i2c->state = -ENXIO;
-                       goto stop;
-               } else if (trans_status & HSI2C_TRANS_ABORT) {
-                       dev_dbg(i2c->dev, "Deal with arbitration lose\n");
-                       i2c->state = -EAGAIN;
-                       goto stop;
-               } else if (trans_status & HSI2C_TIMEOUT_AUTO) {
-                       dev_dbg(i2c->dev, "Accessing device timed out\n");
-                       i2c->state = -ETIMEDOUT;
-                       goto stop;
-               } else if (trans_status & HSI2C_TRANS_DONE) {
-                       i2c->trans_done = 1;
-                       i2c->state = 0;
-               }
-       }
-
-       if ((i2c->msg->flags & I2C_M_RD) && (int_status &
-                       (HSI2C_INT_TRAILING | HSI2C_INT_RX_ALMOSTFULL))) {
-               fifo_status = readl(i2c->regs + HSI2C_FIFO_STATUS);
-               fifo_level = HSI2C_RX_FIFO_LVL(fifo_status);
-               len = min(fifo_level, i2c->msg->len - i2c->msg_ptr);
 
-               while (len > 0) {
-                       byte = (unsigned char)
-                               readl(i2c->regs + HSI2C_RX_DATA);
+       if (i2c->msg->flags & I2C_M_RD) {
+               while ((readl(i2c->regs + HSI2C_FIFO_STATUS) &
+                       0x1000000) == 0) {
+                       byte = (unsigned char)readl(i2c->regs + HSI2C_RX_DATA);
                        i2c->msg->buf[i2c->msg_ptr++] = byte;
-                       len--;
-               }
-               i2c->state = 0;
-       } else if (int_status & HSI2C_INT_TX_ALMOSTEMPTY) {
-               fifo_status = readl(i2c->regs + HSI2C_FIFO_STATUS);
-               fifo_level = HSI2C_TX_FIFO_LVL(fifo_status);
-
-               len = i2c->variant->fifo_depth - fifo_level;
-               if (len > (i2c->msg->len - i2c->msg_ptr)) {
-                       u32 int_en = readl(i2c->regs + HSI2C_INT_ENABLE);
-
-                       int_en &= ~HSI2C_INT_TX_ALMOSTEMPTY_EN;
-                       writel(int_en, i2c->regs + HSI2C_INT_ENABLE);
-                       len = i2c->msg->len - i2c->msg_ptr;
                }
 
-               while (len > 0) {
+               if (i2c->msg_ptr >= i2c->msg->len) {
+                       reg_val = readl(i2c->regs + HSI2C_INT_ENABLE);
+                       reg_val &= ~(HSI2C_INT_RX_ALMOSTFULL_EN);
+                       writel(reg_val, i2c->regs + HSI2C_INT_ENABLE);
+                       exynos5_i2c_stop(i2c);
+               }
+       } else {
+               while ((readl(i2c->regs + HSI2C_FIFO_STATUS) &
+                       0x80) == 0) {
+                       if (i2c->msg_ptr >= i2c->msg->len) {
+                               reg_val = readl(i2c->regs + HSI2C_INT_ENABLE);
+                               reg_val &= ~(HSI2C_INT_TX_ALMOSTEMPTY_EN);
+                               writel(reg_val, i2c->regs + HSI2C_INT_ENABLE);
+                               break;
+                       }
                        byte = i2c->msg->buf[i2c->msg_ptr++];
                        writel(byte, i2c->regs + HSI2C_TX_DATA);
-                       len--;
                }
-               i2c->state = 0;
        }
 
- stop:
-       if ((i2c->trans_done && (i2c->msg->len == i2c->msg_ptr)) ||
-           (i2c->state < 0)) {
-               writel(0, i2c->regs + HSI2C_INT_ENABLE);
-               exynos5_i2c_clr_pend_irq(i2c);
-               complete(&i2c->msg_complete);
+       reg_val = readl(i2c->regs + HSI2C_INT_STATUS);
+
+       /*
+        * Checking Error State in INT_STATUS register
+        */
+       if (reg_val & HSI2C_INT_CHK_TRANS_STATE) {
+               trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
+               dev_err(i2c->dev, "HSI2C Error Interrupt "
+                               "occurred(IS:0x%08x, TR:0x%08x)\n",
+                               (unsigned int)reg_val, (unsigned int)trans_status);
+               i2c->trans_done = -ENXIO;
+               exynos5_i2c_stop(i2c);
+               goto out;
        }
+       /* Checking INT_TRANSFER_DONE */
+       if ((reg_val & HSI2C_INT_TRANSFER_DONE) &&
+               (i2c->msg_ptr >= i2c->msg->len) &&
+               !(i2c->msg->flags & I2C_M_RD))
+               exynos5_i2c_stop(i2c);
 
-       spin_unlock(&i2c->lock);
+out:
+       writel(reg_val, i2c->regs +  HSI2C_INT_STATUS);
 
        return IRQ_HANDLED;
 }
 
-/*
- * exynos5_i2c_wait_bus_idle
- *
- * Wait for the bus to go idle, indicated by the MASTER_BUSY bit being
- * cleared.
- *
- * Returns -EBUSY if the bus cannot be bought to idle
- */
-static int exynos5_i2c_wait_bus_idle(struct exynos5_i2c *i2c)
+static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c,
+                             struct i2c_msg *msgs, int stop)
 {
-       unsigned long stop_time;
-       u32 trans_status;
-
-       /* wait for 100 milli seconds for the bus to be idle */
-       stop_time = jiffies + msecs_to_jiffies(100) + 1;
-       do {
-               trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
-               if (!(trans_status & HSI2C_MASTER_BUSY))
-                       return 0;
-
-               usleep_range(50, 200);
-       } while (time_before(jiffies, stop_time));
+       unsigned long timeout;
+       unsigned long trans_status;
+       unsigned long i2c_ctl;
+       unsigned long i2c_auto_conf;
+       unsigned long i2c_timeout;
+       unsigned long i2c_addr;
+       unsigned long i2c_int_en;
+       unsigned long i2c_fifo_ctl;
+       unsigned char byte;
+       int ret = 0;
+       int operation_mode = i2c->operation_mode;
 
-       return -EBUSY;
-}
+       i2c->msg = msgs;
+       i2c->msg_ptr = 0;
+       i2c->trans_done = 0;
 
-/*
- * exynos5_i2c_message_start: Configures the bus and starts the xfer
- * i2c: struct exynos5_i2c pointer for the current bus
- * stop: Enables stop after transfer if set. Set for last transfer of
- *       in the list of messages.
- *
- * Configures the bus for read/write function
- * Sets chip address to talk to, message length to be sent.
- * Enables appropriate interrupts and sends start xfer command.
- */
-static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
-{
-       u32 i2c_ctl;
-       u32 int_en = 0;
-       u32 i2c_auto_conf = 0;
-       u32 fifo_ctl;
-       unsigned long flags;
-       unsigned short trig_lvl;
-
-       if (i2c->variant->hw == HSI2C_EXYNOS7)
-               int_en |= HSI2C_INT_I2C_TRANS;
-       else
-               int_en |= HSI2C_INT_I2C;
+       reinit_completion(&i2c->msg_complete);
 
        i2c_ctl = readl(i2c->regs + HSI2C_CTL);
-       i2c_ctl &= ~(HSI2C_TXCHON | HSI2C_RXCHON);
-       fifo_ctl = HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN;
+       i2c_auto_conf = readl(i2c->regs + HSI2C_AUTO_CONF);
+       i2c_timeout = readl(i2c->regs + HSI2C_TIMEOUT);
+       i2c_timeout &= ~HSI2C_TIMEOUT_EN;
+       writel(i2c_timeout, i2c->regs + HSI2C_TIMEOUT);
 
-       if (i2c->msg->flags & I2C_M_RD) {
+       i2c_fifo_ctl = HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN |
+               HSI2C_TXFIFO_TRIGGER_LEVEL | HSI2C_RXFIFO_TRIGGER_LEVEL;
+       writel(i2c_fifo_ctl, i2c->regs + HSI2C_FIFO_CTL);
+
+       i2c_int_en = 0;
+       if (msgs->flags & I2C_M_RD) {
+               i2c_ctl &= ~HSI2C_TXCHON;
                i2c_ctl |= HSI2C_RXCHON;
 
                i2c_auto_conf |= HSI2C_READ_WRITE;
 
-               trig_lvl = (i2c->msg->len > i2c->variant->fifo_depth) ?
-                       (i2c->variant->fifo_depth * 3 / 4) : i2c->msg->len;
-               fifo_ctl |= HSI2C_RXFIFO_TRIGGER_LEVEL(trig_lvl);
-
-               int_en |= (HSI2C_INT_RX_ALMOSTFULL_EN |
+               i2c_int_en |= (HSI2C_INT_RX_ALMOSTFULL_EN |
                        HSI2C_INT_TRAILING_EN);
        } else {
+               i2c_ctl &= ~HSI2C_RXCHON;
                i2c_ctl |= HSI2C_TXCHON;
 
-               trig_lvl = (i2c->msg->len > i2c->variant->fifo_depth) ?
-                       (i2c->variant->fifo_depth * 1 / 4) : i2c->msg->len;
-               fifo_ctl |= HSI2C_TXFIFO_TRIGGER_LEVEL(trig_lvl);
+               i2c_auto_conf &= ~HSI2C_READ_WRITE;
 
-               int_en |= HSI2C_INT_TX_ALMOSTEMPTY_EN;
+               i2c_int_en |= HSI2C_INT_TX_ALMOSTEMPTY_EN;
        }
 
-       writel(HSI2C_SLV_ADDR_MAS(i2c->msg->addr), i2c->regs + HSI2C_ADDR);
-
-       writel(fifo_ctl, i2c->regs + HSI2C_FIFO_CTL);
-       writel(i2c_ctl, i2c->regs + HSI2C_CTL);
-
-       /*
-        * Enable interrupts before starting the transfer so that we don't
-        * miss any INT_I2C interrupts.
-        */
-       spin_lock_irqsave(&i2c->lock, flags);
-       writel(int_en, i2c->regs + HSI2C_INT_ENABLE);
+       if (operation_mode == HSI2C_INTERRUPT)
+               exynos5_i2c_clr_pend_irq(i2c);
 
-       if (stop == 1)
+       if ((stop == 1) || (i2c->stop_after_trans == 1))
                i2c_auto_conf |= HSI2C_STOP_AFTER_TRANS;
-       i2c_auto_conf |= i2c->msg->len;
-       i2c_auto_conf |= HSI2C_MASTER_RUN;
-       writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF);
-       spin_unlock_irqrestore(&i2c->lock, flags);
-}
+       else
+               i2c_auto_conf &= ~HSI2C_STOP_AFTER_TRANS;
 
-static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c,
-                             struct i2c_msg *msgs, int stop)
-{
-       unsigned long timeout;
-       int ret;
+       i2c_addr = readl(i2c->regs + HSI2C_ADDR);
+       i2c_addr &= ~(0x3ff << 10);
+       i2c_addr &= ~(0x3ff << 0);
+       i2c_addr &= ~(0xff << 24);
+       i2c_addr |= ((msgs->addr & 0x7f) << 10);
+       writel(i2c_addr, i2c->regs + HSI2C_ADDR);
 
-       i2c->msg = msgs;
-       i2c->msg_ptr = 0;
-       i2c->trans_done = 0;
+       writel(i2c_ctl, i2c->regs + HSI2C_CTL);
 
-       reinit_completion(&i2c->msg_complete);
+       i2c_auto_conf &= ~(0xffff);
+       i2c_auto_conf |= i2c->msg->len;
+       writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF);
 
-       exynos5_i2c_message_start(i2c, stop);
+       i2c_auto_conf = readl(i2c->regs + HSI2C_AUTO_CONF);
+       i2c_auto_conf |= HSI2C_MASTER_RUN;
+       writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF);
 
-       timeout = wait_for_completion_timeout(&i2c->msg_complete,
-                                             EXYNOS5_I2C_TIMEOUT);
-       if (timeout == 0)
-               ret = -ETIMEDOUT;
-       else
-               ret = i2c->state;
+       if (operation_mode == HSI2C_INTERRUPT) {
+               i2c_int_en |= HSI2C_INT_CHK_TRANS_STATE | HSI2C_INT_TRANSFER_DONE;
+               writel(i2c_int_en, i2c->regs + HSI2C_INT_ENABLE);
+               enable_irq(i2c->irq);
+       } else {
+               writel(0x0, i2c->regs + HSI2C_INT_ENABLE);
+       }
 
-       /*
-        * If this is the last message to be transfered (stop == 1)
-        * Then check if the bus can be brought back to idle.
-        */
-       if (ret == 0 && stop)
-               ret = exynos5_i2c_wait_bus_idle(i2c);
+       ret = -EAGAIN;
+       if (msgs->flags & I2C_M_RD) {
+               if (operation_mode == HSI2C_POLLING) {
+                       timeout = jiffies + EXYNOS5_I2C_TIMEOUT;
+                       while (time_before(jiffies, timeout)){
+                               if ((readl(i2c->regs + HSI2C_FIFO_STATUS) &
+                                       0x1000000) == 0) {
+                                       byte = (unsigned char)readl
+                                               (i2c->regs + HSI2C_RX_DATA);
+                                       i2c->msg->buf[i2c->msg_ptr++]
+                                               = byte;
+                               }
+
+                               if (i2c->msg_ptr >= i2c->msg->len) {
+                                       ret = 0;
+                                       break;
+                               }
+                       }
+
+                       if (ret == -EAGAIN) {
+                               dump_i2c_register(i2c);
+                               exynos5_i2c_reset(i2c);
+                               dev_warn(i2c->dev, "rx timeout\n");
+                               return ret;
+                       }
+               } else {
+                       timeout = wait_for_completion_timeout
+                               (&i2c->msg_complete, EXYNOS5_I2C_TIMEOUT);
+
+                       ret = 0;
+                       if (i2c->scl_clk_stretch) {
+                               unsigned long timeout = jiffies + msecs_to_jiffies(100);
+
+                               do {
+                                       trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
+                                       if ((!(trans_status & HSI2C_MAST_ST_MASK)) ||
+                                          ((stop == 0) && (trans_status & HSI2C_MASTER_BUSY))){
+                                               timeout = 0;
+                                               break;
+                                       }
+                               } while(time_before(jiffies, timeout));
+
+                               if (timeout)
+                                       dev_err(i2c->dev, "SDA check timeout!!! = 0x%8lx\n",trans_status);
+                       }
+                       disable_irq(i2c->irq);
+
+                       if (i2c->trans_done < 0) {
+                               dev_err(i2c->dev, "ack was not received at read\n");
+                               ret = i2c->trans_done;
+                               exynos5_i2c_reset(i2c);
+                       }
+
+                       if (timeout == 0) {
+                               dump_i2c_register(i2c);
+                               exynos5_i2c_reset(i2c);
+                               dev_warn(i2c->dev, "rx timeout\n");
+                               ret = -EAGAIN;
+                               return ret;
+                       }
+               }
+       } else {
+               if (operation_mode == HSI2C_POLLING) {
+                       timeout = jiffies + EXYNOS5_I2C_TIMEOUT;
+                       while (time_before(jiffies, timeout) &&
+                               (i2c->msg_ptr < i2c->msg->len)) {
+                               if ((readl(i2c->regs + HSI2C_FIFO_STATUS)
+                                       & HSI2C_TX_FIFO_LVL_MASK) < EXYNOS5_FIFO_SIZE) {
+                                       byte = i2c->msg->buf
+                                               [i2c->msg_ptr++];
+                                       writel(byte,
+                                               i2c->regs + HSI2C_TX_DATA);
+                               }
+                       }
+
+               } else {
+                       timeout = wait_for_completion_timeout
+                               (&i2c->msg_complete, EXYNOS5_I2C_TIMEOUT);
+                       disable_irq(i2c->irq);
+
+                       if (timeout == 0) {
+                               dump_i2c_register(i2c);
+                               exynos5_i2c_reset(i2c);
+                               dev_warn(i2c->dev, "tx timeout\n");
+                               return ret;
+                       }
+
+                       timeout = jiffies + timeout;
+               }
 
-       if (ret < 0) {
-               exynos5_i2c_reset(i2c);
-               if (ret == -ETIMEDOUT)
-                       dev_warn(i2c->dev, "%s timeout\n",
-                                (msgs->flags & I2C_M_RD) ? "rx" : "tx");
+               if (operation_mode == HSI2C_POLLING) {
+                       while (time_before(jiffies, timeout)) {
+                               trans_status = readl(i2c->regs + HSI2C_INT_STATUS);
+                               writel(trans_status, i2c->regs +  HSI2C_INT_STATUS);
+                               if (trans_status & HSI2C_INT_TRANSFER_DONE) {
+                                       ret = 0;
+                                       break;
+                               }
+                               udelay(100);
+                       }
+                       if (ret == -EAGAIN) {
+                               dump_i2c_register(i2c);
+                               exynos5_i2c_reset(i2c);
+                               dev_warn(i2c->dev, "tx timeout\n");
+                               return ret;
+                       }
+               } else {
+                       if (i2c->trans_done < 0) {
+                               dev_err(i2c->dev, "ack was not received at write\n");
+                               ret = i2c->trans_done;
+                               exynos5_i2c_reset(i2c);
+                       } else {
+                               if (i2c->scl_clk_stretch) {
+                                       unsigned long timeout = jiffies + msecs_to_jiffies(100);
+
+                                       do {
+                                               trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
+                                               if ((!(trans_status & HSI2C_MAST_ST_MASK)) ||
+                                                  ((stop == 0) && (trans_status & HSI2C_MASTER_BUSY))){
+                                                       timeout = 0;
+                                                       break;
+                                               }
+                                       } while(time_before(jiffies, timeout));
+
+                                       if (timeout)
+                                               dev_err(i2c->dev, "SDA check timeout!!! = 0x%8lx\n",trans_status);
+                               }
+
+                               ret = 0;
+                       }
+               }
        }
 
-       /* Return the state as in interrupt routine */
        return ret;
 }
 
 static int exynos5_i2c_xfer(struct i2c_adapter *adap,
                        struct i2c_msg *msgs, int num)
 {
-       struct exynos5_i2c *i2c = adap->algo_data;
-       int i = 0, ret = 0, stop = 0;
+       struct exynos5_i2c *i2c = (struct exynos5_i2c *)adap->algo_data;
+       struct i2c_msg *msgs_ptr = msgs;
+       int retry, i = 0;
+       int ret = 0;
+       int stop = 0;
+
+#ifdef CONFIG_PM_RUNTIME
+       int clk_ret = 0;
+#endif
 
        if (i2c->suspended) {
                dev_err(i2c->dev, "HS-I2C is not initialized.\n");
                return -EIO;
        }
 
-       ret = clk_enable(i2c->clk);
-       if (ret)
-               return ret;
+#ifdef CONFIG_PM_RUNTIME
+       clk_ret = pm_runtime_get_sync(i2c->dev);
+       if (clk_ret < 0) {
+               exynos_update_ip_idle_status(i2c->idle_ip_index, 0);
+               clk_prepare_enable(i2c->clk);
+       }
+#else
+       exynos_update_ip_idle_status(i2c->idle_ip_index, 0);
+       clk_prepare_enable(i2c->clk);
+#endif
+       if (i2c->need_hw_init)
+               exynos5_i2c_reset(i2c);
+
+       if (unlikely(!(readl(i2c->regs + HSI2C_CONF)
+                       & HSI2C_AUTO_MODE))) {
+               dev_err(i2c->dev, "HSI2C should be reconfigured\n");
+               exynos5_hsi2c_clock_setup(i2c);
+               exynos5_i2c_init(i2c);
+       }
+
+       for (retry = 0; retry < adap->retries; retry++) {
+               for (i = 0; i < num; i++) {
+                       stop = (i == num - 1);
+
+                       if (i2c->transfer_delay)
+                               udelay(i2c->transfer_delay);
+
+                       ret = exynos5_i2c_xfer_msg(i2c, msgs_ptr, stop);
+
+                       msgs_ptr++;
+
+                       if (ret == -EAGAIN) {
+                               msgs_ptr = msgs;
+                               break;
+                       } else if (ret < 0) {
+                               goto out;
+                       }
+               }
 
-       for (i = 0; i < num; i++, msgs++) {
-               stop = (i == num - 1);
+               if ((i == num) && (ret != -EAGAIN))
+                       break;
 
-               ret = exynos5_i2c_xfer_msg(i2c, msgs, stop);
+               dev_dbg(i2c->dev, "retrying transfer (%d)\n", retry);
 
-               if (ret < 0)
-                       goto out;
+               udelay(100);
        }
 
        if (i == num) {
                ret = num;
        } else {
-               /* Only one message, cannot access the device */
-               if (i == 1)
-                       ret = -EREMOTEIO;
-               else
-                       ret = i;
-
+               ret = -EREMOTEIO;
                dev_warn(i2c->dev, "xfer message failed\n");
        }
 
  out:
-       clk_disable(i2c->clk);
+#ifdef CONFIG_PM_RUNTIME
+       if (clk_ret < 0) {
+               clk_disable_unprepare(i2c->clk);
+               exynos_update_ip_idle_status(i2c->idle_ip_index, 1);
+       } else {
+               pm_runtime_mark_last_busy(i2c->dev);
+               pm_runtime_put_autosuspend(i2c->dev);
+       }
+#else
+       clk_disable_unprepare(i2c->clk);
+       exynos_update_ip_idle_status(i2c->idle_ip_index, 1);
+#endif
+
        return ret;
 }
 
@@ -703,6 +912,27 @@ static const struct i2c_algorithm exynos5_i2c_algorithm = {
        .functionality          = exynos5_i2c_func,
 };
 
+#ifdef CONFIG_CPU_IDLE
+static int exynos5_i2c_notifier(struct notifier_block *self,
+                               unsigned long cmd, void *v)
+{
+       struct exynos5_i2c *i2c;
+
+       switch (cmd) {
+       case LPA_EXIT:
+               list_for_each_entry(i2c, &drvdata_list, node)
+                       i2c->need_hw_init = 1;
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block exynos5_i2c_notifier_block = {
+       .notifier_call = exynos5_i2c_notifier,
+};
+#endif /* CONFIG_CPU_IDLE */
+
 static int exynos5_i2c_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
@@ -710,32 +940,82 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
        struct resource *mem;
        int ret;
 
+       if (!np) {
+               dev_err(&pdev->dev, "no device node\n");
+               return -ENOENT;
+       }
+
        i2c = devm_kzalloc(&pdev->dev, sizeof(struct exynos5_i2c), GFP_KERNEL);
-       if (!i2c)
+       if (!i2c) {
+               dev_err(&pdev->dev, "no memory for state\n");
                return -ENOMEM;
+       }
+
+       /* Mode of operation High/Fast Speed mode */
+       if (of_get_property(np, "samsung,hs-mode", NULL)) {
+               i2c->speed_mode = HSI2C_HIGH_SPD;
+               i2c->fs_clock = HSI2C_FS_TX_CLOCK;
+               if (of_property_read_u32(np, "clock-frequency", &i2c->hs_clock))
+                       i2c->hs_clock = HSI2C_HS_TX_CLOCK;
+       } else {
+               i2c->speed_mode = HSI2C_FAST_SPD;
+               if (of_property_read_u32(np, "clock-frequency", &i2c->fs_clock))
+                       i2c->fs_clock = HSI2C_FS_TX_CLOCK;
+       }
 
-       if (of_property_read_u32(np, "clock-frequency", &i2c->op_clock))
-               i2c->op_clock = HSI2C_FS_TX_CLOCK;
+       /* Mode of operation Polling/Interrupt mode */
+       if (of_get_property(np, "samsung,polling-mode", NULL)) {
+               i2c->operation_mode = HSI2C_POLLING;
+       } else {
+               i2c->operation_mode = HSI2C_INTERRUPT;
+       }
+
+       if (of_get_property(np, "samsung,scl-clk-stretching", NULL))
+               i2c->scl_clk_stretch = 1;
+       else
+               i2c->scl_clk_stretch = 0;
+
+       ret = of_property_read_u32(np, "samsung,transfer_delay", &i2c->transfer_delay);
+       if (!ret)
+               dev_warn(&pdev->dev, "Transfer delay is not needed.\n");
+
+       if (of_get_property(np, "samsung,stop-after-trans", NULL))
+               i2c->stop_after_trans = 1;
+       else
+               i2c->stop_after_trans = 0;
+
+       i2c->idle_ip_index = exynos_get_idle_ip_index(dev_name(&pdev->dev));
 
        strlcpy(i2c->adap.name, "exynos5-i2c", sizeof(i2c->adap.name));
        i2c->adap.owner   = THIS_MODULE;
        i2c->adap.algo    = &exynos5_i2c_algorithm;
-       i2c->adap.retries = 3;
+       i2c->adap.retries = 2;
+       i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
 
        i2c->dev = &pdev->dev;
-       i2c->clk = devm_clk_get(&pdev->dev, "hsi2c");
+       i2c->clk = devm_clk_get(&pdev->dev, "gate_hsi2c");
        if (IS_ERR(i2c->clk)) {
                dev_err(&pdev->dev, "cannot get clock\n");
                return -ENOENT;
        }
 
-       ret = clk_prepare_enable(i2c->clk);
-       if (ret)
-               return ret;
+       i2c->rate_clk = devm_clk_get(&pdev->dev, "rate_hsi2c");
+       if (IS_ERR(i2c->rate_clk)) {
+               dev_err(&pdev->dev, "cannot get rate clock\n");
+               return -ENOENT;
+       }
+
+#ifdef CONFIG_PM_RUNTIME
+       pm_runtime_use_autosuspend(&pdev->dev);
+       pm_runtime_set_autosuspend_delay(&pdev->dev,
+                                       EXYNOS5_HSI2C_RUNTIME_PM_DELAY);
+       pm_runtime_enable(&pdev->dev);
+#endif
 
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
-       if (IS_ERR(i2c->regs)) {
+       if (i2c->regs == NULL) {
+               dev_err(&pdev->dev, "cannot map HS-I2C IO\n");
                ret = PTR_ERR(i2c->regs);
                goto err_clk;
        }
@@ -744,48 +1024,71 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
        i2c->adap.algo_data = i2c;
        i2c->adap.dev.parent = &pdev->dev;
 
-       /* Clear pending interrupts from u-boot or misc causes */
-       exynos5_i2c_clr_pend_irq(i2c);
-
-       spin_lock_init(&i2c->lock);
        init_completion(&i2c->msg_complete);
 
-       i2c->irq = ret = platform_get_irq(pdev, 0);
-       if (ret <= 0) {
-               dev_err(&pdev->dev, "cannot find HS-I2C IRQ\n");
-               ret = -EINVAL;
-               goto err_clk;
-       }
+       if (i2c->operation_mode == HSI2C_INTERRUPT) {
+               i2c->irq = ret = irq_of_parse_and_map(np, 0);
+               if (ret <= 0) {
+                       dev_err(&pdev->dev, "cannot find HS-I2C IRQ\n");
+                       ret = -EINVAL;
+                       goto err_clk;
+               }
 
-       ret = devm_request_irq(&pdev->dev, i2c->irq, exynos5_i2c_irq,
-                               IRQF_NO_SUSPEND | IRQF_ONESHOT,
-                               dev_name(&pdev->dev), i2c);
+               ret = devm_request_irq(&pdev->dev, i2c->irq,
+                                       exynos5_i2c_irq, 0, dev_name(&pdev->dev), i2c);
+               disable_irq(i2c->irq);
 
-       if (ret != 0) {
-               dev_err(&pdev->dev, "cannot request HS-I2C IRQ %d\n", i2c->irq);
-               goto err_clk;
+               if (ret != 0) {
+                       dev_err(&pdev->dev, "cannot request HS-I2C IRQ %d\n",
+                                       i2c->irq);
+                       goto err_clk;
+               }
        }
+       platform_set_drvdata(pdev, i2c);
+#ifdef CONFIG_PM_RUNTIME
+       pm_runtime_get_sync(&pdev->dev);
+#else
+       exynos_update_ip_idle_status(i2c->idle_ip_index, 0);
+       clk_prepare_enable(i2c->clk);
+#endif
 
-       i2c->variant = of_device_get_match_data(&pdev->dev);
+       /* Clear pending interrupts from u-boot or misc causes */
+       exynos5_i2c_clr_pend_irq(i2c);
+       /* Reset i2c SFR from u-boot or misc causes */
+       exynos5_i2c_reset(i2c);
 
        ret = exynos5_hsi2c_clock_setup(i2c);
        if (ret)
                goto err_clk;
 
-       exynos5_i2c_reset(i2c);
+       i2c->bus_id = of_alias_get_id(i2c->adap.dev.of_node, "hsi2c");
+
+       exynos5_i2c_init(i2c);
 
-       ret = i2c_add_adapter(&i2c->adap);
-       if (ret < 0)
+       i2c->adap.nr = -1;
+       ret = i2c_add_numbered_adapter(&i2c->adap);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to add bus to i2c core\n");
                goto err_clk;
+       }
 
-       platform_set_drvdata(pdev, i2c);
+#ifdef CONFIG_PM_RUNTIME
+       pm_runtime_mark_last_busy(&pdev->dev);
+       pm_runtime_put_autosuspend(&pdev->dev);
+#else
+       clk_disable_unprepare(i2c->clk);
+       exynos_update_ip_idle_status(i2c->idle_ip_index, 1);
+#endif
 
-       clk_disable(i2c->clk);
+#if defined(CONFIG_CPU_IDLE)
+       list_add_tail(&i2c->node, &drvdata_list);
+#endif
 
        return 0;
 
  err_clk:
        clk_disable_unprepare(i2c->clk);
+       exynos_update_ip_idle_status(i2c->idle_ip_index, 1);
        return ret;
 }
 
@@ -795,49 +1098,79 @@ static int exynos5_i2c_remove(struct platform_device *pdev)
 
        i2c_del_adapter(&i2c->adap);
 
-       clk_unprepare(i2c->clk);
-
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
 static int exynos5_i2c_suspend_noirq(struct device *dev)
 {
        struct exynos5_i2c *i2c = dev_get_drvdata(dev);
 
+       i2c_lock_adapter(&i2c->adap);
        i2c->suspended = 1;
-
-       clk_unprepare(i2c->clk);
+       i2c_unlock_adapter(&i2c->adap);
 
        return 0;
 }
 
 static int exynos5_i2c_resume_noirq(struct device *dev)
 {
-       struct exynos5_i2c *i2c = dev_get_drvdata(dev);
-       int ret = 0;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
 
-       ret = clk_prepare_enable(i2c->clk);
-       if (ret)
-               return ret;
+       i2c_lock_adapter(&i2c->adap);
+       exynos_update_ip_idle_status(i2c->idle_ip_index, 0);
+       clk_prepare_enable(i2c->clk);
+       exynos5_i2c_reset(i2c);
+       clk_disable_unprepare(i2c->clk);
+       exynos_update_ip_idle_status(i2c->idle_ip_index, 1);
+       i2c->suspended = 0;
+       i2c_unlock_adapter(&i2c->adap);
 
-       ret = exynos5_hsi2c_clock_setup(i2c);
-       if (ret) {
-               clk_disable_unprepare(i2c->clk);
-               return ret;
-       }
+       return 0;
+}
 
-       exynos5_i2c_init(i2c);
-       clk_disable(i2c->clk);
-       i2c->suspended = 0;
+#else
+static int exynos5_i2c_suspend_noirq(struct device *dev)
+{
+       return 0;
+}
 
+static int exynos5_i2c_resume_noirq(struct device *dev)
+{
        return 0;
 }
 #endif
 
-static const struct dev_pm_ops exynos5_i2c_dev_pm_ops = {
+#ifdef CONFIG_PM_RUNTIME
+static int exynos5_i2c_runtime_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+
+       clk_disable_unprepare(i2c->clk);
+       exynos_update_ip_idle_status(i2c->idle_ip_index, 1);
+
+       return 0;
+}
+
+static int exynos5_i2c_runtime_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+
+       exynos_update_ip_idle_status(i2c->idle_ip_index, 0);
+       clk_prepare_enable(i2c->clk);
+
+       return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+static const struct dev_pm_ops exynos5_i2c_pm = {
        SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos5_i2c_suspend_noirq,
                                      exynos5_i2c_resume_noirq)
+       SET_RUNTIME_PM_OPS(exynos5_i2c_runtime_suspend,
+                          exynos5_i2c_runtime_resume, NULL)
 };
 
 static struct platform_driver exynos5_i2c_driver = {
@@ -845,12 +1178,25 @@ static struct platform_driver exynos5_i2c_driver = {
        .remove         = exynos5_i2c_remove,
        .driver         = {
                .name   = "exynos5-hsi2c",
-               .pm     = &exynos5_i2c_dev_pm_ops,
+               .pm     = &exynos5_i2c_pm,
                .of_match_table = exynos5_i2c_match,
        },
 };
 
-module_platform_driver(exynos5_i2c_driver);
+static int __init i2c_adap_exynos5_init(void)
+{
+#ifdef CONFIG_CPU_IDLE
+       exynos_pm_register_notifier(&exynos5_i2c_notifier_block);
+#endif
+       return platform_driver_register(&exynos5_i2c_driver);
+}
+subsys_initcall(i2c_adap_exynos5_init);
+
+static void __exit i2c_adap_exynos5_exit(void)
+{
+       platform_driver_unregister(&exynos5_i2c_driver);
+}
+module_exit(i2c_adap_exynos5_exit);
 
 MODULE_DESCRIPTION("Exynos5 HS-I2C Bus driver");
 MODULE_AUTHOR("Naveen Krishna Chatradhi, <ch.naveen@samsung.com>");