diff options
author | Rajendra Nayak <rnayak@codeaurora.org> | 2020-07-03 15:11:31 +0530 |
---|---|---|
committer | Bjorn Andersson <bjorn.andersson@linaro.org> | 2020-07-13 16:15:53 -0700 |
commit | f79a158d37c26015099f4a7fd7d6592bb2ad3054 (patch) | |
tree | 52400c98c8a045019532f9fb3c49a1f4c5db248b /drivers/spi/spi-qcom-qspi.c | |
parent | 4bdc8c8d2815fe8ef5052c5a5ab51c04a4f0de7b (diff) |
spi: spi-qcom-qspi: Use OPP API to set clk/perf state
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>
Reviewed-by: Matthias Kaehlcke <mka@chromium.org>
Acked-by: Mark Brown <broonie@kernel.org>
Cc: Alok Chauhan <alokc@codeaurora.org>
Cc: Akash Asthana <akashast@codeaurora.org>
Cc: linux-spi@vger.kernel.org
Link: https://lore.kernel.org/r/1593769293-6354-2-git-send-email-rnayak@codeaurora.org
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Diffstat (limited to 'drivers/spi/spi-qcom-qspi.c')
-rw-r--r-- | drivers/spi/spi-qcom-qspi.c | 28 |
1 files changed, 27 insertions, 1 deletions
diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index b5b4cf6ad514..18a59aa23ef8 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -9,6 +9,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> @@ -141,6 +142,8 @@ struct qcom_qspi { struct clk_bulk_data *clks; struct qspi_xfer xfer; struct icc_path *icc_path_cpu_to_qspi; + struct opp_table *opp_table; + bool has_opp_table; /* Lock to protect data accessed by IRQs */ spinlock_t lock; }; @@ -238,7 +241,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; @@ -519,6 +522,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); @@ -526,6 +543,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); @@ -536,11 +556,15 @@ exit_probe_master_put: 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); /* Unregister _before_ disabling pm_runtime() so we stop transfers */ spi_unregister_master(master); pm_runtime_disable(&pdev->dev); + if (ctrl->has_opp_table) + dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_put_clkname(ctrl->opp_table); return 0; } @@ -551,6 +575,8 @@ static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev) struct qcom_qspi *ctrl = spi_master_get_devdata(master); int ret; + /* Drop the performance state vote */ + dev_pm_opp_set_rate(dev, 0); clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks); ret = icc_disable(ctrl->icc_path_cpu_to_qspi); |