]> err.no Git - linux-2.6/blobdiff - drivers/mmc/sdhci.c
[MMC] sdhci: fix timeout loops in sdhci
[linux-2.6] / drivers / mmc / sdhci.c
index 405b6158cb6c0e65600cb0ea8630d4f407c0733e..007e825dcb9378e9ab5bd409add0f49fc550c866 100644 (file)
@@ -371,17 +371,17 @@ static void sdhci_finish_data(struct sdhci_host *host)
 static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 {
        int flags;
-       u32 present;
-       unsigned long max_jiffies;
+       unsigned long timeout;
 
        WARN_ON(host->cmd);
 
        DBG("Sending cmd (%x)\n", cmd->opcode);
 
        /* Wait max 10 ms */
-       max_jiffies = jiffies + (HZ + 99)/100;
-       do {
-               if (time_after(jiffies, max_jiffies)) {
+       timeout = 10;
+       while (readl(host->ioaddr + SDHCI_PRESENT_STATE) &
+               (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) {
+               if (timeout == 0) {
                        printk(KERN_ERR "%s: Controller never released "
                                "inhibit bits. Please report this to "
                                BUGMAIL ".\n", mmc_hostname(host->mmc));
@@ -390,8 +390,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
                        tasklet_schedule(&host->finish_tasklet);
                        return;
                }
-               present = readl(host->ioaddr + SDHCI_PRESENT_STATE);
-       } while (present & (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT));
+               timeout--;
+               mdelay(1);
+       }
 
        mod_timer(&host->timer, jiffies + 10 * HZ);
 
@@ -490,7 +491,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 {
        int div;
        u16 clk;
-       unsigned long max_jiffies;
+       unsigned long timeout;
 
        if (clock == host->clock)
                return;
@@ -511,17 +512,19 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
        writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
 
        /* Wait max 10 ms */
-       max_jiffies = jiffies + (HZ + 99)/100;
-       do {
-               if (time_after(jiffies, max_jiffies)) {
+       timeout = 10;
+       while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL))
+               & SDHCI_CLOCK_INT_STABLE)) {
+               if (timeout == 0) {
                        printk(KERN_ERR "%s: Internal clock never stabilised. "
                                "Please report this to " BUGMAIL ".\n",
                                mmc_hostname(host->mmc));
                        sdhci_dumpregs(host);
                        return;
                }
-               clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL);
-       } while (!(clk & SDHCI_CLOCK_INT_STABLE));
+               timeout--;
+               mdelay(1);
+       }
 
        clk |= SDHCI_CLOCK_CARD_EN;
        writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
@@ -530,6 +533,46 @@ out:
        host->clock = clock;
 }
 
+static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
+{
+       u8 pwr;
+
+       if (host->power == power)
+               return;
+
+       writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
+
+       if (power == (unsigned short)-1)
+               goto out;
+
+       pwr = SDHCI_POWER_ON;
+
+       switch (power) {
+       case MMC_VDD_170:
+       case MMC_VDD_180:
+       case MMC_VDD_190:
+               pwr |= SDHCI_POWER_180;
+               break;
+       case MMC_VDD_290:
+       case MMC_VDD_300:
+       case MMC_VDD_310:
+               pwr |= SDHCI_POWER_300;
+               break;
+       case MMC_VDD_320:
+       case MMC_VDD_330:
+       case MMC_VDD_340:
+               pwr |= SDHCI_POWER_330;
+               break;
+       default:
+               BUG();
+       }
+
+       writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL);
+
+out:
+       host->power = power;
+}
+
 /*****************************************************************************\
  *                                                                           *
  * MMC callbacks                                                             *
@@ -584,9 +627,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        sdhci_set_clock(host, ios->clock);
 
        if (ios->power_mode == MMC_POWER_OFF)
-               writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
+               sdhci_set_power(host, -1);
        else
-               writeb(0xFF, host->ioaddr + SDHCI_POWER_CONTROL);
+               sdhci_set_power(host, ios->vdd);
 
        ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
        if (ios->bus_width == MMC_BUS_WIDTH_4)
@@ -1046,9 +1089,23 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
        mmc->ops = &sdhci_ops;
        mmc->f_min = host->max_clk / 256;
        mmc->f_max = host->max_clk;
-       mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
        mmc->caps = MMC_CAP_4_BIT_DATA;
 
+       mmc->ocr_avail = 0;
+       if (caps & SDHCI_CAN_VDD_330)
+               mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
+       else if (caps & SDHCI_CAN_VDD_300)
+               mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31;
+       else if (caps & SDHCI_CAN_VDD_180)
+               mmc->ocr_avail |= MMC_VDD_17_18|MMC_VDD_18_19;
+
+       if (mmc->ocr_avail == 0) {
+               printk(KERN_ERR "%s: Hardware doesn't report any "
+                       "support voltages.\n", host->slot_descr);
+               ret = -ENODEV;
+               goto unmap;
+       }
+
        spin_lock_init(&host->lock);
 
        /*