i2c: s3c2410: Adds support fimc-is-i2c
authorHyeonkook Kim <hk619.kim@samsung.com>
Thu, 14 Nov 2013 10:25:19 +0000 (19:25 +0900)
committermyung-su.cha <myung-su.cha@samsung.com>
Thu, 10 May 2018 04:40:40 +0000 (13:40 +0900)
This patch supports fimc-is-i2c using device tree
or platform device variant.
If the QUIRK_FIMC_I2C is added, i2c enables bus hold interrupt
and adds clock rate at s3c24xx_i2c_xfer() for fimc-is-i2c.

Signed-off-by: Hyeonkook Kim <hk619.kim@samsung.com>
Conflicts:

drivers/i2c/busses/i2c-s3c2410.c

Change-Id: I8106ba452385c22e5fe2100ae5574b41f9b25769

drivers/i2c/busses/i2c-s3c2410.c

index f426dbbf69d05ecc3ca41ac1308bf044026403dd..8a3706a889903bcaa0d732172f463debb7e297fc 100644 (file)
 #define S3C2410_IICADD                 0x08
 #define S3C2410_IICDS                  0x0C
 #define S3C2440_IICLC                  0x10
+#define S3C2440_IICINT                 0x20
+#define S3C2440_IICNCLK_DIV2           0x28
 
+#define S3C2410_IICCON_BUSHOLD_IRQEN   (1 << 8)
 #define S3C2410_IICCON_ACKEN           (1 << 7)
 #define S3C2410_IICCON_TXDIV_16                (0 << 6)
 #define S3C2410_IICCON_TXDIV_512       (1 << 6)
 
 #define S3C2410_IICLC_FILTER_ON                (1 << 2)
 
+#define S3C2440_IICINT_BUSHOLD_CLEAR   (1 << 8)
+
 /* Treat S3C2410 as baseline hardware, anything else is supported via quirks */
 #define QUIRK_S3C2440          (1 << 0)
 #define QUIRK_HDMIPHY          (1 << 1)
 #define QUIRK_NO_GPIO          (1 << 2)
 #define QUIRK_POLL             (1 << 3)
+#define QUIRK_FIMC_I2C         (1 << 3)
 
 /* Max time to wait for bus to become idle after a xfer (in us) */
 #define S3C2410_IDLE_TIMEOUT   5000
@@ -136,6 +142,9 @@ static const struct platform_device_id s3c24xx_driver_ids[] = {
        }, {
                .name           = "s3c2440-hdmiphy-i2c",
                .driver_data    = QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO,
+       }, {
+               .name           = "exynos5430-fimc-i2c",
+               .driver_data    = QUIRK_S3C2440 | QUIRK_FIMC_I2C,
        }, { },
 };
 MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
@@ -148,6 +157,8 @@ static const struct of_device_id s3c24xx_i2c_match[] = {
        { .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 },
        { .compatible = "samsung,s3c2440-hdmiphy-i2c",
          .data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) },
+       { .compatible = "samsung,exynos5430-fimc-i2c",
+         .data = (void *)(QUIRK_S3C2440 | QUIRK_FIMC_I2C) },
        { .compatible = "samsung,exynos5440-i2c",
          .data = (void *)(QUIRK_S3C2440 | QUIRK_NO_GPIO) },
        { .compatible = "samsung,exynos5-sata-phy-i2c",
@@ -157,6 +168,8 @@ static const struct of_device_id s3c24xx_i2c_match[] = {
 MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
 #endif
 
+static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got);
+
 /* s3c24xx_get_device_quirks
  *
  * Get controller type either from device tree or platform device variant.
@@ -216,16 +229,30 @@ static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c)
 {
        unsigned long tmp;
 
-       tmp = readl(i2c->regs + S3C2410_IICCON);
-       writel(tmp & ~S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);
+       if (i2c->quirks & QUIRK_FIMC_I2C) {
+               /* disable bus hold interrupt */
+               tmp = readl(i2c->regs + S3C2410_IICCON);
+               tmp &= ~S3C2410_IICCON_BUSHOLD_IRQEN;
+               writel(tmp, i2c->regs + S3C2410_IICCON);
+       } else {
+               tmp = readl(i2c->regs + S3C2410_IICCON);
+               writel(tmp & ~S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);
+       }
 }
 
 static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c)
 {
        unsigned long tmp;
 
-       tmp = readl(i2c->regs + S3C2410_IICCON);
-       writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);
+       if (i2c->quirks & QUIRK_FIMC_I2C) {
+               /* enable bus hold interrupt */
+               tmp = readl(i2c->regs + S3C2410_IICCON);
+               tmp |= S3C2410_IICCON_BUSHOLD_IRQEN;
+               writel(tmp, i2c->regs + S3C2410_IICCON);
+       } else {
+               tmp = readl(i2c->regs + S3C2410_IICCON);
+               writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);
+       }
 }
 
 static bool is_ack(struct s3c24xx_i2c *i2c)
@@ -555,9 +582,21 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
        /* acknowlegde the IRQ and get back on with the work */
 
  out_ack:
-       tmp = readl(i2c->regs + S3C2410_IICCON);
-       tmp &= ~S3C2410_IICCON_IRQPEND;
-       writel(tmp, i2c->regs + S3C2410_IICCON);
+       if (i2c->quirks & QUIRK_FIMC_I2C) {
+               /* clear bus hold status flag */
+               tmp = readl(i2c->regs + S3C2440_IICINT);
+               tmp |= S3C2440_IICINT_BUSHOLD_CLEAR;
+               writel(tmp, i2c->regs + S3C2440_IICINT);
+
+               /* release the i2c bus */
+               tmp = readl(i2c->regs + S3C2410_IICCON);
+               tmp |= S3C2410_IICCON_IRQPEND;
+               writel(tmp, i2c->regs + S3C2410_IICCON);
+       } else {
+               tmp = readl(i2c->regs + S3C2410_IICCON);
+               tmp &= ~S3C2410_IICCON_IRQPEND;
+               writel(tmp, i2c->regs + S3C2410_IICCON);
+       }
  out:
        return ret;
 }
@@ -582,10 +621,21 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
 
        if (i2c->state == STATE_IDLE) {
                dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");
-
-               tmp = readl(i2c->regs + S3C2410_IICCON);
-               tmp &= ~S3C2410_IICCON_IRQPEND;
-               writel(tmp, i2c->regs +  S3C2410_IICCON);
+               if (i2c->quirks & QUIRK_FIMC_I2C) {
+                       /* clear bus hold status flag */
+                       tmp = readl(i2c->regs + S3C2440_IICINT);
+                       tmp |= S3C2440_IICINT_BUSHOLD_CLEAR;
+                       writel(tmp, i2c->regs + S3C2440_IICINT);
+
+                       /* release the i2c bus */
+                       tmp = readl(i2c->regs + S3C2410_IICCON);
+                       tmp |= S3C2410_IICCON_IRQPEND;
+                       writel(tmp, i2c->regs + S3C2410_IICCON);
+               } else {
+                       tmp = readl(i2c->regs + S3C2410_IICCON);
+                       tmp &= ~S3C2410_IICCON_IRQPEND;
+                       writel(tmp, i2c->regs +  S3C2410_IICCON);
+               }
                goto out;
        }
 
@@ -777,12 +827,21 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
        struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
        int retry;
        int ret;
+       unsigned int freq;
 
        pm_runtime_get_sync(&adap->dev);
        ret = clk_enable(i2c->clk);
        if (ret)
                return ret;
 
+       if (i2c->quirks & QUIRK_FIMC_I2C) {
+               ret = s3c24xx_i2c_clockrate(i2c, &freq);
+               if (ret < 0) {
+                       dev_err(i2c->dev, "cannot find frequency\n");
+                       return ret;
+               }
+       }
+
        for (retry = 0; retry < adap->retries; retry++) {
 
                ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
@@ -822,24 +881,33 @@ static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
  * return the divisor settings for a given frequency
 */
 
-static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
-                                  unsigned int *div1, unsigned int *divs)
+static int s3c24xx_i2c_calcdivisor(struct s3c24xx_i2c *i2c,
+                       unsigned long clkin, unsigned int wanted,
+                       unsigned int *div1, unsigned int *divs)
 {
        unsigned int calc_divs = clkin / wanted;
        unsigned int calc_div1;
+       unsigned int clk_prescaler;
+
+       if (i2c->quirks & QUIRK_FIMC_I2C) {
+               /* Input NCLK is used directly in i2c */
+               writel(0, i2c->regs + S3C2440_IICNCLK_DIV2);
+               clk_prescaler = 32;
+       } else
+               clk_prescaler = 16;
 
        if (calc_divs > (16*16))
                calc_div1 = 512;
        else
-               calc_div1 = 16;
+               calc_div1 = clk_prescaler;
 
        calc_divs += calc_div1-1;
        calc_divs /= calc_div1;
 
        if (calc_divs == 0)
                calc_divs = 1;
-       if (calc_divs > 17)
-               calc_divs = 17;
+       if (calc_divs > (clk_prescaler + 1))
+               calc_divs = clk_prescaler + 1;
 
        *divs = calc_divs;
        *div1 = calc_div1;
@@ -872,7 +940,8 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
 
        target_frequency /= 1000; /* Target frequency now in KHz */
 
-       freq = s3c24xx_i2c_calcdivisor(clkin, target_frequency, &div1, &divs);
+       freq = s3c24xx_i2c_calcdivisor(i2c, clkin,
+                       target_frequency, &div1, &divs);
 
        if (freq > target_frequency) {
                dev_err(i2c->dev,