aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJavier Martinez Canillas <javier@osg.samsung.com>2016-04-16 21:14:52 -0400
committerWolfram Sang <wsa@the-dreams.de>2016-04-22 09:31:54 -0400
commit10ff4c5239a137abfc896ec73ef3d15a0f86a16a (patch)
tree4174254b698974075a09fd0b2bd904230731b9cb
parent609d5a1b2b35bb62b4b3750396e55453160c2a17 (diff)
i2c: exynos5: Fix possible ABBA deadlock by keeping I2C clock prepared
The exynos5 I2C controller driver always prepares and enables a clock before using it and then disables unprepares it when the clock is not used anymore. But this can cause a possible ABBA deadlock in some scenarios since a driver that uses regmap to access its I2C registers, will first grab the regmap lock and then the I2C xfer function will grab the prepare lock when preparing the I2C clock. But since the clock driver also uses regmap for I2C accesses, preparing a clock will first grab the prepare lock and then the regmap lock when using the regmap API. An example of this happens on the Exynos5422 Odroid XU4 board where a s2mps11 PMIC is used and both the s2mps11 regulators and clk drivers share the same I2C regmap. The possible deadlock is reported by the kernel lockdep: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(sec_core:428:(regmap)->lock); lock(prepare_lock); lock(sec_core:428:(regmap)->lock); lock(prepare_lock); *** DEADLOCK *** Fix it by leaving the code prepared on probe and use {en,dis}able in the I2C transfer function. This patch is similar to commit 34e81ad5f0b6 ("i2c: s3c2410: fix ABBA deadlock by keeping clock prepared") that fixes the same bug in other driver for an I2C controller found in Samsung SoCs. Reported-by: Anand Moon <linux.amoon@gmail.com> Signed-off-by: Javier Martinez Canillas <javier@osg.samsung.com> Reviewed-by: Anand Moon <linux.amoon@gmail.com> Reviewed-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de> Cc: stable@kernel.org
-rw-r--r--drivers/i2c/busses/i2c-exynos5.c24
1 files changed, 19 insertions, 5 deletions
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index b29c7500461a..f54ece8fce78 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -671,7 +671,9 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap,
671 return -EIO; 671 return -EIO;
672 } 672 }
673 673
674 clk_prepare_enable(i2c->clk); 674 ret = clk_enable(i2c->clk);
675 if (ret)
676 return ret;
675 677
676 for (i = 0; i < num; i++, msgs++) { 678 for (i = 0; i < num; i++, msgs++) {
677 stop = (i == num - 1); 679 stop = (i == num - 1);
@@ -695,7 +697,7 @@ static int exynos5_i2c_xfer(struct i2c_adapter *adap,
695 } 697 }
696 698
697 out: 699 out:
698 clk_disable_unprepare(i2c->clk); 700 clk_disable(i2c->clk);
699 return ret; 701 return ret;
700} 702}
701 703
@@ -747,7 +749,9 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
747 return -ENOENT; 749 return -ENOENT;
748 } 750 }
749 751
750 clk_prepare_enable(i2c->clk); 752 ret = clk_prepare_enable(i2c->clk);
753 if (ret)
754 return ret;
751 755
752 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 756 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
753 i2c->regs = devm_ioremap_resource(&pdev->dev, mem); 757 i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
@@ -799,6 +803,10 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
799 803
800 platform_set_drvdata(pdev, i2c); 804 platform_set_drvdata(pdev, i2c);
801 805
806 clk_disable(i2c->clk);
807
808 return 0;
809
802 err_clk: 810 err_clk:
803 clk_disable_unprepare(i2c->clk); 811 clk_disable_unprepare(i2c->clk);
804 return ret; 812 return ret;
@@ -810,6 +818,8 @@ static int exynos5_i2c_remove(struct platform_device *pdev)
810 818
811 i2c_del_adapter(&i2c->adap); 819 i2c_del_adapter(&i2c->adap);
812 820
821 clk_unprepare(i2c->clk);
822
813 return 0; 823 return 0;
814} 824}
815 825
@@ -821,6 +831,8 @@ static int exynos5_i2c_suspend_noirq(struct device *dev)
821 831
822 i2c->suspended = 1; 832 i2c->suspended = 1;
823 833
834 clk_unprepare(i2c->clk);
835
824 return 0; 836 return 0;
825} 837}
826 838
@@ -830,7 +842,9 @@ static int exynos5_i2c_resume_noirq(struct device *dev)
830 struct exynos5_i2c *i2c = platform_get_drvdata(pdev); 842 struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
831 int ret = 0; 843 int ret = 0;
832 844
833 clk_prepare_enable(i2c->clk); 845 ret = clk_prepare_enable(i2c->clk);
846 if (ret)
847 return ret;
834 848
835 ret = exynos5_hsi2c_clock_setup(i2c); 849 ret = exynos5_hsi2c_clock_setup(i2c);
836 if (ret) { 850 if (ret) {
@@ -839,7 +853,7 @@ static int exynos5_i2c_resume_noirq(struct device *dev)
839 } 853 }
840 854
841 exynos5_i2c_init(i2c); 855 exynos5_i2c_init(i2c);
842 clk_disable_unprepare(i2c->clk); 856 clk_disable(i2c->clk);
843 i2c->suspended = 0; 857 i2c->suspended = 0;
844 858
845 return 0; 859 return 0;