sdhci: attach fixed regulator to sdhci host controller.
authorAymen Zayet <aymenx.zayet@intel.com>
Tue, 22 Nov 2011 13:45:12 +0000 (14:45 +0100)
committerbuildbot <buildbot@intel.com>
Mon, 28 Nov 2011 19:38:56 +0000 (11:38 -0800)
BZ: 15061

The WIFI requires a fixed regulator to enable / disable the chip.
So in the early device initialization, the regulator shall be requested
for the sdhci host controller.

Change-Id: I1e2b779c85f7cf14e2d24a3ed1bc651e8b0c8805
Signed-off-by: Aymen Zayet <aymenx.zayet@intel.com>
Reviewed-on: http://android.intel.com:8080/25381
Reviewed-by: Champciaux, NicolasX <nicolasx.champciaux@intel.com>
Tested-by: Champciaux, NicolasX <nicolasx.champciaux@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
arch/x86/platform/mrst/mrst.c
drivers/mmc/host/sdhci-pci.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h

index 33200f2..d58e7c4 100644 (file)
@@ -1343,6 +1343,7 @@ static struct platform_device wl12xx_vwlan_device = {
 #define ICDK_BOARD_REF_CLK 26000000
 #define NCDK_BOARD_REF_CLK 38400000
 
+extern int sdhci_pci_request_regulators(void);
 void __init wl12xx_platform_data_init_post_scu(void *info)
 {
        struct sd_board_info *sd_info = info;
@@ -1401,6 +1402,8 @@ void __init wl12xx_platform_data_init_post_scu(void *info)
        err = platform_device_register(&wl12xx_vwlan_device);
        if (err < 0)
                pr_err("error platform_device_register\n");
+
+       sdhci_pci_request_regulators();
 }
 
 void __init *wl12xx_platform_data_init(void *info)
index 90b4774..fade9fc 100644 (file)
@@ -1019,6 +1019,53 @@ MODULE_DEVICE_TABLE(pci, pci_ids);
  *                                                                           *
 \*****************************************************************************/
 
+static int try_request_regulator(struct device *dev, void *data)
+{
+       struct pci_dev        *pdev = container_of(dev, struct pci_dev, dev);
+       struct sdhci_pci_chip *chip;
+       struct sdhci_pci_slot *slot;
+       struct sdhci_host     *host;
+       int i;
+
+       chip = pci_get_drvdata(pdev);
+       if (!chip)
+               return 0;
+
+       for (i = 0; i < chip->num_slots; i++) {
+               slot = chip->slots[i];
+               if (!slot)
+                       continue;
+               host = slot->host;
+               if (!host)
+                       continue;
+               if (sdhci_try_get_regulator(host) == 0)
+                       mmc_detect_change(host->mmc, 0);
+       }
+       return 0;
+}
+
+static struct pci_driver sdhci_driver;
+
+/**
+ *      sdhci_pci_request_regulators - retry requesting regulators of
+ *                                     all sdhci-pci devices
+ *
+ *      One some platforms, the regulators associated to the mmc are available
+ *      late in the boot.
+ *      sdhci_pci_request_regulators() is called by platform code to retry
+ *      getting the regulators associated to pci sdhcis
+ */
+
+int sdhci_pci_request_regulators(void)
+{
+       /* driver not yet registered */
+       if (!sdhci_driver.driver.p)
+               return 0;
+       return driver_for_each_device(&sdhci_driver.driver,
+                                     NULL, NULL, try_request_regulator);
+}
+EXPORT_SYMBOL_GPL(sdhci_pci_request_regulators);
+
 static int sdhci_pci_enable_dma(struct sdhci_host *host)
 {
        struct sdhci_pci_slot *slot;
index ae22f2c..19bd321 100644 (file)
@@ -2770,6 +2770,36 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev,
 
 EXPORT_SYMBOL_GPL(sdhci_alloc_host);
 
+/**
+ *      sdhci_pci_request_regulators - try requesting regulator of
+ *                                     a sdhci device
+ *
+ *      We take care of race conditions here between sdhci_add_host() (probe)
+ *      and platform code that may kick a retry at anytime during boot.
+ */
+int sdhci_try_get_regulator(struct sdhci_host *host)
+{
+       struct regulator *vmmc;
+       unsigned long flags;
+       if (!host->vmmc) {
+               vmmc = regulator_get(mmc_dev(host->mmc), "vmmc");
+               if (!IS_ERR(vmmc)) {
+                       spin_lock_irqsave(&host->lock, flags);
+                       if (!host->vmmc) {
+                               host->vmmc = vmmc;
+                               spin_unlock_irqrestore(&host->lock, flags);
+                               return 0;
+                       } else {
+                         /* race! we got the regulator twice */
+                               spin_unlock_irqrestore(&host->lock, flags);
+                               regulator_put(vmmc);
+                       }
+               }
+       }
+       return -EAGAIN;
+}
+EXPORT_SYMBOL_GPL(sdhci_try_get_regulator);
+
 int sdhci_add_host(struct sdhci_host *host)
 {
        struct mmc_host *mmc;
@@ -3174,13 +3204,7 @@ int sdhci_add_host(struct sdhci_host *host)
        if (ret)
                goto untasklet;
 
-       host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
-       if (IS_ERR(host->vmmc)) {
-               printk(KERN_INFO "%s: no vmmc regulator found\n", mmc_hostname(mmc));
-               host->vmmc = NULL;
-       } else {
-               regulator_enable(host->vmmc);
-       }
+       sdhci_try_get_regulator(host);
 
        sdhci_do_acquire_ownership(mmc);
 
index 0a5b654..46a992f 100644 (file)
@@ -372,6 +372,7 @@ static inline void *sdhci_priv(struct sdhci_host *host)
 extern void sdhci_card_detect(struct sdhci_host *host);
 extern int sdhci_add_host(struct sdhci_host *host);
 extern void sdhci_remove_host(struct sdhci_host *host, int dead);
+extern int sdhci_try_get_regulator(struct sdhci_host *host);
 
 #ifdef CONFIG_PM
 extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state);