]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 5 Apr 2009 17:18:21 +0000 (10:18 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 5 Apr 2009 17:18:21 +0000 (10:18 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc: (42 commits)
  atmel-mci: fix sdc_reg typo
  tmio_mmc: add maintainer
  mmc: Add OpenFirmware bindings for SDHCI driver
  sdhci: Add quirk for forcing maximum block size to 2048 bytes
  sdhci: Add quirk for controllers that need IRQ re-init after reset
  sdhci: Add quirk for controllers that need small delays for PIO
  sdhci: Add set_clock callback and a quirk for nonstandard clocks
  sdhci: Add get_{max,timeout}_clock callbacks
  sdhci: Add support for hosts reporting inverted write-protect state
  sdhci: Add support for card-detection polling
  sdhci: Enable only relevant (DMA/PIO) interrupts during transfers
  sdhci: Split card-detection IRQs management from sdhci_init()
  sdhci: Add support for bus-specific IO memory accessors
  mmc_spi: adjust for delayed data token response
  omap_hsmmc: Wait for SDBP
  omap_hsmmc: Fix MMC3 dma
  omap_hsmmc: Disable SDBP at suspend
  omap_hsmmc: Do not prefix slot name
  omap_hsmmc: Allow cover switch to cause rescan
  omap_hsmmc: Add 8-bit bus width mode support
  ...

19 files changed:
MAINTAINERS
drivers/mmc/card/block.c
drivers/mmc/core/bus.c
drivers/mmc/core/core.c
drivers/mmc/core/debugfs.c
drivers/mmc/core/sdio_cis.c
drivers/mmc/core/sdio_ops.c
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/atmel-mci.c
drivers/mmc/host/mmc_spi.c
drivers/mmc/host/mvsdio.c [new file with mode: 0644]
drivers/mmc/host/mvsdio.h [new file with mode: 0644]
drivers/mmc/host/omap_hsmmc.c
drivers/mmc/host/sdhci-of.c [new file with mode: 0644]
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
drivers/mmc/host/tmio_mmc.c
drivers/mmc/host/tmio_mmc.h

index 6360b9b9bbbda557860ac202de5cb62cd244c43a..9e2eb319113b89f7ae0757169a283ecef0bbe919 100644 (file)
@@ -2924,6 +2924,12 @@ M:       buytenh@marvell.com
 L:     netdev@vger.kernel.org
 S:     Supported
 
+MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
+P:     Nicolas Pitre
+M:     nico@cam.org
+L:     linux-kernel@vger.kernel.org
+S:     Maintained
+
 MARVELL YUKON / SYSKONNECT DRIVER
 P:     Mirko Lindner
 M:     mlindner@syskonnect.de
@@ -3916,7 +3922,14 @@ S:       Maintained
 SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
 P:     Pierre Ossman
 M:     drzeus-sdhci@drzeus.cx
-L:     sdhci-devel@list.drzeus.cx
+L:     sdhci-devel@lists.ossman.eu
+S:     Maintained
+
+SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF)
+P:     Anton Vorontsov
+M:     avorontsov@ru.mvista.com
+L:     linuxppc-dev@ozlabs.org
+L:     sdhci-devel@lists.ossman.eu
 S:     Maintained
 
 SECURITY SUBSYSTEM
@@ -4394,6 +4407,11 @@ L:       tlinux-users@tce.toshiba-dme.co.jp
 W:     http://www.buzzard.org.uk/toshiba/
 S:     Maintained
 
+TMIO MMC DRIVER
+P:     Ian Molton
+M:     ian@mnementh.co.uk
+S:     Maintained
+
 TPM DEVICE DRIVER
 P:     Debora Velarde
 M:     debora@linux.vnet.ibm.com
index 513eb09a638f305d93f28aa2bdbfe967f70c32fc..fe8041e619eabec7029a12c8f7c0983e2a56346c 100644 (file)
@@ -41,6 +41,8 @@
 
 #include "queue.h"
 
+MODULE_ALIAS("mmc:block");
+
 /*
  * max 8 partitions per card
  */
index f210a8ee6861498f10ec1ecafbcd8a09b0b9f9fa..bdb165f93046f9cdce780a614ddafcc41f3cab91 100644 (file)
@@ -84,6 +84,14 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
        }
 
        retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
+       if (retval)
+               return retval;
+
+       /*
+        * Request the mmc_block device.  Note: that this is a direct request
+        * for the module it carries no information as to what is inserted.
+        */
+       retval = add_uevent_var(env, "MODALIAS=mmc:block");
 
        return retval;
 }
index 1445ea8f10a61bf14df5b7abd25a3850c8b78693..fa073ab3fa34f9d906ecfae588cc023409b78b59 100644 (file)
@@ -298,6 +298,21 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
                        data->timeout_clks = 0;
                }
        }
+       /*
+        * Some cards need very high timeouts if driven in SPI mode.
+        * The worst observed timeout was 900ms after writing a
+        * continuous stream of data until the internal logic
+        * overflowed.
+        */
+       if (mmc_host_is_spi(card->host)) {
+               if (data->flags & MMC_DATA_WRITE) {
+                       if (data->timeout_ns < 1000000000)
+                               data->timeout_ns = 1000000000;  /* 1s */
+               } else {
+                       if (data->timeout_ns < 100000000)
+                               data->timeout_ns =  100000000;  /* 100ms */
+               }
+       }
 }
 EXPORT_SYMBOL(mmc_set_data_timeout);
 
@@ -915,6 +930,7 @@ void mmc_stop_host(struct mmc_host *host)
        spin_unlock_irqrestore(&host->lock, flags);
 #endif
 
+       cancel_delayed_work(&host->detect);
        mmc_flush_scheduled_work();
 
        mmc_bus_get(host);
@@ -942,6 +958,7 @@ void mmc_stop_host(struct mmc_host *host)
  */
 int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
 {
+       cancel_delayed_work(&host->detect);
        mmc_flush_scheduled_work();
 
        mmc_bus_get(host);
@@ -975,6 +992,7 @@ int mmc_resume_host(struct mmc_host *host)
        mmc_bus_get(host);
        if (host->bus_ops && !host->bus_dead) {
                mmc_power_up(host);
+               mmc_select_voltage(host, host->ocr);
                BUG_ON(!host->bus_ops->resume);
                host->bus_ops->resume(host);
        }
index 1237bb4c722bddbe1cb6a7376dbb35e0707c4b3c..610dbd1fcc820a85cf4fcc667281063cb1b26983 100644 (file)
@@ -184,6 +184,68 @@ static int mmc_dbg_card_status_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
                NULL, "%08llx\n");
 
+#define EXT_CSD_STR_LEN 1025
+
+static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
+{
+       struct mmc_card *card = inode->i_private;
+       char *buf;
+       ssize_t n = 0;
+       u8 *ext_csd;
+       int err, i;
+
+       buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ext_csd = kmalloc(512, GFP_KERNEL);
+       if (!ext_csd) {
+               err = -ENOMEM;
+               goto out_free;
+       }
+
+       mmc_claim_host(card->host);
+       err = mmc_send_ext_csd(card, ext_csd);
+       mmc_release_host(card->host);
+       if (err)
+               goto out_free;
+
+       for (i = 511; i >= 0; i--)
+               n += sprintf(buf + n, "%02x", ext_csd[i]);
+       n += sprintf(buf + n, "\n");
+       BUG_ON(n != EXT_CSD_STR_LEN);
+
+       filp->private_data = buf;
+       kfree(ext_csd);
+       return 0;
+
+out_free:
+       kfree(buf);
+       kfree(ext_csd);
+       return err;
+}
+
+static ssize_t mmc_ext_csd_read(struct file *filp, char __user *ubuf,
+                               size_t cnt, loff_t *ppos)
+{
+       char *buf = filp->private_data;
+
+       return simple_read_from_buffer(ubuf, cnt, ppos,
+                                      buf, EXT_CSD_STR_LEN);
+}
+
+static int mmc_ext_csd_release(struct inode *inode, struct file *file)
+{
+       kfree(file->private_data);
+       return 0;
+}
+
+static struct file_operations mmc_dbg_ext_csd_fops = {
+       .open           = mmc_ext_csd_open,
+       .read           = mmc_ext_csd_read,
+       .release        = mmc_ext_csd_release,
+};
+
 void mmc_add_card_debugfs(struct mmc_card *card)
 {
        struct mmc_host *host = card->host;
@@ -211,6 +273,11 @@ void mmc_add_card_debugfs(struct mmc_card *card)
                                        &mmc_dbg_card_status_fops))
                        goto err;
 
+       if (mmc_card_mmc(card))
+               if (!debugfs_create_file("ext_csd", S_IRUSR, root, card,
+                                       &mmc_dbg_ext_csd_fops))
+                       goto err;
+
        return;
 
 err:
index 956bd7677502c3c1ebe69d3c03f65641abce060e..963f2937c5e37da83819249826995aa098751f99 100644 (file)
@@ -223,10 +223,18 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
                if (tpl_code == 0xff)
                        break;
 
+               /* null entries have no link field or data */
+               if (tpl_code == 0x00)
+                       continue;
+
                ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
                if (ret)
                        break;
 
+               /* a size of 0xff also means we're done */
+               if (tpl_link == 0xff)
+                       break;
+
                this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
                if (!this)
                        return -ENOMEM;
index c8fa095a4488689bb9ae35dee41bd482abe3a157..4eb7825fd1a74ce26618c38d27d3b780cc21556d 100644 (file)
@@ -76,6 +76,10 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
        BUG_ON(!card);
        BUG_ON(fn > 7);
 
+       /* sanity check */
+       if (addr & ~0x1FFFF)
+               return -EINVAL;
+
        memset(&cmd, 0, sizeof(struct mmc_command));
 
        cmd.opcode = SD_IO_RW_DIRECT;
@@ -125,6 +129,10 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
        WARN_ON(blocks == 0);
        WARN_ON(blksz == 0);
 
+       /* sanity check */
+       if (addr & ~0x1FFFF)
+               return -EINVAL;
+
        memset(&mrq, 0, sizeof(struct mmc_request));
        memset(&cmd, 0, sizeof(struct mmc_command));
        memset(&data, 0, sizeof(struct mmc_data));
index 6fbb246c40bbfc2f7747c64e5abbb3e4e0ea997f..b4cf691f3f64ec9cdbbc2792d32ae35bbf37bbfe 100644 (file)
@@ -37,6 +37,13 @@ config MMC_SDHCI
 
          If unsure, say N.
 
+config MMC_SDHCI_IO_ACCESSORS
+       bool
+       depends on MMC_SDHCI
+       help
+         This is silent Kconfig symbol that is selected by the drivers that
+         need to overwrite SDHCI IO memory accessors.
+
 config MMC_SDHCI_PCI
        tristate "SDHCI support on PCI bus"
        depends on MMC_SDHCI && PCI
@@ -65,6 +72,17 @@ config MMC_RICOH_MMC
 
          If unsure, say Y.
 
+config MMC_SDHCI_OF
+       tristate "SDHCI support on OpenFirmware platforms"
+       depends on MMC_SDHCI && PPC_OF
+       select MMC_SDHCI_IO_ACCESSORS
+       help
+         This selects the OF support for Secure Digital Host Controller
+         Interfaces. So far, only the Freescale eSDHC controller is known
+         to exist on OF platforms.
+
+         If unsure, say N.
+
 config MMC_OMAP
        tristate "TI OMAP Multimedia Card Interface support"
        depends on ARCH_OMAP
@@ -171,6 +189,17 @@ config MMC_TIFM_SD
           To compile this driver as a module, choose M here: the
          module will be called tifm_sd.
 
+config MMC_MVSDIO
+       tristate "Marvell MMC/SD/SDIO host driver"
+       depends on PLAT_ORION
+       ---help---
+         This selects the Marvell SDIO host driver.
+         SDIO may currently be found on the Kirkwood 88F6281 and 88F6192
+         SoC controllers.
+
+         To compile this driver as a module, choose M here: the
+         module will be called mvsdio.
+
 config MMC_SPI
        tristate "MMC/SD/SDIO over SPI"
        depends on SPI_MASTER && !HIGHMEM && HAS_DMA
index dedec55861d94f9352b7919969a269efe5be86e4..970a997620e1ad3c72838c05c68e39b16548b966 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXC)         += mxcmmc.o
 obj-$(CONFIG_MMC_SDHCI)                += sdhci.o
 obj-$(CONFIG_MMC_SDHCI_PCI)    += sdhci-pci.o
 obj-$(CONFIG_MMC_RICOH_MMC)    += ricoh_mmc.o
+obj-$(CONFIG_MMC_SDHCI_OF)     += sdhci-of.o
 obj-$(CONFIG_MMC_WBSD)         += wbsd.o
 obj-$(CONFIG_MMC_AU1X)         += au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)         += omap.o
@@ -20,6 +21,7 @@ obj-$(CONFIG_MMC_OMAP_HS)     += omap_hsmmc.o
 obj-$(CONFIG_MMC_AT91)         += at91_mci.o
 obj-$(CONFIG_MMC_ATMELMCI)     += atmel-mci.o
 obj-$(CONFIG_MMC_TIFM_SD)      += tifm_sd.o
+obj-$(CONFIG_MMC_MVSDIO)       += mvsdio.o
 obj-$(CONFIG_MMC_SPI)          += mmc_spi.o
 ifeq ($(CONFIG_OF),y)
 obj-$(CONFIG_MMC_SPI)          += of_mmc_spi.o
index e94e92001e7c08f7c02f5d031bce1d0a15120fbb..cf6a100bb38ff99a4fb56588ab3eb9b84ee7cf87 100644 (file)
@@ -812,7 +812,7 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                slot->sdc_reg |= MCI_SDCBUS_1BIT;
                break;
        case MMC_BUS_WIDTH_4:
-               slot->sdc_reg = MCI_SDCBUS_4BIT;
+               slot->sdc_reg |= MCI_SDCBUS_4BIT;
                break;
        }
 
index 87e211df68ac0dcdaf75c6aab9f4cce9ebad97ab..72f8bde4877a790f8d65f95450aae1723e521c84 100644 (file)
@@ -279,8 +279,11 @@ static int mmc_spi_response_get(struct mmc_spi_host *host,
                 * so it can always DMA directly into the target buffer.
                 * It'd probably be better to memcpy() the first chunk and
                 * avoid extra i/o calls...
+                *
+                * Note we check for more than 8 bytes, because in practice,
+                * some SD cards are slow...
                 */
-               for (i = 2; i < 9; i++) {
+               for (i = 2; i < 16; i++) {
                        value = mmc_spi_readbytes(host, 1);
                        if (value < 0)
                                goto done;
@@ -609,6 +612,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
        struct spi_device       *spi = host->spi;
        int                     status, i;
        struct scratch          *scratch = host->data;
+       u32                     pattern;
 
        if (host->mmc->use_spi_crc)
                scratch->crc_val = cpu_to_be16(
@@ -636,8 +640,27 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
         * doesn't necessarily tell whether the write operation succeeded;
         * it just says if the transmission was ok and whether *earlier*
         * writes succeeded; see the standard.
+        *
+        * In practice, there are (even modern SDHC-)cards which are late
+        * in sending the response, and miss the time frame by a few bits,
+        * so we have to cope with this situation and check the response
+        * bit-by-bit. Arggh!!!
         */
-       switch (SPI_MMC_RESPONSE_CODE(scratch->status[0])) {
+       pattern  = scratch->status[0] << 24;
+       pattern |= scratch->status[1] << 16;
+       pattern |= scratch->status[2] << 8;
+       pattern |= scratch->status[3];
+
+       /* First 3 bit of pattern are undefined */
+       pattern |= 0xE0000000;
+
+       /* left-adjust to leading 0 bit */
+       while (pattern & 0x80000000)
+               pattern <<= 1;
+       /* right-adjust for pattern matching. Code is in bit 4..0 now. */
+       pattern >>= 27;
+
+       switch (pattern) {
        case SPI_RESPONSE_ACCEPTED:
                status = 0;
                break;
@@ -668,8 +691,9 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
        /* Return when not busy.  If we didn't collect that status yet,
         * we'll need some more I/O.
         */
-       for (i = 1; i < sizeof(scratch->status); i++) {
-               if (scratch->status[i] != 0)
+       for (i = 4; i < sizeof(scratch->status); i++) {
+               /* card is non-busy if the most recent bit is 1 */
+               if (scratch->status[i] & 0x01)
                        return 0;
        }
        return mmc_spi_wait_unbusy(host, timeout);
@@ -1204,10 +1228,12 @@ static int mmc_spi_probe(struct spi_device *spi)
 
        /* MMC and SD specs only seem to care that sampling is on the
         * rising edge ... meaning SPI modes 0 or 3.  So either SPI mode
-        * should be legit.  We'll use mode 0 since it seems to be a
-        * bit less troublesome on some hardware ... unclear why.
+        * should be legit.  We'll use mode 0 since the steady state is 0,
+        * which is appropriate for hotplugging, unless the platform data
+        * specify mode 3 (if hardware is not compatible to mode 0).
         */
-       spi->mode = SPI_MODE_0;
+       if (spi->mode != SPI_MODE_3)
+               spi->mode = SPI_MODE_0;
        spi->bits_per_word = 8;
 
        status = spi_setup(spi);
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
new file mode 100644 (file)
index 0000000..b5c375d
--- /dev/null
@@ -0,0 +1,885 @@
+/*
+ * Marvell MMC/SD/SDIO driver
+ *
+ * Authors: Maen Suleiman, Nicolas Pitre
+ * Copyright (C) 2008-2009 Marvell Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/mbus.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/mmc/host.h>
+
+#include <asm/sizes.h>
+#include <asm/unaligned.h>
+#include <plat/mvsdio.h>
+
+#include "mvsdio.h"
+
+#define DRIVER_NAME    "mvsdio"
+
+static int maxfreq = MVSD_CLOCKRATE_MAX;
+static int nodma;
+
+struct mvsd_host {
+       void __iomem *base;
+       struct mmc_request *mrq;
+       spinlock_t lock;
+       unsigned int xfer_mode;
+       unsigned int intr_en;
+       unsigned int ctrl;
+       unsigned int pio_size;
+       void *pio_ptr;
+       unsigned int sg_frags;
+       unsigned int ns_per_clk;
+       unsigned int clock;
+       unsigned int base_clock;
+       struct timer_list timer;
+       struct mmc_host *mmc;
+       struct device *dev;
+       struct resource *res;
+       int irq;
+       int gpio_card_detect;
+       int gpio_write_protect;
+};
+
+#define mvsd_write(offs, val)  writel(val, iobase + (offs))
+#define mvsd_read(offs)                readl(iobase + (offs))
+
+static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
+{
+       void __iomem *iobase = host->base;
+       unsigned int tmout;
+       int tmout_index;
+
+       /* If timeout=0 then maximum timeout index is used. */
+       tmout = DIV_ROUND_UP(data->timeout_ns, host->ns_per_clk);
+       tmout += data->timeout_clks;
+       tmout_index = fls(tmout - 1) - 12;
+       if (tmout_index < 0)
+               tmout_index = 0;
+       if (tmout_index > MVSD_HOST_CTRL_TMOUT_MAX)
+               tmout_index = MVSD_HOST_CTRL_TMOUT_MAX;
+
+       dev_dbg(host->dev, "data %s at 0x%08x: blocks=%d blksz=%d tmout=%u (%d)\n",
+               (data->flags & MMC_DATA_READ) ? "read" : "write",
+               (u32)sg_virt(data->sg), data->blocks, data->blksz,
+               tmout, tmout_index);
+
+       host->ctrl &= ~MVSD_HOST_CTRL_TMOUT_MASK;
+       host->ctrl |= MVSD_HOST_CTRL_TMOUT(tmout_index);
+       mvsd_write(MVSD_HOST_CTRL, host->ctrl);
+       mvsd_write(MVSD_BLK_COUNT, data->blocks);
+       mvsd_write(MVSD_BLK_SIZE, data->blksz);
+
+       if (nodma || (data->blksz | data->sg->offset) & 3) {
+               /*
+                * We cannot do DMA on a buffer which offset or size
+                * is not aligned on a 4-byte boundary.
+                */
+               host->pio_size = data->blocks * data->blksz;
+               host->pio_ptr = sg_virt(data->sg);
+               if (!nodma)
+                       printk(KERN_DEBUG "%s: fallback to PIO for data "
+                                         "at 0x%p size %d\n",
+                                         mmc_hostname(host->mmc),
+                                         host->pio_ptr, host->pio_size);
+               return 1;
+       } else {
+               dma_addr_t phys_addr;
+               int dma_dir = (data->flags & MMC_DATA_READ) ?
+                       DMA_FROM_DEVICE : DMA_TO_DEVICE;
+               host->sg_frags = dma_map_sg(mmc_dev(host->mmc), data->sg,
+                                           data->sg_len, dma_dir);
+               phys_addr = sg_dma_address(data->sg);
+               mvsd_write(MVSD_SYS_ADDR_LOW, (u32)phys_addr & 0xffff);
+               mvsd_write(MVSD_SYS_ADDR_HI,  (u32)phys_addr >> 16);
+               return 0;
+       }
+}
+
+static void mvsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+       struct mvsd_host *host = mmc_priv(mmc);
+       void __iomem *iobase = host->base;
+       struct mmc_command *cmd = mrq->cmd;
+       u32 cmdreg = 0, xfer = 0, intr = 0;
+       unsigned long flags;
+
+       BUG_ON(host->mrq != NULL);
+       host->mrq = mrq;
+
+       dev_dbg(host->dev, "cmd %d (hw state 0x%04x)\n",
+               cmd->opcode, mvsd_read(MVSD_HW_STATE));
+
+       cmdreg = MVSD_CMD_INDEX(cmd->opcode);
+
+       if (cmd->flags & MMC_RSP_BUSY)
+               cmdreg |= MVSD_CMD_RSP_48BUSY;
+       else if (cmd->flags & MMC_RSP_136)
+               cmdreg |= MVSD_CMD_RSP_136;
+       else if (cmd->flags & MMC_RSP_PRESENT)
+               cmdreg |= MVSD_CMD_RSP_48;
+       else
+               cmdreg |= MVSD_CMD_RSP_NONE;
+
+       if (cmd->flags & MMC_RSP_CRC)
+               cmdreg |= MVSD_CMD_CHECK_CMDCRC;
+
+       if (cmd->flags & MMC_RSP_OPCODE)
+               cmdreg |= MVSD_CMD_INDX_CHECK;
+
+       if (cmd->flags & MMC_RSP_PRESENT) {
+               cmdreg |= MVSD_UNEXPECTED_RESP;
+               intr |= MVSD_NOR_UNEXP_RSP;
+       }
+
+       if (mrq->data) {
+               struct mmc_data *data = mrq->data;
+               int pio;
+
+               cmdreg |= MVSD_CMD_DATA_PRESENT | MVSD_CMD_CHECK_DATACRC16;
+               xfer |= MVSD_XFER_MODE_HW_WR_DATA_EN;
+               if (data->flags & MMC_DATA_READ)
+                       xfer |= MVSD_XFER_MODE_TO_HOST;
+
+               pio = mvsd_setup_data(host, data);
+               if (pio) {
+                       xfer |= MVSD_XFER_MODE_PIO;
+                       /* PIO section of mvsd_irq has comments on those bits */
+                       if (data->flags & MMC_DATA_WRITE)
+                               intr |= MVSD_NOR_TX_AVAIL;
+                       else if (host->pio_size > 32)
+                               intr |= MVSD_NOR_RX_FIFO_8W;
+                       else
+                               intr |= MVSD_NOR_RX_READY;
+               }
+
+               if (data->stop) {
+                       struct mmc_command *stop = data->stop;
+                       u32 cmd12reg = 0;
+
+                       mvsd_write(MVSD_AUTOCMD12_ARG_LOW, stop->arg & 0xffff);
+                       mvsd_write(MVSD_AUTOCMD12_ARG_HI,  stop->arg >> 16);
+
+                       if (stop->flags & MMC_RSP_BUSY)
+                               cmd12reg |= MVSD_AUTOCMD12_BUSY;
+                       if (stop->flags & MMC_RSP_OPCODE)
+                               cmd12reg |= MVSD_AUTOCMD12_INDX_CHECK;
+                       cmd12reg |= MVSD_AUTOCMD12_INDEX(stop->opcode);
+                       mvsd_write(MVSD_AUTOCMD12_CMD, cmd12reg);
+
+                       xfer |= MVSD_XFER_MODE_AUTO_CMD12;
+                       intr |= MVSD_NOR_AUTOCMD12_DONE;
+               } else {
+                       intr |= MVSD_NOR_XFER_DONE;
+               }
+       } else {
+               intr |= MVSD_NOR_CMD_DONE;
+       }
+
+       mvsd_write(MVSD_ARG_LOW, cmd->arg & 0xffff);
+       mvsd_write(MVSD_ARG_HI,  cmd->arg >> 16);
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       host->xfer_mode &= MVSD_XFER_MODE_INT_CHK_EN;
+       host->xfer_mode |= xfer;
+       mvsd_write(MVSD_XFER_MODE, host->xfer_mode);
+
+       mvsd_write(MVSD_NOR_INTR_STATUS, ~MVSD_NOR_CARD_INT);
+       mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
+       mvsd_write(MVSD_CMD, cmdreg);
+
+       host->intr_en &= MVSD_NOR_CARD_INT;
+       host->intr_en |= intr | MVSD_NOR_ERROR;
+       mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+       mvsd_write(MVSD_ERR_INTR_EN, 0xffff);
+
+       mod_timer(&host->timer, jiffies + 5 * HZ);
+
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static u32 mvsd_finish_cmd(struct mvsd_host *host, struct mmc_command *cmd,
+                          u32 err_status)
+{
+       void __iomem *iobase = host->base;
+
+       if (cmd->flags & MMC_RSP_136) {
+               unsigned int response[8], i;
+               for (i = 0; i < 8; i++)
+                       response[i] = mvsd_read(MVSD_RSP(i));
+               cmd->resp[0] =          ((response[0] & 0x03ff) << 22) |
+                                       ((response[1] & 0xffff) << 6) |
+                                       ((response[2] & 0xfc00) >> 10);
+               cmd->resp[1] =          ((response[2] & 0x03ff) << 22) |
+                                       ((response[3] & 0xffff) << 6) |
+                                       ((response[4] & 0xfc00) >> 10);
+               cmd->resp[2] =          ((response[4] & 0x03ff) << 22) |
+                                       ((response[5] & 0xffff) << 6) |
+                                       ((response[6] & 0xfc00) >> 10);
+               cmd->resp[3] =          ((response[6] & 0x03ff) << 22) |
+                                       ((response[7] & 0x3fff) << 8);
+       } else if (cmd->flags & MMC_RSP_PRESENT) {
+               unsigned int response[3], i;
+               for (i = 0; i < 3; i++)
+                       response[i] = mvsd_read(MVSD_RSP(i));
+               cmd->resp[0] =          ((response[2] & 0x003f) << (8 - 8)) |
+                                       ((response[1] & 0xffff) << (14 - 8)) |
+                                       ((response[0] & 0x03ff) << (30 - 8));
+               cmd->resp[1] =          ((response[0] & 0xfc00) >> 10);
+               cmd->resp[2] = 0;
+               cmd->resp[3] = 0;
+       }
+
+       if (err_status & MVSD_ERR_CMD_TIMEOUT) {
+               cmd->error = -ETIMEDOUT;
+       } else if (err_status & (MVSD_ERR_CMD_CRC | MVSD_ERR_CMD_ENDBIT |
+                                MVSD_ERR_CMD_INDEX | MVSD_ERR_CMD_STARTBIT)) {
+               cmd->error = -EILSEQ;
+       }
+       err_status &= ~(MVSD_ERR_CMD_TIMEOUT | MVSD_ERR_CMD_CRC |
+                       MVSD_ERR_CMD_ENDBIT | MVSD_ERR_CMD_INDEX |
+                       MVSD_ERR_CMD_STARTBIT);
+
+       return err_status;
+}
+
+static u32 mvsd_finish_data(struct mvsd_host *host, struct mmc_data *data,
+                           u32 err_status)
+{
+       void __iomem *iobase = host->base;
+
+       if (host->pio_ptr) {
+               host->pio_ptr = NULL;
+               host->pio_size = 0;
+       } else {
+               dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_frags,
+                            (data->flags & MMC_DATA_READ) ?
+                               DMA_FROM_DEVICE : DMA_TO_DEVICE);
+       }
+
+       if (err_status & MVSD_ERR_DATA_TIMEOUT)
+               data->error = -ETIMEDOUT;
+       else if (err_status & (MVSD_ERR_DATA_CRC | MVSD_ERR_DATA_ENDBIT))
+               data->error = -EILSEQ;
+       else if (err_status & MVSD_ERR_XFER_SIZE)
+               data->error = -EBADE;
+       err_status &= ~(MVSD_ERR_DATA_TIMEOUT | MVSD_ERR_DATA_CRC |
+                       MVSD_ERR_DATA_ENDBIT | MVSD_ERR_XFER_SIZE);
+
+       dev_dbg(host->dev, "data done: blocks_left=%d, bytes_left=%d\n",
+               mvsd_read(MVSD_CURR_BLK_LEFT), mvsd_read(MVSD_CURR_BYTE_LEFT));
+       data->bytes_xfered =
+               (data->blocks - mvsd_read(MVSD_CURR_BLK_LEFT)) * data->blksz;
+       /* We can't be sure about the last block when errors are detected */
+       if (data->bytes_xfered && data->error)
+               data->bytes_xfered -= data->blksz;
+
+       /* Handle Auto cmd 12 response */
+       if (data->stop) {
+               unsigned int response[3], i;
+               for (i = 0; i < 3; i++)
+                       response[i] = mvsd_read(MVSD_AUTO_RSP(i));
+               data->stop->resp[0] =   ((response[2] & 0x003f) << (8 - 8)) |
+                                       ((response[1] & 0xffff) << (14 - 8)) |
+                                       ((response[0] & 0x03ff) << (30 - 8));
+               data->stop->resp[1] =   ((response[0] & 0xfc00) >> 10);
+               data->stop->resp[2] = 0;
+               data->stop->resp[3] = 0;
+
+               if (err_status & MVSD_ERR_AUTOCMD12) {
+                       u32 err_cmd12 = mvsd_read(MVSD_AUTOCMD12_ERR_STATUS);
+                       dev_dbg(host->dev, "c12err 0x%04x\n", err_cmd12);
+                       if (err_cmd12 & MVSD_AUTOCMD12_ERR_NOTEXE)
+                               data->stop->error = -ENOEXEC;
+                       else if (err_cmd12 & MVSD_AUTOCMD12_ERR_TIMEOUT)
+                               data->stop->error = -ETIMEDOUT;
+                       else if (err_cmd12)
+                               data->stop->error = -EILSEQ;
+                       err_status &= ~MVSD_ERR_AUTOCMD12;
+               }
+       }
+
+       return err_status;
+}
+
+static irqreturn_t mvsd_irq(int irq, void *dev)
+{
+       struct mvsd_host *host = dev;
+       void __iomem *iobase = host->base;
+       u32 intr_status, intr_done_mask;
+       int irq_handled = 0;
+
+       intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
+       dev_dbg(host->dev, "intr 0x%04x intr_en 0x%04x hw_state 0x%04x\n",
+               intr_status, mvsd_read(MVSD_NOR_INTR_EN),
+               mvsd_read(MVSD_HW_STATE));
+
+       spin_lock(&host->lock);
+
+       /* PIO handling, if needed. Messy business... */
+       if (host->pio_size &&
+           (intr_status & host->intr_en &
+            (MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W))) {
+               u16 *p = host->pio_ptr;
+               int s = host->pio_size;
+               while (s >= 32 && (intr_status & MVSD_NOR_RX_FIFO_8W)) {
+                       readsw(iobase + MVSD_FIFO, p, 16);
+                       p += 16;
+                       s -= 32;
+                       intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
+               }
+               /*
+                * Normally we'd use < 32 here, but the RX_FIFO_8W bit
+                * doesn't appear to assert when there is exactly 32 bytes
+                * (8 words) left to fetch in a transfer.
+                */
+               if (s <= 32) {
+                       while (s >= 4 && (intr_status & MVSD_NOR_RX_READY)) {
+                               put_unaligned(mvsd_read(MVSD_FIFO), p++);
+                               put_unaligned(mvsd_read(MVSD_FIFO), p++);
+                               s -= 4;
+                               intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
+                       }
+                       if (s && s < 4 && (intr_status & MVSD_NOR_RX_READY)) {
+                               u16 val[2] = {0, 0};
+                               val[0] = mvsd_read(MVSD_FIFO);
+                               val[1] = mvsd_read(MVSD_FIFO);
+                               memcpy(p, &val, s);
+                               s = 0;
+                               intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
+                       }
+                       if (s == 0) {
+                               host->intr_en &=
+                                    ~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W);
+                               mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+                       } else if (host->intr_en & MVSD_NOR_RX_FIFO_8W) {
+                               host->intr_en &= ~MVSD_NOR_RX_FIFO_8W;
+                               host->intr_en |= MVSD_NOR_RX_READY;
+                               mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+                       }
+               }
+               dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n",
+                       s, intr_status, mvsd_read(MVSD_HW_STATE));
+               host->pio_ptr = p;
+               host->pio_size = s;
+               irq_handled = 1;
+       } else if (host->pio_size &&
+                  (intr_status & host->intr_en &
+                   (MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W))) {
+               u16 *p = host->pio_ptr;
+               int s = host->pio_size;
+               /*
+                * The TX_FIFO_8W bit is unreliable. When set, bursting
+                * 16 halfwords all at once in the FIFO drops data. Actually
+                * TX_AVAIL does go off after only one word is pushed even if
+                * TX_FIFO_8W remains set.
+                */
+               while (s >= 4 && (intr_status & MVSD_NOR_TX_AVAIL)) {
+                       mvsd_write(MVSD_FIFO, get_unaligned(p++));
+                       mvsd_write(MVSD_FIFO, get_unaligned(p++));
+                       s -= 4;
+                       intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
+               }
+               if (s < 4) {
+                       if (s && (intr_status & MVSD_NOR_TX_AVAIL)) {
+                               u16 val[2] = {0, 0};
+                               memcpy(&val, p, s);
+                               mvsd_write(MVSD_FIFO, val[0]);
+                               mvsd_write(MVSD_FIFO, val[1]);
+                               s = 0;
+                               intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
+                       }
+                       if (s == 0) {
+                               host->intr_en &=
+                                    ~(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W);
+                               mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+                       }
+               }
+               dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n",
+                       s, intr_status, mvsd_read(MVSD_HW_STATE));
+               host->pio_ptr = p;
+               host->pio_size = s;
+               irq_handled = 1;
+       }
+
+       mvsd_write(MVSD_NOR_INTR_STATUS, intr_status);
+
+       intr_done_mask = MVSD_NOR_CARD_INT | MVSD_NOR_RX_READY |
+                        MVSD_NOR_RX_FIFO_8W | MVSD_NOR_TX_FIFO_8W;
+       if (intr_status & host->intr_en & ~intr_done_mask) {
+               struct mmc_request *mrq = host->mrq;
+               struct mmc_command *cmd = mrq->cmd;
+               u32 err_status = 0;
+
+               del_timer(&host->timer);
+               host->mrq = NULL;
+
+               host->intr_en &= MVSD_NOR_CARD_INT;
+               mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+               mvsd_write(MVSD_ERR_INTR_EN, 0);
+
+               spin_unlock(&host->lock);
+
+               if (intr_status & MVSD_NOR_UNEXP_RSP) {
+                       cmd->error = -EPROTO;
+               } else if (intr_status & MVSD_NOR_ERROR) {
+                       err_status = mvsd_read(MVSD_ERR_INTR_STATUS);
+                       dev_dbg(host->dev, "err 0x%04x\n", err_status);
+               }
+
+               err_status = mvsd_finish_cmd(host, cmd, err_status);
+               if (mrq->data)
+                       err_status = mvsd_finish_data(host, mrq->data, err_status);
+               if (err_status) {
+                       printk(KERN_ERR "%s: unhandled error status %#04x\n",
+                                       mmc_hostname(host->mmc), err_status);
+                       cmd->error = -ENOMSG;
+               }
+
+               mmc_request_done(host->mmc, mrq);
+               irq_handled = 1;
+       } else
+               spin_unlock(&host->lock);
+
+       if (intr_status & MVSD_NOR_CARD_INT) {
+               mmc_signal_sdio_irq(host->mmc);
+               irq_handled = 1;
+       }
+
+       if (irq_handled)
+               return IRQ_HANDLED;
+
+       printk(KERN_ERR "%s: unhandled interrupt status=0x%04x en=0x%04x "
+                       "pio=%d\n", mmc_hostname(host->mmc), intr_status,
+                       host->intr_en, host->pio_size);
+       return IRQ_NONE;
+}
+
+static void mvsd_timeout_timer(unsigned long data)
+{
+       struct mvsd_host *host = (struct mvsd_host *)data;
+       void __iomem *iobase = host->base;
+       struct mmc_request *mrq;
+       unsigned long flags;
+
+       spin_lock_irqsave(&host->lock, flags);
+       mrq = host->mrq;
+       if (mrq) {
+               printk(KERN_ERR "%s: Timeout waiting for hardware interrupt.\n",
+                               mmc_hostname(host->mmc));
+               printk(KERN_ERR "%s: hw_state=0x%04x, intr_status=0x%04x "
+                               "intr_en=0x%04x\n", mmc_hostname(host->mmc),
+                               mvsd_read(MVSD_HW_STATE),
+                               mvsd_read(MVSD_NOR_INTR_STATUS),
+                               mvsd_read(MVSD_NOR_INTR_EN));
+
+               host->mrq = NULL;
+
+               mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW);
+
+               host->xfer_mode &= MVSD_XFER_MODE_INT_CHK_EN;
+               mvsd_write(MVSD_XFER_MODE, host->xfer_mode);
+
+               host->intr_en &= MVSD_NOR_CARD_INT;
+               mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+               mvsd_write(MVSD_ERR_INTR_EN, 0);
+               mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
+
+               mrq->cmd->error = -ETIMEDOUT;
+               mvsd_finish_cmd(host, mrq->cmd, 0);
+               if (mrq->data) {
+                       mrq->data->error = -ETIMEDOUT;
+                       mvsd_finish_data(host, mrq->data, 0);
+               }
+       }
+       spin_unlock_irqrestore(&host->lock, flags);
+
+       if (mrq)
+               mmc_request_done(host->mmc, mrq);
+}
+
+static irqreturn_t mvsd_card_detect_irq(int irq, void *dev)
+{
+       struct mvsd_host *host = dev;
+       mmc_detect_change(host->mmc, msecs_to_jiffies(100));
+       return IRQ_HANDLED;
+}
+
+static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+       struct mvsd_host *host = mmc_priv(mmc);
+       void __iomem *iobase = host->base;
+       unsigned long flags;
+
+       spin_lock_irqsave(&host->lock, flags);
+       if (enable) {
+               host->xfer_mode |= MVSD_XFER_MODE_INT_CHK_EN;
+               host->intr_en |= MVSD_NOR_CARD_INT;
+       } else {
+               host->xfer_mode &= ~MVSD_XFER_MODE_INT_CHK_EN;
+               host->intr_en &= ~MVSD_NOR_CARD_INT;
+       }
+       mvsd_write(MVSD_XFER_MODE, host->xfer_mode);
+       mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int mvsd_get_ro(struct mmc_host *mmc)
+{
+       struct mvsd_host *host = mmc_priv(mmc);
+
+       if (host->gpio_write_protect)
+               return gpio_get_value(host->gpio_write_protect);
+
+       /*
+        * Board doesn't support read only detection; let the mmc core
+        * decide what to do.
+        */
+       return -ENOSYS;
+}
+
+static void mvsd_power_up(struct mvsd_host *host)
+{
+       void __iomem *iobase = host->base;
+       dev_dbg(host->dev, "power up\n");
+       mvsd_write(MVSD_NOR_INTR_EN, 0);
+       mvsd_write(MVSD_ERR_INTR_EN, 0);
+       mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW);
+       mvsd_write(MVSD_XFER_MODE, 0);
+       mvsd_write(MVSD_NOR_STATUS_EN, 0xffff);
+       mvsd_write(MVSD_ERR_STATUS_EN, 0xffff);
+       mvsd_write(MVSD_NOR_INTR_STATUS, 0xffff);
+       mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
+}
+
+static void mvsd_power_down(struct mvsd_host *host)
+{
+       void __iomem *iobase = host->base;
+       dev_dbg(host->dev, "power down\n");
+       mvsd_write(MVSD_NOR_INTR_EN, 0);
+       mvsd_write(MVSD_ERR_INTR_EN, 0);
+       mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW);
+       mvsd_write(MVSD_XFER_MODE, MVSD_XFER_MODE_STOP_CLK);
+       mvsd_write(MVSD_NOR_STATUS_EN, 0);
+       mvsd_write(MVSD_ERR_STATUS_EN, 0);
+       mvsd_write(MVSD_NOR_INTR_STATUS, 0xffff);
+       mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
+}
+
+static void mvsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+       struct mvsd_host *host = mmc_priv(mmc);
+       void __iomem *iobase = host->base;
+       u32 ctrl_reg = 0;
+
+       if (ios->power_mode == MMC_POWER_UP)
+               mvsd_power_up(host);
+
+       if (ios->clock == 0) {
+               mvsd_write(MVSD_XFER_MODE, MVSD_XFER_MODE_STOP_CLK);
+               mvsd_write(MVSD_CLK_DIV, MVSD_BASE_DIV_MAX);
+               host->clock = 0;
+               dev_dbg(host->dev, "clock off\n");
+       } else if (ios->clock != host->clock) {
+               u32 m = DIV_ROUND_UP(host->base_clock, ios->clock) - 1;
+               if (m > MVSD_BASE_DIV_MAX)
+                       m = MVSD_BASE_DIV_MAX;
+               mvsd_write(MVSD_CLK_DIV, m);
+               host->clock = ios->clock;
+               host->ns_per_clk = 1000000000 / (host->base_clock / (m+1));
+               dev_dbg(host->dev, "clock=%d (%d), div=0x%04x\n",
+                       ios->clock, host->base_clock / (m+1), m);
+       }
+
+       /* default transfer mode */
+       ctrl_reg |= MVSD_HOST_CTRL_BIG_ENDIAN;
+       ctrl_reg &= ~MVSD_HOST_CTRL_LSB_FIRST;
+
+       /* default to maximum timeout */
+       ctrl_reg |= MVSD_HOST_CTRL_TMOUT_MASK;
+       ctrl_reg |= MVSD_HOST_CTRL_TMOUT_EN;
+
+       if (ios->bus_mode == MMC_BUSMODE_PUSHPULL)
+               ctrl_reg |= MVSD_HOST_CTRL_PUSH_PULL_EN;
+
+       if (ios->bus_width == MMC_BUS_WIDTH_4)
+               ctrl_reg |= MVSD_HOST_CTRL_DATA_WIDTH_4_BITS;
+
+       if (ios->timing == MMC_TIMING_MMC_HS ||
+           ios->timing == MMC_TIMING_SD_HS)
+               ctrl_reg |= MVSD_HOST_CTRL_HI_SPEED_EN;
+
+       host->ctrl = ctrl_reg;
+       mvsd_write(MVSD_HOST_CTRL, ctrl_reg);
+       dev_dbg(host->dev, "ctrl 0x%04x: %s %s %s\n", ctrl_reg,
+               (ctrl_reg & MVSD_HOST_CTRL_PUSH_PULL_EN) ?
+                       "push-pull" : "open-drain",
+               (ctrl_reg & MVSD_HOST_CTRL_DATA_WIDTH_4_BITS) ?
+                       "4bit-width" : "1bit-width",
+               (ctrl_reg & MVSD_HOST_CTRL_HI_SPEED_EN) ?
+                       "high-speed" : "");
+
+       if (ios->power_mode == MMC_POWER_OFF)
+               mvsd_power_down(host);
+}
+
+static const struct mmc_host_ops mvsd_ops = {
+       .request                = mvsd_request,
+       .get_ro                 = mvsd_get_ro,
+       .set_ios                = mvsd_set_ios,
+       .enable_sdio_irq        = mvsd_enable_sdio_irq,
+};
+
+static void __init mv_conf_mbus_windows(struct mvsd_host *host,
+                                       struct mbus_dram_target_info *dram)
+{
+       void __iomem *iobase = host->base;
+       int i;
+
+       for (i = 0; i < 4; i++) {
+               writel(0, iobase + MVSD_WINDOW_CTRL(i));
+               writel(0, iobase + MVSD_WINDOW_BASE(i));
+       }
+
+       for (i = 0; i < dram->num_cs; i++) {
+               struct mbus_dram_window *cs = dram->cs + i;
+               writel(((cs->size - 1) & 0xffff0000) |
+                      (cs->mbus_attr << 8) |
+                      (dram->mbus_dram_target_id << 4) | 1,
+                      iobase + MVSD_WINDOW_CTRL(i));
+               writel(cs->base, iobase + MVSD_WINDOW_BASE(i));
+       }
+}
+
+static int __init mvsd_probe(struct platform_device *pdev)
+{
+       struct mmc_host *mmc = NULL;
+       struct mvsd_host *host = NULL;
+       const struct mvsdio_platform_data *mvsd_data;
+       struct resource *r;
+       int ret, irq;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       irq = platform_get_irq(pdev, 0);
+       mvsd_data = pdev->dev.platform_data;
+       if (!r || irq < 0 || !mvsd_data)
+               return -ENXIO;
+
+       r = request_mem_region(r->start, SZ_1K, DRIVER_NAME);
+       if (!r)
+               return -EBUSY;
+
+       mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev);
+       if (!mmc) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       host = mmc_priv(mmc);
+       host->mmc = mmc;
+       host->dev = &pdev->dev;
+       host->res = r;
+       host->base_clock = mvsd_data->clock / 2;
+
+       mmc->ops = &mvsd_ops;
+
+       mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+       mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ |
+                   MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
+
+       mmc->f_min = DIV_ROUND_UP(host->base_clock, MVSD_BASE_DIV_MAX);
+       mmc->f_max = maxfreq;
+
+       mmc->max_blk_size = 2048;
+       mmc->max_blk_count = 65535;
+
+       mmc->max_hw_segs = 1;
+       mmc->max_phys_segs = 1;
+       mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
+       mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+
+       spin_lock_init(&host->lock);
+
+       host->base = ioremap(r->start, SZ_4K);
+       if (!host->base) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       /* (Re-)program MBUS remapping windows if we are asked to. */
+       if (mvsd_data->dram != NULL)
+               mv_conf_mbus_windows(host, mvsd_data->dram);
+
+       mvsd_power_down(host);
+
+       ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host);
+       if (ret) {
+               printk(KERN_ERR "%s: cannot assign irq %d\n", DRIVER_NAME, irq);
+               goto out;
+       } else
+               host->irq = irq;
+
+       if (mvsd_data->gpio_card_detect) {
+               ret = gpio_request(mvsd_data->gpio_card_detect,
+                                  DRIVER_NAME " cd");
+               if (ret == 0) {
+                       gpio_direction_input(mvsd_data->gpio_card_detect);
+                       irq = gpio_to_irq(mvsd_data->gpio_card_detect);
+                       ret = request_irq(irq, mvsd_card_detect_irq,
+                                         IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING,
+                                         DRIVER_NAME " cd", host);
+                       if (ret == 0)
+                               host->gpio_card_detect =
+                                       mvsd_data->gpio_card_detect;
+                       else
+                               gpio_free(mvsd_data->gpio_card_detect);
+               }
+       }
+       if (!host->gpio_card_detect)
+               mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+       if (mvsd_data->gpio_write_protect) {
+               ret = gpio_request(mvsd_data->gpio_write_protect,
+                                  DRIVER_NAME " wp");
+               if (ret == 0) {
+                       gpio_direction_input(mvsd_data->gpio_write_protect);
+                       host->gpio_write_protect =
+                               mvsd_data->gpio_write_protect;
+               }
+       }
+
+       setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host);
+       platform_set_drvdata(pdev, mmc);
+       ret = mmc_add_host(mmc);
+       if (ret)
+               goto out;
+
+       printk(KERN_NOTICE "%s: %s driver initialized, ",
+                          mmc_hostname(mmc), DRIVER_NAME);
+       if (host->gpio_card_detect)
+               printk("using GPIO %d for card detection\n",
+                      host->gpio_card_detect);
+       else
+               printk("lacking card detect (fall back to polling)\n");
+       return 0;
+
+out:
+       if (host) {
+               if (host->irq)
+                       free_irq(host->irq, host);
+               if (host->gpio_card_detect) {
+                       free_irq(gpio_to_irq(host->gpio_card_detect), host);
+                       gpio_free(host->gpio_card_detect);
+               }
+               if (host->gpio_write_protect)
+                       gpio_free(host->gpio_write_protect);
+               if (host->base)
+                       iounmap(host->base);
+       }
+       if (r)
+               release_resource(r);
+       if (mmc)
+               mmc_free_host(mmc);
+
+       return ret;
+}
+
+static int __exit mvsd_remove(struct platform_device *pdev)
+{
+       struct mmc_host *mmc = platform_get_drvdata(pdev);
+
+       if (mmc) {
+               struct mvsd_host *host = mmc_priv(mmc);
+
+               if (host->gpio_card_detect) {
+                       free_irq(gpio_to_irq(host->gpio_card_detect), host);
+                       gpio_free(host->gpio_card_detect);
+               }
+               mmc_remove_host(mmc);
+               free_irq(host->irq, host);
+               if (host->gpio_write_protect)
+                       gpio_free(host->gpio_write_protect);
+               del_timer_sync(&host->timer);
+               mvsd_power_down(host);
+               iounmap(host->base);
+               release_resource(host->res);
+               mmc_free_host(mmc);
+       }
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int mvsd_suspend(struct platform_device *dev, pm_message_t state,
+                          u32 level)
+{
+       struct mmc_host *mmc = platform_get_drvdata(dev);
+       int ret = 0;
+
+       if (mmc && level == SUSPEND_DISABLE)
+               ret = mmc_suspend_host(mmc, state);
+
+       return ret;
+}
+
+static int mvsd_resume(struct platform_device *dev, u32 level)
+{
+       struct mmc_host *mmc = platform_dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (mmc && level == RESUME_ENABLE)
+               ret = mmc_resume_host(mmc);
+
+       return ret;
+}
+#else
+#define mvsd_suspend   NULL
+#define mvsd_resume    NULL
+#endif
+
+static struct platform_driver mvsd_driver = {
+       .remove         = __exit_p(mvsd_remove),
+       .suspend        = mvsd_suspend,
+       .resume         = mvsd_resume,
+       .driver         = {
+               .name   = DRIVER_NAME,
+       },
+};
+
+static int __init mvsd_init(void)
+{
+       return platform_driver_probe(&mvsd_driver, mvsd_probe);
+}
+
+static void __exit mvsd_exit(void)
+{
+       platform_driver_unregister(&mvsd_driver);
+}
+
+module_init(mvsd_init);
+module_exit(mvsd_exit);
+
+/* maximum card clock frequency (default 50MHz) */
+module_param(maxfreq, int, 0);
+
+/* force PIO transfers all the time */
+module_param(nodma, int, 0);
+
+MODULE_AUTHOR("Maen Suleiman, Nicolas Pitre");
+MODULE_DESCRIPTION("Marvell MMC,SD,SDIO Host Controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/mvsdio.h b/drivers/mmc/host/mvsdio.h
new file mode 100644 (file)
index 0000000..7d9727b
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ *  Copyright (C) 2008 Marvell Semiconductors, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MVSDIO_H
+#define __MVSDIO_H
+
+/*
+ * Clock rates
+ */
+
+#define MVSD_CLOCKRATE_MAX                     50000000
+#define MVSD_BASE_DIV_MAX                      0x7ff
+
+
+/*
+ * Register offsets
+ */
+
+#define MVSD_SYS_ADDR_LOW                      0x000
+#define MVSD_SYS_ADDR_HI                       0x004
+#define MVSD_BLK_SIZE                          0x008
+#define MVSD_BLK_COUNT                         0x00c
+#define MVSD_ARG_LOW                           0x010
+#define MVSD_ARG_HI                            0x014
+#define MVSD_XFER_MODE                         0x018
+#define MVSD_CMD                               0x01c
+#define MVSD_RSP(i)                            (0x020 + ((i)<<2))
+#define MVSD_RSP0                              0x020
+#define MVSD_RSP1                              0x024
+#define MVSD_RSP2                              0x028
+#define MVSD_RSP3                              0x02c
+#define MVSD_RSP4                              0x030
+#define MVSD_RSP5                              0x034
+#define MVSD_RSP6                              0x038
+#define MVSD_RSP7                              0x03c
+#define MVSD_FIFO                              0x040
+#define MVSD_RSP_CRC7                          0x044
+#define MVSD_HW_STATE                          0x048
+#define MVSD_HOST_CTRL                         0x050
+#define MVSD_BLK_GAP_CTRL                      0x054
+#define MVSD_CLK_CTRL                          0x058
+#define MVSD_SW_RESET                          0x05c
+#define MVSD_NOR_INTR_STATUS                   0x060
+#define MVSD_ERR_INTR_STATUS                   0x064
+#define MVSD_NOR_STATUS_EN                     0x068
+#define MVSD_ERR_STATUS_EN                     0x06c
+#define MVSD_NOR_INTR_EN                       0x070
+#define MVSD_ERR_INTR_EN                       0x074
+#define MVSD_AUTOCMD12_ERR_STATUS              0x078
+#define MVSD_CURR_BYTE_LEFT                    0x07c
+#define MVSD_CURR_BLK_LEFT                     0x080
+#define MVSD_AUTOCMD12_ARG_LOW                 0x084
+#define MVSD_AUTOCMD12_ARG_HI                  0x088
+#define MVSD_AUTOCMD12_CMD                     0x08c
+#define MVSD_AUTO_RSP(i)                       (0x090 + ((i)<<2))
+#define MVSD_AUTO_RSP0                         0x090
+#define MVSD_AUTO_RSP1                         0x094
+#define MVSD_AUTO_RSP2                         0x098
+#define MVSD_CLK_DIV                           0x128
+
+#define MVSD_WINDOW_CTRL(i)                    (0x108 + ((i) << 3))
+#define MVSD_WINDOW_BASE(i)                    (0x10c + ((i) << 3))
+
+
+/*
+ * MVSD_CMD
+ */
+
+#define MVSD_CMD_RSP_NONE                      (0 << 0)
+#define MVSD_CMD_RSP_136                       (1 << 0)
+#define MVSD_CMD_RSP_48                                (2 << 0)
+#define MVSD_CMD_RSP_48BUSY                    (3 << 0)
+
+#define MVSD_CMD_CHECK_DATACRC16               (1 << 2)
+#define MVSD_CMD_CHECK_CMDCRC                  (1 << 3)
+#define MVSD_CMD_INDX_CHECK                    (1 << 4)
+#define MVSD_CMD_DATA_PRESENT                  (1 << 5)
+#define MVSD_UNEXPECTED_RESP                   (1 << 7)
+#define MVSD_CMD_INDEX(x)                      ((x) << 8)
+
+
+/*
+ * MVSD_AUTOCMD12_CMD
+ */
+
+#define MVSD_AUTOCMD12_BUSY                    (1 << 0)
+#define MVSD_AUTOCMD12_INDX_CHECK              (1 << 1)
+#define MVSD_AUTOCMD12_INDEX(x)                        ((x) << 8)
+
+/*
+ * MVSD_XFER_MODE
+ */
+
+#define MVSD_XFER_MODE_WR_DATA_START           (1 << 0)
+#define MVSD_XFER_MODE_HW_WR_DATA_EN           (1 << 1)
+#define MVSD_XFER_MODE_AUTO_CMD12              (1 << 2)
+#define MVSD_XFER_MODE_INT_CHK_EN              (1 << 3)
+#define MVSD_XFER_MODE_TO_HOST                 (1 << 4)
+#define MVSD_XFER_MODE_STOP_CLK                        (1 << 5)
+#define MVSD_XFER_MODE_PIO                     (1 << 6)
+
+
+/*
+ * MVSD_HOST_CTRL
+ */
+
+#define MVSD_HOST_CTRL_PUSH_PULL_EN            (1 << 0)
+
+#define MVSD_HOST_CTRL_CARD_TYPE_MEM_ONLY      (0 << 1)
+#define MVSD_HOST_CTRL_CARD_TYPE_IO_ONLY       (1 << 1)
+#define MVSD_HOST_CTRL_CARD_TYPE_IO_MEM_COMBO  (2 << 1)
+#define MVSD_HOST_CTRL_CARD_TYPE_IO_MMC        (3 << 1)
+#define MVSD_HOST_CTRL_CARD_TYPE_MASK          (3 << 1)
+
+#define MVSD_HOST_CTRL_BIG_ENDIAN              (1 << 3)
+#define MVSD_HOST_CTRL_LSB_FIRST               (1 << 4)
+#define MVSD_HOST_CTRL_DATA_WIDTH_4_BITS       (1 << 9)
+#define MVSD_HOST_CTRL_HI_SPEED_EN             (1 << 10)
+
+#define MVSD_HOST_CTRL_TMOUT_MAX               0xf
+#define MVSD_HOST_CTRL_TMOUT_MASK              (0xf << 11)
+#define MVSD_HOST_CTRL_TMOUT(x)                ((x) << 11)
+#define MVSD_HOST_CTRL_TMOUT_EN                (1 << 15)
+
+
+/*
+ * MVSD_SW_RESET
+ */
+
+#define MVSD_SW_RESET_NOW                      (1 << 8)
+
+
+/*
+ * Normal interrupt status bits
+ */
+
+#define MVSD_NOR_CMD_DONE                      (1 << 0)
+#define MVSD_NOR_XFER_DONE                     (1 << 1)
+#define MVSD_NOR_BLK_GAP_EVT                   (1 << 2)
+#define MVSD_NOR_DMA_DONE                      (1 << 3)
+#define MVSD_NOR_TX_AVAIL                      (1 << 4)
+#define MVSD_NOR_RX_READY                      (1 << 5)
+#define MVSD_NOR_CARD_INT                      (1 << 8)
+#define MVSD_NOR_READ_WAIT_ON                  (1 << 9)
+#define MVSD_NOR_RX_FIFO_8W                    (1 << 10)
+#define MVSD_NOR_TX_FIFO_8W                    (1 << 11)
+#define MVSD_NOR_SUSPEND_ON                    (1 << 12)
+#define MVSD_NOR_AUTOCMD12_DONE                        (1 << 13)
+#define MVSD_NOR_UNEXP_RSP                     (1 << 14)
+#define MVSD_NOR_ERROR                         (1 << 15)
+
+
+/*
+ * Error status bits
+ */
+
+#define MVSD_ERR_CMD_TIMEOUT                   (1 << 0)
+#define MVSD_ERR_CMD_CRC                       (1 << 1)
+#define MVSD_ERR_CMD_ENDBIT                    (1 << 2)
+#define MVSD_ERR_CMD_INDEX                     (1 << 3)
+#define MVSD_ERR_DATA_TIMEOUT                  (1 << 4)
+#define MVSD_ERR_DATA_CRC                      (1 << 5)
+#define MVSD_ERR_DATA_ENDBIT                   (1 << 6)
+#define MVSD_ERR_AUTOCMD12                     (1 << 8)
+#define MVSD_ERR_CMD_STARTBIT                  (1 << 9)
+#define MVSD_ERR_XFER_SIZE                     (1 << 10)
+#define MVSD_ERR_RESP_T_BIT                    (1 << 11)
+#define MVSD_ERR_CRC_ENDBIT                    (1 << 12)
+#define MVSD_ERR_CRC_STARTBIT                  (1 << 13)
+#define MVSD_ERR_CRC_STATUS                    (1 << 14)
+
+
+/*
+ * CMD12 error status bits
+ */
+
+#define MVSD_AUTOCMD12_ERR_NOTEXE              (1 << 0)
+#define MVSD_AUTOCMD12_ERR_TIMEOUT             (1 << 1)
+#define MVSD_AUTOCMD12_ERR_CRC                 (1 << 2)
+#define MVSD_AUTOCMD12_ERR_ENDBIT              (1 << 3)
+#define MVSD_AUTOCMD12_ERR_INDEX               (1 << 4)
+#define MVSD_AUTOCMD12_ERR_RESP_T_BIT          (1 << 5)
+#define MVSD_AUTOCMD12_ERR_RESP_STARTBIT       (1 << 6)
+
+#endif
index 3916a5618e288e4cd5680a1127ce025efcc8b387..d183be6f2a5f7ab369ef8b60c5cfd25815db110f 100644 (file)
@@ -56,6 +56,7 @@
 #define SDVS18                 (0x5 << 9)
 #define SDVS30                 (0x6 << 9)
 #define SDVS33                 (0x7 << 9)
+#define SDVS_MASK              0x00000E00
 #define SDVSCLR                        0xFFFFF1FF
 #define SDVSDET                        0x00000400
 #define AUTOIDLE               0x1
@@ -76,6 +77,7 @@
 #define MSBS                   (1 << 5)
 #define BCE                    (1 << 1)
 #define FOUR_BIT               (1 << 1)
+#define DW8                    (1 << 5)
 #define CC                     0x1
 #define TC                     0x02
 #define OD                     0x1
  */
 #define OMAP_MMC1_DEVID                0
 #define OMAP_MMC2_DEVID                1
+#define OMAP_MMC3_DEVID                2
 
-#define OMAP_MMC_DATADIR_NONE  0
-#define OMAP_MMC_DATADIR_READ  1
-#define OMAP_MMC_DATADIR_WRITE 2
 #define MMC_TIMEOUT_MS         20
 #define OMAP_MMC_MASTER_CLOCK  96000000
 #define DRIVER_NAME            "mmci-omap-hs"
@@ -137,18 +137,18 @@ struct mmc_omap_host {
        resource_size_t         mapbase;
        unsigned int            id;
        unsigned int            dma_len;
-       unsigned int            dma_dir;
+       unsigned int            dma_sg_idx;
        unsigned char           bus_mode;
-       unsigned char           datadir;
        u32                     *buffer;
        u32                     bytesleft;
        int                     suspended;
        int                     irq;
        int                     carddetect;
        int                     use_dma, dma_ch;
-       int                     initstr;
+       int                     dma_line_tx, dma_line_rx;
        int                     slot_id;
        int                     dbclk_enabled;
+       int                     response_busy;
        struct  omap_mmc_platform_data  *pdata;
 };
 
@@ -218,7 +218,7 @@ mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
        struct mmc_omap_host *host = mmc_priv(mmc);
        struct omap_mmc_slot_data slot = host->pdata->slots[host->slot_id];
 
-       return sprintf(buf, "slot:%s\n", slot.name);
+       return sprintf(buf, "%s\n", slot.name);
 }
 
 static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL);
@@ -243,10 +243,14 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
        OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
        OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
 
+       host->response_busy = 0;
        if (cmd->flags & MMC_RSP_PRESENT) {
                if (cmd->flags & MMC_RSP_136)
                        resptype = 1;
-               else
+               else if (cmd->flags & MMC_RSP_BUSY) {
+                       resptype = 3;
+                       host->response_busy = 1;
+               } else
                        resptype = 2;
        }
 
@@ -275,19 +279,35 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
        OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
 }
 
+static int
+mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data)
+{
+       if (data->flags & MMC_DATA_WRITE)
+               return DMA_TO_DEVICE;
+       else
+               return DMA_FROM_DEVICE;
+}
+
 /*
  * Notify the transfer complete to MMC core
  */
 static void
 mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
 {
+       if (!data) {
+               struct mmc_request *mrq = host->mrq;
+
+               host->mrq = NULL;
+               mmc_omap_fclk_lazy_disable(host);
+               mmc_request_done(host->mmc, mrq);
+               return;
+       }
+
        host->data = NULL;
 
        if (host->use_dma && host->dma_ch != -1)
                dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
-                       host->dma_dir);
-
-       host->datadir = OMAP_MMC_DATADIR_NONE;
+                       mmc_omap_get_dma_dir(host, data));
 
        if (!data->error)
                data->bytes_xfered += data->blocks * (data->blksz);
@@ -322,7 +342,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
                        cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10);
                }
        }
-       if (host->data == NULL || cmd->error) {
+       if ((host->data == NULL && !host->response_busy) || cmd->error) {
                host->mrq = NULL;
                mmc_request_done(host->mmc, cmd->mrq);
        }
@@ -331,19 +351,18 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
 /*
  * DMA clean up for command errors
  */
-static void mmc_dma_cleanup(struct mmc_omap_host *host)
+static void mmc_dma_cleanup(struct mmc_omap_host *host, int errno)
 {
-       host->data->error = -ETIMEDOUT;
+       host->data->error = errno;
 
        if (host->use_dma && host->dma_ch != -1) {
                dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
-                       host->dma_dir);
+                       mmc_omap_get_dma_dir(host, host->data));
                omap_free_dma(host->dma_ch);
                host->dma_ch = -1;
                up(&host->sem);
        }
        host->data = NULL;
-       host->datadir = OMAP_MMC_DATADIR_NONE;
 }
 
 /*
@@ -412,7 +431,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
        struct mmc_data *data;
        int end_cmd = 0, end_trans = 0, status;
 
-       if (host->cmd == NULL && host->data == NULL) {
+       if (host->mrq == NULL) {
                OMAP_HSMMC_WRITE(host->base, STAT,
                        OMAP_HSMMC_READ(host->base, STAT));
                return IRQ_HANDLED;
@@ -437,18 +456,24 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                                }
                                end_cmd = 1;
                        }
-                       if (host->data) {
-                               mmc_dma_cleanup(host);
+                       if (host->data || host->response_busy) {
+                               if (host->data)
+                                       mmc_dma_cleanup(host, -ETIMEDOUT);
+                               host->response_busy = 0;
                                mmc_omap_reset_controller_fsm(host, SRD);
                        }
                }
                if ((status & DATA_TIMEOUT) ||
                        (status & DATA_CRC)) {
-                       if (host->data) {
-                               if (status & DATA_TIMEOUT)
-                                       mmc_dma_cleanup(host);
+                       if (host->data || host->response_busy) {
+                               int err = (status & DATA_TIMEOUT) ?
+                                               -ETIMEDOUT : -EILSEQ;
+
+                               if (host->data)
+                                       mmc_dma_cleanup(host, err);
                                else
-                                       host->data->error = -EILSEQ;
+                                       host->mrq->cmd->error = err;
+                               host->response_busy = 0;
                                mmc_omap_reset_controller_fsm(host, SRD);
                                end_trans = 1;
                        }
@@ -473,6 +498,19 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static void set_sd_bus_power(struct mmc_omap_host *host)
+{
+       unsigned long i;
+
+       OMAP_HSMMC_WRITE(host->base, HCTL,
+                        OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+       for (i = 0; i < loops_per_jiffy; i++) {
+               if (OMAP_HSMMC_READ(host->base, HCTL) & SDBP)
+                       break;
+               cpu_relax();
+       }
+}
+
 /*
  * Switch MMC interface voltage ... only relevant for MMC1.
  *
@@ -485,9 +523,6 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
        u32 reg_val = 0;
        int ret;
 
-       if (host->id != OMAP_MMC1_DEVID)
-               return 0;
-
        /* Disable the clocks */
        clk_disable(host->fclk);
        clk_disable(host->iclk);
@@ -532,9 +567,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
                reg_val |= SDVS30;
 
        OMAP_HSMMC_WRITE(host->base, HCTL, reg_val);
-
-       OMAP_HSMMC_WRITE(host->base, HCTL,
-               OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+       set_sd_bus_power(host);
 
        return 0;
 err:
@@ -551,7 +584,10 @@ static void mmc_omap_detect(struct work_struct *work)
                                                mmc_carddetect_work);
        struct omap_mmc_slot_data *slot = &mmc_slot(host);
 
-       host->carddetect = slot->card_detect(slot->card_detect_irq);
+       if (mmc_slot(host).card_detect)
+               host->carddetect = slot->card_detect(slot->card_detect_irq);
+       else
+               host->carddetect = -ENOSYS;
 
        sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
        if (host->carddetect) {
@@ -574,6 +610,48 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host,
+                                    struct mmc_data *data)
+{
+       int sync_dev;
+
+       if (data->flags & MMC_DATA_WRITE)
+               sync_dev = host->dma_line_tx;
+       else
+               sync_dev = host->dma_line_rx;
+       return sync_dev;
+}
+
+static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
+                                      struct mmc_data *data,
+                                      struct scatterlist *sgl)
+{
+       int blksz, nblk, dma_ch;
+
+       dma_ch = host->dma_ch;
+       if (data->flags & MMC_DATA_WRITE) {
+               omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+                       (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+               omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+                       sg_dma_address(sgl), 0, 0);
+       } else {
+               omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+                                       (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+               omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+                       sg_dma_address(sgl), 0, 0);
+       }
+
+       blksz = host->data->blksz;
+       nblk = sg_dma_len(sgl) / blksz;
+
+       omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
+                       blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
+                       mmc_omap_get_dma_sync_dev(host, data),
+                       !(data->flags & MMC_DATA_WRITE));
+
+       omap_start_dma(dma_ch);
+}
+
 /*
  * DMA call back function
  */
@@ -587,6 +665,14 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
        if (host->dma_ch < 0)
                return;
 
+       host->dma_sg_idx++;
+       if (host->dma_sg_idx < host->dma_len) {
+               /* Fire up the next transfer. */
+               mmc_omap_config_dma_params(host, host->data,
+                                          host->data->sg + host->dma_sg_idx);
+               return;
+       }
+
        omap_free_dma(host->dma_ch);
        host->dma_ch = -1;
        /*
@@ -596,39 +682,29 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
        up(&host->sem);
 }
 
-/*
- * Configure dma src and destination parameters
- */
-static int mmc_omap_config_dma_param(int sync_dir, struct mmc_omap_host *host,
-                               struct mmc_data *data)
-{
-       if (sync_dir == 0) {
-               omap_set_dma_dest_params(host->dma_ch, 0,
-                       OMAP_DMA_AMODE_CONSTANT,
-                       (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
-               omap_set_dma_src_params(host->dma_ch, 0,
-                       OMAP_DMA_AMODE_POST_INC,
-                       sg_dma_address(&data->sg[0]), 0, 0);
-       } else {
-               omap_set_dma_src_params(host->dma_ch, 0,
-                       OMAP_DMA_AMODE_CONSTANT,
-                       (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
-               omap_set_dma_dest_params(host->dma_ch, 0,
-                       OMAP_DMA_AMODE_POST_INC,
-                       sg_dma_address(&data->sg[0]), 0, 0);
-       }
-       return 0;
-}
 /*
  * Routine to configure and start DMA for the MMC card
  */
 static int
 mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
 {
-       int sync_dev, sync_dir = 0;
-       int dma_ch = 0, ret = 0, err = 1;
+       int dma_ch = 0, ret = 0, err = 1, i;
        struct mmc_data *data = req->data;
 
+       /* Sanity check: all the SG entries must be aligned by block size. */
+       for (i = 0; i < host->dma_len; i++) {
+               struct scatterlist *sgl;
+
+               sgl = data->sg + i;
+               if (sgl->length % data->blksz)
+                       return -EINVAL;
+       }
+       if ((data->blksz % 4) != 0)
+               /* REVISIT: The MMC buffer increments only when MSB is written.
+                * Return error for blksz which is non multiple of four.
+                */
+               return -EINVAL;
+
        /*
         * If for some reason the DMA transfer is still active,
         * we wait for timeout period and free the dma
@@ -647,49 +723,22 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
                        return err;
        }
 
-       if (!(data->flags & MMC_DATA_WRITE)) {
-               host->dma_dir = DMA_FROM_DEVICE;
-               if (host->id == OMAP_MMC1_DEVID)
-                       sync_dev = OMAP24XX_DMA_MMC1_RX;
-               else
-                       sync_dev = OMAP24XX_DMA_MMC2_RX;
-       } else {
-               host->dma_dir = DMA_TO_DEVICE;
-               if (host->id == OMAP_MMC1_DEVID)
-                       sync_dev = OMAP24XX_DMA_MMC1_TX;
-               else
-                       sync_dev = OMAP24XX_DMA_MMC2_TX;
-       }
-
-       ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb,
-                       host, &dma_ch);
+       ret = omap_request_dma(mmc_omap_get_dma_sync_dev(host, data), "MMC/SD",
+                              mmc_omap_dma_cb,host, &dma_ch);
        if (ret != 0) {
-               dev_dbg(mmc_dev(host->mmc),
+               dev_err(mmc_dev(host->mmc),
                        "%s: omap_request_dma() failed with %d\n",
                        mmc_hostname(host->mmc), ret);
                return ret;
        }
 
        host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
-                       data->sg_len, host->dma_dir);
+                       data->sg_len, mmc_omap_get_dma_dir(host, data));
        host->dma_ch = dma_ch;
+       host->dma_sg_idx = 0;
 
-       if (!(data->flags & MMC_DATA_WRITE))
-               mmc_omap_config_dma_param(1, host, data);
-       else
-               mmc_omap_config_dma_param(0, host, data);
-
-       if ((data->blksz % 4) == 0)
-               omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
-                       (data->blksz / 4), data->blocks, OMAP_DMA_SYNC_FRAME,
-                       sync_dev, sync_dir);
-       else
-               /* REVISIT: The MMC buffer increments only when MSB is written.
-                * Return error for blksz which is non multiple of four.
-                */
-               return -EINVAL;
+       mmc_omap_config_dma_params(host, data, data->sg);
 
-       omap_start_dma(dma_ch);
        return 0;
 }
 
@@ -739,7 +788,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
        host->data = req->data;
 
        if (req->data == NULL) {
-               host->datadir = OMAP_MMC_DATADIR_NONE;
                OMAP_HSMMC_WRITE(host->base, BLK, 0);
                return 0;
        }
@@ -748,9 +796,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
                                        | (req->data->blocks << 16));
        set_data_timeout(host, req);
 
-       host->datadir = (req->data->flags & MMC_DATA_WRITE) ?
-                       OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ;
-
        if (host->use_dma) {
                ret = mmc_omap_start_dma_transfer(host, req);
                if (ret != 0) {
@@ -782,36 +827,29 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        u16 dsor = 0;
        unsigned long regval;
        unsigned long timeout;
+       u32 con;
 
        switch (ios->power_mode) {
        case MMC_POWER_OFF:
                mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
-               /*
-                * Reset interface voltage to 3V if it's 1.8V now;
-                * only relevant on MMC-1, the others always use 1.8V.
-                *
-                * REVISIT: If we are able to detect cards after unplugging
-                * a 1.8V card, this code should not be needed.
-                */
-               if (host->id != OMAP_MMC1_DEVID)
-                       break;
-               if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) {
-                       int vdd = fls(host->mmc->ocr_avail) - 1;
-                       if (omap_mmc_switch_opcond(host, vdd) != 0)
-                               host->mmc->ios.vdd = vdd;
-               }
                break;
        case MMC_POWER_UP:
                mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd);
                break;
        }
 
+       con = OMAP_HSMMC_READ(host->base, CON);
        switch (mmc->ios.bus_width) {
+       case MMC_BUS_WIDTH_8:
+               OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
+               break;
        case MMC_BUS_WIDTH_4:
+               OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
                OMAP_HSMMC_WRITE(host->base, HCTL,
                        OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
                break;
        case MMC_BUS_WIDTH_1:
+               OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
                OMAP_HSMMC_WRITE(host->base, HCTL,
                        OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
                break;
@@ -891,6 +929,33 @@ static int omap_hsmmc_get_ro(struct mmc_host *mmc)
        return pdata->slots[0].get_ro(host->dev, 0);
 }
 
+static void omap_hsmmc_init(struct mmc_omap_host *host)
+{
+       u32 hctl, capa, value;
+
+       /* Only MMC1 supports 3.0V */
+       if (host->id == OMAP_MMC1_DEVID) {
+               hctl = SDVS30;
+               capa = VS30 | VS18;
+       } else {
+               hctl = SDVS18;
+               capa = VS18;
+       }
+
+       value = OMAP_HSMMC_READ(host->base, HCTL) & ~SDVS_MASK;
+       OMAP_HSMMC_WRITE(host->base, HCTL, value | hctl);
+
+       value = OMAP_HSMMC_READ(host->base, CAPA);
+       OMAP_HSMMC_WRITE(host->base, CAPA, value | capa);
+
+       /* Set the controller to AUTO IDLE mode */
+       value = OMAP_HSMMC_READ(host->base, SYSCONFIG);
+       OMAP_HSMMC_WRITE(host->base, SYSCONFIG, value | AUTOIDLE);
+
+       /* Set SD bus power bit */
+       set_sd_bus_power(host);
+}
+
 static struct mmc_host_ops mmc_omap_ops = {
        .request = omap_mmc_request,
        .set_ios = omap_mmc_set_ios,
@@ -906,7 +971,6 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        struct mmc_omap_host *host = NULL;
        struct resource *res;
        int ret = 0, irq;
-       u32 hctl, capa;
 
        if (pdata == NULL) {
                dev_err(&pdev->dev, "Platform Data is missing\n");
@@ -996,10 +1060,11 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
                else
                        host->dbclk_enabled = 1;
 
-#ifdef CONFIG_MMC_BLOCK_BOUNCE
-       mmc->max_phys_segs = 1;
-       mmc->max_hw_segs = 1;
-#endif
+       /* Since we do only SG emulation, we can have as many segs
+        * as we want. */
+       mmc->max_phys_segs = 1024;
+       mmc->max_hw_segs = 1024;
+
        mmc->max_blk_size = 512;       /* Block Length at max can be 1024 */
        mmc->max_blk_count = 0xFFFF;    /* No. of Blocks is 16 bits */
        mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
@@ -1008,31 +1073,31 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        mmc->ocr_avail = mmc_slot(host).ocr_mask;
        mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
 
-       if (pdata->slots[host->slot_id].wires >= 4)
+       if (pdata->slots[host->slot_id].wires >= 8)
+               mmc->caps |= MMC_CAP_8_BIT_DATA;
+       else if (pdata->slots[host->slot_id].wires >= 4)
                mmc->caps |= MMC_CAP_4_BIT_DATA;
 
-       /* Only MMC1 supports 3.0V */
-       if (host->id == OMAP_MMC1_DEVID) {
-               hctl = SDVS30;
-               capa = VS30 | VS18;
-       } else {
-               hctl = SDVS18;
-               capa = VS18;
-       }
-
-       OMAP_HSMMC_WRITE(host->base, HCTL,
-                       OMAP_HSMMC_READ(host->base, HCTL) | hctl);
-
-       OMAP_HSMMC_WRITE(host->base, CAPA,
-                       OMAP_HSMMC_READ(host->base, CAPA) | capa);
-
-       /* Set the controller to AUTO IDLE mode */
-       OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
-                       OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
+       omap_hsmmc_init(host);
 
-       /* Set SD bus power bit */
-       OMAP_HSMMC_WRITE(host->base, HCTL,
-                       OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+       /* Select DMA lines */
+       switch (host->id) {
+       case OMAP_MMC1_DEVID:
+               host->dma_line_tx = OMAP24XX_DMA_MMC1_TX;
+               host->dma_line_rx = OMAP24XX_DMA_MMC1_RX;
+               break;
+       case OMAP_MMC2_DEVID:
+               host->dma_line_tx = OMAP24XX_DMA_MMC2_TX;
+               host->dma_line_rx = OMAP24XX_DMA_MMC2_RX;
+               break;
+       case OMAP_MMC3_DEVID:
+               host->dma_line_tx = OMAP34XX_DMA_MMC3_TX;
+               host->dma_line_rx = OMAP34XX_DMA_MMC3_RX;
+               break;
+       default:
+               dev_err(mmc_dev(host->mmc), "Invalid MMC id\n");
+               goto err_irq;
+       }
 
        /* Request IRQ for MMC operations */
        ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED,
@@ -1051,7 +1116,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        }
 
        /* Request IRQ for card detect */
-       if ((mmc_slot(host).card_detect_irq) && (mmc_slot(host).card_detect)) {
+       if ((mmc_slot(host).card_detect_irq)) {
                ret = request_irq(mmc_slot(host).card_detect_irq,
                                  omap_mmc_cd_handler,
                                  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
@@ -1074,8 +1139,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
                if (ret < 0)
                        goto err_slot_name;
        }
-       if (mmc_slot(host).card_detect_irq && mmc_slot(host).card_detect &&
-                       host->pdata->slots[host->slot_id].get_cover_state) {
+       if (mmc_slot(host).card_detect_irq &&
+           host->pdata->slots[host->slot_id].get_cover_state) {
                ret = device_create_file(&mmc->class_dev,
                                        &dev_attr_cover_switch);
                if (ret < 0)
@@ -1173,20 +1238,8 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
                                                " level suspend\n");
                        }
 
-                       if (host->id == OMAP_MMC1_DEVID
-                                       && !(OMAP_HSMMC_READ(host->base, HCTL)
-                                                       & SDVSDET)) {
-                               OMAP_HSMMC_WRITE(host->base, HCTL,
-                                       OMAP_HSMMC_READ(host->base, HCTL)
-                                       & SDVSCLR);
-                               OMAP_HSMMC_WRITE(host->base, HCTL,
-                                       OMAP_HSMMC_READ(host->base, HCTL)
-                                       | SDVS30);
-                               OMAP_HSMMC_WRITE(host->base, HCTL,
-                                       OMAP_HSMMC_READ(host->base, HCTL)
-                                       | SDBP);
-                       }
-
+                       OMAP_HSMMC_WRITE(host->base, HCTL,
+                                        OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
                        clk_disable(host->fclk);
                        clk_disable(host->iclk);
                        clk_disable(host->dbclk);
@@ -1222,6 +1275,8 @@ static int omap_mmc_resume(struct platform_device *pdev)
                        dev_dbg(mmc_dev(host->mmc),
                                        "Enabling debounce clk failed\n");
 
+               omap_hsmmc_init(host);
+
                if (host->pdata->resume) {
                        ret = host->pdata->resume(&pdev->dev, host->slot_id);
                        if (ret)
diff --git a/drivers/mmc/host/sdhci-of.c b/drivers/mmc/host/sdhci-of.c
new file mode 100644 (file)
index 0000000..3ff4ac3
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * OpenFirmware bindings for Secure Digital Host Controller Interface.
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ *         Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mmc/host.h>
+#include "sdhci.h"
+
+struct sdhci_of_data {
+       unsigned int quirks;
+       struct sdhci_ops ops;
+};
+
+struct sdhci_of_host {
+       unsigned int clock;
+       u16 xfer_mode_shadow;
+};
+
+/*
+ * Ops and quirks for the Freescale eSDHC controller.
+ */
+
+#define ESDHC_DMA_SYSCTL       0x40c
+#define ESDHC_DMA_SNOOP                0x00000040
+
+#define ESDHC_SYSTEM_CONTROL   0x2c
+#define ESDHC_CLOCK_MASK       0x0000fff0
+#define ESDHC_PREDIV_SHIFT     8
+#define ESDHC_DIVIDER_SHIFT    4
+#define ESDHC_CLOCK_PEREN      0x00000004
+#define ESDHC_CLOCK_HCKEN      0x00000002
+#define ESDHC_CLOCK_IPGEN      0x00000001
+
+static u32 esdhc_readl(struct sdhci_host *host, int reg)
+{
+       return in_be32(host->ioaddr + reg);
+}
+
+static u16 esdhc_readw(struct sdhci_host *host, int reg)
+{
+       return in_be16(host->ioaddr + (reg ^ 0x2));
+}
+
+static u8 esdhc_readb(struct sdhci_host *host, int reg)
+{
+       return in_8(host->ioaddr + (reg ^ 0x3));
+}
+
+static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
+{
+       out_be32(host->ioaddr + reg, val);
+}
+
+static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
+{
+       struct sdhci_of_host *of_host = sdhci_priv(host);
+       int base = reg & ~0x3;
+       int shift = (reg & 0x2) * 8;
+
+       switch (reg) {
+       case SDHCI_TRANSFER_MODE:
+               /*
+                * Postpone this write, we must do it together with a
+                * command write that is down below.
+                */
+               of_host->xfer_mode_shadow = val;
+               return;
+       case SDHCI_COMMAND:
+               esdhc_writel(host, val << 16 | of_host->xfer_mode_shadow,
+                            SDHCI_TRANSFER_MODE);
+               return;
+       case SDHCI_BLOCK_SIZE:
+               /*
+                * Two last DMA bits are reserved, and first one is used for
+                * non-standard blksz of 4096 bytes that we don't support
+                * yet. So clear the DMA boundary bits.
+                */
+               val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
+               /* fall through */
+       }
+       clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift);
+}
+
+static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+       int base = reg & ~0x3;
+       int shift = (reg & 0x3) * 8;
+
+       clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
+}
+
+static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+       int div;
+       int pre_div = 2;
+
+       clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
+                 ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
+
+       if (clock == 0)
+               goto out;
+
+       if (host->max_clk / 16 > clock) {
+               for (; pre_div < 256; pre_div *= 2) {
+                       if (host->max_clk / pre_div < clock * 16)
+                               break;
+               }
+       }
+
+       for (div = 1; div <= 16; div++) {
+               if (host->max_clk / (div * pre_div) <= clock)
+                       break;
+       }
+
+       pre_div >>= 1;
+
+       setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
+                 ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN |
+                 div << ESDHC_DIVIDER_SHIFT | pre_div << ESDHC_PREDIV_SHIFT);
+       mdelay(100);
+out:
+       host->clock = clock;
+}
+
+static int esdhc_enable_dma(struct sdhci_host *host)
+{
+       setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
+       return 0;
+}
+
+static unsigned int esdhc_get_max_clock(struct sdhci_host *host)
+{
+       struct sdhci_of_host *of_host = sdhci_priv(host);
+
+       return of_host->clock;
+}
+
+static unsigned int esdhc_get_timeout_clock(struct sdhci_host *host)
+{
+       struct sdhci_of_host *of_host = sdhci_priv(host);
+
+       return of_host->clock / 1000;
+}
+
+static struct sdhci_of_data sdhci_esdhc = {
+       .quirks = SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
+                 SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+                 SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
+                 SDHCI_QUIRK_NO_BUSY_IRQ |
+                 SDHCI_QUIRK_NONSTANDARD_CLOCK |
+                 SDHCI_QUIRK_PIO_NEEDS_DELAY |
+                 SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET |
+                 SDHCI_QUIRK_NO_CARD_NO_RESET,
+       .ops = {
+               .readl = esdhc_readl,
+               .readw = esdhc_readw,
+               .readb = esdhc_readb,
+               .writel = esdhc_writel,
+               .writew = esdhc_writew,
+               .writeb = esdhc_writeb,
+               .set_clock = esdhc_set_clock,
+               .enable_dma = esdhc_enable_dma,
+               .get_max_clock = esdhc_get_max_clock,
+               .get_timeout_clock = esdhc_get_timeout_clock,
+       },
+};
+
+#ifdef CONFIG_PM
+
+static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state)
+{
+       struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+       return mmc_suspend_host(host->mmc, state);
+}
+
+static int sdhci_of_resume(struct of_device *ofdev)
+{
+       struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+       return mmc_resume_host(host->mmc);
+}
+
+#else
+
+#define sdhci_of_suspend NULL
+#define sdhci_of_resume NULL
+
+#endif
+
+static int __devinit sdhci_of_probe(struct of_device *ofdev,
+                                const struct of_device_id *match)
+{
+       struct device_node *np = ofdev->node;
+       struct sdhci_of_data *sdhci_of_data = match->data;
+       struct sdhci_host *host;
+       struct sdhci_of_host *of_host;
+       const u32 *clk;
+       int size;
+       int ret;
+
+       if (!of_device_is_available(np))
+               return -ENODEV;
+
+       host = sdhci_alloc_host(&ofdev->dev, sizeof(*of_host));
+       if (!host)
+               return -ENOMEM;
+
+       of_host = sdhci_priv(host);
+       dev_set_drvdata(&ofdev->dev, host);
+
+       host->ioaddr = of_iomap(np, 0);
+       if (!host->ioaddr) {
+               ret = -ENOMEM;
+               goto err_addr_map;
+       }
+
+       host->irq = irq_of_parse_and_map(np, 0);
+       if (!host->irq) {
+               ret = -EINVAL;
+               goto err_no_irq;
+       }
+
+       host->hw_name = dev_name(&ofdev->dev);
+       if (sdhci_of_data) {
+               host->quirks = sdhci_of_data->quirks;
+               host->ops = &sdhci_of_data->ops;
+       }
+
+       clk = of_get_property(np, "clock-frequency", &size);
+       if (clk && size == sizeof(*clk) && *clk)
+               of_host->clock = *clk;
+
+       ret = sdhci_add_host(host);
+       if (ret)
+               goto err_add_host;
+
+       return 0;
+
+err_add_host:
+       irq_dispose_mapping(host->irq);
+err_no_irq:
+       iounmap(host->ioaddr);
+err_addr_map:
+       sdhci_free_host(host);
+       return ret;
+}
+
+static int __devexit sdhci_of_remove(struct of_device *ofdev)
+{
+       struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+       sdhci_remove_host(host, 0);
+       sdhci_free_host(host);
+       irq_dispose_mapping(host->irq);
+       iounmap(host->ioaddr);
+       return 0;
+}
+
+static const struct of_device_id sdhci_of_match[] = {
+       { .compatible = "fsl,mpc8379-esdhc", .data = &sdhci_esdhc, },
+       { .compatible = "fsl,mpc8536-esdhc", .data = &sdhci_esdhc, },
+       { .compatible = "generic-sdhci", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, sdhci_of_match);
+
+static struct of_platform_driver sdhci_of_driver = {
+       .driver.name = "sdhci-of",
+       .match_table = sdhci_of_match,
+       .probe = sdhci_of_probe,
+       .remove = __devexit_p(sdhci_of_remove),
+       .suspend = sdhci_of_suspend,
+       .resume = sdhci_of_resume,
+};
+
+static int __init sdhci_of_init(void)
+{
+       return of_register_platform_driver(&sdhci_of_driver);
+}
+module_init(sdhci_of_init);
+
+static void __exit sdhci_of_exit(void)
+{
+       of_unregister_platform_driver(&sdhci_of_driver);
+}
+module_exit(sdhci_of_exit);
+
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface OF driver");
+MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, "
+             "Anton Vorontsov <avorontsov@ru.mvista.com>");
+MODULE_LICENSE("GPL");
index accb592764edb4522a24160eced3db7afca13f4a..30d8e3d4e6fdbc727605aa3f99563980c38940dd 100644 (file)
@@ -48,35 +48,35 @@ static void sdhci_dumpregs(struct sdhci_host *host)
        printk(KERN_DEBUG DRIVER_NAME ": ============== REGISTER DUMP ==============\n");
 
        printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version:  0x%08x\n",
-               readl(host->ioaddr + SDHCI_DMA_ADDRESS),
-               readw(host->ioaddr + SDHCI_HOST_VERSION));
+               sdhci_readl(host, SDHCI_DMA_ADDRESS),
+               sdhci_readw(host, SDHCI_HOST_VERSION));
        printk(KERN_DEBUG DRIVER_NAME ": Blk size: 0x%08x | Blk cnt:  0x%08x\n",
-               readw(host->ioaddr + SDHCI_BLOCK_SIZE),
-               readw(host->ioaddr + SDHCI_BLOCK_COUNT));
+               sdhci_readw(host, SDHCI_BLOCK_SIZE),
+               sdhci_readw(host, SDHCI_BLOCK_COUNT));
        printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
-               readl(host->ioaddr + SDHCI_ARGUMENT),
-               readw(host->ioaddr + SDHCI_TRANSFER_MODE));
+               sdhci_readl(host, SDHCI_ARGUMENT),
+               sdhci_readw(host, SDHCI_TRANSFER_MODE));
        printk(KERN_DEBUG DRIVER_NAME ": Present:  0x%08x | Host ctl: 0x%08x\n",
-               readl(host->ioaddr + SDHCI_PRESENT_STATE),
-               readb(host->ioaddr + SDHCI_HOST_CONTROL));
+               sdhci_readl(host, SDHCI_PRESENT_STATE),
+               sdhci_readb(host, SDHCI_HOST_CONTROL));
        printk(KERN_DEBUG DRIVER_NAME ": Power:    0x%08x | Blk gap:  0x%08x\n",
-               readb(host->ioaddr + SDHCI_POWER_CONTROL),
-               readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL));
+               sdhci_readb(host, SDHCI_POWER_CONTROL),
+               sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
        printk(KERN_DEBUG DRIVER_NAME ": Wake-up:  0x%08x | Clock:    0x%08x\n",
-               readb(host->ioaddr + SDHCI_WAKE_UP_CONTROL),
-               readw(host->ioaddr + SDHCI_CLOCK_CONTROL));
+               sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
+               sdhci_readw(host, SDHCI_CLOCK_CONTROL));
        printk(KERN_DEBUG DRIVER_NAME ": Timeout:  0x%08x | Int stat: 0x%08x\n",
-               readb(host->ioaddr + SDHCI_TIMEOUT_CONTROL),
-               readl(host->ioaddr + SDHCI_INT_STATUS));
+               sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
+               sdhci_readl(host, SDHCI_INT_STATUS));
        printk(KERN_DEBUG DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
-               readl(host->ioaddr + SDHCI_INT_ENABLE),
-               readl(host->ioaddr + SDHCI_SIGNAL_ENABLE));
+               sdhci_readl(host, SDHCI_INT_ENABLE),
+               sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
        printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
-               readw(host->ioaddr + SDHCI_ACMD12_ERR),
-               readw(host->ioaddr + SDHCI_SLOT_INT_STATUS));
+               sdhci_readw(host, SDHCI_ACMD12_ERR),
+               sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
        printk(KERN_DEBUG DRIVER_NAME ": Caps:     0x%08x | Max curr: 0x%08x\n",
-               readl(host->ioaddr + SDHCI_CAPABILITIES),
-               readl(host->ioaddr + SDHCI_MAX_CURRENT));
+               sdhci_readl(host, SDHCI_CAPABILITIES),
+               sdhci_readl(host, SDHCI_MAX_CURRENT));
 
        printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n");
 }
@@ -87,17 +87,65 @@ static void sdhci_dumpregs(struct sdhci_host *host)
  *                                                                           *
 \*****************************************************************************/
 
+static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
+{
+       u32 ier;
+
+       ier = sdhci_readl(host, SDHCI_INT_ENABLE);
+       ier &= ~clear;
+       ier |= set;
+       sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+       sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+}
+
+static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs)
+{
+       sdhci_clear_set_irqs(host, 0, irqs);
+}
+
+static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs)
+{
+       sdhci_clear_set_irqs(host, irqs, 0);
+}
+
+static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
+{
+       u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
+
+       if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+               return;
+
+       if (enable)
+               sdhci_unmask_irqs(host, irqs);
+       else
+               sdhci_mask_irqs(host, irqs);
+}
+
+static void sdhci_enable_card_detection(struct sdhci_host *host)
+{
+       sdhci_set_card_detection(host, true);
+}
+
+static void sdhci_disable_card_detection(struct sdhci_host *host)
+{
+       sdhci_set_card_detection(host, false);
+}
+
 static void sdhci_reset(struct sdhci_host *host, u8 mask)
 {
        unsigned long timeout;
+       u32 uninitialized_var(ier);
 
        if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
-               if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
+               if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
                        SDHCI_CARD_PRESENT))
                        return;
        }
 
-       writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET);
+       if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
+               ier = sdhci_readl(host, SDHCI_INT_ENABLE);
+
+       sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
 
        if (mask & SDHCI_RESET_ALL)
                host->clock = 0;
@@ -106,7 +154,7 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
        timeout = 100;
 
        /* hw clears the bit when it's done */
-       while (readb(host->ioaddr + SDHCI_SOFTWARE_RESET) & mask) {
+       while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
                if (timeout == 0) {
                        printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
                                mmc_hostname(host->mmc), (int)mask);
@@ -116,42 +164,44 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
                timeout--;
                mdelay(1);
        }
+
+       if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
+               sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
 }
 
 static void sdhci_init(struct sdhci_host *host)
 {
-       u32 intmask;
-
        sdhci_reset(host, SDHCI_RESET_ALL);
 
-       intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
+       sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
+               SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
                SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
                SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
-               SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT |
-               SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL |
-               SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE |
-               SDHCI_INT_ADMA_ERROR;
+               SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE);
+}
 
-       writel(intmask, host->ioaddr + SDHCI_INT_ENABLE);
-       writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+static void sdhci_reinit(struct sdhci_host *host)
+{
+       sdhci_init(host);
+       sdhci_enable_card_detection(host);
 }
 
 static void sdhci_activate_led(struct sdhci_host *host)
 {
        u8 ctrl;
 
-       ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+       ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
        ctrl |= SDHCI_CTRL_LED;
-       writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+       sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 }
 
 static void sdhci_deactivate_led(struct sdhci_host *host)
 {
        u8 ctrl;
 
-       ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+       ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
        ctrl &= ~SDHCI_CTRL_LED;
-       writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+       sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 }
 
 #ifdef SDHCI_USE_LEDS_CLASS
@@ -205,7 +255,7 @@ static void sdhci_read_block_pio(struct sdhci_host *host)
 
                while (len) {
                        if (chunk == 0) {
-                               scratch = readl(host->ioaddr + SDHCI_BUFFER);
+                               scratch = sdhci_readl(host, SDHCI_BUFFER);
                                chunk = 4;
                        }
 
@@ -257,7 +307,7 @@ static void sdhci_write_block_pio(struct sdhci_host *host)
                        len--;
 
                        if ((chunk == 4) || ((len == 0) && (blksize == 0))) {
-                               writel(scratch, host->ioaddr + SDHCI_BUFFER);
+                               sdhci_writel(host, scratch, SDHCI_BUFFER);
                                chunk = 0;
                                scratch = 0;
                        }
@@ -292,7 +342,10 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
                (host->data->blocks == 1))
                mask = ~0;
 
-       while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+       while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
+               if (host->quirks & SDHCI_QUIRK_PIO_NEEDS_DELAY)
+                       udelay(100);
+
                if (host->data->flags & MMC_DATA_READ)
                        sdhci_read_block_pio(host);
                else
@@ -561,6 +614,17 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data)
        return count;
 }
 
+static void sdhci_set_transfer_irqs(struct sdhci_host *host)
+{
+       u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL;
+       u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR;
+
+       if (host->flags & SDHCI_REQ_USE_DMA)
+               sdhci_clear_set_irqs(host, pio_irqs, dma_irqs);
+       else
+               sdhci_clear_set_irqs(host, dma_irqs, pio_irqs);
+}
+
 static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
 {
        u8 count;
@@ -581,7 +645,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
        host->data_early = 0;
 
        count = sdhci_calc_timeout(host, data);
-       writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
+       sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
 
        if (host->flags & SDHCI_USE_DMA)
                host->flags |= SDHCI_REQ_USE_DMA;
@@ -661,8 +725,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
                                WARN_ON(1);
                                host->flags &= ~SDHCI_REQ_USE_DMA;
                        } else {
-                               writel(host->adma_addr,
-                                       host->ioaddr + SDHCI_ADMA_ADDRESS);
+                               sdhci_writel(host, host->adma_addr,
+                                       SDHCI_ADMA_ADDRESS);
                        }
                } else {
                        int sg_cnt;
@@ -681,8 +745,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
                                host->flags &= ~SDHCI_REQ_USE_DMA;
                        } else {
                                WARN_ON(sg_cnt != 1);
-                               writel(sg_dma_address(data->sg),
-                                       host->ioaddr + SDHCI_DMA_ADDRESS);
+                               sdhci_writel(host, sg_dma_address(data->sg),
+                                       SDHCI_DMA_ADDRESS);
                        }
                }
        }
@@ -693,14 +757,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
         * is ADMA.
         */
        if (host->version >= SDHCI_SPEC_200) {
-               ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+               ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
                ctrl &= ~SDHCI_CTRL_DMA_MASK;
                if ((host->flags & SDHCI_REQ_USE_DMA) &&
                        (host->flags & SDHCI_USE_ADMA))
                        ctrl |= SDHCI_CTRL_ADMA32;
                else
                        ctrl |= SDHCI_CTRL_SDMA;
-               writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+               sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
        }
 
        if (!(host->flags & SDHCI_REQ_USE_DMA)) {
@@ -709,10 +773,11 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
                host->blocks = data->blocks;
        }
 
+       sdhci_set_transfer_irqs(host);
+
        /* We do not handle DMA boundaries, so set it to max (512 KiB) */
-       writew(SDHCI_MAKE_BLKSZ(7, data->blksz),
-               host->ioaddr + SDHCI_BLOCK_SIZE);
-       writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT);
+       sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, data->blksz), SDHCI_BLOCK_SIZE);
+       sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
 }
 
 static void sdhci_set_transfer_mode(struct sdhci_host *host,
@@ -733,7 +798,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
        if (host->flags & SDHCI_REQ_USE_DMA)
                mode |= SDHCI_TRNS_DMA;
 
-       writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE);
+       sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
 }
 
 static void sdhci_finish_data(struct sdhci_host *host)
@@ -802,7 +867,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
        if (host->mrq->data && (cmd == host->mrq->data->stop))
                mask &= ~SDHCI_DATA_INHIBIT;
 
-       while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+       while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
                if (timeout == 0) {
                        printk(KERN_ERR "%s: Controller never released "
                                "inhibit bit(s).\n", mmc_hostname(host->mmc));
@@ -821,7 +886,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 
        sdhci_prepare_data(host, cmd->data);
 
-       writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);
+       sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
 
        sdhci_set_transfer_mode(host, cmd->data);
 
@@ -849,8 +914,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
        if (cmd->data)
                flags |= SDHCI_CMD_DATA;
 
-       writew(SDHCI_MAKE_CMD(cmd->opcode, flags),
-               host->ioaddr + SDHCI_COMMAND);
+       sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
 }
 
 static void sdhci_finish_command(struct sdhci_host *host)
@@ -863,15 +927,15 @@ static void sdhci_finish_command(struct sdhci_host *host)
                if (host->cmd->flags & MMC_RSP_136) {
                        /* CRC is stripped so we need to do some shifting. */
                        for (i = 0;i < 4;i++) {
-                               host->cmd->resp[i] = readl(host->ioaddr +
+                               host->cmd->resp[i] = sdhci_readl(host,
                                        SDHCI_RESPONSE + (3-i)*4) << 8;
                                if (i != 3)
                                        host->cmd->resp[i] |=
-                                               readb(host->ioaddr +
+                                               sdhci_readb(host,
                                                SDHCI_RESPONSE + (3-i)*4-1);
                        }
                } else {
-                       host->cmd->resp[0] = readl(host->ioaddr + SDHCI_RESPONSE);
+                       host->cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
                }
        }
 
@@ -895,7 +959,13 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
        if (clock == host->clock)
                return;
 
-       writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+       if (host->ops->set_clock) {
+               host->ops->set_clock(host, clock);
+               if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK)
+                       return;
+       }
+
+       sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
 
        if (clock == 0)
                goto out;
@@ -908,11 +978,11 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 
        clk = div << SDHCI_DIVIDER_SHIFT;
        clk |= SDHCI_CLOCK_INT_EN;
-       writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+       sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
 
        /* Wait max 10 ms */
        timeout = 10;
-       while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL))
+       while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
                & SDHCI_CLOCK_INT_STABLE)) {
                if (timeout == 0) {
                        printk(KERN_ERR "%s: Internal clock never "
@@ -925,7 +995,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
        }
 
        clk |= SDHCI_CLOCK_CARD_EN;
-       writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+       sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
 
 out:
        host->clock = clock;
@@ -939,7 +1009,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
                return;
 
        if (power == (unsigned short)-1) {
-               writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
+               sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
                goto out;
        }
 
@@ -948,7 +1018,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
         * a new value. Some controllers don't seem to like this though.
         */
        if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
-               writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
+               sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
 
        pwr = SDHCI_POWER_ON;
 
@@ -973,10 +1043,9 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
         * and set turn on power at the same time, so set the voltage first.
         */
        if ((host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER))
-               writeb(pwr & ~SDHCI_POWER_ON,
-                               host->ioaddr + SDHCI_POWER_CONTROL);
+               sdhci_writeb(host, pwr & ~SDHCI_POWER_ON, SDHCI_POWER_CONTROL);
 
-       writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL);
+       sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
 
 out:
        host->power = power;
@@ -991,6 +1060,7 @@ out:
 static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
        struct sdhci_host *host;
+       bool present;
        unsigned long flags;
 
        host = mmc_priv(mmc);
@@ -1005,8 +1075,14 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
        host->mrq = mrq;
 
-       if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
-               || (host->flags & SDHCI_DEVICE_DEAD)) {
+       /* If polling, assume that the card is always present. */
+       if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+               present = true;
+       else
+               present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+                               SDHCI_CARD_PRESENT;
+
+       if (!present || host->flags & SDHCI_DEVICE_DEAD) {
                host->mrq->cmd->error = -ENOMEDIUM;
                tasklet_schedule(&host->finish_tasklet);
        } else
@@ -1034,8 +1110,8 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
         * Should clear out any weird states.
         */
        if (ios->power_mode == MMC_POWER_OFF) {
-               writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE);
-               sdhci_init(host);
+               sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
+               sdhci_reinit(host);
        }
 
        sdhci_set_clock(host, ios->clock);
@@ -1045,7 +1121,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        else
                sdhci_set_power(host, ios->vdd);
 
-       ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+       ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
 
        if (ios->bus_width == MMC_BUS_WIDTH_4)
                ctrl |= SDHCI_CTRL_4BITBUS;
@@ -1057,7 +1133,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        else
                ctrl &= ~SDHCI_CTRL_HISPD;
 
-       writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+       sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 
        /*
         * Some (ENE) controllers go apeshit on some ios operation,
@@ -1085,10 +1161,12 @@ static int sdhci_get_ro(struct mmc_host *mmc)
        if (host->flags & SDHCI_DEVICE_DEAD)
                present = 0;
        else
-               present = readl(host->ioaddr + SDHCI_PRESENT_STATE);
+               present = sdhci_readl(host, SDHCI_PRESENT_STATE);
 
        spin_unlock_irqrestore(&host->lock, flags);
 
+       if (host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT)
+               return !!(present & SDHCI_WRITE_PROTECT);
        return !(present & SDHCI_WRITE_PROTECT);
 }
 
@@ -1096,7 +1174,6 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
 {
        struct sdhci_host *host;
        unsigned long flags;
-       u32 ier;
 
        host = mmc_priv(mmc);
 
@@ -1105,15 +1182,10 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
        if (host->flags & SDHCI_DEVICE_DEAD)
                goto out;
 
-       ier = readl(host->ioaddr + SDHCI_INT_ENABLE);
-
-       ier &= ~SDHCI_INT_CARD_INT;
        if (enable)
-               ier |= SDHCI_INT_CARD_INT;
-
-       writel(ier, host->ioaddr + SDHCI_INT_ENABLE);
-       writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE);
-
+               sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
+       else
+               sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
 out:
        mmiowb();
 
@@ -1142,7 +1214,7 @@ static void sdhci_tasklet_card(unsigned long param)
 
        spin_lock_irqsave(&host->lock, flags);
 
-       if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+       if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
                if (host->mrq) {
                        printk(KERN_ERR "%s: Card removed during transfer!\n",
                                mmc_hostname(host->mmc));
@@ -1346,8 +1418,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
                 * we need to at least restart the transfer.
                 */
                if (intmask & SDHCI_INT_DMA_END)
-                       writel(readl(host->ioaddr + SDHCI_DMA_ADDRESS),
-                               host->ioaddr + SDHCI_DMA_ADDRESS);
+                       sdhci_writel(host, sdhci_readl(host, SDHCI_DMA_ADDRESS),
+                               SDHCI_DMA_ADDRESS);
 
                if (intmask & SDHCI_INT_DATA_END) {
                        if (host->cmd) {
@@ -1373,7 +1445,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
 
        spin_lock(&host->lock);
 
-       intmask = readl(host->ioaddr + SDHCI_INT_STATUS);
+       intmask = sdhci_readl(host, SDHCI_INT_STATUS);
 
        if (!intmask || intmask == 0xffffffff) {
                result = IRQ_NONE;
@@ -1384,22 +1456,22 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
                mmc_hostname(host->mmc), intmask);
 
        if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
-               writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),
-                       host->ioaddr + SDHCI_INT_STATUS);
+               sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
+                       SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
                tasklet_schedule(&host->card_tasklet);
        }
 
        intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
 
        if (intmask & SDHCI_INT_CMD_MASK) {
-               writel(intmask & SDHCI_INT_CMD_MASK,
-                       host->ioaddr + SDHCI_INT_STATUS);
+               sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
+                       SDHCI_INT_STATUS);
                sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
        }
 
        if (intmask & SDHCI_INT_DATA_MASK) {
-               writel(intmask & SDHCI_INT_DATA_MASK,
-                       host->ioaddr + SDHCI_INT_STATUS);
+               sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK,
+                       SDHCI_INT_STATUS);
                sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
        }
 
@@ -1410,7 +1482,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
        if (intmask & SDHCI_INT_BUS_POWER) {
                printk(KERN_ERR "%s: Card is consuming too much power!\n",
                        mmc_hostname(host->mmc));
-               writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS);
+               sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
        }
 
        intmask &= ~SDHCI_INT_BUS_POWER;
@@ -1425,7 +1497,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
                        mmc_hostname(host->mmc), intmask);
                sdhci_dumpregs(host);
 
-               writel(intmask, host->ioaddr + SDHCI_INT_STATUS);
+               sdhci_writel(host, intmask, SDHCI_INT_STATUS);
        }
 
        result = IRQ_HANDLED;
@@ -1455,6 +1527,8 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
 {
        int ret;
 
+       sdhci_disable_card_detection(host);
+
        ret = mmc_suspend_host(host->mmc, state);
        if (ret)
                return ret;
@@ -1487,6 +1561,8 @@ int sdhci_resume_host(struct sdhci_host *host)
        if (ret)
                return ret;
 
+       sdhci_enable_card_detection(host);
+
        return 0;
 }
 
@@ -1537,7 +1613,7 @@ int sdhci_add_host(struct sdhci_host *host)
 
        sdhci_reset(host, SDHCI_RESET_ALL);
 
-       host->version = readw(host->ioaddr + SDHCI_HOST_VERSION);
+       host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
        host->version = (host->version & SDHCI_SPEC_VER_MASK)
                                >> SDHCI_SPEC_VER_SHIFT;
        if (host->version > SDHCI_SPEC_200) {
@@ -1546,7 +1622,7 @@ int sdhci_add_host(struct sdhci_host *host)
                        host->version);
        }
 
-       caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
+       caps = sdhci_readl(host, SDHCI_CAPABILITIES);
 
        if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
                host->flags |= SDHCI_USE_DMA;
@@ -1614,19 +1690,27 @@ int sdhci_add_host(struct sdhci_host *host)
 
        host->max_clk =
                (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
+       host->max_clk *= 1000000;
        if (host->max_clk == 0) {
-               printk(KERN_ERR "%s: Hardware doesn't specify base clock "
-                       "frequency.\n", mmc_hostname(mmc));
-               return -ENODEV;
+               if (!host->ops->get_max_clock) {
+                       printk(KERN_ERR
+                              "%s: Hardware doesn't specify base clock "
+                              "frequency.\n", mmc_hostname(mmc));
+                       return -ENODEV;
+               }
+               host->max_clk = host->ops->get_max_clock(host);
        }
-       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", mmc_hostname(mmc));
-               return -ENODEV;
+               if (!host->ops->get_timeout_clock) {
+                       printk(KERN_ERR
+                              "%s: Hardware doesn't specify timeout clock "
+                              "frequency.\n", mmc_hostname(mmc));
+                       return -ENODEV;
+               }
+               host->timeout_clk = host->ops->get_timeout_clock(host);
        }
        if (caps & SDHCI_TIMEOUT_CLK_UNIT)
                host->timeout_clk *= 1000;
@@ -1642,6 +1726,9 @@ int sdhci_add_host(struct sdhci_host *host)
        if (caps & SDHCI_CAN_DO_HISPD)
                mmc->caps |= MMC_CAP_SD_HIGHSPEED;
 
+       if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+               mmc->caps |= MMC_CAP_NEEDS_POLL;
+
        mmc->ocr_avail = 0;
        if (caps & SDHCI_CAN_VDD_330)
                mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
@@ -1690,13 +1777,19 @@ int sdhci_add_host(struct sdhci_host *host)
         * Maximum block size. This varies from controller to controller and
         * is specified in the capabilities register.
         */
-       mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT;
-       if (mmc->max_blk_size >= 3) {
-               printk(KERN_WARNING "%s: Invalid maximum block size, "
-                       "assuming 512 bytes\n", mmc_hostname(mmc));
-               mmc->max_blk_size = 512;
-       } else
-               mmc->max_blk_size = 512 << mmc->max_blk_size;
+       if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
+               mmc->max_blk_size = 2;
+       } else {
+               mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
+                               SDHCI_MAX_BLOCK_SHIFT;
+               if (mmc->max_blk_size >= 3) {
+                       printk(KERN_WARNING "%s: Invalid maximum block size, "
+                               "assuming 512 bytes\n", mmc_hostname(mmc));
+                       mmc->max_blk_size = 0;
+               }
+       }
+
+       mmc->max_blk_size = 512 << mmc->max_blk_size;
 
        /*
         * Maximum block count.
@@ -1746,6 +1839,8 @@ int sdhci_add_host(struct sdhci_host *host)
                (host->flags & SDHCI_USE_ADMA)?"A":"",
                (host->flags & SDHCI_USE_DMA)?"DMA":"PIO");
 
+       sdhci_enable_card_detection(host);
+
        return 0;
 
 #ifdef SDHCI_USE_LEDS_CLASS
@@ -1782,6 +1877,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
                spin_unlock_irqrestore(&host->lock, flags);
        }
 
+       sdhci_disable_card_detection(host);
+
        mmc_remove_host(host->mmc);
 
 #ifdef SDHCI_USE_LEDS_CLASS
index 43c37c68d07af44c15ab89bb372730b13517be5a..f20a834f430925827d9b8490ab481f058ff97696 100644 (file)
@@ -10,6 +10,9 @@
  */
 
 #include <linux/scatterlist.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/io.h>
 
 /*
  * Controller registers
                SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
                SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
                SDHCI_INT_DATA_END_BIT)
+#define SDHCI_INT_ALL_MASK     ((unsigned int)-1)
 
 #define SDHCI_ACMD12_ERR       0x3C
 
@@ -210,6 +214,18 @@ struct sdhci_host {
 #define SDHCI_QUIRK_BROKEN_SMALL_PIO                   (1<<13)
 /* Controller does not provide transfer-complete interrupt when not busy */
 #define SDHCI_QUIRK_NO_BUSY_IRQ                                (1<<14)
+/* Controller has unreliable card detection */
+#define SDHCI_QUIRK_BROKEN_CARD_DETECTION              (1<<15)
+/* Controller reports inverted write-protect state */
+#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT             (1<<16)
+/* Controller has nonstandard clock management */
+#define SDHCI_QUIRK_NONSTANDARD_CLOCK                  (1<<17)
+/* Controller does not like fast PIO transfers */
+#define SDHCI_QUIRK_PIO_NEEDS_DELAY                    (1<<18)
+/* Controller losing signal/interrupt enable states after reset */
+#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET           (1<<19)
+/* Controller has to be forced to use block size of 2048 bytes */
+#define SDHCI_QUIRK_FORCE_BLK_SZ_2048                  (1<<20)
 
        int                     irq;            /* Device IRQ */
        void __iomem *          ioaddr;         /* Mapped address */
@@ -267,9 +283,105 @@ struct sdhci_host {
 
 
 struct sdhci_ops {
+#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
+       u32             (*readl)(struct sdhci_host *host, int reg);
+       u16             (*readw)(struct sdhci_host *host, int reg);
+       u8              (*readb)(struct sdhci_host *host, int reg);
+       void            (*writel)(struct sdhci_host *host, u32 val, int reg);
+       void            (*writew)(struct sdhci_host *host, u16 val, int reg);
+       void            (*writeb)(struct sdhci_host *host, u8 val, int reg);
+#endif
+
+       void    (*set_clock)(struct sdhci_host *host, unsigned int clock);
+
        int             (*enable_dma)(struct sdhci_host *host);
+       unsigned int    (*get_max_clock)(struct sdhci_host *host);
+       unsigned int    (*get_timeout_clock)(struct sdhci_host *host);
 };
 
+#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
+
+static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
+{
+       if (unlikely(host->ops->writel))
+               host->ops->writel(host, val, reg);
+       else
+               writel(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
+{
+       if (unlikely(host->ops->writew))
+               host->ops->writew(host, val, reg);
+       else
+               writew(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+       if (unlikely(host->ops->writeb))
+               host->ops->writeb(host, val, reg);
+       else
+               writeb(val, host->ioaddr + reg);
+}
+
+static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
+{
+       if (unlikely(host->ops->readl))
+               return host->ops->readl(host, reg);
+       else
+               return readl(host->ioaddr + reg);
+}
+
+static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
+{
+       if (unlikely(host->ops->readw))
+               return host->ops->readw(host, reg);
+       else
+               return readw(host->ioaddr + reg);
+}
+
+static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
+{
+       if (unlikely(host->ops->readb))
+               return host->ops->readb(host, reg);
+       else
+               return readb(host->ioaddr + reg);
+}
+
+#else
+
+static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
+{
+       writel(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
+{
+       writew(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+       writeb(val, host->ioaddr + reg);
+}
+
+static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
+{
+       return readl(host->ioaddr + reg);
+}
+
+static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
+{
+       return readw(host->ioaddr + reg);
+}
+
+static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
+{
+       return readb(host->ioaddr + reg);
+}
+
+#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */
 
 extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
        size_t priv_size);
index 6a7a619048339a556be68aa7a3640dbf4720e023..63fbd5b7d312b503e285a68a1895af709c134027 100644 (file)
@@ -568,11 +568,11 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
        host->mmc = mmc;
        platform_set_drvdata(dev, mmc);
 
-       host->ctl = ioremap(res_ctl->start, res_ctl->end - res_ctl->start);
+       host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));
        if (!host->ctl)
                goto host_free;
 
-       host->cnf = ioremap(res_cnf->start, res_cnf->end - res_cnf->start);
+       host->cnf = ioremap(res_cnf->start, resource_size(res_cnf));
        if (!host->cnf)
                goto unmap_ctl;
 
@@ -650,10 +650,10 @@ static int __devexit tmio_mmc_remove(struct platform_device *dev)
        if (mmc) {
                struct tmio_mmc_host *host = mmc_priv(mmc);
                mmc_remove_host(mmc);
-               mmc_free_host(mmc);
                free_irq(host->irq, host);
                iounmap(host->ctl);
                iounmap(host->cnf);
+               mmc_free_host(mmc);
        }
 
        return 0;
index ba2b4240a86ad722c82a3e910d8cbb129305664b..9c831ab2ece6d37af2bbfadce6311b85bc011c22 100644 (file)
@@ -8,6 +8,9 @@
  * published by the Free Software Foundation.
  *
  */
+
+#include <linux/highmem.h>
+
 #define CNF_CMD     0x04
 #define CNF_CTL_BASE   0x10
 #define CNF_INT_PIN  0x3d