i2c: exynos5: Add bus clock support
authorSam Protsenko <semen.protsenko@linaro.org>
Thu, 9 Dec 2021 14:03:13 +0000 (16:03 +0200)
committerWolfram Sang <wsa@kernel.org>
Thu, 9 Dec 2021 14:46:01 +0000 (15:46 +0100)
In new Exynos SoCs (like Exynos850) where HSI2C is implemented as a
part of USIv2 block, there are two clocks provided to HSI2C controller:
  - PCLK: bus clock (APB), provides access to register interface
  - IPCLK: operating IP-core clock; SCL is derived from this one

Both clocks have to be asserted for HSI2C to be functional in that case.

Add code to obtain and enable/disable PCLK in addition to already
handled operating clock. Make it optional though, as older Exynos SoC
variants only have one HSI2C clock.

Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
Reviewed-by: Chanho Park <chanho61.park@samsung.com>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
drivers/i2c/busses/i2c-exynos5.c

index c7e3cae99d1330978ca0be94669d429cb591d485..693903e8089211cdf9baed12fb7970dd6558c696 100644 (file)
@@ -182,7 +182,8 @@ struct exynos5_i2c {
        unsigned int            irq;
 
        void __iomem            *regs;
-       struct clk              *clk;
+       struct clk              *clk;           /* operating clock */
+       struct clk              *pclk;          /* bus clock */
        struct device           *dev;
        int                     state;
 
@@ -757,10 +758,14 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap,
        struct exynos5_i2c *i2c = adap->algo_data;
        int i, ret;
 
-       ret = clk_enable(i2c->clk);
+       ret = clk_enable(i2c->pclk);
        if (ret)
                return ret;
 
+       ret = clk_enable(i2c->clk);
+       if (ret)
+               goto err_pclk;
+
        for (i = 0; i < num; ++i) {
                ret = exynos5_i2c_xfer_msg(i2c, msgs + i, i + 1 == num);
                if (ret)
@@ -768,6 +773,8 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap,
        }
 
        clk_disable(i2c->clk);
+err_pclk:
+       clk_disable(i2c->pclk);
 
        return ret ?: num;
 }
@@ -807,10 +814,18 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
                return -ENOENT;
        }
 
-       ret = clk_prepare_enable(i2c->clk);
+       i2c->pclk = devm_clk_get(&pdev->dev, "hsi2c_pclk");
+       if (IS_ERR(i2c->pclk))
+               i2c->pclk = NULL; /* pclk is optional */
+
+       ret = clk_prepare_enable(i2c->pclk);
        if (ret)
                return ret;
 
+       ret = clk_prepare_enable(i2c->clk);
+       if (ret)
+               goto err_pclk;
+
        i2c->regs = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(i2c->regs)) {
                ret = PTR_ERR(i2c->regs);
@@ -853,11 +868,15 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, i2c);
 
        clk_disable(i2c->clk);
+       clk_disable(i2c->pclk);
 
        return 0;
 
  err_clk:
        clk_disable_unprepare(i2c->clk);
+
+ err_pclk:
+       clk_disable_unprepare(i2c->pclk);
        return ret;
 }
 
@@ -868,6 +887,7 @@ static int exynos5_i2c_remove(struct platform_device *pdev)
        i2c_del_adapter(&i2c->adap);
 
        clk_unprepare(i2c->clk);
+       clk_unprepare(i2c->pclk);
 
        return 0;
 }
@@ -879,6 +899,7 @@ static int exynos5_i2c_suspend_noirq(struct device *dev)
 
        i2c_mark_adapter_suspended(&i2c->adap);
        clk_unprepare(i2c->clk);
+       clk_unprepare(i2c->pclk);
 
        return 0;
 }
@@ -888,21 +909,30 @@ static int exynos5_i2c_resume_noirq(struct device *dev)
        struct exynos5_i2c *i2c = dev_get_drvdata(dev);
        int ret = 0;
 
-       ret = clk_prepare_enable(i2c->clk);
+       ret = clk_prepare_enable(i2c->pclk);
        if (ret)
                return ret;
 
+       ret = clk_prepare_enable(i2c->clk);
+       if (ret)
+               goto err_pclk;
+
        ret = exynos5_hsi2c_clock_setup(i2c);
-       if (ret) {
-               clk_disable_unprepare(i2c->clk);
-               return ret;
-       }
+       if (ret)
+               goto err_clk;
 
        exynos5_i2c_init(i2c);
        clk_disable(i2c->clk);
+       clk_disable(i2c->pclk);
        i2c_mark_adapter_resumed(&i2c->adap);
 
        return 0;
+
+err_clk:
+       clk_disable_unprepare(i2c->clk);
+err_pclk:
+       clk_disable_unprepare(i2c->pclk);
+       return ret;
 }
 #endif