From 4f1450de5da03cfaa12417376d9e22336800a29e Mon Sep 17 00:00:00 2001 From: Youngmin Nam Date: Tue, 15 Nov 2016 10:31:44 +0900 Subject: [PATCH] [COMMON] spi: s3c64xx: call runtime pm callback directly in suspend / resume function If there is transfer request in system suspend stage, the runtime suspend callback will not be called after transfer completion because the runtime suspend callback of pm_runtime_put_autosuspend() is queued into workqueue as freezable. After that, without calling runtime suspend callback before system suspend, it can cause lock up by central sequencer stuck because clock request is high. To fix this issue, runtime pm callback should be called directly in suspend / resume function if runtime pm status is not "suspended". Change-Id: Iac05b22e50f29bf24fc04eb06719105e65e48ec3 Signed-off-by: Youngmin Nam --- drivers/spi/spi-s3c64xx.c | 184 ++++++++++++++++++++------------------ 1 file changed, 95 insertions(+), 89 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index dff3cf502023..641b550cbe68 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1720,6 +1720,95 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static void s3c64xx_spi_pin_ctrl(struct device *dev, int en) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); + struct pinctrl_state *pin_stat; + + if (!sdd->pin_idle) + return; + + pin_stat = en ? sdd->pin_def : sdd->pin_idle; + if (!IS_ERR(pin_stat)) { + sdd->pinctrl->state = NULL; + if (pinctrl_select_state(sdd->pinctrl, pin_stat)) + dev_err(dev, "could not set pinctrl.\n"); + } else { + dev_warn(dev, "pinctrl stat is null pointer.\n"); + } +} + +static int s3c64xx_spi_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); + struct s3c64xx_spi_info *sci = sdd->cntrlr_info; + + if (__clk_get_enable_count(sdd->clk)) + clk_disable_unprepare(sdd->clk); + if (__clk_get_enable_count(sdd->src_clk)) + clk_disable_unprepare(sdd->src_clk); + + exynos_update_ip_idle_status(sdd->idle_ip_index, 1); + + /* Free DMA channels */ + if (sci->dma_mode == DMA_MODE && sdd->is_probed && sdd->ops != NULL) { + #ifdef CONFIG_ARM64 + sdd->ops->release((unsigned long)sdd->rx_dma.ch, + &s3c64xx_spi_dma_client); + sdd->ops->release((unsigned long)sdd->tx_dma.ch, + &s3c64xx_spi_dma_client); + #else + sdd->ops->release((enum dma_ch)sdd->rx_dma.ch, + &s3c64xx_spi_dma_client); + sdd->ops->release((enum dma_ch)sdd->tx_dma.ch, + &s3c64xx_spi_dma_client); + #endif + sdd->rx_dma.ch = NULL; + sdd->tx_dma.ch = NULL; + } + + s3c64xx_spi_pin_ctrl(dev, 0); + + return 0; +} + +static int s3c64xx_spi_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); + struct s3c64xx_spi_info *sci = sdd->cntrlr_info; + + s3c64xx_spi_pin_ctrl(dev, 1); + + if (sci->dma_mode == DMA_MODE && sdd->is_probed) { + /* Acquire DMA channels */ + while (!acquire_dma(sdd)) + usleep_range(10000, 11000); + } + + if (sci->domain == DOMAIN_TOP) { + exynos_update_ip_idle_status(sdd->idle_ip_index, 0); + clk_prepare_enable(sdd->src_clk); + clk_prepare_enable(sdd->clk); + } + +#if defined(CONFIG_VIDEO_EXYNOS_FIMC_IS) || defined(CONFIG_VIDEO_EXYNOS_FIMC_IS2) + else if (sci->domain == DOMAIN_CAM1 || sci->domain == DOMAIN_ISP) { + exynos_update_ip_idle_status(sdd->idle_ip_index, 0); + clk_prepare_enable(sdd->src_clk); + clk_prepare_enable(sdd->clk); + + s3c64xx_spi_hwinit(sdd, sdd->port_id); + } +#endif + + return 0; +} +#endif /* CONFIG_PM */ + #ifdef CONFIG_PM_SLEEP static int s3c64xx_spi_suspend_operation(struct device *dev) { @@ -1743,6 +1832,9 @@ static int s3c64xx_spi_suspend_operation(struct device *dev) exynos_update_ip_idle_status(sdd->idle_ip_index, 1); } #endif + if (!pm_runtime_status_suspended(dev)) + s3c64xx_spi_runtime_suspend(dev); + sdd->cur_speed = 0; /* Output Clock is stopped */ return 0; @@ -1755,6 +1847,9 @@ static int s3c64xx_spi_resume_operation(struct device *dev) struct s3c64xx_spi_info *sci = sdd->cntrlr_info; int ret; + if (!pm_runtime_status_suspended(dev)) + s3c64xx_spi_runtime_resume(dev); + if (sci->domain == DOMAIN_TOP) { /* Enable the clock */ exynos_update_ip_idle_status(sdd->idle_ip_index, 0); @@ -1854,95 +1949,6 @@ static int s3c64xx_spi_resume(struct device *dev) } #endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PM -static void s3c64xx_spi_pin_ctrl(struct device *dev, int en) -{ - struct spi_master *master = dev_get_drvdata(dev); - struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); - struct pinctrl_state *pin_stat; - - if (!sdd->pin_idle) - return; - - pin_stat = en ? sdd->pin_def : sdd->pin_idle; - if (!IS_ERR(pin_stat)) { - sdd->pinctrl->state = NULL; - if (pinctrl_select_state(sdd->pinctrl, pin_stat)) - dev_err(dev, "could not set pinctrl.\n"); - } else { - dev_warn(dev, "pinctrl stat is null pointer.\n"); - } -} - -static int s3c64xx_spi_runtime_suspend(struct device *dev) -{ - struct spi_master *master = dev_get_drvdata(dev); - struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); - struct s3c64xx_spi_info *sci = sdd->cntrlr_info; - - if (__clk_get_enable_count(sdd->clk)) - clk_disable_unprepare(sdd->clk); - if (__clk_get_enable_count(sdd->src_clk)) - clk_disable_unprepare(sdd->src_clk); - - exynos_update_ip_idle_status(sdd->idle_ip_index, 1); - - /* Free DMA channels */ - if (sci->dma_mode == DMA_MODE && sdd->is_probed && sdd->ops != NULL) { - #ifdef CONFIG_ARM64 - sdd->ops->release((unsigned long)sdd->rx_dma.ch, - &s3c64xx_spi_dma_client); - sdd->ops->release((unsigned long)sdd->tx_dma.ch, - &s3c64xx_spi_dma_client); - #else - sdd->ops->release((enum dma_ch)sdd->rx_dma.ch, - &s3c64xx_spi_dma_client); - sdd->ops->release((enum dma_ch)sdd->tx_dma.ch, - &s3c64xx_spi_dma_client); - #endif - sdd->rx_dma.ch = NULL; - sdd->tx_dma.ch = NULL; - } - - s3c64xx_spi_pin_ctrl(dev, 0); - - return 0; -} - -static int s3c64xx_spi_runtime_resume(struct device *dev) -{ - struct spi_master *master = dev_get_drvdata(dev); - struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); - struct s3c64xx_spi_info *sci = sdd->cntrlr_info; - - s3c64xx_spi_pin_ctrl(dev, 1); - - if (sci->dma_mode == DMA_MODE && sdd->is_probed) { - /* Acquire DMA channels */ - while (!acquire_dma(sdd)) - usleep_range(10000, 11000); - } - - if (sci->domain == DOMAIN_TOP) { - exynos_update_ip_idle_status(sdd->idle_ip_index, 0); - clk_prepare_enable(sdd->src_clk); - clk_prepare_enable(sdd->clk); - } - -#if defined(CONFIG_VIDEO_EXYNOS_FIMC_IS) || defined(CONFIG_VIDEO_EXYNOS_FIMC_IS2) - else if (sci->domain == DOMAIN_CAM1 || sci->domain == DOMAIN_ISP) { - exynos_update_ip_idle_status(sdd->idle_ip_index, 0); - clk_prepare_enable(sdd->src_clk); - clk_prepare_enable(sdd->clk); - - s3c64xx_spi_hwinit(sdd, sdd->port_id); - } -#endif - - return 0; -} -#endif /* CONFIG_PM */ - static const struct dev_pm_ops s3c64xx_spi_pm = { SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume) SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend_noirq, s3c64xx_spi_resume_noirq) -- 2.20.1