mediatek: cleanly backport and add fix for I2C driver
[openwrt/staging/dangole.git] / target / linux / mediatek / patches-5.15 / 840-08-v5.19-i2c-mediatek-Optimize-master_xfer-and-avoid-circular.patch
1 From 2831f9a53ec3a16012d2d23590e3ebad6084b763 Mon Sep 17 00:00:00 2001
2 From: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
3 Date: Mon, 11 Apr 2022 15:21:07 +0200
4 Subject: [PATCH 08/16] i2c: mediatek: Optimize master_xfer() and avoid
5 circular locking
6 MIME-Version: 1.0
7 Content-Type: text/plain; charset=UTF-8
8 Content-Transfer-Encoding: 8bit
9
10 Especially (but not only) during probe, it may happen that multiple
11 devices are communicating via i2c (or multiple i2c busses) and
12 sometimes while others are probing asynchronously.
13 For example, a Cr50 TPM may be filling entropy (or userspace may be
14 reading random data) while the rt5682 (i2c) codec driver reads/sets
15 some registers, like while getting/setting a clock's rate, which
16 happens both during probe and during system operation.
17
18 In this driver, the mtk_i2c_transfer() function (which is the i2c
19 .master_xfer() callback) was granularly managing the clocks by
20 performing a clk_bulk_prepare_enable() to start them and its inverse.
21 This is not only creating possible circular locking dependencies in
22 the some cases (like former explanation), but it's also suboptimal,
23 as clk_core prepare/unprepare operations are using mutex locking,
24 which creates a bit of unwanted overhead (for example, i2c trackpads
25 will call master_xfer() every few milliseconds!).
26
27 With this commit, we avoid both the circular locking and additional
28 overhead by changing how we handle the clocks in this driver:
29 - Prepare the clocks during probe (and PM resume)
30 - Enable/disable clocks in mtk_i2c_transfer()
31 - Unprepare the clocks only for driver removal (and PM suspend)
32
33 For the sake of providing a full explanation: during probe, the
34 clocks are not only prepared but also enabled, as this is needed
35 for some hardware initialization but, after that, we are disabling
36 but not unpreparing them, leaving an expected state for the
37 aforementioned clock handling strategy.
38
39 Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
40 Tested-by: NĂ­colas F. R. A. Prado <nfraprado@collabora.com>
41 Reviewed-by: Qii Wang <qii.wang@mediatek.com>
42 Signed-off-by: Wolfram Sang <wsa@kernel.org>
43 ---
44 drivers/i2c/busses/i2c-mt65xx.c | 11 +++++++----
45 1 file changed, 7 insertions(+), 4 deletions(-)
46
47 --- a/drivers/i2c/busses/i2c-mt65xx.c
48 +++ b/drivers/i2c/busses/i2c-mt65xx.c
49 @@ -1177,7 +1177,7 @@ static int mtk_i2c_transfer(struct i2c_a
50 int left_num = num;
51 struct mtk_i2c *i2c = i2c_get_adapdata(adap);
52
53 - ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks);
54 + ret = clk_bulk_enable(I2C_MT65XX_CLK_MAX, i2c->clocks);
55 if (ret)
56 return ret;
57
58 @@ -1231,7 +1231,7 @@ static int mtk_i2c_transfer(struct i2c_a
59 ret = num;
60
61 err_exit:
62 - clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
63 + clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks);
64 return ret;
65 }
66
67 @@ -1412,7 +1412,7 @@ static int mtk_i2c_probe(struct platform
68 return ret;
69 }
70 mtk_i2c_init_hw(i2c);
71 - clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
72 + clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks);
73
74 ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq,
75 IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE,
76 @@ -1439,6 +1439,8 @@ static int mtk_i2c_remove(struct platfor
77
78 i2c_del_adapter(&i2c->adap);
79
80 + clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
81 +
82 return 0;
83 }
84
85 @@ -1448,6 +1450,7 @@ static int mtk_i2c_suspend_noirq(struct
86 struct mtk_i2c *i2c = dev_get_drvdata(dev);
87
88 i2c_mark_adapter_suspended(&i2c->adap);
89 + clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
90
91 return 0;
92 }
93 @@ -1465,7 +1468,7 @@ static int mtk_i2c_resume_noirq(struct d
94
95 mtk_i2c_init_hw(i2c);
96
97 - clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks);
98 + clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks);
99
100 i2c_mark_adapter_resumed(&i2c->adap);
101