i2c: exynos5: Add bus clock support

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>
This commit is contained in:
Sam Protsenko 2021-12-09 16:03:13 +02:00 committed by Wolfram Sang
parent 3f68910259
commit 697ad2490c

View 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