mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-24 17:23:25 -05:00
spi: atmel-quadspi: add runtime pm support
Merge series from Claudiu Beznea <claudiu.beznea@microchip.com>: The following series adds runtime PM support for atmel-quadspi driver. clk_disable()/clk_enable() is called on proper runtime_suspend()/runtime_resume() ops. Along with it 2 minor cleanups were added (patches 2/3, 3/3).
This commit is contained in:
commit
c349fad389
1 changed files with 93 additions and 11 deletions
|
@ -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 */
|
||||
|
@ -285,7 +286,7 @@ static bool atmel_qspi_supports_op(struct spi_mem *mem,
|
|||
|
||||
/* special case not supported by hardware */
|
||||
if (op->addr.nbytes == 2 && op->cmd.buswidth != op->addr.buswidth &&
|
||||
op->dummy.nbytes == 0)
|
||||
op->dummy.nbytes == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -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 SIMPLE_DEV_PM_OPS(atmel_qspi_pm_ops, atmel_qspi_suspend,
|
||||
atmel_qspi_resume);
|
||||
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 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 = {};
|
||||
|
||||
|
@ -704,7 +786,7 @@ static struct platform_driver atmel_qspi_driver = {
|
|||
.driver = {
|
||||
.name = "atmel_qspi",
|
||||
.of_match_table = atmel_qspi_dt_ids,
|
||||
.pm = &atmel_qspi_pm_ops,
|
||||
.pm = pm_ptr(&atmel_qspi_pm_ops),
|
||||
},
|
||||
.probe = atmel_qspi_probe,
|
||||
.remove = atmel_qspi_remove,
|
||||
|
|
Loading…
Add table
Reference in a new issue