Commit cff80645 authored by Akash Asthana's avatar Akash Asthana Committed by Bjorn Andersson
Browse files

spi: spi-qcom-qspi: Add interconnect support



Get the interconnect paths for QSPI device and vote according to the
current bus speed of the driver.

Acked-by: default avatarMark Brown <broonie@kernel.org>
Signed-off-by: default avatarAkash Asthana <akashast@codeaurora.org>
Reviewed-by: default avatarMatthias Kaehlcke <mka@chromium.org>
Link: https://lore.kernel.org/r/1592908737-7068-8-git-send-email-akashast@codeaurora.org


Signed-off-by: default avatarBjorn Andersson <bjorn.andersson@linaro.org>
parent 0e3b8a81
Loading
Loading
Loading
Loading
+55 −1
Original line number Diff line number Diff line
@@ -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,8 @@ 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;
	/* Lock to protect data accessed by IRQs */
	spinlock_t lock;
};

@@ -229,6 +231,7 @@ static int qcom_qspi_transfer_one(struct spi_master *master,
	int ret;
	unsigned long speed_hz;
	unsigned long flags;
	unsigned int avg_bw_cpu;

	speed_hz = slv->max_speed_hz;
	if (xfer->speed_hz)
@@ -241,6 +244,18 @@ static int qcom_qspi_transfer_one(struct spi_master *master,
		return ret;
	}

	/*
	 * Set BW quota for CPU as driver supports FIFO mode only.
	 * We don't have explicit peak requirement so keep it equal to avg_bw.
	 */
	avg_bw_cpu = Bps_to_icc(speed_hz);
	ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, avg_bw_cpu, avg_bw_cpu);
	if (ret) {
		dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n",
			__func__, ret);
		return ret;
	}

	spin_lock_irqsave(&ctrl->lock, flags);

	/* We are half duplex, so either rx or tx will be set */
@@ -458,6 +473,29 @@ 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: %d\n", ret);
		goto exit_probe_master_put;
	}
	/* Set BW vote for register access */
	ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, Bps_to_icc(1000),
				Bps_to_icc(1000));
	if (ret) {
		dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n",
				__func__, ret);
		goto exit_probe_master_put;
	}

	ret = icc_disable(ctrl->icc_path_cpu_to_qspi);
	if (ret) {
		dev_err(ctrl->dev, "%s: ICC disable failed for cpu: %d\n",
				__func__, ret);
		goto exit_probe_master_put;
	}

	ret = platform_get_irq(pdev, 0);
	if (ret < 0)
		goto exit_probe_master_put;
@@ -511,9 +549,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_disable(ctrl->icc_path_cpu_to_qspi);
	if (ret) {
		dev_err_ratelimited(ctrl->dev, "%s: ICC disable failed for cpu: %d\n",
			__func__, ret);
		return ret;
	}

	return 0;
}

@@ -521,6 +567,14 @@ 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_enable(ctrl->icc_path_cpu_to_qspi);
	if (ret) {
		dev_err_ratelimited(ctrl->dev, "%s: ICC enable failed for cpu: %d\n",
			__func__, ret);
		return ret;
	}

	return clk_bulk_prepare_enable(QSPI_NUM_CLKS, ctrl->clks);
}