From 0975de8532e058cb314dbe1ef4722006c7240b1d Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Thu, 30 Jul 2015 12:35:18 +0200 Subject: [PATCH] i2c: i2c-s3c2410: Add quirks for Exynos5433 ISP I2C This is required to ensure proper operation of the ISP I2C controller from the Linux driver and the FIMC-IS firmware level. Change-Id: If845cf516fb8aa29a0e6b1b1533208b63fecdc69 Signed-off-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki --- drivers/i2c/busses/Kconfig | 2 +- drivers/i2c/busses/i2c-s3c2410.c | 107 ++++++++++++++++++++++++++++++++++----- 2 files changed, 94 insertions(+), 15 deletions(-) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 45a3f3ca..1248827 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -872,7 +872,7 @@ config HAVE_S3C2410_I2C config I2C_S3C2410 tristate "S3C2410 I2C Driver" - depends on HAVE_S3C2410_I2C + depends on HAVE_S3C2410_I2C || ARCH_EXYNOS help Say Y here to include support for I2C controller in the Samsung SoCs. diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 5d97510..e932b9f 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -49,14 +49,23 @@ #define S3C2410_IICADD 0x08 #define S3C2410_IICDS 0x0C #define S3C2440_IICLC 0x10 +#define S3C2440_CLK_BYPASS 0x14 +#define S3C2440_IICINT 0x20 +#define S3C2440_IICNCLK_DIV2 0x28 #define S3C2410_IICCON_ACKEN (1 << 7) #define S3C2410_IICCON_TXDIV_16 (0 << 6) #define S3C2410_IICCON_TXDIV_512 (1 << 6) #define S3C2410_IICCON_IRQEN (1 << 5) #define S3C2410_IICCON_IRQPEND (1 << 4) +#define S3C2410_IICCON_BUS_RELEASE (1 << 4) +#define S3C2410_IICCON_IRQEN (1 << 5) #define S3C2410_IICCON_SCALE(x) ((x) & 0xf) #define S3C2410_IICCON_SCALEMASK (0xf) +#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_IICSTAT_MASTER_RX (2 << 6) #define S3C2410_IICSTAT_MASTER_TX (3 << 6) @@ -80,11 +89,14 @@ #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_ISP_I2C (1 << 4) /* Max time to wait for bus to become idle after a xfer (in us) */ #define S3C2410_IDLE_TIMEOUT 5000 @@ -130,6 +142,8 @@ struct s3c24xx_i2c { #endif struct regmap *sysreg; unsigned int sys_i2c_cfg; + + u32 reg_iiccon; }; static const struct platform_device_id s3c24xx_driver_ids[] = { @@ -158,6 +172,8 @@ static const struct of_device_id s3c24xx_i2c_match[] = { .data = (void *)(QUIRK_S3C2440 | QUIRK_NO_GPIO) }, { .compatible = "samsung,exynos5-sata-phy-i2c", .data = (void *)(QUIRK_S3C2440 | QUIRK_POLL | QUIRK_NO_GPIO) }, + { .compatible = "samsung,exynos5433-isp-i2c", + .data = (void *)(QUIRK_S3C2440 | QUIRK_ISP_I2C) }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); @@ -210,6 +226,8 @@ static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c) unsigned long tmp; tmp = readl(i2c->regs + S3C2410_IICCON); + if (i2c->quirks & QUIRK_ISP_I2C) + tmp &= ~S3C2410_IICCON_BUS_RELEASE; writel(tmp | S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON); } @@ -219,7 +237,13 @@ 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_ISP_I2C) + tmp &= ~S3C2410_IICCON_BUSHOLD_IRQEN; + else + tmp &= ~S3C2410_IICCON_IRQEN; + + writel(tmp, i2c->regs + S3C2410_IICCON); } static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c) @@ -227,7 +251,13 @@ 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_ISP_I2C) + tmp |= S3C2410_IICCON_BUSHOLD_IRQEN; + else + tmp |= S3C2410_IICCON_IRQEN; + + writel(tmp, i2c->regs + S3C2410_IICCON); } static bool is_ack(struct s3c24xx_i2c *i2c) @@ -556,9 +586,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_ISP_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,9 +624,22 @@ 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_ISP_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; } @@ -775,6 +830,11 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, if (ret) return ret; + if (i2c->quirks & QUIRK_ISP_I2C) { + writel(0, i2c->regs + S3C2410_IICSTAT); + writel(i2c->reg_iiccon, i2c->regs + S3C2410_IICCON); + } + for (retry = 0; retry < adap->retries; retry++) { ret = s3c24xx_i2c_doxfer(i2c, msgs, num); @@ -809,24 +869,35 @@ 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, + +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_ISP_I2C) { + /* Input NCLK is used directly in i2c */ + writel(0, i2c->regs + S3C2440_IICNCLK_DIV2); + writeb(1, i2c->regs + S3C2440_CLK_BYPASS); + 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; @@ -842,12 +913,17 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted, static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) { struct s3c2410_platform_i2c *pdata = i2c->pdata; - unsigned long clkin = clk_get_rate(i2c->clk); + unsigned long clkin; unsigned int divs, div1; unsigned long target_frequency; u32 iiccon; int freq; + if (i2c->quirks & QUIRK_ISP_I2C) + clkin = 24000000UL; /* NCLK is sourced from OSCCLK (24 MHz) */ + else + clkin = clk_get_rate(i2c->clk); + i2c->clkrate = clkin; clkin /= 1000; /* clkin now in KHz */ @@ -857,7 +933,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, @@ -880,6 +957,8 @@ static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got) writel(iiccon, i2c->regs + S3C2410_IICCON); + i2c->reg_iiccon = iiccon; + if (i2c->quirks & QUIRK_S3C2440) { unsigned long sda_delay; -- 2.7.4