static int sdhci_pltfm_suspend(struct device *device) { struct sdio_dev *dev = platform_get_drvdata(to_platform_device(device)); struct sdhci_host *host = dev->host; int ret = 0; if (!sdhci_pltfm_rpm_enabled(dev)) { ret = sdhci_pltfm_clk_enable(dev, 1); if (ret) { dev_err(dev->dev, "Failed to enable clock during suspend\n"); return -EAGAIN; } } host->mmc->pm_flags |= host->mmc->pm_caps; ret = sdhci_suspend_host(host); if (ret) { dev_err(dev->dev, "Unable to suspend sdhci host err=%d\n", ret); return ret; } if (sdhci_pltfm_rpm_enabled(dev)) { /* * Note that we havent done a get_sync. The * pm core takes care of that. */ pm_runtime_put_sync_suspend(dev->dev); } else { ret = sdhci_pltfm_clk_enable(dev, 0); if (ret) { dev_err(dev->dev, "Failed to disable clock during suspend\n"); /* Not really a big error to cry and return */ } } if(dev->devtype == SDIO_DEV_TYPE_SDMMC) { printk(KERN_ERR "mmc regulator off.. delay 50ms added\n"); mdelay(50);// this is Samsung internal specification. } dev->suspended = 1; return 0; }
static int sdhci_pltfm_runtime_resume(struct device *device) { int ret = 0; unsigned long flags; struct sdio_dev *dev = platform_get_drvdata(to_platform_device(device)); struct sdhci_host *host = dev->host; /* This is never going to happen, but still */ if (!sdhci_pltfm_rpm_enabled(dev)) { dev_err(dev->dev, "Spurious rpm resume call\n"); /* But no menaing in returning error */ return 0; } spin_lock_irqsave(&host->lock, flags); host->runtime_suspended = false; spin_unlock_irqrestore(&host->lock, flags); ret = sdhci_pltfm_clk_enable(dev, 1); if (ret) { dev_err(dev->dev, "Failed to enable clock during run time resume\n"); return -EAGAIN; } return 0; }
static int sdhci_pltfm_resume(struct device *device) { struct sdio_dev *dev = platform_get_drvdata(to_platform_device(device)); struct sdhci_host *host = dev->host; int ret = 0; if (sdhci_pltfm_rpm_enabled(dev)) { /* * Note that we havent done a put_sync. The * pm core takes care of that. */ pm_runtime_get_sync(dev->dev); } else { ret = sdhci_pltfm_clk_enable(dev, 1); if (ret) { dev_err(dev->dev, "Failed to enable clock during resume\n"); return -EAGAIN; } } ret = sdhci_resume_host(host); if (ret) { dev_err(dev->dev, "Unable to resume sdhci host err=%d\n", ret); return ret; } if (!sdhci_pltfm_rpm_enabled(dev)) { ret = sdhci_pltfm_clk_enable(dev, 0); if (ret) { dev_err(dev->dev, "Failed to disable clock during resume\n"); /* Not really a big error to cry and return*/ } } dev->suspended = 0; if (dev->devtype == SDIO_DEV_TYPE_EMMC) host->mmc->pm_flags |= MMC_PM_KEEP_POWER; return 0; }
static void __devexit sdhci_pltfm_runtime_pm_forbid(struct device *device) { struct sdio_dev *dev = platform_get_drvdata(to_platform_device(device)); if (!sdhci_pltfm_rpm_enabled(dev)) return; pm_runtime_forbid(device); pm_runtime_get_noresume(device); pm_runtime_disable(device); }
static void sdhci_pltfm_shutdown(struct platform_device *pdev) { u16 clk; int ret; struct sdio_dev *dev = platform_get_drvdata(pdev); struct sdhci_host *host = dev->host; if (sdhci_pltfm_rpm_enabled(dev)) { pm_runtime_get_sync(dev->dev); } else { ret = sdhci_pltfm_clk_enable(dev, 1); if (ret) dev_err(dev->dev, "enable clock during shutdown failed\n"); } /* Certain cards don't like abrupt clock * shutdown, and they go insane if we do so. * When PM runtime autosuspend is enabled, * it takes time for the clock to be cut, * but during this time, the system reboot * can abruptly cut it off. Avoid that by * disabling the clock to the card. */ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); clk &= ~SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); if (sdhci_pltfm_rpm_enabled(dev)) { pm_runtime_put_sync_suspend(dev->dev); } else { ret = sdhci_pltfm_clk_enable(dev, 0); if (ret) dev_err(dev->dev, "disable clock during shutdown failed\n"); } }
static void __devinit sdhci_pltfm_runtime_pm_init(struct device *device) { struct sdio_dev *dev = platform_get_drvdata(to_platform_device(device)); if (!sdhci_pltfm_rpm_enabled(dev)) return; pm_runtime_irq_safe(device); pm_runtime_enable(device); if (dev->devtype == SDIO_DEV_TYPE_WIFI) pm_runtime_set_autosuspend_delay(device, KONA_MMC_WIFI_AUTOSUSPEND_DELAY); else pm_runtime_set_autosuspend_delay(device, KONA_MMC_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(device); }
/* * Software emulation of the SD card insertion/removal. Set insert=1 for insert * and insert=0 for removal */ static int bcm_kona_sd_card_emulate(struct sdio_dev *dev, int insert) { struct sdhci_host *host = dev->host; uint32_t val; unsigned long flags; int ret = 0; if(strcmp("mmc2", mmc_hostname(host->mmc)) == 0) printk ("PNN: [%s]sd_card_emulate : %s\n",mmc_hostname(host->mmc), insert ? "insert" : "removal"); #ifndef CONFIG_ARCH_ISLAND /* * The clock enabled here will be disabled in * sdhci_tasklet_card */ if (sdhci_pltfm_rpm_enabled(dev)) { /* * The code below can be executed just * after a device resume. pm core * enables the runtime pm for the device * when the resume callback returns. * It is possible that we reach here before * that. One option is to wait for the RPM * to be enabled, but this can create an * obvious issue when we perform card * insert/removal during the probe. We cant * remove the pm_runtime_get_sync with a * direct clock enable here, because the * sdhci_irq will complain about "interrupt * while runtime suspened". We cant even check * for pm_runtime status and take an appropriate * action, because if the status changes by the * time the clcok disable is done in card tasklet, * it will result in unbalanced RPM usage. Moreover * why to wait for something in an ISR when we have * an option. * So a better option is to do both. */ pm_runtime_get_sync(dev->dev); } /* Enable clock once irrespective of RPM state */ ret = sdhci_pltfm_clk_enable(dev, 1); if (ret) { dev_err(dev->dev, "enable clock during card emulate failed\n"); return -EAGAIN; } #endif /* this function can be called from various contexts including ISR */ spin_lock_irqsave(&host->lock, flags); /* Ensure SD bus scanning to detect media change */ host->mmc->rescan_disable = 0; /* Back-to-Back register write needs a delay of min 10uS. * We keep 20uS */ udelay(20); val = sdhci_readl(host, KONA_SDHOST_CORESTAT); if (insert) { val |= KONA_SDHOST_CD_SW; sdhci_writel(host, val, KONA_SDHOST_CORESTAT); } else { val &= ~KONA_SDHOST_CD_SW; sdhci_writel(host, val, KONA_SDHOST_CORESTAT); } spin_unlock_irqrestore(&host->lock, flags); return 0; }
static int sdhci_rpm_enabled(struct sdhci_host *host) { struct sdio_dev *dev = sdhci_priv(host); return sdhci_pltfm_rpm_enabled(dev); }
static int __devexit sdhci_pltfm_remove(struct platform_device *pdev) { struct sdio_dev *dev = platform_get_drvdata(pdev); struct sdhci_host *host = dev->host; struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); int dead; u32 scratch; int ret = 0; atomic_set(&dev->initialized, 0); gDevs[dev->devtype] = NULL; printk("%s",__func__); if (dev->devtype == SDIO_DEV_TYPE_SDMMC && dev->cd_gpio >= 0) { free_irq(gpio_to_irq(dev->cd_gpio), dev); gpio_free(dev->cd_gpio); } if (dev->vdd_sdxc_regulator && dev->devtype == SDIO_DEV_TYPE_SDMMC) { /* Playing safe- if regulator is enabled, disable it first */ if (regulator_is_enabled(dev->vdd_sdxc_regulator) > 0) regulator_disable(dev->vdd_sdxc_regulator); regulator_put(dev->vdd_sdxc_regulator); } proc_term(pdev); if (sdhci_pltfm_rpm_enabled(dev)) { pm_runtime_get_sync(dev->dev); } else { ret = sdhci_pltfm_clk_enable(dev, 1); if (ret) dev_err(dev->dev, "enable clock during pltfm remove failed\n"); } dead = 0; scratch = readl(host->ioaddr + SDHCI_INT_STATUS); if (scratch == (u32)-1) dead = 1; sdhci_remove_host(host, dead); if (sdhci_pltfm_rpm_enabled(dev)) { pm_runtime_put_sync_suspend(dev->dev); } else { ret = sdhci_pltfm_clk_enable(dev, 0); if (ret) dev_err(dev->dev, "disable clock during pltfm remove failed\n"); } #if !defined(CONFIG_MACH_BCM2850_FPGA) && !defined(CONFIG_MACH_BCM_FPGA) clk_disable(dev->sleep_clk); clk_put(dev->sleep_clk); clk_put(dev->peri_clk); #endif sdhci_pltfm_runtime_pm_forbid(dev->dev); kfree(dev->cd_int_wake_lock_name); wake_lock_destroy(&dev->cd_int_wake_lock); platform_set_drvdata(pdev, NULL); if (dev->devtype == SDIO_DEV_TYPE_EMMC || dev->devtype == SDIO_DEV_TYPE_SDMMC) { kfree(pdev->dev.platform_data); pdev->dev.platform_data = NULL; } kfree(dev); iounmap(host->ioaddr); release_mem_region(iomem->start, resource_size(iomem)); sdhci_free_host(host); return 0; }