]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/mmc/host/mmci.c
ARM: 5720/1: Move MMCI header to amba include dir
[linux-2.6.git] / drivers / mmc / host / mmci.c
index d11c2d23ceea2bbdd732d6112ce840f1a3a76a5a..79205e565c075fb51d1b81bdfa264d98f98a92c8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/mmc/mmci.c - ARM PrimeCell MMCI PL180/1 driver
+ *  linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver
  *
  *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
  *
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/highmem.h>
+#include <linux/log2.h>
 #include <linux/mmc/host.h>
 #include <linux/amba/bus.h>
 #include <linux/clk.h>
+#include <linux/scatterlist.h>
+#include <linux/gpio.h>
+#include <linux/amba/mmci.h>
 
 #include <asm/cacheflush.h>
 #include <asm/div64.h>
 #include <asm/io.h>
-#include <asm/scatterlist.h>
 #include <asm/sizes.h>
-#include <asm/mach/mmc.h>
 
 #include "mmci.h"
 
 
 static unsigned int fmax = 515633;
 
+/*
+ * This must be called with host->lock held
+ */
+static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
+{
+       u32 clk = 0;
+
+       if (desired) {
+               if (desired >= host->mclk) {
+                       clk = MCI_CLK_BYPASS;
+                       host->cclk = host->mclk;
+               } else {
+                       clk = host->mclk / (2 * desired) - 1;
+                       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;
+               /* This hasn't proven to be worthwhile */
+               /* clk |= MCI_CLK_PWRSAVE; */
+       }
+
+       if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+               clk |= MCI_WIDE_BUS;
+
+       writel(clk, host->base + MMCICLOCK);
+}
+
 static void
 mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
 {
@@ -154,11 +186,11 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
        }
        if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
                if (status & MCI_DATACRCFAIL)
-                       data->error = MMC_ERR_BADCRC;
+                       data->error = -EILSEQ;
                else if (status & MCI_DATATIMEOUT)
-                       data->error = MMC_ERR_TIMEOUT;
+                       data->error = -ETIMEDOUT;
                else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
-                       data->error = MMC_ERR_FIFO;
+                       data->error = -EIO;
                status |= MCI_DATAEND;
 
                /*
@@ -166,7 +198,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
                 * partially written to a page is properly coherent.
                 */
                if (host->sg_len && data->flags & MMC_DATA_READ)
-                       flush_dcache_page(host->sg_ptr->page);
+                       flush_dcache_page(sg_page(host->sg_ptr));
        }
        if (status & MCI_DATAEND) {
                mmci_stop_data(host);
@@ -193,12 +225,12 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
        cmd->resp[3] = readl(base + MMCIRESPONSE3);
 
        if (status & MCI_CMDTIMEOUT) {
-               cmd->error = MMC_ERR_TIMEOUT;
+               cmd->error = -ETIMEDOUT;
        } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) {
-               cmd->error = MMC_ERR_BADCRC;
+               cmd->error = -EILSEQ;
        }
 
-       if (!cmd->data || cmd->error != MMC_ERR_NONE) {
+       if (!cmd->data || cmd->error) {
                if (host->data)
                        mmci_stop_data(host);
                mmci_request_end(host, cmd->mrq);
@@ -212,9 +244,10 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema
        void __iomem *base = host->base;
        char *ptr = buffer;
        u32 status;
+       int host_remain = host->size;
 
        do {
-               int count = host->size - (readl(base + MMCIFIFOCNT) << 2);
+               int count = host_remain - (readl(base + MMCIFIFOCNT) << 2);
 
                if (count > remain)
                        count = remain;
@@ -226,6 +259,7 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema
 
                ptr += count;
                remain -= count;
+               host_remain -= count;
 
                if (remain == 0)
                        break;
@@ -318,7 +352,7 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
                 * page, ensure that the data cache is coherent.
                 */
                if (status & MCI_RXACTIVE)
-                       flush_dcache_page(host->sg_ptr->page);
+                       flush_dcache_page(sg_page(host->sg_ptr));
 
                if (!mmci_next_sg(host))
                        break;
@@ -388,10 +422,19 @@ 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);
 
-       spin_lock_irq(&host->lock);
+       if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
+               printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n",
+                       mmc_hostname(mmc), mrq->data->blksz);
+               mrq->cmd->error = -EINVAL;
+               mmc_request_done(mmc, mrq);
+               return;
+       }
+
+       spin_lock_irqsave(&host->lock, flags);
 
        host->mrq = mrq;
 
@@ -400,26 +443,14 @@ 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)
 {
        struct mmci_host *host = mmc_priv(mmc);
-       u32 clk = 0, pwr = 0;
-
-       if (ios->clock) {
-               if (ios->clock >= host->mclk) {
-                       clk = MCI_CLK_BYPASS;
-                       host->cclk = host->mclk;
-               } else {
-                       clk = host->mclk / (2 * ios->clock) - 1;
-                       if (clk > 256)
-                               clk = 255;
-                       host->cclk = host->mclk / (2 * (clk + 1));
-               }
-               clk |= MCI_CLK_ENABLE;
-       }
+       u32 pwr = 0;
+       unsigned long flags;
 
        if (host->plat->translate_vdd)
                pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
@@ -428,35 +459,75 @@ 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 != AMBA_VENDOR_ST) {
+                       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 != AMBA_VENDOR_ST)
+                       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);
+       spin_lock_irqsave(&host->lock, flags);
+
+       mmci_set_clkreg(host, ios->clock);
 
        if (host->pwr != pwr) {
                host->pwr = pwr;
                writel(pwr, host->base + MMCIPOWER);
        }
+
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int mmci_get_ro(struct mmc_host *mmc)
+{
+       struct mmci_host *host = mmc_priv(mmc);
+
+       if (host->gpio_wp == -ENOSYS)
+               return -ENOSYS;
+
+       return gpio_get_value(host->gpio_wp);
+}
+
+static int mmci_get_cd(struct mmc_host *mmc)
+{
+       struct mmci_host *host = mmc_priv(mmc);
+       unsigned int status;
+
+       if (host->gpio_cd == -ENOSYS)
+               status = host->plat->status(mmc_dev(host->mmc));
+       else
+               status = gpio_get_value(host->gpio_cd);
+
+       return !status;
 }
 
 static const struct mmc_host_ops mmci_ops = {
        .request        = mmci_request,
        .set_ios        = mmci_set_ios,
+       .get_ro         = mmci_get_ro,
+       .get_cd         = mmci_get_cd,
 };
 
 static void mmci_check_status(unsigned long data)
 {
        struct mmci_host *host = (struct mmci_host *)data;
-       unsigned int status;
+       unsigned int status = mmci_get_cd(host->mmc);
 
-       status = host->plat->status(mmc_dev(host->mmc));
        if (status ^ host->oldstat)
                mmc_detect_change(host->mmc, 0);
 
@@ -464,9 +535,9 @@ 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, struct amba_id *id)
 {
-       struct mmc_platform_data *plat = dev->dev.platform_data;
+       struct mmci_platform_data *plat = dev->dev.platform_data;
        struct mmci_host *host;
        struct mmc_host *mmc;
        int ret;
@@ -488,7 +559,17 @@ static int mmci_probe(struct amba_device *dev, void *id)
        }
 
        host = mmc_priv(mmc);
-       host->clk = clk_get(&dev->dev, "MCLK");
+       host->mmc = mmc;
+
+       host->gpio_wp = -ENOSYS;
+       host->gpio_cd = -ENOSYS;
+
+       host->hw_designer = amba_manf(dev);
+       host->hw_revision = amba_rev(dev);
+       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;
@@ -501,8 +582,19 @@ static int mmci_probe(struct amba_device *dev, void *id)
 
        host->plat = plat;
        host->mclk = clk_get_rate(host->clk);
-       host->mmc = mmc;
-       host->base = ioremap(dev->res.start, SZ_4K);
+       /*
+        * 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->base = ioremap(dev->res.start, resource_size(&dev->res));
        if (!host->base) {
                ret = -ENOMEM;
                goto clk_disable;
@@ -512,7 +604,7 @@ 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;
+       mmc->caps = plat->capabilities;
 
        /*
         * We can do SGIO
@@ -548,6 +640,27 @@ static int mmci_probe(struct amba_device *dev, void *id)
        writel(0, host->base + MMCIMASK1);
        writel(0xfff, host->base + MMCICLEAR);
 
+#ifdef CONFIG_GPIOLIB
+       if (gpio_is_valid(plat->gpio_cd)) {
+               ret = gpio_request(plat->gpio_cd, DRIVER_NAME " (cd)");
+               if (ret == 0)
+                       ret = gpio_direction_input(plat->gpio_cd);
+               if (ret == 0)
+                       host->gpio_cd = plat->gpio_cd;
+               else if (ret != -ENOSYS)
+                       goto err_gpio_cd;
+       }
+       if (gpio_is_valid(plat->gpio_wp)) {
+               ret = gpio_request(plat->gpio_wp, DRIVER_NAME " (wp)");
+               if (ret == 0)
+                       ret = gpio_direction_input(plat->gpio_wp);
+               if (ret == 0)
+                       host->gpio_wp = plat->gpio_wp;
+               else if (ret != -ENOSYS)
+                       goto err_gpio_wp;
+       }
+#endif
+
        ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);
        if (ret)
                goto unmap;
@@ -559,6 +672,7 @@ static int mmci_probe(struct amba_device *dev, void *id)
        writel(MCI_IRQENABLE, host->base + MMCIMASK0);
 
        amba_set_drvdata(dev, mmc);
+       host->oldstat = mmci_get_cd(host->mmc);
 
        mmc_add_host(mmc);
 
@@ -577,6 +691,12 @@ static int mmci_probe(struct amba_device *dev, void *id)
  irq0_free:
        free_irq(dev->irq[0], host);
  unmap:
+       if (host->gpio_wp != -ENOSYS)
+               gpio_free(host->gpio_wp);
+ err_gpio_wp:
+       if (host->gpio_cd != -ENOSYS)
+               gpio_free(host->gpio_cd);
+ err_gpio_cd:
        iounmap(host->base);
  clk_disable:
        clk_disable(host->clk);
@@ -590,7 +710,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);
 
@@ -612,6 +732,11 @@ static int mmci_remove(struct amba_device *dev)
                free_irq(dev->irq[0], host);
                free_irq(dev->irq[1], host);
 
+               if (host->gpio_wp != -ENOSYS)
+                       gpio_free(host->gpio_wp);
+               if (host->gpio_cd != -ENOSYS)
+                       gpio_free(host->gpio_cd);
+
                iounmap(host->base);
                clk_disable(host->clk);
                clk_put(host->clk);
@@ -670,6 +795,15 @@ static struct amba_id mmci_ids[] = {
                .id     = 0x00041181,
                .mask   = 0x000fffff,
        },
+       /* ST Micro variants */
+       {
+               .id     = 0x00180180,
+               .mask   = 0x00ffffff,
+       },
+       {
+               .id     = 0x00280180,
+               .mask   = 0x00ffffff,
+       },
        { 0, 0 },
 };
 
@@ -678,7 +812,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,