fix-glamo-mci-slow-clock-until-first-bulk.patch
authorAndy Green <andy@openmoko.com>
Mon, 22 Sep 2008 21:38:03 +0000 (22:38 +0100)
committerAndy Green <agreen@pads.home.warmcat.com>
Mon, 22 Sep 2008 21:38:03 +0000 (22:38 +0100)
This patch adds another module parameter to glamo-mci which sets the
SD Card clock rate used inbetween powering the card and the completion of
the first bulk transfer.  You can set it from kernel commandline like this.

glamo_mci.sd_post_power_clock=1000000

The period between changing the power state and the first bulk transfer
completion is critical because larger SDHC cards take longer to initialize
before they can service the bulk transfer, and the Glamo MMC unit has a
fixed timeout length of a maximum of 4095 x 16 x SD Card clocks.  Large
cards like 8GB Sandisk SDHC are not ready before this timeout is used up
at default 16MHz.

Subsequently, the card can handle 16MHz SD Clock and timeout durations
okay.

By default this patch operates the SD Clock at only 1MHz until the first
bulk transfer is completed after each powerup action from the MCI stack.  It
also keeps the SD Clock running during this time, and disables the SD Clock
if the card is not present and the MCI stack removes power.

Signed-off-by: Andy Green <andy@openmoko.com>

drivers/mfd/glamo/glamo-mci.c
drivers/mfd/glamo/glamo-mci.h

index 3eece08..cff43db 100644 (file)
@@ -74,6 +74,23 @@ static int sd_slow_ratio = 8;
 module_param(sd_slow_ratio, int, 0644);
 
 /*
+ * Post-power SD clock rate
+ *
+ * you can override this on kernel commandline using
+ *
+ *   glamo_mci.sd_post_power_clock=1000000
+ *
+ * for example
+ *
+ * After changing power to card, clock is held at this rate until first bulk
+ * transfer completes
+ */
+
+static int sd_post_power_clock = 1000000;
+module_param(sd_post_power_clock, int, 0644);
+
+
+/*
  * SD Signal drive strength
  *
  * you can override this on kernel commandline using
@@ -240,21 +257,35 @@ static void __glamo_mci_fix_card_div(struct glamo_mci_host *host, int div)
                writew(readw(glamo_mci_def_pdata.pglamo->base +
                     GLAMO_REG_CLOCK_GEN5_1) & (~GLAMO_CLOCK_GEN51_EN_DIV_TCLK),
                     glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
-       } else {
-               /* set the nearest prescaler factor
-               *
-               * register shared with SCLK divisor -- no chance of race because
-               * we don't use sensor interface
-               */
-               writew_dly((readw(glamo_mci_def_pdata.pglamo->base +
-                               GLAMO_REG_CLOCK_GEN8) & 0xff00) | div,
-                      glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN8);
-               /* enable clock to divider input */
-               writew_dly(readw(glamo_mci_def_pdata.pglamo->base +
-                       GLAMO_REG_CLOCK_GEN5_1) | GLAMO_CLOCK_GEN51_EN_DIV_TCLK,
-                    glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
+
+               goto done;
        }
 
+       if (host->force_slow_during_powerup)
+               div = host->clk_rate / sd_post_power_clock;
+       else
+               if (host->pdata->glamo_mci_use_slow)
+                       if ((host->pdata->glamo_mci_use_slow)())
+                               div = div * sd_slow_ratio;
+
+       if (div > 255)
+               div = 255;
+
+       /*
+        * set the nearest prescaler factor
+        *
+        * register shared with SCLK divisor -- no chance of race because
+        * we don't use sensor interface
+        */
+       writew_dly((readw(glamo_mci_def_pdata.pglamo->base +
+                       GLAMO_REG_CLOCK_GEN8) & 0xff00) | div,
+               glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN8);
+       /* enable clock to divider input */
+       writew_dly(readw(glamo_mci_def_pdata.pglamo->base +
+               GLAMO_REG_CLOCK_GEN5_1) | GLAMO_CLOCK_GEN51_EN_DIV_TCLK,
+               glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
+
+done:
        spin_unlock_irqrestore(&clock_lock, flags);
 }
 
@@ -284,7 +315,7 @@ static int __glamo_mci_set_card_clock(struct glamo_mci_host *host, int freq,
                if (division)
                        *division = 0xff;
 
-               if (!sd_idleclk)
+               if (!sd_idleclk && !host->force_slow_during_powerup)
                        /* clock off */
                        __glamo_mci_fix_card_div(host, -1);
        }
@@ -327,6 +358,10 @@ static void glamo_mci_irq(unsigned int irq, struct irq_desc *desc)
                goto done;
        }
 
+       /* disable the initial slow start after first bulk transfer */
+       if (host->force_slow_during_powerup)
+               host->force_slow_during_powerup--;
+
        if (host->pio_active == XFER_READ)
                do_pio_read(host);
 
@@ -341,7 +376,7 @@ static void glamo_mci_irq(unsigned int irq, struct irq_desc *desc)
                host->cmd_is_stop = 0;
        }
 
-       if (!sd_idleclk)
+       if (!sd_idleclk && !host->force_slow_during_powerup)
                /* clock off */
                __glamo_mci_fix_card_div(host, -1);
 
@@ -357,7 +392,6 @@ static int glamo_mci_send_command(struct glamo_mci_host *host,
 {
        u8 u8a[6];
        u16 fire = 0;
-       u16 timeout = 0xfff; /* max glamo MMC timeout, in units of 16 clocks */
 
        /* if we can't do it, reject as busy */
        if (!readw_dly(host->base + GLAMO_REG_MMC_RB_STAT1) &
@@ -467,15 +501,9 @@ static int glamo_mci_send_command(struct glamo_mci_host *host,
                fire |= GLAMO_FIRE_MMC_CC_BASIC; /* "basic command" */
                break;
        }
-       /* enforce timeout, clipping at default 65520 clocks if larger */
-       if (cmd->data)
-               /* so long as there is one... */
-               if (cmd->data->timeout_clks &&
-                   /* ... and it is not longer than we can handle */
-                   (cmd->data->timeout_clks <= 0xffff))
-                       timeout = cmd->data->timeout_clks >> 4; /* / 16 clks */
 
-       writew(timeout, host->base + GLAMO_REG_MMC_TIMEOUT);
+       /* always largest timeout */
+       writew(0xfff, host->base + GLAMO_REG_MMC_TIMEOUT);
 
        /* Generate interrupt on txfer */
        writew_dly((readw_dly(host->base + GLAMO_REG_MMC_BASIC) & 0x3e) |
@@ -576,14 +604,7 @@ static void glamo_mci_send_request(struct mmc_host *mmc)
        /* resume requested clock rate
         * scale it down by sd_slow_ratio if platform requests it
         */
-       if (host->pdata->glamo_mci_use_slow)
-               if ((host->pdata->glamo_mci_use_slow)())
-                       __glamo_mci_fix_card_div(host, host->clk_div *
-                                                                sd_slow_ratio);
-               else
-                       __glamo_mci_fix_card_div(host, host->clk_div);
-       else
-               __glamo_mci_fix_card_div(host, host->clk_div);
+       __glamo_mci_fix_card_div(host, host->clk_div);
 
        if (glamo_mci_send_command(host, cmd))
                goto bail;
@@ -633,6 +654,7 @@ static void glamo_mci_send_request(struct mmc_host *mmc)
                goto done;
        if (!(cmd->data->flags & (MMC_DATA_READ | MMC_DATA_WRITE)))
                goto done;
+
        /*
         * Otherwise can can use the interrupt as async completion --
         * if there is read data coming, or we wait for write data to complete,
@@ -676,7 +698,7 @@ done:
        host->mrq = NULL;
        mmc_request_done(host->mmc, cmd->mrq);
 bail:
-       if (!sd_idleclk)
+       if (!sd_idleclk && !host->force_slow_during_powerup)
                /* stop the clock to card */
                __glamo_mci_fix_card_div(host, -1);
 }
@@ -718,6 +740,12 @@ static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        switch(ios->power_mode) {
        case MMC_POWER_ON:
        case MMC_POWER_UP:
+               /*
+                * we should use very slow clock until first bulk
+                * transfer completes OK
+                */
+               host->force_slow_during_powerup = 1;
+
                if (host->vdd_current != ios->vdd) {
                        host->pdata->glamo_set_mci_power(ios->power_mode,
                                                         ios->vdd);
@@ -734,6 +762,9 @@ static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        default:
                if (host->power_mode_current == MMC_POWER_OFF)
                        break;
+               /* never want clocking with dead card */
+               __glamo_mci_fix_card_div(host, -1);
+
                glamo_engine_disable(glamo_mci_def_pdata.pglamo,
                                     GLAMO_ENGINE_MMC);
                host->pdata->glamo_set_mci_power(MMC_POWER_OFF, 0);
@@ -751,7 +782,7 @@ static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        if (powering)
                msleep(1);
 
-       if (!sd_idleclk)
+       if (!sd_idleclk && !host->force_slow_during_powerup)
                /* stop the clock to card, because we are idle until transfer */
                __glamo_mci_fix_card_div(host, -1);
 
@@ -954,14 +985,7 @@ static int glamo_mci_suspend(struct platform_device *dev, pm_message_t state)
         * make sure the clock was running during suspend and consequently
         * resume
         */
-       if (host->pdata->glamo_mci_use_slow)
-               if ((host->pdata->glamo_mci_use_slow)())
-                       __glamo_mci_fix_card_div(host, host->clk_div *
-                                                                sd_slow_ratio);
-               else
-                       __glamo_mci_fix_card_div(host, host->clk_div);
-       else
-               __glamo_mci_fix_card_div(host, host->clk_div);
+       __glamo_mci_fix_card_div(host, host->clk_div);
 
        /* we are going to do more commands to override this in
         * mmc_suspend_host(), so we need to change sd_idleclk for the
index f3f170e..38f6376 100644 (file)
@@ -44,6 +44,8 @@ struct glamo_mci_host {
        unsigned long           real_rate;
        u8                      prescaler;
 
+       int                     force_slow_during_powerup;
+
        unsigned                sdiimsk;
        int                     dodma;