@@ -2,6 +2,7 @@
// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
#include <linux/clk.h>
+#include <linux/interconnect.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -139,7 +140,10 @@ struct qcom_qspi {
struct device *dev;
struct clk_bulk_data *clks;
struct qspi_xfer xfer;
- /* Lock to protect xfer and IRQ accessed registers */
+ struct icc_path *icc_path_cpu_to_qspi;
+ unsigned int avg_bw_cpu;
+ unsigned int peak_bw_cpu;
+ /* Lock to protect data accessed by IRQs */
spinlock_t lock;
};
@@ -241,6 +245,16 @@ static int qcom_qspi_transfer_one(struct spi_master *master,
return ret;
}
+ /* Set BW quota for CPU as driver supports FIFO mode only. */
+ ctrl->avg_bw_cpu = Bps_to_icc(speed_hz);
+ ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, ctrl->avg_bw_cpu,
+ ctrl->peak_bw_cpu);
+ if (ret) {
+ dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu\n",
+ __func__);
+ return ret;
+ }
+
spin_lock_irqsave(&ctrl->lock, flags);
/* We are half duplex, so either rx or tx will be set */
@@ -458,6 +472,16 @@ static int qcom_qspi_probe(struct platform_device *pdev)
if (ret)
goto exit_probe_master_put;
+ ctrl->icc_path_cpu_to_qspi = devm_of_icc_get(dev, "qspi-config");
+ if (IS_ERR(ctrl->icc_path_cpu_to_qspi)) {
+ ret = PTR_ERR(ctrl->icc_path_cpu_to_qspi);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get cpu path, ret:%d\n", ret);
+ goto exit_probe_master_put;
+ }
+ /* Put BW vote on CPU path for register access */
+ ctrl->avg_bw_cpu = Bps_to_icc(1000);
+
ret = platform_get_irq(pdev, 0);
if (ret < 0)
goto exit_probe_master_put;
@@ -511,9 +535,17 @@ 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);
+ int ret;
clk_bulk_disable_unprepare(QSPI_NUM_CLKS, ctrl->clks);
+ ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, 0, 0);
+ if (ret) {
+ dev_err_ratelimited(ctrl->dev, "%s: ICC BW remove failed for cpu\n",
+ __func__);
+ return ret;
+ }
+
return 0;
}
@@ -521,6 +553,15 @@ static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct qcom_qspi *ctrl = spi_master_get_devdata(master);
+ int ret;
+
+ ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, ctrl->avg_bw_cpu,
+ ctrl->peak_bw_cpu);
+ if (ret) {
+ dev_err_ratelimited(ctrl->dev, "%s: ICC BW voting failed for cpu\n",
+ __func__);
+ return ret;
+ }
return clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks);
}
Get the interconnect paths for QSPI device and vote according to the current bus speed of the driver. Signed-off-by: Akash Asthana <akashast@codeaurora.org> Reviewed by: Matthias Kaehlcke <mka@chromium.org> --- Changes in V2: - As per Bjorn's comment, introduced and using devm_of_icc_get API for getting path handle - As per Matthias comment, added error handling for icc_set_bw call Changes in V3: - No Change. Changes in V4: - As per Mark's comment move peak_bw guess as twice of avg_bw if nothing mentioned explicitly to ICC core. drivers/spi/spi-qcom-qspi.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-)