Unverified Commit 4a2f83b7 authored by Claudiu Beznea's avatar Claudiu Beznea Committed by Mark Brown
Browse files

spi: atmel-quadspi: add runtime pm support



Add runtime PM support for atmel-quadspi which will disable/enable
QSPI clocks on proper runtime_suspend/runtime_resume ops.

Signed-off-by: default avatarClaudiu Beznea <claudiu.beznea@microchip.com>
Link: https://lore.kernel.org/r/20220609084246.1795419-2-claudiu.beznea@microchip.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 5dfac65b
Loading
Loading
Loading
Loading
+91 −9
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi-mem.h>

/* QSPI register offsets */
@@ -417,9 +418,13 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
	if (op->addr.val + op->data.nbytes > aq->mmap_size)
		return -ENOTSUPP;

	err = pm_runtime_resume_and_get(&aq->pdev->dev);
	if (err < 0)
		return err;

	err = atmel_qspi_set_cfg(aq, op, &offset);
	if (err)
		return err;
		goto pm_runtime_put;

	/* Skip to the final steps if there is no data */
	if (op->data.nbytes) {
@@ -441,7 +446,7 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
	/* Poll INSTRuction End status */
	sr = atmel_qspi_read(aq, QSPI_SR);
	if ((sr & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED)
		return err;
		goto pm_runtime_put;

	/* Wait for INSTRuction End interrupt */
	reinit_completion(&aq->cmd_completion);
@@ -452,6 +457,9 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
		err = -ETIMEDOUT;
	atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IDR);

pm_runtime_put:
	pm_runtime_mark_last_busy(&aq->pdev->dev);
	pm_runtime_put_autosuspend(&aq->pdev->dev);
	return err;
}

@@ -472,6 +480,7 @@ static int atmel_qspi_setup(struct spi_device *spi)
	struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
	unsigned long src_rate;
	u32 scbr;
	int ret;

	if (ctrl->busy)
		return -EBUSY;
@@ -488,9 +497,16 @@ static int atmel_qspi_setup(struct spi_device *spi)
	if (scbr > 0)
		scbr--;

	ret = pm_runtime_resume_and_get(ctrl->dev.parent);
	if (ret < 0)
		return ret;

	aq->scr = QSPI_SCR_SCBR(scbr);
	atmel_qspi_write(aq->scr, aq, QSPI_SCR);

	pm_runtime_mark_last_busy(ctrl->dev.parent);
	pm_runtime_put_autosuspend(ctrl->dev.parent);

	return 0;
}

@@ -621,11 +637,24 @@ static int atmel_qspi_probe(struct platform_device *pdev)
	if (err)
		goto disable_qspick;

	pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
	pm_runtime_use_autosuspend(&pdev->dev);
	pm_runtime_set_active(&pdev->dev);
	pm_runtime_enable(&pdev->dev);
	pm_runtime_get_noresume(&pdev->dev);

	atmel_qspi_init(aq);

	err = spi_register_controller(ctrl);
	if (err)
	if (err) {
		pm_runtime_put_noidle(&pdev->dev);
		pm_runtime_disable(&pdev->dev);
		pm_runtime_set_suspended(&pdev->dev);
		pm_runtime_dont_use_autosuspend(&pdev->dev);
		goto disable_qspick;
	}
	pm_runtime_mark_last_busy(&pdev->dev);
	pm_runtime_put_autosuspend(&pdev->dev);

	return 0;

@@ -641,9 +670,18 @@ static int atmel_qspi_remove(struct platform_device *pdev)
{
	struct spi_controller *ctrl = platform_get_drvdata(pdev);
	struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
	int ret;

	ret = pm_runtime_resume_and_get(&pdev->dev);
	if (ret < 0)
		return ret;

	spi_unregister_controller(ctrl);
	atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR);

	pm_runtime_disable(&pdev->dev);
	pm_runtime_put_noidle(&pdev->dev);

	clk_disable_unprepare(aq->qspick);
	clk_disable_unprepare(aq->pclk);
	return 0;
@@ -653,10 +691,19 @@ static int __maybe_unused atmel_qspi_suspend(struct device *dev)
{
	struct spi_controller *ctrl = dev_get_drvdata(dev);
	struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
	int ret;

	ret = pm_runtime_resume_and_get(dev);
	if (ret < 0)
		return ret;

	atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR);
	clk_disable_unprepare(aq->qspick);
	clk_disable_unprepare(aq->pclk);

	pm_runtime_mark_last_busy(dev);
	pm_runtime_force_suspend(dev);

	clk_unprepare(aq->qspick);
	clk_unprepare(aq->pclk);

	return 0;
}
@@ -665,19 +712,54 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
{
	struct spi_controller *ctrl = dev_get_drvdata(dev);
	struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
	int ret;

	clk_prepare_enable(aq->pclk);
	clk_prepare_enable(aq->qspick);
	clk_prepare(aq->pclk);
	clk_prepare(aq->qspick);

	ret = pm_runtime_force_resume(dev);
	if (ret < 0)
		return ret;

	atmel_qspi_init(aq);

	atmel_qspi_write(aq->scr, aq, QSPI_SCR);

	pm_runtime_mark_last_busy(dev);
	pm_runtime_put_autosuspend(dev);

	return 0;
}

static int __maybe_unused atmel_qspi_runtime_suspend(struct device *dev)
{
	struct spi_controller *ctrl = dev_get_drvdata(dev);
	struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);

	clk_disable(aq->qspick);
	clk_disable(aq->pclk);

	return 0;
}

static SIMPLE_DEV_PM_OPS(atmel_qspi_pm_ops, atmel_qspi_suspend,
			 atmel_qspi_resume);
static int __maybe_unused atmel_qspi_runtime_resume(struct device *dev)
{
	struct spi_controller *ctrl = dev_get_drvdata(dev);
	struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
	int ret;

	ret = clk_enable(aq->pclk);
	if (ret)
		return ret;

	return clk_enable(aq->qspick);
}

static const struct dev_pm_ops __maybe_unused atmel_qspi_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(atmel_qspi_suspend, atmel_qspi_resume)
	SET_RUNTIME_PM_OPS(atmel_qspi_runtime_suspend,
			   atmel_qspi_runtime_resume, NULL)
};

static const struct atmel_qspi_caps atmel_sama5d2_qspi_caps = {};