Loading drivers/mmc/core/sdio.c +1 −0 Original line number Diff line number Diff line Loading @@ -1021,6 +1021,7 @@ static int mmc_sdio_resume(struct mmc_host *host) mmc_release_host(host); host->pm_flags &= ~MMC_PM_KEEP_POWER; host->pm_flags &= ~MMC_PM_WAKE_SDIO_IRQ; return err; } Loading drivers/mmc/host/sdhci-msm.c +193 −17 Original line number Diff line number Diff line Loading @@ -133,6 +133,7 @@ #define INVALID_TUNING_PHASE -1 #define sdhci_is_valid_gpio_wakeup_int(_h) ((_h)->sdiowakeup_irq >= 0) #define SDHCI_MSM_MIN_CLOCK 400000 #define CORE_FREQ_100MHZ (100 * 1000 * 1000) #define TCXO_FREQ 19200000 Loading Loading @@ -463,6 +464,9 @@ struct sdhci_msm_host { bool cqhci_offset_changed; bool reg_store; struct reset_control *core_reset; int sdiowakeup_irq; bool is_sdiowakeup_enabled; bool sdio_pending_processing; }; static struct sdhci_msm_host *sdhci_slot[2]; Loading Loading @@ -1978,6 +1982,43 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) __func__, req_type); } /* * Acquire spin-lock host->lock before calling this function */ static void sdhci_msm_cfg_sdiowakeup_gpio_irq(struct sdhci_host *host, bool enable) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); if (enable && !msm_host->is_sdiowakeup_enabled) enable_irq(msm_host->sdiowakeup_irq); else if (!enable && msm_host->is_sdiowakeup_enabled) disable_irq_nosync(msm_host->sdiowakeup_irq); else dev_warn(&msm_host->pdev->dev, "%s: wakeup to config: %d curr: %d\n", __func__, enable, msm_host->is_sdiowakeup_enabled); msm_host->is_sdiowakeup_enabled = enable; } static irqreturn_t sdhci_msm_sdiowakeup_irq(int irq, void *data) { struct sdhci_host *host = (struct sdhci_host *)data; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); unsigned long flags; pr_debug("%s: irq (%d) received\n", __func__, irq); spin_lock_irqsave(&host->lock, flags); sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false); spin_unlock_irqrestore(&host->lock, flags); msm_host->sdio_pending_processing = true; return IRQ_HANDLED; } static void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); Loading Loading @@ -3555,6 +3596,33 @@ static void sdhci_msm_clkgate_bus_delayed_work(struct work_struct *work) sdhci_msm_bus_voting(host, false); } static int sdhci_msm_ungate_clocks(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); int ret = 0; sdhci_msm_bus_voting(host, true); ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks), msm_host->bulk_clks); if (ret) { dev_err(&msm_host->pdev->dev, "Failed to enable clocks %d\n", ret); sdhci_msm_bus_voting(host, false); return ret; } sdhci_msm_registers_restore(host); sdhci_msm_toggle_fifo_write_clk(host); /* * Whenever core-clock is gated dynamically, it's needed to * restore the SDR DLL settings when the clock is ungated. */ if (msm_host->restore_dll_config && msm_host->clk_rate) sdhci_msm_restore_sdr_dll_config(host); return ret; } /* Find cpu group qos from a given cpu */ static struct qos_cpu_group *cpu_to_group(struct sdhci_msm_qos_req *r, int cpu) { Loading Loading @@ -4161,6 +4229,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) const struct sdhci_msm_variant_info *var_info; struct device *dev = &pdev->dev; struct device_node *node = pdev->dev.of_node; unsigned long flags; host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host)); if (IS_ERR(host)) Loading Loading @@ -4428,6 +4497,31 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; msm_host->sdiowakeup_irq = platform_get_irq_byname(pdev, "sdiowakeup_irq"); if (sdhci_is_valid_gpio_wakeup_int(msm_host)) { dev_info(&pdev->dev, "%s: sdiowakeup_irq = %d\n", __func__, msm_host->sdiowakeup_irq); msm_host->is_sdiowakeup_enabled = true; ret = request_irq(msm_host->sdiowakeup_irq, sdhci_msm_sdiowakeup_irq, IRQF_SHARED | IRQF_TRIGGER_HIGH, "sdhci-msm sdiowakeup", host); if (ret) { dev_err(&pdev->dev, "%s: request sdiowakeup IRQ %d: failed: %d\n", __func__, msm_host->sdiowakeup_irq, ret); msm_host->sdiowakeup_irq = -1; msm_host->is_sdiowakeup_enabled = false; goto vreg_deinit; } else { spin_lock_irqsave(&host->lock, flags); sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false); msm_host->sdio_pending_processing = false; spin_unlock_irqrestore(&host->lock, flags); } } sdhci_msm_set_regulator_caps(msm_host); #if defined(CONFIG_SDC_QTI) Loading Loading @@ -4511,6 +4605,53 @@ static int sdhci_msm_remove(struct platform_device *pdev) return 0; } static int sdhci_msm_cfg_sdio_wakeup(struct sdhci_host *host, bool enable) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); unsigned long flags; int ret = 0; if (!(host->mmc->card && mmc_card_sdio(host->mmc->card) && sdhci_is_valid_gpio_wakeup_int(msm_host) && mmc_card_wake_sdio_irq(host->mmc))) { msm_host->sdio_pending_processing = false; return 1; } spin_lock_irqsave(&host->lock, flags); if (enable) { /* configure DAT1 gpio if applicable */ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) { msm_host->sdio_pending_processing = false; ret = enable_irq_wake(msm_host->sdiowakeup_irq); if (!ret) sdhci_msm_cfg_sdiowakeup_gpio_irq(host, true); goto out; } else { pr_err("%s: sdiowakeup_irq(%d) invalid\n", mmc_hostname(host->mmc), enable); } } else { if (sdhci_is_valid_gpio_wakeup_int(msm_host)) { ret = disable_irq_wake(msm_host->sdiowakeup_irq); sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false); msm_host->sdio_pending_processing = false; } else { pr_err("%s: sdiowakeup_irq(%d)invalid\n", mmc_hostname(host->mmc), enable); } } out: if (ret) pr_err("%s: %s: %sable wakeup: failed: %d gpio: %d\n", mmc_hostname(host->mmc), __func__, enable ? "en" : "dis", ret, msm_host->sdiowakeup_irq); spin_unlock_irqrestore(&host->lock, flags); return ret; } static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); Loading @@ -4523,10 +4664,32 @@ static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev) sdhci_msm_unvote_qos_all(msm_host); skip_qos: if (host->mmc->card && mmc_card_sdio(host->mmc->card)) goto skip_clk_gating; queue_delayed_work(msm_host->workq, &msm_host->clk_gating_work, msecs_to_jiffies(msm_host->clk_gating_delay)); skip_clk_gating: return 0; } static int sdhci_msm_resume_early(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); int ret = 0; if (host->mmc->card && mmc_card_sdio(host->mmc->card)) { if (msm_host->is_sdiowakeup_enabled) sdhci_msm_cfg_sdio_wakeup(host, false); ret = sdhci_msm_ungate_clocks(host); if (ret) return ret; } return 0; } Loading @@ -4538,27 +4701,17 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev) struct sdhci_msm_qos_req *qos_req = msm_host->sdhci_qos; int ret; if (host->mmc->card && mmc_card_sdio(host->mmc->card)) goto skip_clk_ungating; ret = cancel_delayed_work_sync(&msm_host->clk_gating_work); if (!ret) { sdhci_msm_bus_voting(host, true); ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks), msm_host->bulk_clks); if (ret) { dev_err(dev, "Failed to enable clocks %d\n", ret); sdhci_msm_bus_voting(host, false); ret = sdhci_msm_ungate_clocks(host); if (ret) return ret; } sdhci_msm_registers_restore(host); sdhci_msm_toggle_fifo_write_clk(host); /* * Whenever core-clock is gated dynamically, it's needed to * restore the SDR DLL settings when the clock is ungated. */ if (msm_host->restore_dll_config && msm_host->clk_rate) sdhci_msm_restore_sdr_dll_config(host); } skip_clk_ungating: if (!qos_req) return 0; sdhci_msm_vote_pmqos(msm_host->mmc, Loading @@ -4573,19 +4726,42 @@ static int sdhci_msm_suspend_late(struct device *dev) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); if (host->mmc->card && mmc_card_sdio(host->mmc->card)) { sdhci_msm_cfg_sdio_wakeup(host, true); /* Start gating work asap for SDIO card in late suspend */ queue_delayed_work(msm_host->workq, &msm_host->clk_gating_work, 0); } if (flush_delayed_work(&msm_host->clk_gating_work)) dev_dbg(dev, "%s Waited for clk_gating_work to finish\n", __func__); return 0; } static int sdhci_msm_suspend_noirq(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); int ret = 0; if (host->mmc->card && mmc_card_sdio(host->mmc->card)) if (msm_host->sdio_pending_processing) ret = -EBUSY; return ret; } static const struct dev_pm_ops sdhci_msm_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) SET_LATE_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend_late, NULL) SET_LATE_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend_late, sdhci_msm_resume_early) SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume, NULL) .suspend_noirq = sdhci_msm_suspend_noirq, }; static struct platform_driver sdhci_msm_driver = { Loading Loading
drivers/mmc/core/sdio.c +1 −0 Original line number Diff line number Diff line Loading @@ -1021,6 +1021,7 @@ static int mmc_sdio_resume(struct mmc_host *host) mmc_release_host(host); host->pm_flags &= ~MMC_PM_KEEP_POWER; host->pm_flags &= ~MMC_PM_WAKE_SDIO_IRQ; return err; } Loading
drivers/mmc/host/sdhci-msm.c +193 −17 Original line number Diff line number Diff line Loading @@ -133,6 +133,7 @@ #define INVALID_TUNING_PHASE -1 #define sdhci_is_valid_gpio_wakeup_int(_h) ((_h)->sdiowakeup_irq >= 0) #define SDHCI_MSM_MIN_CLOCK 400000 #define CORE_FREQ_100MHZ (100 * 1000 * 1000) #define TCXO_FREQ 19200000 Loading Loading @@ -463,6 +464,9 @@ struct sdhci_msm_host { bool cqhci_offset_changed; bool reg_store; struct reset_control *core_reset; int sdiowakeup_irq; bool is_sdiowakeup_enabled; bool sdio_pending_processing; }; static struct sdhci_msm_host *sdhci_slot[2]; Loading Loading @@ -1978,6 +1982,43 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) __func__, req_type); } /* * Acquire spin-lock host->lock before calling this function */ static void sdhci_msm_cfg_sdiowakeup_gpio_irq(struct sdhci_host *host, bool enable) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); if (enable && !msm_host->is_sdiowakeup_enabled) enable_irq(msm_host->sdiowakeup_irq); else if (!enable && msm_host->is_sdiowakeup_enabled) disable_irq_nosync(msm_host->sdiowakeup_irq); else dev_warn(&msm_host->pdev->dev, "%s: wakeup to config: %d curr: %d\n", __func__, enable, msm_host->is_sdiowakeup_enabled); msm_host->is_sdiowakeup_enabled = enable; } static irqreturn_t sdhci_msm_sdiowakeup_irq(int irq, void *data) { struct sdhci_host *host = (struct sdhci_host *)data; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); unsigned long flags; pr_debug("%s: irq (%d) received\n", __func__, irq); spin_lock_irqsave(&host->lock, flags); sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false); spin_unlock_irqrestore(&host->lock, flags); msm_host->sdio_pending_processing = true; return IRQ_HANDLED; } static void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); Loading Loading @@ -3555,6 +3596,33 @@ static void sdhci_msm_clkgate_bus_delayed_work(struct work_struct *work) sdhci_msm_bus_voting(host, false); } static int sdhci_msm_ungate_clocks(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); int ret = 0; sdhci_msm_bus_voting(host, true); ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks), msm_host->bulk_clks); if (ret) { dev_err(&msm_host->pdev->dev, "Failed to enable clocks %d\n", ret); sdhci_msm_bus_voting(host, false); return ret; } sdhci_msm_registers_restore(host); sdhci_msm_toggle_fifo_write_clk(host); /* * Whenever core-clock is gated dynamically, it's needed to * restore the SDR DLL settings when the clock is ungated. */ if (msm_host->restore_dll_config && msm_host->clk_rate) sdhci_msm_restore_sdr_dll_config(host); return ret; } /* Find cpu group qos from a given cpu */ static struct qos_cpu_group *cpu_to_group(struct sdhci_msm_qos_req *r, int cpu) { Loading Loading @@ -4161,6 +4229,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) const struct sdhci_msm_variant_info *var_info; struct device *dev = &pdev->dev; struct device_node *node = pdev->dev.of_node; unsigned long flags; host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host)); if (IS_ERR(host)) Loading Loading @@ -4428,6 +4497,31 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; msm_host->sdiowakeup_irq = platform_get_irq_byname(pdev, "sdiowakeup_irq"); if (sdhci_is_valid_gpio_wakeup_int(msm_host)) { dev_info(&pdev->dev, "%s: sdiowakeup_irq = %d\n", __func__, msm_host->sdiowakeup_irq); msm_host->is_sdiowakeup_enabled = true; ret = request_irq(msm_host->sdiowakeup_irq, sdhci_msm_sdiowakeup_irq, IRQF_SHARED | IRQF_TRIGGER_HIGH, "sdhci-msm sdiowakeup", host); if (ret) { dev_err(&pdev->dev, "%s: request sdiowakeup IRQ %d: failed: %d\n", __func__, msm_host->sdiowakeup_irq, ret); msm_host->sdiowakeup_irq = -1; msm_host->is_sdiowakeup_enabled = false; goto vreg_deinit; } else { spin_lock_irqsave(&host->lock, flags); sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false); msm_host->sdio_pending_processing = false; spin_unlock_irqrestore(&host->lock, flags); } } sdhci_msm_set_regulator_caps(msm_host); #if defined(CONFIG_SDC_QTI) Loading Loading @@ -4511,6 +4605,53 @@ static int sdhci_msm_remove(struct platform_device *pdev) return 0; } static int sdhci_msm_cfg_sdio_wakeup(struct sdhci_host *host, bool enable) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); unsigned long flags; int ret = 0; if (!(host->mmc->card && mmc_card_sdio(host->mmc->card) && sdhci_is_valid_gpio_wakeup_int(msm_host) && mmc_card_wake_sdio_irq(host->mmc))) { msm_host->sdio_pending_processing = false; return 1; } spin_lock_irqsave(&host->lock, flags); if (enable) { /* configure DAT1 gpio if applicable */ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) { msm_host->sdio_pending_processing = false; ret = enable_irq_wake(msm_host->sdiowakeup_irq); if (!ret) sdhci_msm_cfg_sdiowakeup_gpio_irq(host, true); goto out; } else { pr_err("%s: sdiowakeup_irq(%d) invalid\n", mmc_hostname(host->mmc), enable); } } else { if (sdhci_is_valid_gpio_wakeup_int(msm_host)) { ret = disable_irq_wake(msm_host->sdiowakeup_irq); sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false); msm_host->sdio_pending_processing = false; } else { pr_err("%s: sdiowakeup_irq(%d)invalid\n", mmc_hostname(host->mmc), enable); } } out: if (ret) pr_err("%s: %s: %sable wakeup: failed: %d gpio: %d\n", mmc_hostname(host->mmc), __func__, enable ? "en" : "dis", ret, msm_host->sdiowakeup_irq); spin_unlock_irqrestore(&host->lock, flags); return ret; } static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); Loading @@ -4523,10 +4664,32 @@ static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev) sdhci_msm_unvote_qos_all(msm_host); skip_qos: if (host->mmc->card && mmc_card_sdio(host->mmc->card)) goto skip_clk_gating; queue_delayed_work(msm_host->workq, &msm_host->clk_gating_work, msecs_to_jiffies(msm_host->clk_gating_delay)); skip_clk_gating: return 0; } static int sdhci_msm_resume_early(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); int ret = 0; if (host->mmc->card && mmc_card_sdio(host->mmc->card)) { if (msm_host->is_sdiowakeup_enabled) sdhci_msm_cfg_sdio_wakeup(host, false); ret = sdhci_msm_ungate_clocks(host); if (ret) return ret; } return 0; } Loading @@ -4538,27 +4701,17 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev) struct sdhci_msm_qos_req *qos_req = msm_host->sdhci_qos; int ret; if (host->mmc->card && mmc_card_sdio(host->mmc->card)) goto skip_clk_ungating; ret = cancel_delayed_work_sync(&msm_host->clk_gating_work); if (!ret) { sdhci_msm_bus_voting(host, true); ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks), msm_host->bulk_clks); if (ret) { dev_err(dev, "Failed to enable clocks %d\n", ret); sdhci_msm_bus_voting(host, false); ret = sdhci_msm_ungate_clocks(host); if (ret) return ret; } sdhci_msm_registers_restore(host); sdhci_msm_toggle_fifo_write_clk(host); /* * Whenever core-clock is gated dynamically, it's needed to * restore the SDR DLL settings when the clock is ungated. */ if (msm_host->restore_dll_config && msm_host->clk_rate) sdhci_msm_restore_sdr_dll_config(host); } skip_clk_ungating: if (!qos_req) return 0; sdhci_msm_vote_pmqos(msm_host->mmc, Loading @@ -4573,19 +4726,42 @@ static int sdhci_msm_suspend_late(struct device *dev) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); if (host->mmc->card && mmc_card_sdio(host->mmc->card)) { sdhci_msm_cfg_sdio_wakeup(host, true); /* Start gating work asap for SDIO card in late suspend */ queue_delayed_work(msm_host->workq, &msm_host->clk_gating_work, 0); } if (flush_delayed_work(&msm_host->clk_gating_work)) dev_dbg(dev, "%s Waited for clk_gating_work to finish\n", __func__); return 0; } static int sdhci_msm_suspend_noirq(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); int ret = 0; if (host->mmc->card && mmc_card_sdio(host->mmc->card)) if (msm_host->sdio_pending_processing) ret = -EBUSY; return ret; } static const struct dev_pm_ops sdhci_msm_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) SET_LATE_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend_late, NULL) SET_LATE_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend_late, sdhci_msm_resume_early) SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume, NULL) .suspend_noirq = sdhci_msm_suspend_noirq, }; static struct platform_driver sdhci_msm_driver = { Loading