Merge branch 'for-rmk' of git://git.pengutronix.de/git/imx/linux-2.6 into devel
[safe/jmp/linux-2.6] / drivers / mmc / host / mmci.c
index 626ac08..a663429 100644 (file)
@@ -391,6 +391,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
 static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
        struct mmci_host *host = mmc_priv(mmc);
+       unsigned long flags;
 
        WARN_ON(host->mrq != NULL);
 
@@ -402,7 +403,7 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
                return;
        }
 
-       spin_lock_irq(&host->lock);
+       spin_lock_irqsave(&host->lock, flags);
 
        host->mrq = mrq;
 
@@ -411,7 +412,7 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
        mmci_start_command(host, mrq->cmd, 0);
 
-       spin_unlock_irq(&host->lock);
+       spin_unlock_irqrestore(&host->lock, flags);
 }
 
 static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@@ -425,10 +426,12 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                        host->cclk = host->mclk;
                } else {
                        clk = host->mclk / (2 * ios->clock) - 1;
-                       if (clk > 256)
+                       if (clk >= 256)
                                clk = 255;
                        host->cclk = host->mclk / (2 * (clk + 1));
                }
+               if (host->hw_designer == 0x80)
+                       clk |= MCI_FCEN; /* Bug fix in ST IP block */
                clk |= MCI_CLK_ENABLE;
        }
 
@@ -439,15 +442,27 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        case MMC_POWER_OFF:
                break;
        case MMC_POWER_UP:
-               pwr |= MCI_PWR_UP;
-               break;
+               /* The ST version does not have this, fall through to POWER_ON */
+               if (host->hw_designer != 0x80) {
+                       pwr |= MCI_PWR_UP;
+                       break;
+               }
        case MMC_POWER_ON:
                pwr |= MCI_PWR_ON;
                break;
        }
 
-       if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
-               pwr |= MCI_ROD;
+       if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
+               if (host->hw_designer != 0x80)
+                       pwr |= MCI_ROD;
+               else {
+                       /*
+                        * The ST Micro variant use the ROD bit for something
+                        * else and only has OD (Open Drain).
+                        */
+                       pwr |= MCI_OD;
+               }
+       }
 
        writel(clk, host->base + MMCICLOCK);
 
@@ -475,7 +490,7 @@ static void mmci_check_status(unsigned long data)
        mod_timer(&host->timer, jiffies + HZ);
 }
 
-static int mmci_probe(struct amba_device *dev, void *id)
+static int __devinit mmci_probe(struct amba_device *dev, void *id)
 {
        struct mmc_platform_data *plat = dev->dev.platform_data;
        struct mmci_host *host;
@@ -499,7 +514,13 @@ static int mmci_probe(struct amba_device *dev, void *id)
        }
 
        host = mmc_priv(mmc);
-       host->clk = clk_get(&dev->dev, "MCLK");
+       /* Bits 12 thru 19 is the designer */
+       host->hw_designer = (dev->periphid >> 12) & 0xff;
+       /* Bits 20 thru 23 is the revison */
+       host->hw_revision = (dev->periphid >> 20) & 0xf;
+       DBG(host, "designer ID = 0x%02x\n", host->hw_designer);
+       DBG(host, "revision = 0x%01x\n", host->hw_revision);
+       host->clk = clk_get(&dev->dev, NULL);
        if (IS_ERR(host->clk)) {
                ret = PTR_ERR(host->clk);
                host->clk = NULL;
@@ -512,6 +533,18 @@ static int mmci_probe(struct amba_device *dev, void *id)
 
        host->plat = plat;
        host->mclk = clk_get_rate(host->clk);
+       /*
+        * According to the spec, mclk is max 100 MHz,
+        * so we try to adjust the clock down to this,
+        * (if possible).
+        */
+       if (host->mclk > 100000000) {
+               ret = clk_set_rate(host->clk, 100000000);
+               if (ret < 0)
+                       goto clk_disable;
+               host->mclk = clk_get_rate(host->clk);
+               DBG(host, "eventual mclk rate: %u Hz\n", host->mclk);
+       }
        host->mmc = mmc;
        host->base = ioremap(dev->res.start, SZ_4K);
        if (!host->base) {
@@ -523,7 +556,6 @@ static int mmci_probe(struct amba_device *dev, void *id)
        mmc->f_min = (host->mclk + 511) / 512;
        mmc->f_max = min(host->mclk, fmax);
        mmc->ocr_avail = plat->ocr_mask;
-       mmc->caps = MMC_CAP_MULTIWRITE;
 
        /*
         * We can do SGIO
@@ -601,7 +633,7 @@ static int mmci_probe(struct amba_device *dev, void *id)
        return ret;
 }
 
-static int mmci_remove(struct amba_device *dev)
+static int __devexit mmci_remove(struct amba_device *dev)
 {
        struct mmc_host *mmc = amba_get_drvdata(dev);
 
@@ -681,6 +713,15 @@ static struct amba_id mmci_ids[] = {
                .id     = 0x00041181,
                .mask   = 0x000fffff,
        },
+       /* ST Micro variants */
+       {
+               .id     = 0x00180180,
+               .mask   = 0x00ffffff,
+       },
+       {
+               .id     = 0x00280180,
+               .mask   = 0x00ffffff,
+       },
        { 0, 0 },
 };
 
@@ -689,7 +730,7 @@ static struct amba_driver mmci_driver = {
                .name   = DRIVER_NAME,
        },
        .probe          = mmci_probe,
-       .remove         = mmci_remove,
+       .remove         = __devexit_p(mmci_remove),
        .suspend        = mmci_suspend,
        .resume         = mmci_resume,
        .id_table       = mmci_ids,