* [v1,0/1] i2c: mediatek: add runtime PM operations and bus regulator control @ 2024-09-20 14:36 zoie.lin 2024-09-20 14:36 ` [v1,1/1] " zoie.lin 0 siblings, 1 reply; 4+ messages in thread From: zoie.lin @ 2024-09-20 14:36 UTC (permalink / raw) To: Qii Wang, Andi Shyti, Matthias Brugger, AngeloGioacchino Del Regno Cc: Project_Global_Chrome_Upstream_Group, linux-i2c, linux-kernel, linux-arm-kernel, linux-mediatek, zoie.lin This series is based on linux-next, tag: next-20240919 This series adds support for runtime PM operations and bus regulator control in the MediaTek i2c driver. The changes include the implementation of runtime PM operations and the integration of bus regulator control to manage bus power. zoie.lin (1): i2c: mediatek: add runtime PM operations and bus regulator control drivers/i2c/busses/i2c-mt65xx.c | 72 ++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 11 deletions(-) -- 2.45.2 ^ permalink raw reply [flat|nested] 4+ messages in thread
* [v1,1/1] i2c: mediatek: add runtime PM operations and bus regulator control 2024-09-20 14:36 [v1,0/1] i2c: mediatek: add runtime PM operations and bus regulator control zoie.lin @ 2024-09-20 14:36 ` zoie.lin 2024-09-24 8:39 ` AngeloGioacchino Del Regno 0 siblings, 1 reply; 4+ messages in thread From: zoie.lin @ 2024-09-20 14:36 UTC (permalink / raw) To: Qii Wang, Andi Shyti, Matthias Brugger, AngeloGioacchino Del Regno Cc: Project_Global_Chrome_Upstream_Group, linux-i2c, linux-kernel, linux-arm-kernel, linux-mediatek, zoie.lin This commit introduces support for runtime PM operations in the I2C driver, enabling runtime suspend and resume functionality. Although in the most platforms, the bus power of i2c are always on, some platforms disable the i2c bus power in order to meet low power request. This implementation includes bus regulator control to facilitate proper handling of the bus power based on platform requirements. Signed-off-by: zoie.lin <zoie.lin@mediatek.com> --- drivers/i2c/busses/i2c-mt65xx.c | 72 ++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index e0ba653dec2d..aae0189ba210 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -21,6 +21,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/scatterlist.h> #include <linux/sched.h> #include <linux/slab.h> @@ -1245,8 +1246,8 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, int left_num = num; struct mtk_i2c *i2c = i2c_get_adapdata(adap); - ret = clk_bulk_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); - if (ret) + ret = pm_runtime_get_sync(i2c->dev); + if (ret < 0) return ret; i2c->auto_restart = i2c->dev_comp->auto_restart; @@ -1299,7 +1300,9 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, ret = num; err_exit: - clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); + pm_runtime_mark_last_busy(i2c->dev); + pm_runtime_put_autosuspend(i2c->dev); + return ret; } @@ -1370,6 +1373,41 @@ static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c) return 0; } +static int mtk_i2c_runtime_suspend(struct device *dev) +{ + struct mtk_i2c *i2c = dev_get_drvdata(dev); + + clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); + if (i2c->adap.bus_regulator) + regulator_disable(i2c->adap.bus_regulator); + + return 0; +} + +static int mtk_i2c_runtime_resume(struct device *dev) +{ + int ret = 0; + struct mtk_i2c *i2c = dev_get_drvdata(dev); + + if (i2c->adap.bus_regulator) { + ret = regulator_enable(i2c->adap.bus_regulator); + if (ret < 0) { + dev_err(dev, "enable regulator failed!\n"); + return ret; + } + } + + ret = clk_bulk_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); + if (ret < 0) { + dev_err(dev, "clock enable failed!\n"); + if (i2c->adap.bus_regulator) + regulator_disable(i2c->adap.bus_regulator); + return ret; + } + + return ret; +} + static int mtk_i2c_probe(struct platform_device *pdev) { int ret = 0; @@ -1472,13 +1510,19 @@ static int mtk_i2c_probe(struct platform_device *pdev) } } - ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); + ret = clk_bulk_prepare(I2C_MT65XX_CLK_MAX, i2c->clocks); if (ret) { - dev_err(&pdev->dev, "clock enable failed!\n"); + dev_err(&pdev->dev, "clk_bulk_prepare failed\n"); return ret; } + + platform_set_drvdata(pdev, i2c); + + ret = mtk_i2c_runtime_resume(i2c->dev); + if (ret < 0) + goto err_clk_bulk_unprepare; mtk_i2c_init_hw(i2c); - clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); + mtk_i2c_runtime_suspend(i2c->dev); ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq, IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE, @@ -1486,19 +1530,22 @@ static int mtk_i2c_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "Request I2C IRQ %d fail\n", irq); - goto err_bulk_unprepare; + goto err_clk_bulk_unprepare; } + pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); i2c_set_adapdata(&i2c->adap, i2c); ret = i2c_add_adapter(&i2c->adap); if (ret) - goto err_bulk_unprepare; - - platform_set_drvdata(pdev, i2c); + goto err_pm_runtime_disable; return 0; -err_bulk_unprepare: +err_pm_runtime_disable: + pm_runtime_disable(&pdev->dev); +err_clk_bulk_unprepare: clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); return ret; @@ -1510,6 +1557,7 @@ static void mtk_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&i2c->adap); + pm_runtime_disable(&pdev->dev); clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); } @@ -1546,6 +1594,8 @@ static int mtk_i2c_resume_noirq(struct device *dev) static const struct dev_pm_ops mtk_i2c_pm = { NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_i2c_suspend_noirq, mtk_i2c_resume_noirq) + SET_RUNTIME_PM_OPS(mtk_i2c_runtime_suspend, mtk_i2c_runtime_resume, + NULL) }; static struct platform_driver mtk_i2c_driver = { -- 2.45.2 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [v1,1/1] i2c: mediatek: add runtime PM operations and bus regulator control 2024-09-20 14:36 ` [v1,1/1] " zoie.lin @ 2024-09-24 8:39 ` AngeloGioacchino Del Regno 2024-09-25 8:18 ` Andi Shyti 0 siblings, 1 reply; 4+ messages in thread From: AngeloGioacchino Del Regno @ 2024-09-24 8:39 UTC (permalink / raw) To: zoie.lin, Qii Wang, Andi Shyti, Matthias Brugger Cc: Project_Global_Chrome_Upstream_Group, linux-i2c, linux-kernel, linux-arm-kernel, linux-mediatek Il 20/09/24 16:36, zoie.lin ha scritto: > This commit introduces support for runtime PM operations in > the I2C driver, enabling runtime suspend and resume functionality. > > Although in the most platforms, the bus power of i2c are always > on, some platforms disable the i2c bus power in order to meet > low power request. > > This implementation includes bus regulator control to facilitate > proper handling of the bus power based on platform requirements. > > Signed-off-by: zoie.lin <zoie.lin@mediatek.com> Hello Zoie, Your name does not technically have any "." inside, so please fix it so that it reads `Zoie Lin <zoie.lin@mediatek.com>`. Moreover, this implementation can be improved. Check below: You missed pm_runtime_status_suspended() checks in suspend/resume callbacks and, if the bus wasn't already runtime suspended when reaching suspend(), that will not get the bus regulators powered off when suspending; analogously, if the device was runtime suspended, the regulators and clocks will stay on when resuming from system sleep until first usage. So add the checks: static int mtk_i2c_suspend_noirq(struct device *dev) { struct mtk_i2c *i2c = dev_get_drvdata(dev); i2c_mark_adapter_suspended(&i2c->adap); if (!pm_runtime_status_suspended(dev)) mtk_i2c_runtime_suspend(dev); clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); return 0; } static int mtk_i2c_resume_noirq(struct device *dev) { int ret; struct mtk_i2c *i2c = dev_get_drvdata(dev); ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); if (ret) { dev_err(dev, "clock enable failed!\n"); return ret; } mtk_i2c_init_hw(i2c); if (pm_runtime_status_suspended(dev)) ret = mtk_i2c_runtime_suspend(dev); i2c_mark_adapter_resumed(&i2c->adap); return 0; } > --- > drivers/i2c/busses/i2c-mt65xx.c | 72 ++++++++++++++++++++++++++++----- > 1 file changed, 61 insertions(+), 11 deletions(-) > > diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c > index e0ba653dec2d..aae0189ba210 100644 > --- a/drivers/i2c/busses/i2c-mt65xx.c > +++ b/drivers/i2c/busses/i2c-mt65xx.c > @@ -21,6 +21,7 @@ > #include <linux/module.h> > #include <linux/of.h> > #include <linux/platform_device.h> > +#include <linux/pm_runtime.h> > #include <linux/scatterlist.h> > #include <linux/sched.h> > #include <linux/slab.h> > @@ -1245,8 +1246,8 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, > int left_num = num; > struct mtk_i2c *i2c = i2c_get_adapdata(adap); > > - ret = clk_bulk_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); > - if (ret) > + ret = pm_runtime_get_sync(i2c->dev); ret = pm_runtime_resume_and_get(i2c->dev); > + if (ret < 0) > return ret; > > i2c->auto_restart = i2c->dev_comp->auto_restart; > @@ -1299,7 +1300,9 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, > ret = num; > > err_exit: > - clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); > + pm_runtime_mark_last_busy(i2c->dev); > + pm_runtime_put_autosuspend(i2c->dev); > + > return ret; > } > > @@ -1370,6 +1373,41 @@ static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c) > return 0; > } > > +static int mtk_i2c_runtime_suspend(struct device *dev) > +{ > + struct mtk_i2c *i2c = dev_get_drvdata(dev); > + > + clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); > + if (i2c->adap.bus_regulator) > + regulator_disable(i2c->adap.bus_regulator); > + > + return 0; > +} > + > +static int mtk_i2c_runtime_resume(struct device *dev) > +{ > + int ret = 0; > + struct mtk_i2c *i2c = dev_get_drvdata(dev); struct mtk_i2c *i2c = dev_get_drvdata(dev); int ret; > + > + if (i2c->adap.bus_regulator) { > + ret = regulator_enable(i2c->adap.bus_regulator); > + if (ret < 0) { `ret` can't be > 0. `if (ret) {` is enough. > + dev_err(dev, "enable regulator failed!\n"); > + return ret; > + } > + } > + > + ret = clk_bulk_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); > + if (ret < 0) { if (ret) { > + dev_err(dev, "clock enable failed!\n"); This print is unnecessary. > + if (i2c->adap.bus_regulator) > + regulator_disable(i2c->adap.bus_regulator); > + return ret; > + } > + > + return ret; return 0; > +} > + > static int mtk_i2c_probe(struct platform_device *pdev) > { > int ret = 0; > @@ -1472,13 +1510,19 @@ static int mtk_i2c_probe(struct platform_device *pdev) > } > } > > - ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); > + ret = clk_bulk_prepare(I2C_MT65XX_CLK_MAX, i2c->clocks); > if (ret) { > - dev_err(&pdev->dev, "clock enable failed!\n"); > + dev_err(&pdev->dev, "clk_bulk_prepare failed\n"); This print is anyway redundant, as clk_bulk_prepare() already prints upon failures, so you can as well simply remove it instead of changing it. > return ret; > } > + > + platform_set_drvdata(pdev, i2c); > + > + ret = mtk_i2c_runtime_resume(i2c->dev); > + if (ret < 0) > + goto err_clk_bulk_unprepare; > mtk_i2c_init_hw(i2c); > - clk_bulk_disable(I2C_MT65XX_CLK_MAX, i2c->clocks); > + mtk_i2c_runtime_suspend(i2c->dev); > > ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq, > IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE, > @@ -1486,19 +1530,22 @@ static int mtk_i2c_probe(struct platform_device *pdev) > if (ret < 0) { > dev_err(&pdev->dev, > "Request I2C IRQ %d fail\n", irq); > - goto err_bulk_unprepare; > + goto err_clk_bulk_unprepare; > } > + pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); One full second as autosuspend delay? Can this be shortened to 500? 250? How was the one second wait chosen? How much time does mtk_i2c_runtime_resume() take to resume the bus? > + pm_runtime_use_autosuspend(&pdev->dev); > + pm_runtime_enable(&pdev->dev); > > i2c_set_adapdata(&i2c->adap, i2c); > ret = i2c_add_adapter(&i2c->adap); > if (ret) > - goto err_bulk_unprepare; > - > - platform_set_drvdata(pdev, i2c); > + goto err_pm_runtime_disable; > > return 0; > > -err_bulk_unprepare: > +err_pm_runtime_disable: > + pm_runtime_disable(&pdev->dev); > +err_clk_bulk_unprepare: > clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); > > return ret; > @@ -1510,6 +1557,7 @@ static void mtk_i2c_remove(struct platform_device *pdev) > > i2c_del_adapter(&i2c->adap); > > + pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); > clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); > } > Regards, Angelo ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [v1,1/1] i2c: mediatek: add runtime PM operations and bus regulator control 2024-09-24 8:39 ` AngeloGioacchino Del Regno @ 2024-09-25 8:18 ` Andi Shyti 0 siblings, 0 replies; 4+ messages in thread From: Andi Shyti @ 2024-09-25 8:18 UTC (permalink / raw) To: AngeloGioacchino Del Regno Cc: zoie.lin, Qii Wang, Matthias Brugger, Project_Global_Chrome_Upstream_Group, linux-i2c, linux-kernel, linux-arm-kernel, linux-mediatek Hi, ... > > @@ -1486,19 +1530,22 @@ static int mtk_i2c_probe(struct platform_device *pdev) > > if (ret < 0) { > > dev_err(&pdev->dev, > > "Request I2C IRQ %d fail\n", irq); > > - goto err_bulk_unprepare; > > + goto err_clk_bulk_unprepare; > > } > > + pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); > > One full second as autosuspend delay? Can this be shortened to 500? 250? > > How was the one second wait chosen? > > How much time does mtk_i2c_runtime_resume() take to resume the bus? Besides that, please add a comment to explain why you chose 1000/500/250. Arbitrary values are not much appreciated. Thanks, Andi ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2024-09-25 8:18 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-09-20 14:36 [v1,0/1] i2c: mediatek: add runtime PM operations and bus regulator control zoie.lin 2024-09-20 14:36 ` [v1,1/1] " zoie.lin 2024-09-24 8:39 ` AngeloGioacchino Del Regno 2024-09-25 8:18 ` Andi Shyti
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox