Loading Documentation/devicetree/bindings/mmc/sdhci-msm.txt +4 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,9 @@ Optional Properties: no default value that the driver assumes if this property is not specified. So if this property is not specified, then SDHC driver will not vote for PM QOS. - qcom,dat1-mpm-int: specifies MPM interrupt number (e.g. sdhc_2 node below) corresponding to DAT1 line of SDHC (used only if slot has dedicated DAT1 MSM pin (not GPIO)) In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage). - qcom,<supply>-always-on - specifies whether supply should be kept "on" always. Loading Loading @@ -165,4 +168,5 @@ Example: <81 512 106496 212992>, /* 208 MB/s */ <81 512 2147483647 4294967295>; /* Max. bandwidth */ qcom,bus-bw-vectors-bps = <0 13631488 27262976 54525952 109051904 218103808 4294967295>; qcom,dat1-mpm-int = <44>; }; drivers/mmc/host/sdhci-msm.c +219 −3 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ #include <linux/delay.h> #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/irq.h> #include <linux/mmc/mmc.h> #include <linux/pm.h> #include <linux/pm_runtime.h> Loading @@ -38,10 +39,18 @@ #include <linux/dma-mapping.h> #include <mach/gpio.h> #include <mach/msm_bus.h> #include <mach/mpm.h> #include <linux/iopoll.h> #include "sdhci-pltfm.h" enum sdc_mpm_pin_state { SDC_DAT1_DISABLE, SDC_DAT1_ENABLE, SDC_DAT1_ENWAKE, SDC_DAT1_DISWAKE, }; #define SDHCI_VER_100 0x2B #define CORE_HC_MODE 0x78 #define HC_MODE_EN 0x1 Loading Loading @@ -165,6 +174,9 @@ #define INVALID_TUNING_PHASE -1 #define sdhci_is_valid_mpm_wakeup_int(_h) ((_h)->pdata->mpm_sdiowakeup_int >= 0) #define sdhci_is_valid_gpio_wakeup_int(_h) ((_h)->pdata->sdiowakeup_irq >= 0) static const u32 tuning_block_64[] = { 0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE, 0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777, Loading Loading @@ -292,6 +304,8 @@ struct sdhci_msm_pltfm_data { struct sdhci_msm_bus_voting_data *voting_data; u32 *sup_clk_table; unsigned char sup_clk_cnt; int mpm_sdiowakeup_int; int sdiowakeup_irq; }; struct sdhci_msm_bus_vote { Loading Loading @@ -328,6 +342,7 @@ struct sdhci_msm_host { u8 saved_tuning_phase; bool en_auto_cmd21; struct device_attribute auto_cmd21_attr; bool is_sdiowakeup_enabled; }; enum vdd_io_level { Loading Loading @@ -1435,7 +1450,7 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev) struct device_node *np = dev->of_node; u32 bus_width = 0; u32 cpu_dma_latency; int len, i; int len, i, mpm_int; int clk_table_len; u32 *clk_table = NULL; enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW; Loading Loading @@ -1530,6 +1545,12 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev) if (of_get_property(np, "qcom,nonremovable", NULL)) pdata->nonremovable = true; if (!of_property_read_u32(np, "qcom,dat1-mpm-int", &mpm_int)) pdata->mpm_sdiowakeup_int = mpm_int; else pdata->mpm_sdiowakeup_int = -1; return pdata; out: return NULL; Loading Loading @@ -2032,6 +2053,39 @@ static int sdhci_msm_set_vdd_io_vol(struct sdhci_msm_pltfm_data *pdata, return ret; } /* * 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 = pltfm_host->priv; if (enable && !msm_host->is_sdiowakeup_enabled) enable_irq(msm_host->pdata->sdiowakeup_irq); else if (!enable && msm_host->is_sdiowakeup_enabled) disable_irq_nosync(msm_host->pdata->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; 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); return IRQ_HANDLED; } static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) { struct sdhci_host *host = (struct sdhci_host *)data; Loading Loading @@ -2729,6 +2783,38 @@ static struct sdhci_ops sdhci_msm_ops = { .config_auto_tuning_cmd = sdhci_msm_config_auto_tuning_cmd, }; static int sdhci_msm_cfg_mpm_pin_wakeup(struct sdhci_host *host, unsigned mode) { int ret = 0; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; unsigned int pin = msm_host->pdata->mpm_sdiowakeup_int; if (!pin) return 0; switch (mode) { case SDC_DAT1_DISABLE: ret = msm_mpm_enable_pin(pin, 0); break; case SDC_DAT1_ENABLE: ret = msm_mpm_set_pin_type(pin, IRQ_TYPE_LEVEL_LOW); if (!ret) ret = msm_mpm_enable_pin(pin, 1); break; case SDC_DAT1_ENWAKE: ret = msm_mpm_set_pin_wake(pin, 1); break; case SDC_DAT1_DISWAKE: ret = msm_mpm_set_pin_wake(pin, 0); break; default: ret = -EINVAL; break; } return ret; } static int sdhci_msm_probe(struct platform_device *pdev) { struct sdhci_host *host; Loading @@ -2738,6 +2824,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) int ret = 0, dead = 0; u16 host_version; u32 pwr, irq_status, irq_ctl; unsigned long flags; pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__); msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host), Loading Loading @@ -2990,7 +3077,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->mmc->caps2 |= MMC_CAP2_CLK_SCALE; msm_host->mmc->caps2 |= MMC_CAP2_STOP_REQUEST; msm_host->mmc->caps2 |= MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE; msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER; msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ; if (msm_host->pdata->nonremovable) msm_host->mmc->caps |= MMC_CAP_NONREMOVABLE; Loading @@ -3016,6 +3103,27 @@ static int sdhci_msm_probe(struct platform_device *pdev) dev_err(&pdev->dev, "%s: Failed to set dma mask\n", __func__); } msm_host->pdata->sdiowakeup_irq = platform_get_irq_byname(pdev, "sdiowakeup_irq"); if (msm_host->pdata->sdiowakeup_irq >= 0) { msm_host->is_sdiowakeup_enabled = true; ret = request_irq(msm_host->pdata->sdiowakeup_irq, sdhci_msm_sdiowakeup_irq, IRQF_SHARED | IRQF_TRIGGER_LOW, "sdhci-msm sdiowakeup", host); if (ret) { dev_err(&pdev->dev, "%s: request sdiowakeup IRQ %d: failed: %d\n", __func__, msm_host->pdata->sdiowakeup_irq, ret); msm_host->pdata->sdiowakeup_irq = -1; msm_host->is_sdiowakeup_enabled = false; goto free_cd_gpio; } else { spin_lock_irqsave(&host->lock, flags); sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false); spin_unlock_irqrestore(&host->lock, flags); } } ret = sdhci_add_host(host); if (ret) { dev_err(&pdev->dev, "Add host failed (%d)\n", ret); Loading Loading @@ -3061,6 +3169,16 @@ static int sdhci_msm_probe(struct platform_device *pdev) device_remove_file(&pdev->dev, &msm_host->auto_cmd21_attr); } if (msm_host->pdata->mpm_sdiowakeup_int != -1) { ret = sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_ENABLE); if (ret) { pr_err("%s: enabling wakeup: failed: ret: %d\n", mmc_hostname(host->mmc), ret); ret = 0; msm_host->pdata->mpm_sdiowakeup_int = -1; } } /* Successful initialization */ goto out; Loading @@ -3072,6 +3190,8 @@ remove_host: free_cd_gpio: if (gpio_is_valid(msm_host->pdata->status_gpio)) mmc_gpio_free_cd(msm_host->mmc); if (sdhci_is_valid_gpio_wakeup_int(msm_host)) free_irq(msm_host->pdata->sdiowakeup_irq, host); vreg_deinit: sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false); bus_unregister: Loading Loading @@ -3117,6 +3237,12 @@ static int sdhci_msm_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); sdhci_pltfm_free(pdev); if (sdhci_is_valid_mpm_wakeup_int(msm_host)) sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_DISABLE); if (sdhci_is_valid_gpio_wakeup_int(msm_host)) free_irq(msm_host->pdata->sdiowakeup_irq, host); if (gpio_is_valid(msm_host->pdata->status_gpio)) mmc_gpio_free_cd(msm_host->mmc); Loading @@ -3132,13 +3258,75 @@ 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 = pltfm_host->priv; unsigned long flags; int ret = 0; if (!(host->mmc->card && mmc_card_sdio(host->mmc->card) && (sdhci_is_valid_mpm_wakeup_int(msm_host) || sdhci_is_valid_gpio_wakeup_int(msm_host)) && mmc_card_wake_sdio_irq(host->mmc))) { return 1; } spin_lock_irqsave(&host->lock, flags); if (enable) { /* configure DAT1 gpio if applicable */ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) { ret = enable_irq_wake(msm_host->pdata->sdiowakeup_irq); if (!ret) sdhci_msm_cfg_sdiowakeup_gpio_irq(host, true); goto out; } else { ret = sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_ENWAKE); if (ret) goto out; ret = enable_irq_wake(host->irq); if (ret) sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_DISWAKE); } } else { if (sdhci_is_valid_gpio_wakeup_int(msm_host)) { ret = disable_irq_wake(msm_host->pdata->sdiowakeup_irq); sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false); } else { ret = sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_DISWAKE); if (ret) goto out; ret = disable_irq_wake(host->irq); } } out: if (ret) pr_err("%s: %s: %sable wakeup: failed: %d gpio: %d mpm: %d\n", mmc_hostname(host->mmc), __func__, enable ? "en" : "dis", ret, msm_host->pdata->sdiowakeup_irq, msm_host->pdata->mpm_sdiowakeup_int); spin_unlock_irqrestore(&host->lock, flags); return ret; } static int sdhci_msm_runtime_suspend(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 = pltfm_host->priv; int ret; ret = sdhci_msm_cfg_sdio_wakeup(host, true); /* pwr_irq is not monitored by mpm on suspend, hence disable it */ if (!ret) goto skip_disable_host_irq; disable_irq(host->irq); skip_disable_host_irq: disable_irq(msm_host->pwr_irq); /* Loading @@ -3159,10 +3347,17 @@ static int sdhci_msm_runtime_resume(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 = pltfm_host->priv; int ret; ret = sdhci_msm_cfg_sdio_wakeup(host, false); if (!ret) goto skip_enable_host_irq; enable_irq(msm_host->pwr_irq); enable_irq(host->irq); skip_enable_host_irq: enable_irq(msm_host->pwr_irq); return 0; } Loading Loading @@ -3214,6 +3409,26 @@ static int sdhci_msm_resume(struct device *dev) out: return ret; } 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 = pltfm_host->priv; int ret = 0; /* * ksdioirqd may get scheduled after sdhc suspend, hence retry * suspend in case the clocks are ON */ if (atomic_read(&msm_host->clks_on)) { pr_warn("%s: %s: clock ON after suspend, aborting suspend\n", mmc_hostname(host->mmc), __func__); ret = -EAGAIN; } return ret; } #endif #ifdef CONFIG_PM Loading @@ -3221,6 +3436,7 @@ static const struct dev_pm_ops sdhci_msm_pmops = { SET_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend, sdhci_msm_resume) SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume, NULL) .suspend_noirq = sdhci_msm_suspend_noirq, }; #define SDHCI_MSM_PMOPS (&sdhci_msm_pmops) Loading Loading
Documentation/devicetree/bindings/mmc/sdhci-msm.txt +4 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,9 @@ Optional Properties: no default value that the driver assumes if this property is not specified. So if this property is not specified, then SDHC driver will not vote for PM QOS. - qcom,dat1-mpm-int: specifies MPM interrupt number (e.g. sdhc_2 node below) corresponding to DAT1 line of SDHC (used only if slot has dedicated DAT1 MSM pin (not GPIO)) In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage). - qcom,<supply>-always-on - specifies whether supply should be kept "on" always. Loading Loading @@ -165,4 +168,5 @@ Example: <81 512 106496 212992>, /* 208 MB/s */ <81 512 2147483647 4294967295>; /* Max. bandwidth */ qcom,bus-bw-vectors-bps = <0 13631488 27262976 54525952 109051904 218103808 4294967295>; qcom,dat1-mpm-int = <44>; };
drivers/mmc/host/sdhci-msm.c +219 −3 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ #include <linux/delay.h> #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/irq.h> #include <linux/mmc/mmc.h> #include <linux/pm.h> #include <linux/pm_runtime.h> Loading @@ -38,10 +39,18 @@ #include <linux/dma-mapping.h> #include <mach/gpio.h> #include <mach/msm_bus.h> #include <mach/mpm.h> #include <linux/iopoll.h> #include "sdhci-pltfm.h" enum sdc_mpm_pin_state { SDC_DAT1_DISABLE, SDC_DAT1_ENABLE, SDC_DAT1_ENWAKE, SDC_DAT1_DISWAKE, }; #define SDHCI_VER_100 0x2B #define CORE_HC_MODE 0x78 #define HC_MODE_EN 0x1 Loading Loading @@ -165,6 +174,9 @@ #define INVALID_TUNING_PHASE -1 #define sdhci_is_valid_mpm_wakeup_int(_h) ((_h)->pdata->mpm_sdiowakeup_int >= 0) #define sdhci_is_valid_gpio_wakeup_int(_h) ((_h)->pdata->sdiowakeup_irq >= 0) static const u32 tuning_block_64[] = { 0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE, 0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777, Loading Loading @@ -292,6 +304,8 @@ struct sdhci_msm_pltfm_data { struct sdhci_msm_bus_voting_data *voting_data; u32 *sup_clk_table; unsigned char sup_clk_cnt; int mpm_sdiowakeup_int; int sdiowakeup_irq; }; struct sdhci_msm_bus_vote { Loading Loading @@ -328,6 +342,7 @@ struct sdhci_msm_host { u8 saved_tuning_phase; bool en_auto_cmd21; struct device_attribute auto_cmd21_attr; bool is_sdiowakeup_enabled; }; enum vdd_io_level { Loading Loading @@ -1435,7 +1450,7 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev) struct device_node *np = dev->of_node; u32 bus_width = 0; u32 cpu_dma_latency; int len, i; int len, i, mpm_int; int clk_table_len; u32 *clk_table = NULL; enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW; Loading Loading @@ -1530,6 +1545,12 @@ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev) if (of_get_property(np, "qcom,nonremovable", NULL)) pdata->nonremovable = true; if (!of_property_read_u32(np, "qcom,dat1-mpm-int", &mpm_int)) pdata->mpm_sdiowakeup_int = mpm_int; else pdata->mpm_sdiowakeup_int = -1; return pdata; out: return NULL; Loading Loading @@ -2032,6 +2053,39 @@ static int sdhci_msm_set_vdd_io_vol(struct sdhci_msm_pltfm_data *pdata, return ret; } /* * 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 = pltfm_host->priv; if (enable && !msm_host->is_sdiowakeup_enabled) enable_irq(msm_host->pdata->sdiowakeup_irq); else if (!enable && msm_host->is_sdiowakeup_enabled) disable_irq_nosync(msm_host->pdata->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; 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); return IRQ_HANDLED; } static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) { struct sdhci_host *host = (struct sdhci_host *)data; Loading Loading @@ -2729,6 +2783,38 @@ static struct sdhci_ops sdhci_msm_ops = { .config_auto_tuning_cmd = sdhci_msm_config_auto_tuning_cmd, }; static int sdhci_msm_cfg_mpm_pin_wakeup(struct sdhci_host *host, unsigned mode) { int ret = 0; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; unsigned int pin = msm_host->pdata->mpm_sdiowakeup_int; if (!pin) return 0; switch (mode) { case SDC_DAT1_DISABLE: ret = msm_mpm_enable_pin(pin, 0); break; case SDC_DAT1_ENABLE: ret = msm_mpm_set_pin_type(pin, IRQ_TYPE_LEVEL_LOW); if (!ret) ret = msm_mpm_enable_pin(pin, 1); break; case SDC_DAT1_ENWAKE: ret = msm_mpm_set_pin_wake(pin, 1); break; case SDC_DAT1_DISWAKE: ret = msm_mpm_set_pin_wake(pin, 0); break; default: ret = -EINVAL; break; } return ret; } static int sdhci_msm_probe(struct platform_device *pdev) { struct sdhci_host *host; Loading @@ -2738,6 +2824,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) int ret = 0, dead = 0; u16 host_version; u32 pwr, irq_status, irq_ctl; unsigned long flags; pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__); msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host), Loading Loading @@ -2990,7 +3077,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->mmc->caps2 |= MMC_CAP2_CLK_SCALE; msm_host->mmc->caps2 |= MMC_CAP2_STOP_REQUEST; msm_host->mmc->caps2 |= MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE; msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER; msm_host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ; if (msm_host->pdata->nonremovable) msm_host->mmc->caps |= MMC_CAP_NONREMOVABLE; Loading @@ -3016,6 +3103,27 @@ static int sdhci_msm_probe(struct platform_device *pdev) dev_err(&pdev->dev, "%s: Failed to set dma mask\n", __func__); } msm_host->pdata->sdiowakeup_irq = platform_get_irq_byname(pdev, "sdiowakeup_irq"); if (msm_host->pdata->sdiowakeup_irq >= 0) { msm_host->is_sdiowakeup_enabled = true; ret = request_irq(msm_host->pdata->sdiowakeup_irq, sdhci_msm_sdiowakeup_irq, IRQF_SHARED | IRQF_TRIGGER_LOW, "sdhci-msm sdiowakeup", host); if (ret) { dev_err(&pdev->dev, "%s: request sdiowakeup IRQ %d: failed: %d\n", __func__, msm_host->pdata->sdiowakeup_irq, ret); msm_host->pdata->sdiowakeup_irq = -1; msm_host->is_sdiowakeup_enabled = false; goto free_cd_gpio; } else { spin_lock_irqsave(&host->lock, flags); sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false); spin_unlock_irqrestore(&host->lock, flags); } } ret = sdhci_add_host(host); if (ret) { dev_err(&pdev->dev, "Add host failed (%d)\n", ret); Loading Loading @@ -3061,6 +3169,16 @@ static int sdhci_msm_probe(struct platform_device *pdev) device_remove_file(&pdev->dev, &msm_host->auto_cmd21_attr); } if (msm_host->pdata->mpm_sdiowakeup_int != -1) { ret = sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_ENABLE); if (ret) { pr_err("%s: enabling wakeup: failed: ret: %d\n", mmc_hostname(host->mmc), ret); ret = 0; msm_host->pdata->mpm_sdiowakeup_int = -1; } } /* Successful initialization */ goto out; Loading @@ -3072,6 +3190,8 @@ remove_host: free_cd_gpio: if (gpio_is_valid(msm_host->pdata->status_gpio)) mmc_gpio_free_cd(msm_host->mmc); if (sdhci_is_valid_gpio_wakeup_int(msm_host)) free_irq(msm_host->pdata->sdiowakeup_irq, host); vreg_deinit: sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false); bus_unregister: Loading Loading @@ -3117,6 +3237,12 @@ static int sdhci_msm_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); sdhci_pltfm_free(pdev); if (sdhci_is_valid_mpm_wakeup_int(msm_host)) sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_DISABLE); if (sdhci_is_valid_gpio_wakeup_int(msm_host)) free_irq(msm_host->pdata->sdiowakeup_irq, host); if (gpio_is_valid(msm_host->pdata->status_gpio)) mmc_gpio_free_cd(msm_host->mmc); Loading @@ -3132,13 +3258,75 @@ 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 = pltfm_host->priv; unsigned long flags; int ret = 0; if (!(host->mmc->card && mmc_card_sdio(host->mmc->card) && (sdhci_is_valid_mpm_wakeup_int(msm_host) || sdhci_is_valid_gpio_wakeup_int(msm_host)) && mmc_card_wake_sdio_irq(host->mmc))) { return 1; } spin_lock_irqsave(&host->lock, flags); if (enable) { /* configure DAT1 gpio if applicable */ if (sdhci_is_valid_gpio_wakeup_int(msm_host)) { ret = enable_irq_wake(msm_host->pdata->sdiowakeup_irq); if (!ret) sdhci_msm_cfg_sdiowakeup_gpio_irq(host, true); goto out; } else { ret = sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_ENWAKE); if (ret) goto out; ret = enable_irq_wake(host->irq); if (ret) sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_DISWAKE); } } else { if (sdhci_is_valid_gpio_wakeup_int(msm_host)) { ret = disable_irq_wake(msm_host->pdata->sdiowakeup_irq); sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false); } else { ret = sdhci_msm_cfg_mpm_pin_wakeup(host, SDC_DAT1_DISWAKE); if (ret) goto out; ret = disable_irq_wake(host->irq); } } out: if (ret) pr_err("%s: %s: %sable wakeup: failed: %d gpio: %d mpm: %d\n", mmc_hostname(host->mmc), __func__, enable ? "en" : "dis", ret, msm_host->pdata->sdiowakeup_irq, msm_host->pdata->mpm_sdiowakeup_int); spin_unlock_irqrestore(&host->lock, flags); return ret; } static int sdhci_msm_runtime_suspend(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 = pltfm_host->priv; int ret; ret = sdhci_msm_cfg_sdio_wakeup(host, true); /* pwr_irq is not monitored by mpm on suspend, hence disable it */ if (!ret) goto skip_disable_host_irq; disable_irq(host->irq); skip_disable_host_irq: disable_irq(msm_host->pwr_irq); /* Loading @@ -3159,10 +3347,17 @@ static int sdhci_msm_runtime_resume(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 = pltfm_host->priv; int ret; ret = sdhci_msm_cfg_sdio_wakeup(host, false); if (!ret) goto skip_enable_host_irq; enable_irq(msm_host->pwr_irq); enable_irq(host->irq); skip_enable_host_irq: enable_irq(msm_host->pwr_irq); return 0; } Loading Loading @@ -3214,6 +3409,26 @@ static int sdhci_msm_resume(struct device *dev) out: return ret; } 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 = pltfm_host->priv; int ret = 0; /* * ksdioirqd may get scheduled after sdhc suspend, hence retry * suspend in case the clocks are ON */ if (atomic_read(&msm_host->clks_on)) { pr_warn("%s: %s: clock ON after suspend, aborting suspend\n", mmc_hostname(host->mmc), __func__); ret = -EAGAIN; } return ret; } #endif #ifdef CONFIG_PM Loading @@ -3221,6 +3436,7 @@ static const struct dev_pm_ops sdhci_msm_pmops = { SET_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend, sdhci_msm_resume) SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume, NULL) .suspend_noirq = sdhci_msm_suspend_noirq, }; #define SDHCI_MSM_PMOPS (&sdhci_msm_pmops) Loading