]> err.no Git - linux-2.6/blobdiff - drivers/mmc/sdhci.c
[MMC] sdhci: proper timeout handling
[linux-2.6] / drivers / mmc / sdhci.c
index 8b811d94371c38c7666f28c7360d4d5f32e837cd..877226e2ffae6b68d81586e277b23bae3b537d8e 100644 (file)
 
 #define BUGMAIL "<sdhci-devel@list.drzeus.cx>"
 
-#ifdef CONFIG_MMC_DEBUG
 #define DBG(f, x...) \
-       printk(KERN_DEBUG DRIVER_NAME " [%s()]: " f, __func__,## x)
-#else
-#define DBG(f, x...) do { } while (0)
-#endif
+       pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x)
 
 static const struct pci_device_id pci_ids[] __devinitdata = {
        /* handle any SD host controller */
@@ -98,12 +94,27 @@ static void sdhci_dumpregs(struct sdhci_host *host)
 
 static void sdhci_reset(struct sdhci_host *host, u8 mask)
 {
+       unsigned long timeout;
+
        writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET);
 
-       if (mask & SDHCI_RESET_ALL) {
+       if (mask & SDHCI_RESET_ALL)
                host->clock = 0;
 
-               mdelay(50);
+       /* Wait max 100 ms */
+       timeout = 100;
+
+       /* hw clears the bit when it's done */
+       while (readb(host->ioaddr + SDHCI_SOFTWARE_RESET) & mask) {
+               if (timeout == 0) {
+                       printk(KERN_ERR "%s: Reset 0x%x never completed. "
+                               "Please report this to " BUGMAIL ".\n",
+                               mmc_hostname(host->mmc), (int)mask);
+                       sdhci_dumpregs(host);
+                       return;
+               }
+               timeout--;
+               mdelay(1);
        }
 }
 
@@ -117,9 +128,6 @@ static void sdhci_init(struct sdhci_host *host)
 
        writel(intmask, host->ioaddr + SDHCI_INT_ENABLE);
        writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE);
-
-       /* This is unknown magic. */
-       writeb(0xE, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
 }
 
 static void sdhci_activate_led(struct sdhci_host *host)
@@ -263,6 +271,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
 static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
 {
        u16 mode;
+       u8 count;
+       unsigned target_timeout, current_timeout;
 
        WARN_ON(host->data);
 
@@ -272,10 +282,41 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
        }
 
        DBG("blksz %04x blks %04x flags %08x\n",
-               1 << data->blksz_bits, data->blocks, data->flags);
+               data->blksz, data->blocks, data->flags);
        DBG("tsac %d ms nsac %d clk\n",
                data->timeout_ns / 1000000, data->timeout_clks);
 
+       /* timeout in us */
+       target_timeout = data->timeout_ns / 1000 +
+               data->timeout_clks / host->clock;
+
+       /*
+        * Figure out needed cycles.
+        * We do this in steps in order to fit inside a 32 bit int.
+        * The first step is the minimum timeout, which will have a
+        * minimum resolution of 6 bits:
+        * (1) 2^13*1000 > 2^22,
+        * (2) host->timeout_clk < 2^16
+        *     =>
+        *     (1) / (2) > 2^6
+        */
+       count = 0;
+       current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+       while (current_timeout < target_timeout) {
+               count++;
+               current_timeout <<= 1;
+               if (count >= 0xF)
+                       break;
+       }
+
+       if (count >= 0xF) {
+               printk(KERN_WARNING "%s: Too large timeout requested!\n",
+                       mmc_hostname(host->mmc));
+               count = 0xE;
+       }
+
+       writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
+
        mode = SDHCI_TRNS_BLK_CNT_EN;
        if (data->blocks > 1)
                mode |= SDHCI_TRNS_MULTI;
@@ -286,7 +327,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
 
        writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE);
 
-       writew(1 << data->blksz_bits, host->ioaddr + SDHCI_BLOCK_SIZE);
+       writew(data->blksz, host->ioaddr + SDHCI_BLOCK_SIZE);
        writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT);
 
        if (host->flags & SDHCI_USE_DMA) {
@@ -298,7 +339,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
 
                writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS);
        } else {
-               host->size = (1 << data->blksz_bits) * data->blocks;
+               host->size = data->blksz * data->blocks;
 
                host->cur_sg = data->sg;
                host->num_sg = data->sg_len;
@@ -339,7 +380,7 @@ static void sdhci_finish_data(struct sdhci_host *host)
                blocks = 0;
        else
                blocks = readw(host->ioaddr + SDHCI_BLOCK_COUNT);
-       data->bytes_xfered = (1 << data->blksz_bits) * (data->blocks - blocks);
+       data->bytes_xfered = data->blksz * (data->blocks - blocks);
 
        if ((data->error == MMC_ERR_NONE) && blocks) {
                printk(KERN_ERR "%s: Controller signalled completion even "
@@ -375,17 +416,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));
@@ -394,8 +435,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);
 
@@ -494,7 +536,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;
@@ -515,17 +557,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);
@@ -534,6 +578,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                                                             *
@@ -574,27 +658,21 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
        spin_lock_irqsave(&host->lock, flags);
 
-       DBG("clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n",
-            ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select,
-            ios->vdd, ios->bus_width);
-
        /*
         * Reset the chip on each power off.
         * Should clear out any weird states.
         */
        if (ios->power_mode == MMC_POWER_OFF) {
                writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE);
-               spin_unlock_irqrestore(&host->lock, flags);
                sdhci_init(host);
-               spin_lock_irqsave(&host->lock, flags);
        }
 
        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)
@@ -1038,18 +1116,50 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
        else /* XXX: Hack to get MMC layer to avoid highmem */
                pdev->dma_mask = 0;
 
-       host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
+       host->max_clk =
+               (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
+       if (host->max_clk == 0) {
+               printk(KERN_ERR "%s: Hardware doesn't specify base clock "
+                       "frequency.\n", host->slot_descr);
+               ret = -ENODEV;
+               goto unmap;
+       }
        host->max_clk *= 1000000;
 
+       host->timeout_clk =
+               (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
+       if (host->timeout_clk == 0) {
+               printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "
+                       "frequency.\n", host->slot_descr);
+               ret = -ENODEV;
+               goto unmap;
+       }
+       if (caps & SDHCI_TIMEOUT_CLK_UNIT)
+               host->timeout_clk *= 1000;
+
        /*
         * Set host parameters.
         */
        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);
 
        /*
@@ -1081,12 +1191,12 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
        tasklet_init(&host->finish_tasklet,
                sdhci_tasklet_finish, (unsigned long)host);
 
-       setup_timer(&host->timer, sdhci_timeout_timer, (int)host);
+       setup_timer(&host->timer, sdhci_timeout_timer, (long)host);
 
        ret = request_irq(host->irq, sdhci_irq, SA_SHIRQ,
                host->slot_descr, host);
        if (ret)
-               goto unmap;
+               goto untasklet;
 
        sdhci_init(host);
 
@@ -1105,10 +1215,10 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
 
        return 0;
 
-unmap:
+untasklet:
        tasklet_kill(&host->card_tasklet);
        tasklet_kill(&host->finish_tasklet);
-
+unmap:
        iounmap(host->ioaddr);
 release:
        pci_release_region(pdev, host->bar);
@@ -1152,13 +1262,18 @@ static int __devinit sdhci_probe(struct pci_dev *pdev,
        const struct pci_device_id *ent)
 {
        int ret, i;
-       u8 slots;
+       u8 slots, rev;
        struct sdhci_chip *chip;
 
        BUG_ON(pdev == NULL);
        BUG_ON(ent == NULL);
 
-       DBG("found at %s\n", pci_name(pdev));
+       pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
+
+       printk(KERN_INFO DRIVER_NAME
+               ": SDHCI controller found at %s [%04x:%04x] (rev %x)\n",
+               pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
+               (int)rev);
 
        ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots);
        if (ret)