diff options
author | Han Xu <han.xu@nxp.com> | 2018-08-22 14:20:34 -0500 |
---|---|---|
committer | Jason Liu <jason.hui.liu@nxp.com> | 2019-02-12 10:33:40 +0800 |
commit | 5395e3826ba75aae9496992e7ab5b145d61a257b (patch) | |
tree | caa993eb95c45e55850d62584dd223aec273a391 /drivers/mtd | |
parent | bbeb65b716df3386512d9de3e6a8ad813ee4a04e (diff) |
MLK-18859-2: mtd: fsl-quadspi: enable runtime suspend/resume
enable runtime suspend/resume for quadspi controller
Signed-off-by: Han Xu <han.xu@nxp.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/spi-nor/fsl-quadspi.c | 102 |
1 files changed, 80 insertions, 22 deletions
diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c index 976a8a937639..5b82ea76602b 100644 --- a/drivers/mtd/spi-nor/fsl-quadspi.c +++ b/drivers/mtd/spi-nor/fsl-quadspi.c @@ -30,6 +30,10 @@ #include <linux/mutex.h> #include <linux/pm_qos.h> #include <linux/sizes.h> +#include <linux/pm_runtime.h> + +/* runtime pm timeout */ +#define QUADSPI_RPM_TIMEOUT 50 /* 50ms */ /* Controller needs driver to swap endian */ #define QUADSPI_QUIRK_SWAP_ENDIAN (1 << 0) @@ -298,6 +302,8 @@ struct fsl_qspi { bool ddr_enabled; struct mutex lock; struct pm_qos_request pm_qos_req; +#define FSL_QUADSPI_INITIALIZED (1 << 0) + int flags; }; static inline int needs_swap_endian(struct fsl_qspi *q) @@ -770,12 +776,24 @@ static void fsl_qspi_clk_disable_unprep(struct fsl_qspi *q) } +static int fsl_qspi_init_rpm(struct fsl_qspi *q) +{ + struct device *dev = q->dev; + + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, QUADSPI_RPM_TIMEOUT); + pm_runtime_use_autosuspend(dev); + + return 0; +} + /* We use this function to do some basic init for spi_nor_scan(). */ static int fsl_qspi_nor_setup(struct fsl_qspi *q) { void __iomem *base = q->iobase; u32 reg; int ret; + /* disable and unprepare clock to avoid glitch pass to controller */ fsl_qspi_clk_disable_unprep(q); @@ -1054,9 +1072,11 @@ static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops) mutex_lock(&q->lock); - ret = fsl_qspi_clk_prep_enable(q); - if (ret) + ret = pm_runtime_get_sync(q->dev); + if (ret < 0) { + dev_err(q->dev, "Failed to enable clock\n"); goto err_mutex; + } fsl_qspi_set_base_addr(q, nor); return 0; @@ -1070,7 +1090,8 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) { struct fsl_qspi *q = nor->priv; - fsl_qspi_clk_disable_unprep(q); + pm_runtime_mark_last_busy(q->dev); + pm_runtime_put_autosuspend(q->dev); mutex_unlock(&q->lock); } @@ -1101,6 +1122,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) if (!q->devtype_data) return -ENODEV; platform_set_drvdata(pdev, q); + dev_set_drvdata(dev, q); /* find the resources */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI"); @@ -1143,7 +1165,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) if (ret) q->ddr_smp = 0; - ret = fsl_qspi_clk_prep_enable(q); + ret = fsl_qspi_init_rpm(q); if (ret) { dev_err(dev, "can not enable the clock\n"); goto clk_failed; @@ -1153,19 +1175,25 @@ static int fsl_qspi_probe(struct platform_device *pdev) ret = platform_get_irq(pdev, 0); if (ret < 0) { dev_err(dev, "failed to get the irq: %d\n", ret); - goto irq_failed; + goto clk_failed; } ret = devm_request_irq(dev, ret, fsl_qspi_irq_handler, 0, pdev->name, q); if (ret) { dev_err(dev, "failed to request irq: %d\n", ret); - goto irq_failed; + goto clk_failed; + } + + ret = pm_runtime_get_sync(q->dev); + if (ret < 0) { + dev_err(q->dev, "Failed to enable clock\n"); + return ret; } ret = fsl_qspi_nor_setup(q); if (ret) - goto irq_failed; + goto clk_failed; if (of_get_property(np, "fsl,qspi-has-second-chip", NULL)) q->has_second_chip = true; @@ -1239,7 +1267,12 @@ static int fsl_qspi_probe(struct platform_device *pdev) if (ret) goto last_init_failed; - fsl_qspi_clk_disable_unprep(q); + pm_runtime_mark_last_busy(q->dev); + pm_runtime_put_autosuspend(q->dev); + + /* indicate the controller has been initialized */ + q->flags |= FSL_QUADSPI_INITIALIZED; + return 0; last_init_failed: @@ -1251,10 +1284,10 @@ last_init_failed: } mutex_failed: mutex_destroy(&q->lock); -irq_failed: - fsl_qspi_clk_disable_unprep(q); clk_failed: dev_err(dev, "Freescale QuadSPI probe failed\n"); + pm_runtime_dont_use_autosuspend(q->dev); + pm_runtime_disable(q->dev); return ret; } @@ -1282,42 +1315,67 @@ static int fsl_qspi_remove(struct platform_device *pdev) return 0; } -static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state) +static int fsl_qspi_initialized(struct fsl_qspi *q) +{ + return q->flags & FSL_QUADSPI_INITIALIZED; +} + +static int fsl_qspi_need_reinit(struct fsl_qspi *q) +{ + /* we always enable AHB data transfer bit for best performance, */ + /* so check this register bit to determine if the controller */ + /* once lost power, such as suspend/resume and need to be re-init */ + + return !(readl(q->iobase + QUADSPI_BUF3CR) + & QUADSPI_BUF3CR_ADATSZ_MASK); +} + +static int fsl_qspi_runtime_suspend(struct device *dev) { - pinctrl_pm_select_sleep_state(&pdev->dev); + struct fsl_qspi *q = dev_get_drvdata(dev); + + pinctrl_pm_select_sleep_state(dev); + fsl_qspi_clk_disable_unprep(q); + return 0; } -static int fsl_qspi_resume(struct platform_device *pdev) +static int fsl_qspi_runtime_resume(struct device *dev) { int ret; - struct fsl_qspi *q = platform_get_drvdata(pdev); + struct fsl_qspi *q = dev_get_drvdata(dev); - pinctrl_pm_select_default_state(&pdev->dev); + pinctrl_pm_select_default_state(dev); ret = fsl_qspi_clk_prep_enable(q); if (ret) return ret; - fsl_qspi_nor_setup(q); - fsl_qspi_set_map_addr(q); - fsl_qspi_nor_setup_last(q); - - fsl_qspi_clk_disable_unprep(q); + if (fsl_qspi_initialized(q) && + fsl_qspi_need_reinit(q)) { + fsl_qspi_nor_setup(q); + fsl_qspi_set_map_addr(q); + fsl_qspi_nor_setup_last(q); + q->ddr_enabled = false; + } return 0; } +static const struct dev_pm_ops fsl_qspi_pm_ops = { + SET_RUNTIME_PM_OPS(fsl_qspi_runtime_suspend, fsl_qspi_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + static struct platform_driver fsl_qspi_driver = { .driver = { .name = "fsl-quadspi", .bus = &platform_bus_type, + .pm = &fsl_qspi_pm_ops, .of_match_table = fsl_qspi_dt_ids, }, .probe = fsl_qspi_probe, .remove = fsl_qspi_remove, - .suspend = fsl_qspi_suspend, - .resume = fsl_qspi_resume, }; module_platform_driver(fsl_qspi_driver); |