1818#include <linux/of_device.h>
1919#include <linux/delay.h>
2020#include <linux/mmc/mmc.h>
21+ #include <linux/pm_runtime.h>
2122#include <linux/slab.h>
2223
2324#include "sdhci-pltfm.h"
6869#define CMUX_SHIFT_PHASE_SHIFT 24
6970#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT)
7071
72+ #define MSM_MMC_AUTOSUSPEND_DELAY_MS 50
7173struct sdhci_msm_host {
7274 struct platform_device * pdev ;
7375 void __iomem * core_mem ; /* MSM SDCC mapped address */
@@ -659,12 +661,26 @@ static int sdhci_msm_probe(struct platform_device *pdev)
659661 goto clk_disable ;
660662 }
661663
664+ pm_runtime_get_noresume (& pdev -> dev );
665+ pm_runtime_set_active (& pdev -> dev );
666+ pm_runtime_enable (& pdev -> dev );
667+ pm_runtime_set_autosuspend_delay (& pdev -> dev ,
668+ MSM_MMC_AUTOSUSPEND_DELAY_MS );
669+ pm_runtime_use_autosuspend (& pdev -> dev );
670+
662671 ret = sdhci_add_host (host );
663672 if (ret )
664- goto clk_disable ;
673+ goto pm_runtime_disable ;
674+
675+ pm_runtime_mark_last_busy (& pdev -> dev );
676+ pm_runtime_put_autosuspend (& pdev -> dev );
665677
666678 return 0 ;
667679
680+ pm_runtime_disable :
681+ pm_runtime_disable (& pdev -> dev );
682+ pm_runtime_set_suspended (& pdev -> dev );
683+ pm_runtime_put_noidle (& pdev -> dev );
668684clk_disable :
669685 clk_disable_unprepare (msm_host -> clk );
670686pclk_disable :
@@ -686,6 +702,11 @@ static int sdhci_msm_remove(struct platform_device *pdev)
686702 0xffffffff );
687703
688704 sdhci_remove_host (host , dead );
705+
706+ pm_runtime_get_sync (& pdev -> dev );
707+ pm_runtime_disable (& pdev -> dev );
708+ pm_runtime_put_noidle (& pdev -> dev );
709+
689710 clk_disable_unprepare (msm_host -> clk );
690711 clk_disable_unprepare (msm_host -> pclk );
691712 if (!IS_ERR (msm_host -> bus_clk ))
@@ -694,12 +715,57 @@ static int sdhci_msm_remove(struct platform_device *pdev)
694715 return 0 ;
695716}
696717
718+ #ifdef CONFIG_PM
719+ static int sdhci_msm_runtime_suspend (struct device * dev )
720+ {
721+ struct sdhci_host * host = dev_get_drvdata (dev );
722+ struct sdhci_pltfm_host * pltfm_host = sdhci_priv (host );
723+ struct sdhci_msm_host * msm_host = sdhci_pltfm_priv (pltfm_host );
724+
725+ clk_disable_unprepare (msm_host -> clk );
726+ clk_disable_unprepare (msm_host -> pclk );
727+
728+ return 0 ;
729+ }
730+
731+ static int sdhci_msm_runtime_resume (struct device * dev )
732+ {
733+ struct sdhci_host * host = dev_get_drvdata (dev );
734+ struct sdhci_pltfm_host * pltfm_host = sdhci_priv (host );
735+ struct sdhci_msm_host * msm_host = sdhci_pltfm_priv (pltfm_host );
736+ int ret ;
737+
738+ ret = clk_prepare_enable (msm_host -> clk );
739+ if (ret ) {
740+ dev_err (dev , "clk_enable failed for core_clk: %d\n" , ret );
741+ return ret ;
742+ }
743+ ret = clk_prepare_enable (msm_host -> pclk );
744+ if (ret ) {
745+ dev_err (dev , "clk_enable failed for iface_clk: %d\n" , ret );
746+ clk_disable_unprepare (msm_host -> clk );
747+ return ret ;
748+ }
749+
750+ return 0 ;
751+ }
752+ #endif
753+
754+ static const struct dev_pm_ops sdhci_msm_pm_ops = {
755+ SET_SYSTEM_SLEEP_PM_OPS (pm_runtime_force_suspend ,
756+ pm_runtime_force_resume )
757+ SET_RUNTIME_PM_OPS (sdhci_msm_runtime_suspend ,
758+ sdhci_msm_runtime_resume ,
759+ NULL )
760+ };
761+
697762static struct platform_driver sdhci_msm_driver = {
698763 .probe = sdhci_msm_probe ,
699764 .remove = sdhci_msm_remove ,
700765 .driver = {
701766 .name = "sdhci_msm" ,
702767 .of_match_table = sdhci_msm_dt_match ,
768+ .pm = & sdhci_msm_pm_ops ,
703769 },
704770};
705771
0 commit comments