Message ID | 1588507469-31889-7-git-send-email-rnayak@codeaurora.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | None | expand |
Hi Rajendra, On Sun, May 03, 2020 at 05:34:29PM +0530, Rajendra Nayak wrote: > QSPI needs to vote on a performance state of a power domain depending on > the clock rate. Add support for it by specifying the perf state/clock rate > as an OPP table in device tree. > > Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org> > Cc: Mark Brown <broonie@kernel.org> > Cc: Alok Chauhan <alokc@codeaurora.org> > Cc: Akash Asthana <akashast@codeaurora.org> > Cc: linux-spi@vger.kernel.org > --- > drivers/spi/spi-qcom-qspi.c | 29 ++++++++++++++++++++++++++++- > 1 file changed, 28 insertions(+), 1 deletion(-) > > diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c > index 3c4f83b..eb53c00 100644 > --- a/drivers/spi/spi-qcom-qspi.c > +++ b/drivers/spi/spi-qcom-qspi.c > @@ -8,6 +8,7 @@ > #include <linux/of.h> > #include <linux/of_platform.h> > #include <linux/pm_runtime.h> > +#include <linux/pm_opp.h> > #include <linux/spi/spi.h> > #include <linux/spi/spi-mem.h> > > @@ -139,6 +140,8 @@ struct qcom_qspi { > struct device *dev; > struct clk_bulk_data *clks; > struct qspi_xfer xfer; > + struct opp_table *opp_table; > + bool has_opp_table; > /* Lock to protect xfer and IRQ accessed registers */ > spinlock_t lock; > }; > @@ -235,7 +238,7 @@ static int qcom_qspi_transfer_one(struct spi_master *master, > speed_hz = xfer->speed_hz; > > /* In regular operation (SBL_EN=1) core must be 4x transfer clock */ > - ret = clk_set_rate(ctrl->clks[QSPI_CLK_CORE].clk, speed_hz * 4); > + ret = dev_pm_opp_set_rate(ctrl->dev, speed_hz * 4); > if (ret) { > dev_err(ctrl->dev, "Failed to set core clk %d\n", ret); > return ret; > @@ -481,6 +484,20 @@ static int qcom_qspi_probe(struct platform_device *pdev) > master->handle_err = qcom_qspi_handle_err; > master->auto_runtime_pm = true; > > + ctrl->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core"); > + if (IS_ERR(ctrl->opp_table)) { > + ret = PTR_ERR(ctrl->opp_table); > + goto exit_probe_master_put; > + } > + /* OPP table is optional */ > + ret = dev_pm_opp_of_add_table(&pdev->dev); > + if (!ret) { > + ctrl->has_opp_table = true; > + } else if (ret != -ENODEV) { > + dev_err(&pdev->dev, "invalid OPP table in device tree\n"); > + goto exit_probe_master_put; > + } > + > pm_runtime_enable(dev); > > ret = spi_register_master(master); > @@ -488,6 +505,9 @@ static int qcom_qspi_probe(struct platform_device *pdev) > return 0; > > pm_runtime_disable(dev); > + if (ctrl->has_opp_table) > + dev_pm_opp_of_remove_table(&pdev->dev); > + dev_pm_opp_put_clkname(ctrl->opp_table); > > exit_probe_master_put: > spi_master_put(master); > @@ -498,6 +518,11 @@ static int qcom_qspi_probe(struct platform_device *pdev) > static int qcom_qspi_remove(struct platform_device *pdev) > { > struct spi_master *master = platform_get_drvdata(pdev); > + struct qcom_qspi *ctrl = spi_master_get_devdata(master); > + > + if (ctrl->has_opp_table) > + dev_pm_opp_of_remove_table(&pdev->dev); > + dev_pm_opp_put_clkname(ctrl->opp_table); IIUC there can still be active transfers before the controller is unregistered. If that is correct the above should be done after the spi_unregister_master() call below. > > /* Unregister _before_ disabling pm_runtime() so we stop transfers */ > spi_unregister_master(master); > @@ -512,6 +537,8 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev) > struct spi_master *master = dev_get_drvdata(dev); > struct qcom_qspi *ctrl = spi_master_get_devdata(master); > > + /* Drop the performance state vote */ > + dev_pm_opp_set_rate(dev, 0); > clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks); > > return 0;
On 5/5/2020 10:08 PM, Matthias Kaehlcke wrote: > Hi Rajendra, > > On Sun, May 03, 2020 at 05:34:29PM +0530, Rajendra Nayak wrote: >> QSPI needs to vote on a performance state of a power domain depending on >> the clock rate. Add support for it by specifying the perf state/clock rate >> as an OPP table in device tree. >> >> Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org> >> Cc: Mark Brown <broonie@kernel.org> >> Cc: Alok Chauhan <alokc@codeaurora.org> >> Cc: Akash Asthana <akashast@codeaurora.org> >> Cc: linux-spi@vger.kernel.org >> --- >> drivers/spi/spi-qcom-qspi.c | 29 ++++++++++++++++++++++++++++- >> 1 file changed, 28 insertions(+), 1 deletion(-) >> >> diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c >> index 3c4f83b..eb53c00 100644 >> --- a/drivers/spi/spi-qcom-qspi.c >> +++ b/drivers/spi/spi-qcom-qspi.c >> @@ -8,6 +8,7 @@ >> #include <linux/of.h> >> #include <linux/of_platform.h> >> #include <linux/pm_runtime.h> >> +#include <linux/pm_opp.h> >> #include <linux/spi/spi.h> >> #include <linux/spi/spi-mem.h> >> >> @@ -139,6 +140,8 @@ struct qcom_qspi { >> struct device *dev; >> struct clk_bulk_data *clks; >> struct qspi_xfer xfer; >> + struct opp_table *opp_table; >> + bool has_opp_table; >> /* Lock to protect xfer and IRQ accessed registers */ >> spinlock_t lock; >> }; >> @@ -235,7 +238,7 @@ static int qcom_qspi_transfer_one(struct spi_master *master, >> speed_hz = xfer->speed_hz; >> >> /* In regular operation (SBL_EN=1) core must be 4x transfer clock */ >> - ret = clk_set_rate(ctrl->clks[QSPI_CLK_CORE].clk, speed_hz * 4); >> + ret = dev_pm_opp_set_rate(ctrl->dev, speed_hz * 4); >> if (ret) { >> dev_err(ctrl->dev, "Failed to set core clk %d\n", ret); >> return ret; >> @@ -481,6 +484,20 @@ static int qcom_qspi_probe(struct platform_device *pdev) >> master->handle_err = qcom_qspi_handle_err; >> master->auto_runtime_pm = true; >> >> + ctrl->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core"); >> + if (IS_ERR(ctrl->opp_table)) { >> + ret = PTR_ERR(ctrl->opp_table); >> + goto exit_probe_master_put; >> + } >> + /* OPP table is optional */ >> + ret = dev_pm_opp_of_add_table(&pdev->dev); >> + if (!ret) { >> + ctrl->has_opp_table = true; >> + } else if (ret != -ENODEV) { >> + dev_err(&pdev->dev, "invalid OPP table in device tree\n"); >> + goto exit_probe_master_put; >> + } >> + >> pm_runtime_enable(dev); >> >> ret = spi_register_master(master); >> @@ -488,6 +505,9 @@ static int qcom_qspi_probe(struct platform_device *pdev) >> return 0; >> >> pm_runtime_disable(dev); >> + if (ctrl->has_opp_table) >> + dev_pm_opp_of_remove_table(&pdev->dev); >> + dev_pm_opp_put_clkname(ctrl->opp_table); >> >> exit_probe_master_put: >> spi_master_put(master); >> @@ -498,6 +518,11 @@ static int qcom_qspi_probe(struct platform_device *pdev) >> static int qcom_qspi_remove(struct platform_device *pdev) >> { >> struct spi_master *master = platform_get_drvdata(pdev); >> + struct qcom_qspi *ctrl = spi_master_get_devdata(master); >> + >> + if (ctrl->has_opp_table) >> + dev_pm_opp_of_remove_table(&pdev->dev); >> + dev_pm_opp_put_clkname(ctrl->opp_table); > > IIUC there can still be active transfers before the controller is > unregistered. If that is correct the above should be done after the > spi_unregister_master() call below. ah, true. i should have read the comment below :) >> >> /* Unregister _before_ disabling pm_runtime() so we stop transfers */ >> spi_unregister_master(master); >> @@ -512,6 +537,8 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev) >> struct spi_master *master = dev_get_drvdata(dev); >> struct qcom_qspi *ctrl = spi_master_get_devdata(master); >> >> + /* Drop the performance state vote */ >> + dev_pm_opp_set_rate(dev, 0); >> clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks); >> >> return 0;
diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index 3c4f83b..eb53c00 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -8,6 +8,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> +#include <linux/pm_opp.h> #include <linux/spi/spi.h> #include <linux/spi/spi-mem.h> @@ -139,6 +140,8 @@ struct qcom_qspi { struct device *dev; struct clk_bulk_data *clks; struct qspi_xfer xfer; + struct opp_table *opp_table; + bool has_opp_table; /* Lock to protect xfer and IRQ accessed registers */ spinlock_t lock; }; @@ -235,7 +238,7 @@ static int qcom_qspi_transfer_one(struct spi_master *master, speed_hz = xfer->speed_hz; /* In regular operation (SBL_EN=1) core must be 4x transfer clock */ - ret = clk_set_rate(ctrl->clks[QSPI_CLK_CORE].clk, speed_hz * 4); + ret = dev_pm_opp_set_rate(ctrl->dev, speed_hz * 4); if (ret) { dev_err(ctrl->dev, "Failed to set core clk %d\n", ret); return ret; @@ -481,6 +484,20 @@ static int qcom_qspi_probe(struct platform_device *pdev) master->handle_err = qcom_qspi_handle_err; master->auto_runtime_pm = true; + ctrl->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core"); + if (IS_ERR(ctrl->opp_table)) { + ret = PTR_ERR(ctrl->opp_table); + goto exit_probe_master_put; + } + /* OPP table is optional */ + ret = dev_pm_opp_of_add_table(&pdev->dev); + if (!ret) { + ctrl->has_opp_table = true; + } else if (ret != -ENODEV) { + dev_err(&pdev->dev, "invalid OPP table in device tree\n"); + goto exit_probe_master_put; + } + pm_runtime_enable(dev); ret = spi_register_master(master); @@ -488,6 +505,9 @@ static int qcom_qspi_probe(struct platform_device *pdev) return 0; pm_runtime_disable(dev); + if (ctrl->has_opp_table) + dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_put_clkname(ctrl->opp_table); exit_probe_master_put: spi_master_put(master); @@ -498,6 +518,11 @@ static int qcom_qspi_probe(struct platform_device *pdev) static int qcom_qspi_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); + struct qcom_qspi *ctrl = spi_master_get_devdata(master); + + if (ctrl->has_opp_table) + dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_put_clkname(ctrl->opp_table); /* Unregister _before_ disabling pm_runtime() so we stop transfers */ spi_unregister_master(master); @@ -512,6 +537,8 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev) struct spi_master *master = dev_get_drvdata(dev); struct qcom_qspi *ctrl = spi_master_get_devdata(master); + /* Drop the performance state vote */ + dev_pm_opp_set_rate(dev, 0); clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks); return 0;
QSPI needs to vote on a performance state of a power domain depending on the clock rate. Add support for it by specifying the perf state/clock rate as an OPP table in device tree. Signed-off-by: Rajendra Nayak <rnayak@codeaurora.org> Cc: Mark Brown <broonie@kernel.org> Cc: Alok Chauhan <alokc@codeaurora.org> Cc: Akash Asthana <akashast@codeaurora.org> Cc: linux-spi@vger.kernel.org --- drivers/spi/spi-qcom-qspi.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-)