summaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorTimothy Pearson <tpearson@raptorengineering.com>2017-08-23 14:45:25 -0500
committerTimothy Pearson <tpearson@raptorengineering.com>2017-08-23 14:45:25 -0500
commitfcbb27b0ec6dcbc5a5108cb8fb19eae64593d204 (patch)
tree22962a4387943edc841c72a4e636a068c66d58fd /drivers/mmc
downloadast2050-linux-kernel-fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204.zip
ast2050-linux-kernel-fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204.tar.gz
Initial import of modified Linux 2.6.28 tree
Original upstream URL: git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git | branch linux-2.6.28.y
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/Kconfig30
-rw-r--r--drivers/mmc/Makefile12
-rw-r--r--drivers/mmc/card/Kconfig52
-rw-r--r--drivers/mmc/card/Makefile14
-rw-r--r--drivers/mmc/card/block.c615
-rw-r--r--drivers/mmc/card/mmc_test.c1211
-rw-r--r--drivers/mmc/card/queue.c370
-rw-r--r--drivers/mmc/card/queue.h31
-rw-r--r--drivers/mmc/card/sdio_uart.c1161
-rw-r--r--drivers/mmc/core/Kconfig16
-rw-r--r--drivers/mmc/core/Makefile15
-rw-r--r--drivers/mmc/core/bus.c286
-rw-r--r--drivers/mmc/core/bus.h31
-rw-r--r--drivers/mmc/core/core.c862
-rw-r--r--drivers/mmc/core/core.h63
-rw-r--r--drivers/mmc/core/debugfs.c225
-rw-r--r--drivers/mmc/core/host.c179
-rw-r--r--drivers/mmc/core/host.h18
-rw-r--r--drivers/mmc/core/mmc.c716
-rw-r--r--drivers/mmc/core/mmc_ops.c404
-rw-r--r--drivers/mmc/core/mmc_ops.h30
-rw-r--r--drivers/mmc/core/sd.c698
-rw-r--r--drivers/mmc/core/sd_ops.c350
-rw-r--r--drivers/mmc/core/sd_ops.h24
-rw-r--r--drivers/mmc/core/sdio.c441
-rw-r--r--drivers/mmc/core/sdio_bus.c262
-rw-r--r--drivers/mmc/core/sdio_bus.h22
-rw-r--r--drivers/mmc/core/sdio_cis.c352
-rw-r--r--drivers/mmc/core/sdio_cis.h23
-rw-r--r--drivers/mmc/core/sdio_io.c637
-rw-r--r--drivers/mmc/core/sdio_irq.c273
-rw-r--r--drivers/mmc/core/sdio_ops.c176
-rw-r--r--drivers/mmc/core/sdio_ops.h22
-rw-r--r--drivers/mmc/host/Kconfig205
-rw-r--r--drivers/mmc/host/Makefile26
-rw-r--r--drivers/mmc/host/ast_sdhci.c1929
-rw-r--r--drivers/mmc/host/at91_mci.c1250
-rw-r--r--drivers/mmc/host/atmel-mci-regs.h95
-rw-r--r--drivers/mmc/host/atmel-mci.c1769
-rw-r--r--drivers/mmc/host/au1xmmc.c1206
-rw-r--r--drivers/mmc/host/imxmmc.c1151
-rw-r--r--drivers/mmc/host/imxmmc.h67
-rw-r--r--drivers/mmc/host/mmc_spi.c1439
-rw-r--r--drivers/mmc/host/mmci.c725
-rw-r--r--drivers/mmc/host/mmci.h179
-rw-r--r--drivers/mmc/host/omap.c1629
-rw-r--r--drivers/mmc/host/pxamci.c731
-rw-r--r--drivers/mmc/host/pxamci.h90
-rw-r--r--drivers/mmc/host/ricoh_mmc.c257
-rw-r--r--drivers/mmc/host/s3cmci.c1568
-rw-r--r--drivers/mmc/host/s3cmci.h74
-rw-r--r--drivers/mmc/host/sdhci-pci.c735
-rw-r--r--drivers/mmc/host/sdhci.c1841
-rw-r--r--drivers/mmc/host/sdhci.h291
-rw-r--r--drivers/mmc/host/sdricoh_cs.c574
-rw-r--r--drivers/mmc/host/tifm_sd.c1095
-rw-r--r--drivers/mmc/host/tmio_mmc.c691
-rw-r--r--drivers/mmc/host/tmio_mmc.h194
-rw-r--r--drivers/mmc/host/wbsd.c2047
-rw-r--r--drivers/mmc/host/wbsd.h185
60 files changed, 31664 insertions, 0 deletions
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
new file mode 100644
index 0000000..f2eeb38
--- /dev/null
+++ b/drivers/mmc/Kconfig
@@ -0,0 +1,30 @@
+#
+# MMC subsystem configuration
+#
+
+menuconfig MMC
+ tristate "MMC/SD/SDIO card support"
+ depends on HAS_IOMEM
+ help
+ This selects MultiMediaCard, Secure Digital and Secure
+ Digital I/O support.
+
+ If you want MMC/SD/SDIO support, you should say Y here and
+ also to your specific host controller driver.
+
+config MMC_DEBUG
+ bool "MMC debugging"
+ depends on MMC != n
+ help
+ This is an option for use by developers; most people should
+ say N here. This enables MMC core and driver debugging.
+
+if MMC
+
+source "drivers/mmc/core/Kconfig"
+
+source "drivers/mmc/card/Kconfig"
+
+source "drivers/mmc/host/Kconfig"
+
+endif # MMC
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
new file mode 100644
index 0000000..9979f5e
--- /dev/null
+++ b/drivers/mmc/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the kernel mmc device drivers.
+#
+
+ifeq ($(CONFIG_MMC_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
+obj-$(CONFIG_MMC) += core/
+obj-$(CONFIG_MMC) += card/
+obj-$(CONFIG_MMC) += host/
+
diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
new file mode 100644
index 0000000..3f2a912
--- /dev/null
+++ b/drivers/mmc/card/Kconfig
@@ -0,0 +1,52 @@
+#
+# MMC/SD card drivers
+#
+
+comment "MMC/SD/SDIO Card Drivers"
+
+config MMC_BLOCK
+ tristate "MMC block device driver"
+ depends on BLOCK
+ default y
+ help
+ Say Y here to enable the MMC block device driver support.
+ This provides a block device driver, which you can use to
+ mount the filesystem. Almost everyone wishing MMC support
+ should say Y or M here.
+
+config MMC_BLOCK_BOUNCE
+ bool "Use bounce buffer for simple hosts"
+ depends on MMC_BLOCK
+ default y
+ help
+ SD/MMC is a high latency protocol where it is crucial to
+ send large requests in order to get high performance. Many
+ controllers, however, are restricted to continuous memory
+ (i.e. they can't do scatter-gather), something the kernel
+ rarely can provide.
+
+ Say Y here to help these restricted hosts by bouncing
+ requests back and forth from a large buffer. You will get
+ a big performance gain at the cost of up to 64 KiB of
+ physical memory.
+
+ If unsure, say Y here.
+
+config SDIO_UART
+ tristate "SDIO UART/GPS class support"
+ help
+ SDIO function driver for SDIO cards that implements the UART
+ class, as well as the GPS class which appears like a UART.
+
+config MMC_TEST
+ tristate "MMC host test driver"
+ default n
+ help
+ Development driver that performs a series of reads and writes
+ to a memory card in order to expose certain well known bugs
+ in host controllers. The tests are executed by writing to the
+ "test" file in sysfs under each card. Note that whatever is
+ on your card will be overwritten by these tests.
+
+ This driver is only of interest to those developing or
+ testing a host driver. Most people should say N here.
diff --git a/drivers/mmc/card/Makefile b/drivers/mmc/card/Makefile
new file mode 100644
index 0000000..0d40751
--- /dev/null
+++ b/drivers/mmc/card/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for MMC/SD card drivers
+#
+
+ifeq ($(CONFIG_MMC_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
+obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
+mmc_block-objs := block.o queue.o
+obj-$(CONFIG_MMC_TEST) += mmc_test.o
+
+obj-$(CONFIG_SDIO_UART) += sdio_uart.o
+
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
new file mode 100644
index 0000000..db7e4c4
--- /dev/null
+++ b/drivers/mmc/card/block.c
@@ -0,0 +1,615 @@
+/*
+ * Block driver for media (i.e., flash cards)
+ *
+ * Copyright 2002 Hewlett-Packard Company
+ * Copyright 2005-2008 Pierre Ossman
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Author: Andrew Christian
+ * 28 May 2002
+ */
+#include <linux/moduleparam.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+#include <linux/mutex.h>
+#include <linux/scatterlist.h>
+#include <linux/string_helpers.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "queue.h"
+
+/*
+ * max 8 partitions per card
+ */
+#define MMC_SHIFT 3
+#define MMC_NUM_MINORS (256 >> MMC_SHIFT)
+
+static DECLARE_BITMAP(dev_use, MMC_NUM_MINORS);
+
+/*
+ * There is one mmc_blk_data per slot.
+ */
+struct mmc_blk_data {
+ spinlock_t lock;
+ struct gendisk *disk;
+ struct mmc_queue queue;
+
+ unsigned int usage;
+ unsigned int read_only;
+};
+
+static DEFINE_MUTEX(open_lock);
+
+static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
+{
+ struct mmc_blk_data *md;
+
+ mutex_lock(&open_lock);
+ md = disk->private_data;
+ if (md && md->usage == 0)
+ md = NULL;
+ if (md)
+ md->usage++;
+ mutex_unlock(&open_lock);
+
+ return md;
+}
+
+static void mmc_blk_put(struct mmc_blk_data *md)
+{
+ mutex_lock(&open_lock);
+ md->usage--;
+ if (md->usage == 0) {
+ int devidx = MINOR(disk_devt(md->disk)) >> MMC_SHIFT;
+ __clear_bit(devidx, dev_use);
+
+ put_disk(md->disk);
+ kfree(md);
+ }
+ mutex_unlock(&open_lock);
+}
+
+static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
+ int ret = -ENXIO;
+
+ if (md) {
+ if (md->usage == 2)
+ check_disk_change(bdev);
+ ret = 0;
+
+ if ((mode & FMODE_WRITE) && md->read_only) {
+ mmc_blk_put(md);
+ ret = -EROFS;
+ }
+ }
+
+ return ret;
+}
+
+static int mmc_blk_release(struct gendisk *disk, fmode_t mode)
+{
+ struct mmc_blk_data *md = disk->private_data;
+
+ mmc_blk_put(md);
+ return 0;
+}
+
+static int
+mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+ geo->cylinders = get_capacity(bdev->bd_disk) / (4 * 16);
+ geo->heads = 4;
+ geo->sectors = 16;
+ return 0;
+}
+
+static struct block_device_operations mmc_bdops = {
+ .open = mmc_blk_open,
+ .release = mmc_blk_release,
+ .getgeo = mmc_blk_getgeo,
+ .owner = THIS_MODULE,
+};
+
+struct mmc_blk_request {
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_command stop;
+ struct mmc_data data;
+};
+
+static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
+{
+ int err;
+ u32 blocks;
+
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_data data;
+ unsigned int timeout_us;
+
+ struct scatterlist sg;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_APP_CMD;
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err)
+ return (u32)-1;
+ if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD))
+ return (u32)-1;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = SD_APP_SEND_NUM_WR_BLKS;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ memset(&data, 0, sizeof(struct mmc_data));
+
+ data.timeout_ns = card->csd.tacc_ns * 100;
+ data.timeout_clks = card->csd.tacc_clks * 100;
+
+ timeout_us = data.timeout_ns / 1000;
+ timeout_us += data.timeout_clks * 1000 /
+ (card->host->ios.clock / 1000);
+
+ if (timeout_us > 100000) {
+ data.timeout_ns = 100000000;
+ data.timeout_clks = 0;
+ }
+
+ data.blksz = 4;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.sg = &sg;
+ data.sg_len = 1;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ sg_init_one(&sg, &blocks, 4);
+
+ mmc_wait_for_req(card->host, &mrq);
+
+ if (cmd.error || data.error)
+ return (u32)-1;
+
+ blocks = ntohl(blocks);
+
+ return blocks;
+}
+
+static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+{
+ struct mmc_blk_data *md = mq->data;
+ struct mmc_card *card = md->queue.card;
+ struct mmc_blk_request brq;
+ int ret = 1;
+
+ mmc_claim_host(card->host);
+
+ do {
+ struct mmc_command cmd;
+ u32 readcmd, writecmd;
+
+ memset(&brq, 0, sizeof(struct mmc_blk_request));
+ brq.mrq.cmd = &brq.cmd;
+ brq.mrq.data = &brq.data;
+
+ brq.cmd.arg = req->sector;
+ if (!mmc_card_blockaddr(card))
+ brq.cmd.arg <<= 9;
+ brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+ brq.data.blksz = 512;
+ brq.stop.opcode = MMC_STOP_TRANSMISSION;
+ brq.stop.arg = 0;
+ brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ brq.data.blocks = req->nr_sectors;
+
+ if (brq.data.blocks > 1) {
+ /* SPI multiblock writes terminate using a special
+ * token, not a STOP_TRANSMISSION request.
+ */
+ if (!mmc_host_is_spi(card->host)
+ || rq_data_dir(req) == READ)
+ brq.mrq.stop = &brq.stop;
+ readcmd = MMC_READ_MULTIPLE_BLOCK;
+ writecmd = MMC_WRITE_MULTIPLE_BLOCK;
+ } else {
+ brq.mrq.stop = NULL;
+ readcmd = MMC_READ_SINGLE_BLOCK;
+ writecmd = MMC_WRITE_BLOCK;
+ }
+
+ if (rq_data_dir(req) == READ) {
+ brq.cmd.opcode = readcmd;
+ brq.data.flags |= MMC_DATA_READ;
+ } else {
+ brq.cmd.opcode = writecmd;
+ brq.data.flags |= MMC_DATA_WRITE;
+ }
+
+ mmc_set_data_timeout(&brq.data, card);
+
+ brq.data.sg = mq->sg;
+ brq.data.sg_len = mmc_queue_map_sg(mq);
+
+ mmc_queue_bounce_pre(mq);
+
+ mmc_wait_for_req(card->host, &brq.mrq);
+
+ mmc_queue_bounce_post(mq);
+
+ /*
+ * Check for errors here, but don't jump to cmd_err
+ * until later as we need to wait for the card to leave
+ * programming mode even when things go wrong.
+ */
+ if (brq.cmd.error) {
+ printk(KERN_ERR "%s: error %d sending read/write command\n",
+ req->rq_disk->disk_name, brq.cmd.error);
+ }
+
+ if (brq.data.error) {
+ printk(KERN_ERR "%s: error %d transferring data\n",
+ req->rq_disk->disk_name, brq.data.error);
+ }
+
+ if (brq.stop.error) {
+ printk(KERN_ERR "%s: error %d sending stop command\n",
+ req->rq_disk->disk_name, brq.stop.error);
+ }
+
+ if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
+ do {
+ int err;
+
+ cmd.opcode = MMC_SEND_STATUS;
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(card->host, &cmd, 5);
+ if (err) {
+ printk(KERN_ERR "%s: error %d requesting status\n",
+ req->rq_disk->disk_name, err);
+ goto cmd_err;
+ }
+ /*
+ * Some cards mishandle the status bits,
+ * so make sure to check both the busy
+ * indication and the card state.
+ */
+ } while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
+ (R1_CURRENT_STATE(cmd.resp[0]) == 7));
+
+#if 0
+ if (cmd.resp[0] & ~0x00000900)
+ printk(KERN_ERR "%s: status = %08x\n",
+ req->rq_disk->disk_name, cmd.resp[0]);
+ if (mmc_decode_status(cmd.resp))
+ goto cmd_err;
+#endif
+ }
+
+ if (brq.cmd.error || brq.data.error || brq.stop.error)
+ goto cmd_err;
+
+ /*
+ * A block was successfully transferred.
+ */
+ spin_lock_irq(&md->lock);
+ ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
+ spin_unlock_irq(&md->lock);
+ } while (ret);
+
+ mmc_release_host(card->host);
+
+ return 1;
+
+ cmd_err:
+ /*
+ * If this is an SD card and we're writing, we can first
+ * mark the known good sectors as ok.
+ *
+ * If the card is not SD, we can still ok written sectors
+ * as reported by the controller (which might be less than
+ * the real number of written sectors, but never more).
+ *
+ * For reads we just fail the entire chunk as that should
+ * be safe in all cases.
+ */
+ if (rq_data_dir(req) != READ) {
+ if (mmc_card_sd(card)) {
+ u32 blocks;
+
+ blocks = mmc_sd_num_wr_blocks(card);
+ if (blocks != (u32)-1) {
+ spin_lock_irq(&md->lock);
+ ret = __blk_end_request(req, 0, blocks << 9);
+ spin_unlock_irq(&md->lock);
+ }
+ } else {
+ spin_lock_irq(&md->lock);
+ ret = __blk_end_request(req, 0, brq.data.bytes_xfered);
+ spin_unlock_irq(&md->lock);
+ }
+ }
+
+ mmc_release_host(card->host);
+
+ spin_lock_irq(&md->lock);
+ while (ret)
+ ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));
+ spin_unlock_irq(&md->lock);
+
+ return 0;
+}
+
+
+static inline int mmc_blk_readonly(struct mmc_card *card)
+{
+ return mmc_card_readonly(card) ||
+ !(card->csd.cmdclass & CCC_BLOCK_WRITE);
+}
+
+static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
+{
+ struct mmc_blk_data *md;
+ int devidx, ret;
+
+ devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);
+ if (devidx >= MMC_NUM_MINORS)
+ return ERR_PTR(-ENOSPC);
+ __set_bit(devidx, dev_use);
+
+ md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
+ if (!md) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+
+ /*
+ * Set the read-only status based on the supported commands
+ * and the write protect switch.
+ */
+ md->read_only = mmc_blk_readonly(card);
+
+ md->disk = alloc_disk(1 << MMC_SHIFT);
+ if (md->disk == NULL) {
+ ret = -ENOMEM;
+ goto err_kfree;
+ }
+
+ spin_lock_init(&md->lock);
+ md->usage = 1;
+
+ ret = mmc_init_queue(&md->queue, card, &md->lock);
+ if (ret)
+ goto err_putdisk;
+
+ md->queue.issue_fn = mmc_blk_issue_rq;
+ md->queue.data = md;
+
+ md->disk->major = MMC_BLOCK_MAJOR;
+ md->disk->first_minor = devidx << MMC_SHIFT;
+ md->disk->fops = &mmc_bdops;
+ md->disk->private_data = md;
+ md->disk->queue = md->queue.queue;
+ md->disk->driverfs_dev = &card->dev;
+
+ /*
+ * As discussed on lkml, GENHD_FL_REMOVABLE should:
+ *
+ * - be set for removable media with permanent block devices
+ * - be unset for removable block devices with permanent media
+ *
+ * Since MMC block devices clearly fall under the second
+ * case, we do not set GENHD_FL_REMOVABLE. Userspace
+ * should use the block device creation/destruction hotplug
+ * messages to tell when the card is present.
+ */
+
+ sprintf(md->disk->disk_name, "mmcblk%d", devidx);
+
+ blk_queue_hardsect_size(md->queue.queue, 512);
+
+ if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
+ /*
+ * The EXT_CSD sector count is in number or 512 byte
+ * sectors.
+ */
+ set_capacity(md->disk, card->ext_csd.sectors);
+ } else {
+ /*
+ * The CSD capacity field is in units of read_blkbits.
+ * set_capacity takes units of 512 bytes.
+ */
+ set_capacity(md->disk,
+ card->csd.capacity << (card->csd.read_blkbits - 9));
+ }
+ return md;
+
+ err_putdisk:
+ put_disk(md->disk);
+ err_kfree:
+ kfree(md);
+ out:
+ return ERR_PTR(ret);
+}
+
+static int
+mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
+{
+ struct mmc_command cmd;
+ int err;
+
+ /* Block-addressed cards ignore MMC_SET_BLOCKLEN. */
+ if (mmc_card_blockaddr(card))
+ return 0;
+
+ mmc_claim_host(card->host);
+ cmd.opcode = MMC_SET_BLOCKLEN;
+ cmd.arg = 512;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(card->host, &cmd, 5);
+ mmc_release_host(card->host);
+
+ if (err) {
+ printk(KERN_ERR "%s: unable to set block size to %d: %d\n",
+ md->disk->disk_name, cmd.arg, err);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mmc_blk_probe(struct mmc_card *card)
+{
+ struct mmc_blk_data *md;
+ int err;
+
+ char cap_str[10];
+
+ /*
+ * Check that the card supports the command class(es) we need.
+ */
+ if (!(card->csd.cmdclass & CCC_BLOCK_READ))
+ return -ENODEV;
+
+ md = mmc_blk_alloc(card);
+ if (IS_ERR(md))
+ return PTR_ERR(md);
+
+ err = mmc_blk_set_blksize(md, card);
+ if (err)
+ goto out;
+
+ string_get_size((u64)get_capacity(md->disk) << 9, STRING_UNITS_2,
+ cap_str, sizeof(cap_str));
+ printk(KERN_INFO "%s: %s %s %s %s\n",
+ md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
+ cap_str, md->read_only ? "(ro)" : "");
+
+ mmc_set_drvdata(card, md);
+ add_disk(md->disk);
+ return 0;
+
+ out:
+ mmc_blk_put(md);
+
+ return err;
+}
+
+static void mmc_blk_remove(struct mmc_card *card)
+{
+ struct mmc_blk_data *md = mmc_get_drvdata(card);
+
+ if (md) {
+ /* Stop new requests from getting into the queue */
+ del_gendisk(md->disk);
+
+ /* Then flush out any already in there */
+ mmc_cleanup_queue(&md->queue);
+
+ mmc_blk_put(md);
+ }
+ mmc_set_drvdata(card, NULL);
+}
+
+#ifdef CONFIG_PM
+static int mmc_blk_suspend(struct mmc_card *card, pm_message_t state)
+{
+ struct mmc_blk_data *md = mmc_get_drvdata(card);
+
+ if (md) {
+ mmc_queue_suspend(&md->queue);
+ }
+ return 0;
+}
+
+static int mmc_blk_resume(struct mmc_card *card)
+{
+ struct mmc_blk_data *md = mmc_get_drvdata(card);
+
+ if (md) {
+ mmc_blk_set_blksize(md, card);
+ mmc_queue_resume(&md->queue);
+ }
+ return 0;
+}
+#else
+#define mmc_blk_suspend NULL
+#define mmc_blk_resume NULL
+#endif
+
+static struct mmc_driver mmc_driver = {
+ .drv = {
+ .name = "mmcblk",
+ },
+ .probe = mmc_blk_probe,
+ .remove = mmc_blk_remove,
+ .suspend = mmc_blk_suspend,
+ .resume = mmc_blk_resume,
+};
+
+static int __init mmc_blk_init(void)
+{
+ int res;
+
+ res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
+ if (res)
+ goto out;
+
+ res = mmc_register_driver(&mmc_driver);
+ if (res)
+ goto out2;
+
+ return 0;
+ out2:
+ unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
+ out:
+ return res;
+}
+
+static void __exit mmc_blk_exit(void)
+{
+ mmc_unregister_driver(&mmc_driver);
+ unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
+}
+
+module_init(mmc_blk_init);
+module_exit(mmc_blk_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver");
+
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
new file mode 100644
index 0000000..b9f1e84
--- /dev/null
+++ b/drivers/mmc/card/mmc_test.c
@@ -0,0 +1,1211 @@
+/*
+ * linux/drivers/mmc/card/mmc_test.c
+ *
+ * Copyright 2007-2008 Pierre Ossman
+ *
+ * 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/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+
+#include <linux/scatterlist.h>
+
+#define RESULT_OK 0
+#define RESULT_FAIL 1
+#define RESULT_UNSUP_HOST 2
+#define RESULT_UNSUP_CARD 3
+
+#define BUFFER_ORDER 2
+#define BUFFER_SIZE (PAGE_SIZE << BUFFER_ORDER)
+
+struct mmc_test_card {
+ struct mmc_card *card;
+
+ u8 scratch[BUFFER_SIZE];
+ u8 *buffer;
+#ifdef CONFIG_HIGHMEM
+ struct page *highmem;
+#endif
+};
+
+/*******************************************************************/
+/* General helper functions */
+/*******************************************************************/
+
+/*
+ * Configure correct block size in card
+ */
+static int mmc_test_set_blksize(struct mmc_test_card *test, unsigned size)
+{
+ struct mmc_command cmd;
+ int ret;
+
+ cmd.opcode = MMC_SET_BLOCKLEN;
+ cmd.arg = size;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Fill in the mmc_request structure given a set of transfer parameters.
+ */
+static void mmc_test_prepare_mrq(struct mmc_test_card *test,
+ struct mmc_request *mrq, struct scatterlist *sg, unsigned sg_len,
+ unsigned dev_addr, unsigned blocks, unsigned blksz, int write)
+{
+ BUG_ON(!mrq || !mrq->cmd || !mrq->data || !mrq->stop);
+
+ if (blocks > 1) {
+ mrq->cmd->opcode = write ?
+ MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
+ } else {
+ mrq->cmd->opcode = write ?
+ MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
+ }
+
+ mrq->cmd->arg = dev_addr;
+ mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ if (blocks == 1)
+ mrq->stop = NULL;
+ else {
+ mrq->stop->opcode = MMC_STOP_TRANSMISSION;
+ mrq->stop->arg = 0;
+ mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
+ }
+
+ mrq->data->blksz = blksz;
+ mrq->data->blocks = blocks;
+ mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+ mrq->data->sg = sg;
+ mrq->data->sg_len = sg_len;
+
+ mmc_set_data_timeout(mrq->data, test->card);
+}
+
+/*
+ * Wait for the card to finish the busy state
+ */
+static int mmc_test_wait_busy(struct mmc_test_card *test)
+{
+ int ret, busy;
+ struct mmc_command cmd;
+
+ busy = 0;
+ do {
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SEND_STATUS;
+ cmd.arg = test->card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ ret = mmc_wait_for_cmd(test->card->host, &cmd, 0);
+ if (ret)
+ break;
+
+ if (!busy && !(cmd.resp[0] & R1_READY_FOR_DATA)) {
+ busy = 1;
+ printk(KERN_INFO "%s: Warning: Host did not "
+ "wait for busy state to end.\n",
+ mmc_hostname(test->card->host));
+ }
+ } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
+
+ return ret;
+}
+
+/*
+ * Transfer a single sector of kernel addressable data
+ */
+static int mmc_test_buffer_transfer(struct mmc_test_card *test,
+ u8 *buffer, unsigned addr, unsigned blksz, int write)
+{
+ int ret;
+
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_command stop;
+ struct mmc_data data;
+
+ struct scatterlist sg;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&data, 0, sizeof(struct mmc_data));
+ memset(&stop, 0, sizeof(struct mmc_command));
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+ mrq.stop = &stop;
+
+ sg_init_one(&sg, buffer, blksz);
+
+ mmc_test_prepare_mrq(test, &mrq, &sg, 1, addr, 1, blksz, write);
+
+ mmc_wait_for_req(test->card->host, &mrq);
+
+ if (cmd.error)
+ return cmd.error;
+ if (data.error)
+ return data.error;
+
+ ret = mmc_test_wait_busy(test);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*******************************************************************/
+/* Test preparation and cleanup */
+/*******************************************************************/
+
+/*
+ * Fill the first couple of sectors of the card with known data
+ * so that bad reads/writes can be detected
+ */
+static int __mmc_test_prepare(struct mmc_test_card *test, int write)
+{
+ int ret, i;
+
+ ret = mmc_test_set_blksize(test, 512);
+ if (ret)
+ return ret;
+
+ if (write)
+ memset(test->buffer, 0xDF, 512);
+ else {
+ for (i = 0;i < 512;i++)
+ test->buffer[i] = i;
+ }
+
+ for (i = 0;i < BUFFER_SIZE / 512;i++) {
+ ret = mmc_test_buffer_transfer(test, test->buffer, i * 512, 512, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mmc_test_prepare_write(struct mmc_test_card *test)
+{
+ return __mmc_test_prepare(test, 1);
+}
+
+static int mmc_test_prepare_read(struct mmc_test_card *test)
+{
+ return __mmc_test_prepare(test, 0);
+}
+
+static int mmc_test_cleanup(struct mmc_test_card *test)
+{
+ int ret, i;
+
+ ret = mmc_test_set_blksize(test, 512);
+ if (ret)
+ return ret;
+
+ memset(test->buffer, 0, 512);
+
+ for (i = 0;i < BUFFER_SIZE / 512;i++) {
+ ret = mmc_test_buffer_transfer(test, test->buffer, i * 512, 512, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*******************************************************************/
+/* Test execution helpers */
+/*******************************************************************/
+
+/*
+ * Modifies the mmc_request to perform the "short transfer" tests
+ */
+static void mmc_test_prepare_broken_mrq(struct mmc_test_card *test,
+ struct mmc_request *mrq, int write)
+{
+ BUG_ON(!mrq || !mrq->cmd || !mrq->data);
+
+ if (mrq->data->blocks > 1) {
+ mrq->cmd->opcode = write ?
+ MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
+ mrq->stop = NULL;
+ } else {
+ mrq->cmd->opcode = MMC_SEND_STATUS;
+ mrq->cmd->arg = test->card->rca << 16;
+ }
+}
+
+/*
+ * Checks that a normal transfer didn't have any errors
+ */
+static int mmc_test_check_result(struct mmc_test_card *test,
+ struct mmc_request *mrq)
+{
+ int ret;
+
+ BUG_ON(!mrq || !mrq->cmd || !mrq->data);
+
+ ret = 0;
+
+ if (!ret && mrq->cmd->error)
+ ret = mrq->cmd->error;
+ if (!ret && mrq->data->error)
+ ret = mrq->data->error;
+ if (!ret && mrq->stop && mrq->stop->error)
+ ret = mrq->stop->error;
+ if (!ret && mrq->data->bytes_xfered !=
+ mrq->data->blocks * mrq->data->blksz)
+ ret = RESULT_FAIL;
+
+ if (ret == -EINVAL)
+ ret = RESULT_UNSUP_HOST;
+
+ return ret;
+}
+
+/*
+ * Checks that a "short transfer" behaved as expected
+ */
+static int mmc_test_check_broken_result(struct mmc_test_card *test,
+ struct mmc_request *mrq)
+{
+ int ret;
+
+ BUG_ON(!mrq || !mrq->cmd || !mrq->data);
+
+ ret = 0;
+
+ if (!ret && mrq->cmd->error)
+ ret = mrq->cmd->error;
+ if (!ret && mrq->data->error == 0)
+ ret = RESULT_FAIL;
+ if (!ret && mrq->data->error != -ETIMEDOUT)
+ ret = mrq->data->error;
+ if (!ret && mrq->stop && mrq->stop->error)
+ ret = mrq->stop->error;
+ if (mrq->data->blocks > 1) {
+ if (!ret && mrq->data->bytes_xfered > mrq->data->blksz)
+ ret = RESULT_FAIL;
+ } else {
+ if (!ret && mrq->data->bytes_xfered > 0)
+ ret = RESULT_FAIL;
+ }
+
+ if (ret == -EINVAL)
+ ret = RESULT_UNSUP_HOST;
+
+ return ret;
+}
+
+/*
+ * Tests a basic transfer with certain parameters
+ */
+static int mmc_test_simple_transfer(struct mmc_test_card *test,
+ struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
+ unsigned blocks, unsigned blksz, int write)
+{
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_command stop;
+ struct mmc_data data;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&data, 0, sizeof(struct mmc_data));
+ memset(&stop, 0, sizeof(struct mmc_command));
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+ mrq.stop = &stop;
+
+ mmc_test_prepare_mrq(test, &mrq, sg, sg_len, dev_addr,
+ blocks, blksz, write);
+
+ mmc_wait_for_req(test->card->host, &mrq);
+
+ mmc_test_wait_busy(test);
+
+ return mmc_test_check_result(test, &mrq);
+}
+
+/*
+ * Tests a transfer where the card will fail completely or partly
+ */
+static int mmc_test_broken_transfer(struct mmc_test_card *test,
+ unsigned blocks, unsigned blksz, int write)
+{
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_command stop;
+ struct mmc_data data;
+
+ struct scatterlist sg;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&data, 0, sizeof(struct mmc_data));
+ memset(&stop, 0, sizeof(struct mmc_command));
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+ mrq.stop = &stop;
+
+ sg_init_one(&sg, test->buffer, blocks * blksz);
+
+ mmc_test_prepare_mrq(test, &mrq, &sg, 1, 0, blocks, blksz, write);
+ mmc_test_prepare_broken_mrq(test, &mrq, write);
+
+ mmc_wait_for_req(test->card->host, &mrq);
+
+ mmc_test_wait_busy(test);
+
+ return mmc_test_check_broken_result(test, &mrq);
+}
+
+/*
+ * Does a complete transfer test where data is also validated
+ *
+ * Note: mmc_test_prepare() must have been done before this call
+ */
+static int mmc_test_transfer(struct mmc_test_card *test,
+ struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
+ unsigned blocks, unsigned blksz, int write)
+{
+ int ret, i;
+ unsigned long flags;
+
+ if (write) {
+ for (i = 0;i < blocks * blksz;i++)
+ test->scratch[i] = i;
+ } else {
+ memset(test->scratch, 0, BUFFER_SIZE);
+ }
+ local_irq_save(flags);
+ sg_copy_from_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
+ local_irq_restore(flags);
+
+ ret = mmc_test_set_blksize(test, blksz);
+ if (ret)
+ return ret;
+
+ ret = mmc_test_simple_transfer(test, sg, sg_len, dev_addr,
+ blocks, blksz, write);
+ if (ret)
+ return ret;
+
+ if (write) {
+ int sectors;
+
+ ret = mmc_test_set_blksize(test, 512);
+ if (ret)
+ return ret;
+
+ sectors = (blocks * blksz + 511) / 512;
+ if ((sectors * 512) == (blocks * blksz))
+ sectors++;
+
+ if ((sectors * 512) > BUFFER_SIZE)
+ return -EINVAL;
+
+ memset(test->buffer, 0, sectors * 512);
+
+ for (i = 0;i < sectors;i++) {
+ ret = mmc_test_buffer_transfer(test,
+ test->buffer + i * 512,
+ dev_addr + i * 512, 512, 0);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0;i < blocks * blksz;i++) {
+ if (test->buffer[i] != (u8)i)
+ return RESULT_FAIL;
+ }
+
+ for (;i < sectors * 512;i++) {
+ if (test->buffer[i] != 0xDF)
+ return RESULT_FAIL;
+ }
+ } else {
+ local_irq_save(flags);
+ sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
+ local_irq_restore(flags);
+ for (i = 0;i < blocks * blksz;i++) {
+ if (test->scratch[i] != (u8)i)
+ return RESULT_FAIL;
+ }
+ }
+
+ return 0;
+}
+
+/*******************************************************************/
+/* Tests */
+/*******************************************************************/
+
+struct mmc_test_case {
+ const char *name;
+
+ int (*prepare)(struct mmc_test_card *);
+ int (*run)(struct mmc_test_card *);
+ int (*cleanup)(struct mmc_test_card *);
+};
+
+static int mmc_test_basic_write(struct mmc_test_card *test)
+{
+ int ret;
+ struct scatterlist sg;
+
+ ret = mmc_test_set_blksize(test, 512);
+ if (ret)
+ return ret;
+
+ sg_init_one(&sg, test->buffer, 512);
+
+ ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_basic_read(struct mmc_test_card *test)
+{
+ int ret;
+ struct scatterlist sg;
+
+ ret = mmc_test_set_blksize(test, 512);
+ if (ret)
+ return ret;
+
+ sg_init_one(&sg, test->buffer, 512);
+
+ ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_verify_write(struct mmc_test_card *test)
+{
+ int ret;
+ struct scatterlist sg;
+
+ sg_init_one(&sg, test->buffer, 512);
+
+ ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_verify_read(struct mmc_test_card *test)
+{
+ int ret;
+ struct scatterlist sg;
+
+ sg_init_one(&sg, test->buffer, 512);
+
+ ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_multi_write(struct mmc_test_card *test)
+{
+ int ret;
+ unsigned int size;
+ struct scatterlist sg;
+
+ if (test->card->host->max_blk_count == 1)
+ return RESULT_UNSUP_HOST;
+
+ size = PAGE_SIZE * 2;
+ size = min(size, test->card->host->max_req_size);
+ size = min(size, test->card->host->max_seg_size);
+ size = min(size, test->card->host->max_blk_count * 512);
+
+ if (size < 1024)
+ return RESULT_UNSUP_HOST;
+
+ sg_init_one(&sg, test->buffer, size);
+
+ ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_multi_read(struct mmc_test_card *test)
+{
+ int ret;
+ unsigned int size;
+ struct scatterlist sg;
+
+ if (test->card->host->max_blk_count == 1)
+ return RESULT_UNSUP_HOST;
+
+ size = PAGE_SIZE * 2;
+ size = min(size, test->card->host->max_req_size);
+ size = min(size, test->card->host->max_seg_size);
+ size = min(size, test->card->host->max_blk_count * 512);
+
+ if (size < 1024)
+ return RESULT_UNSUP_HOST;
+
+ sg_init_one(&sg, test->buffer, size);
+
+ ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_pow2_write(struct mmc_test_card *test)
+{
+ int ret, i;
+ struct scatterlist sg;
+
+ if (!test->card->csd.write_partial)
+ return RESULT_UNSUP_CARD;
+
+ for (i = 1; i < 512;i <<= 1) {
+ sg_init_one(&sg, test->buffer, i);
+ ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mmc_test_pow2_read(struct mmc_test_card *test)
+{
+ int ret, i;
+ struct scatterlist sg;
+
+ if (!test->card->csd.read_partial)
+ return RESULT_UNSUP_CARD;
+
+ for (i = 1; i < 512;i <<= 1) {
+ sg_init_one(&sg, test->buffer, i);
+ ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mmc_test_weird_write(struct mmc_test_card *test)
+{
+ int ret, i;
+ struct scatterlist sg;
+
+ if (!test->card->csd.write_partial)
+ return RESULT_UNSUP_CARD;
+
+ for (i = 3; i < 512;i += 7) {
+ sg_init_one(&sg, test->buffer, i);
+ ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mmc_test_weird_read(struct mmc_test_card *test)
+{
+ int ret, i;
+ struct scatterlist sg;
+
+ if (!test->card->csd.read_partial)
+ return RESULT_UNSUP_CARD;
+
+ for (i = 3; i < 512;i += 7) {
+ sg_init_one(&sg, test->buffer, i);
+ ret = mmc_test_transfer(test, &sg, 1, 0, 1, i, 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mmc_test_align_write(struct mmc_test_card *test)
+{
+ int ret, i;
+ struct scatterlist sg;
+
+ for (i = 1;i < 4;i++) {
+ sg_init_one(&sg, test->buffer + i, 512);
+ ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mmc_test_align_read(struct mmc_test_card *test)
+{
+ int ret, i;
+ struct scatterlist sg;
+
+ for (i = 1;i < 4;i++) {
+ sg_init_one(&sg, test->buffer + i, 512);
+ ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mmc_test_align_multi_write(struct mmc_test_card *test)
+{
+ int ret, i;
+ unsigned int size;
+ struct scatterlist sg;
+
+ if (test->card->host->max_blk_count == 1)
+ return RESULT_UNSUP_HOST;
+
+ size = PAGE_SIZE * 2;
+ size = min(size, test->card->host->max_req_size);
+ size = min(size, test->card->host->max_seg_size);
+ size = min(size, test->card->host->max_blk_count * 512);
+
+ if (size < 1024)
+ return RESULT_UNSUP_HOST;
+
+ for (i = 1;i < 4;i++) {
+ sg_init_one(&sg, test->buffer + i, size);
+ ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mmc_test_align_multi_read(struct mmc_test_card *test)
+{
+ int ret, i;
+ unsigned int size;
+ struct scatterlist sg;
+
+ if (test->card->host->max_blk_count == 1)
+ return RESULT_UNSUP_HOST;
+
+ size = PAGE_SIZE * 2;
+ size = min(size, test->card->host->max_req_size);
+ size = min(size, test->card->host->max_seg_size);
+ size = min(size, test->card->host->max_blk_count * 512);
+
+ if (size < 1024)
+ return RESULT_UNSUP_HOST;
+
+ for (i = 1;i < 4;i++) {
+ sg_init_one(&sg, test->buffer + i, size);
+ ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mmc_test_xfersize_write(struct mmc_test_card *test)
+{
+ int ret;
+
+ ret = mmc_test_set_blksize(test, 512);
+ if (ret)
+ return ret;
+
+ ret = mmc_test_broken_transfer(test, 1, 512, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_xfersize_read(struct mmc_test_card *test)
+{
+ int ret;
+
+ ret = mmc_test_set_blksize(test, 512);
+ if (ret)
+ return ret;
+
+ ret = mmc_test_broken_transfer(test, 1, 512, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_multi_xfersize_write(struct mmc_test_card *test)
+{
+ int ret;
+
+ if (test->card->host->max_blk_count == 1)
+ return RESULT_UNSUP_HOST;
+
+ ret = mmc_test_set_blksize(test, 512);
+ if (ret)
+ return ret;
+
+ ret = mmc_test_broken_transfer(test, 2, 512, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
+{
+ int ret;
+
+ if (test->card->host->max_blk_count == 1)
+ return RESULT_UNSUP_HOST;
+
+ ret = mmc_test_set_blksize(test, 512);
+ if (ret)
+ return ret;
+
+ ret = mmc_test_broken_transfer(test, 2, 512, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+#ifdef CONFIG_HIGHMEM
+
+static int mmc_test_write_high(struct mmc_test_card *test)
+{
+ int ret;
+ struct scatterlist sg;
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, test->highmem, 512, 0);
+
+ ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_read_high(struct mmc_test_card *test)
+{
+ int ret;
+ struct scatterlist sg;
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, test->highmem, 512, 0);
+
+ ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_multi_write_high(struct mmc_test_card *test)
+{
+ int ret;
+ unsigned int size;
+ struct scatterlist sg;
+
+ if (test->card->host->max_blk_count == 1)
+ return RESULT_UNSUP_HOST;
+
+ size = PAGE_SIZE * 2;
+ size = min(size, test->card->host->max_req_size);
+ size = min(size, test->card->host->max_seg_size);
+ size = min(size, test->card->host->max_blk_count * 512);
+
+ if (size < 1024)
+ return RESULT_UNSUP_HOST;
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, test->highmem, size, 0);
+
+ ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_multi_read_high(struct mmc_test_card *test)
+{
+ int ret;
+ unsigned int size;
+ struct scatterlist sg;
+
+ if (test->card->host->max_blk_count == 1)
+ return RESULT_UNSUP_HOST;
+
+ size = PAGE_SIZE * 2;
+ size = min(size, test->card->host->max_req_size);
+ size = min(size, test->card->host->max_seg_size);
+ size = min(size, test->card->host->max_blk_count * 512);
+
+ if (size < 1024)
+ return RESULT_UNSUP_HOST;
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, test->highmem, size, 0);
+
+ ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+#endif /* CONFIG_HIGHMEM */
+
+static const struct mmc_test_case mmc_test_cases[] = {
+ {
+ .name = "Basic write (no data verification)",
+ .run = mmc_test_basic_write,
+ },
+
+ {
+ .name = "Basic read (no data verification)",
+ .run = mmc_test_basic_read,
+ },
+
+ {
+ .name = "Basic write (with data verification)",
+ .prepare = mmc_test_prepare_write,
+ .run = mmc_test_verify_write,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Basic read (with data verification)",
+ .prepare = mmc_test_prepare_read,
+ .run = mmc_test_verify_read,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Multi-block write",
+ .prepare = mmc_test_prepare_write,
+ .run = mmc_test_multi_write,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Multi-block read",
+ .prepare = mmc_test_prepare_read,
+ .run = mmc_test_multi_read,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Power of two block writes",
+ .prepare = mmc_test_prepare_write,
+ .run = mmc_test_pow2_write,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Power of two block reads",
+ .prepare = mmc_test_prepare_read,
+ .run = mmc_test_pow2_read,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Weird sized block writes",
+ .prepare = mmc_test_prepare_write,
+ .run = mmc_test_weird_write,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Weird sized block reads",
+ .prepare = mmc_test_prepare_read,
+ .run = mmc_test_weird_read,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Badly aligned write",
+ .prepare = mmc_test_prepare_write,
+ .run = mmc_test_align_write,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Badly aligned read",
+ .prepare = mmc_test_prepare_read,
+ .run = mmc_test_align_read,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Badly aligned multi-block write",
+ .prepare = mmc_test_prepare_write,
+ .run = mmc_test_align_multi_write,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Badly aligned multi-block read",
+ .prepare = mmc_test_prepare_read,
+ .run = mmc_test_align_multi_read,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Correct xfer_size at write (start failure)",
+ .run = mmc_test_xfersize_write,
+ },
+
+ {
+ .name = "Correct xfer_size at read (start failure)",
+ .run = mmc_test_xfersize_read,
+ },
+
+ {
+ .name = "Correct xfer_size at write (midway failure)",
+ .run = mmc_test_multi_xfersize_write,
+ },
+
+ {
+ .name = "Correct xfer_size at read (midway failure)",
+ .run = mmc_test_multi_xfersize_read,
+ },
+
+#ifdef CONFIG_HIGHMEM
+
+ {
+ .name = "Highmem write",
+ .prepare = mmc_test_prepare_write,
+ .run = mmc_test_write_high,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Highmem read",
+ .prepare = mmc_test_prepare_read,
+ .run = mmc_test_read_high,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Multi-block highmem write",
+ .prepare = mmc_test_prepare_write,
+ .run = mmc_test_multi_write_high,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "Multi-block highmem read",
+ .prepare = mmc_test_prepare_read,
+ .run = mmc_test_multi_read_high,
+ .cleanup = mmc_test_cleanup,
+ },
+
+#endif /* CONFIG_HIGHMEM */
+
+};
+
+static DEFINE_MUTEX(mmc_test_lock);
+
+static void mmc_test_run(struct mmc_test_card *test, int testcase)
+{
+ int i, ret;
+
+ printk(KERN_INFO "%s: Starting tests of card %s...\n",
+ mmc_hostname(test->card->host), mmc_card_id(test->card));
+
+ mmc_claim_host(test->card->host);
+
+ for (i = 0;i < ARRAY_SIZE(mmc_test_cases);i++) {
+ if (testcase && ((i + 1) != testcase))
+ continue;
+
+ printk(KERN_INFO "%s: Test case %d. %s...\n",
+ mmc_hostname(test->card->host), i + 1,
+ mmc_test_cases[i].name);
+
+ if (mmc_test_cases[i].prepare) {
+ ret = mmc_test_cases[i].prepare(test);
+ if (ret) {
+ printk(KERN_INFO "%s: Result: Prepare "
+ "stage failed! (%d)\n",
+ mmc_hostname(test->card->host),
+ ret);
+ continue;
+ }
+ }
+
+ ret = mmc_test_cases[i].run(test);
+ switch (ret) {
+ case RESULT_OK:
+ printk(KERN_INFO "%s: Result: OK\n",
+ mmc_hostname(test->card->host));
+ break;
+ case RESULT_FAIL:
+ printk(KERN_INFO "%s: Result: FAILED\n",
+ mmc_hostname(test->card->host));
+ break;
+ case RESULT_UNSUP_HOST:
+ printk(KERN_INFO "%s: Result: UNSUPPORTED "
+ "(by host)\n",
+ mmc_hostname(test->card->host));
+ break;
+ case RESULT_UNSUP_CARD:
+ printk(KERN_INFO "%s: Result: UNSUPPORTED "
+ "(by card)\n",
+ mmc_hostname(test->card->host));
+ break;
+ default:
+ printk(KERN_INFO "%s: Result: ERROR (%d)\n",
+ mmc_hostname(test->card->host), ret);
+ }
+
+ if (mmc_test_cases[i].cleanup) {
+ ret = mmc_test_cases[i].cleanup(test);
+ if (ret) {
+ printk(KERN_INFO "%s: Warning: Cleanup "
+ "stage failed! (%d)\n",
+ mmc_hostname(test->card->host),
+ ret);
+ }
+ }
+ }
+
+ mmc_release_host(test->card->host);
+
+ printk(KERN_INFO "%s: Tests completed.\n",
+ mmc_hostname(test->card->host));
+}
+
+static ssize_t mmc_test_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ mutex_lock(&mmc_test_lock);
+ mutex_unlock(&mmc_test_lock);
+
+ return 0;
+}
+
+static ssize_t mmc_test_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mmc_card *card;
+ struct mmc_test_card *test;
+ int testcase;
+
+ card = container_of(dev, struct mmc_card, dev);
+
+ testcase = simple_strtol(buf, NULL, 10);
+
+ test = kzalloc(sizeof(struct mmc_test_card), GFP_KERNEL);
+ if (!test)
+ return -ENOMEM;
+
+ test->card = card;
+
+ test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
+#ifdef CONFIG_HIGHMEM
+ test->highmem = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, BUFFER_ORDER);
+#endif
+
+#ifdef CONFIG_HIGHMEM
+ if (test->buffer && test->highmem) {
+#else
+ if (test->buffer) {
+#endif
+ mutex_lock(&mmc_test_lock);
+ mmc_test_run(test, testcase);
+ mutex_unlock(&mmc_test_lock);
+ }
+
+#ifdef CONFIG_HIGHMEM
+ __free_pages(test->highmem, BUFFER_ORDER);
+#endif
+ kfree(test->buffer);
+ kfree(test);
+
+ return count;
+}
+
+static DEVICE_ATTR(test, S_IWUSR | S_IRUGO, mmc_test_show, mmc_test_store);
+
+static int mmc_test_probe(struct mmc_card *card)
+{
+ int ret;
+
+ if ((card->type != MMC_TYPE_MMC) && (card->type != MMC_TYPE_SD))
+ return -ENODEV;
+
+ ret = device_create_file(&card->dev, &dev_attr_test);
+ if (ret)
+ return ret;
+
+ dev_info(&card->dev, "Card claimed for testing.\n");
+
+ return 0;
+}
+
+static void mmc_test_remove(struct mmc_card *card)
+{
+ device_remove_file(&card->dev, &dev_attr_test);
+}
+
+static struct mmc_driver mmc_driver = {
+ .drv = {
+ .name = "mmc_test",
+ },
+ .probe = mmc_test_probe,
+ .remove = mmc_test_remove,
+};
+
+static int __init mmc_test_init(void)
+{
+ return mmc_register_driver(&mmc_driver);
+}
+
+static void __exit mmc_test_exit(void)
+{
+ mmc_unregister_driver(&mmc_driver);
+}
+
+module_init(mmc_test_init);
+module_exit(mmc_test_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Multimedia Card (MMC) host test driver");
+MODULE_AUTHOR("Pierre Ossman");
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
new file mode 100644
index 0000000..7a72e75
--- /dev/null
+++ b/drivers/mmc/card/queue.c
@@ -0,0 +1,370 @@
+/*
+ * linux/drivers/mmc/card/queue.c
+ *
+ * Copyright (C) 2003 Russell King, All Rights Reserved.
+ * Copyright 2006-2007 Pierre Ossman
+ *
+ * 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/blkdev.h>
+#include <linux/freezer.h>
+#include <linux/kthread.h>
+#include <linux/scatterlist.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include "queue.h"
+
+#define MMC_QUEUE_BOUNCESZ 65536
+
+#define MMC_QUEUE_SUSPENDED (1 << 0)
+
+/*
+ * Prepare a MMC request. This just filters out odd stuff.
+ */
+static int mmc_prep_request(struct request_queue *q, struct request *req)
+{
+ /*
+ * We only like normal block requests.
+ */
+ if (!blk_fs_request(req)) {
+ blk_dump_rq_flags(req, "MMC bad request");
+ return BLKPREP_KILL;
+ }
+
+ req->cmd_flags |= REQ_DONTPREP;
+
+ return BLKPREP_OK;
+}
+
+static int mmc_queue_thread(void *d)
+{
+ struct mmc_queue *mq = d;
+ struct request_queue *q = mq->queue;
+
+ current->flags |= PF_MEMALLOC;
+
+ down(&mq->thread_sem);
+ do {
+ struct request *req = NULL;
+
+ spin_lock_irq(q->queue_lock);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!blk_queue_plugged(q))
+ req = elv_next_request(q);
+ mq->req = req;
+ spin_unlock_irq(q->queue_lock);
+
+ if (!req) {
+ if (kthread_should_stop()) {
+ set_current_state(TASK_RUNNING);
+ break;
+ }
+ up(&mq->thread_sem);
+ schedule();
+ down(&mq->thread_sem);
+ continue;
+ }
+ set_current_state(TASK_RUNNING);
+
+ mq->issue_fn(mq, req);
+ } while (1);
+ up(&mq->thread_sem);
+
+ return 0;
+}
+
+/*
+ * Generic MMC request handler. This is called for any queue on a
+ * particular host. When the host is not busy, we look for a request
+ * on any queue on this host, and attempt to issue it. This may
+ * not be the queue we were asked to process.
+ */
+static void mmc_request(struct request_queue *q)
+{
+ struct mmc_queue *mq = q->queuedata;
+ struct request *req;
+ int ret;
+
+ if (!mq) {
+ printk(KERN_ERR "MMC: killing requests for dead queue\n");
+ while ((req = elv_next_request(q)) != NULL) {
+ do {
+ ret = __blk_end_request(req, -EIO,
+ blk_rq_cur_bytes(req));
+ } while (ret);
+ }
+ return;
+ }
+
+ if (!mq->req)
+ wake_up_process(mq->thread);
+}
+
+/**
+ * mmc_init_queue - initialise a queue structure.
+ * @mq: mmc queue
+ * @card: mmc card to attach this queue
+ * @lock: queue lock
+ *
+ * Initialise a MMC card request queue.
+ */
+int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock)
+{
+ struct mmc_host *host = card->host;
+ u64 limit = BLK_BOUNCE_HIGH;
+ int ret;
+
+ if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
+ limit = *mmc_dev(host)->dma_mask;
+
+ mq->card = card;
+ mq->queue = blk_init_queue(mmc_request, lock);
+ if (!mq->queue)
+ return -ENOMEM;
+
+ mq->queue->queuedata = mq;
+ mq->req = NULL;
+
+ blk_queue_prep_rq(mq->queue, mmc_prep_request);
+ blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN, NULL);
+ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
+
+#ifdef CONFIG_MMC_BLOCK_BOUNCE
+ if (host->max_hw_segs == 1) {
+ unsigned int bouncesz;
+
+ bouncesz = MMC_QUEUE_BOUNCESZ;
+
+ if (bouncesz > host->max_req_size)
+ bouncesz = host->max_req_size;
+ if (bouncesz > host->max_seg_size)
+ bouncesz = host->max_seg_size;
+ if (bouncesz > (host->max_blk_count * 512))
+ bouncesz = host->max_blk_count * 512;
+
+ if (bouncesz > 512) {
+ mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
+ if (!mq->bounce_buf) {
+ printk(KERN_WARNING "%s: unable to "
+ "allocate bounce buffer\n",
+ mmc_card_name(card));
+ }
+ }
+
+ if (mq->bounce_buf) {
+ blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY);
+ blk_queue_max_sectors(mq->queue, bouncesz / 512);
+ blk_queue_max_phys_segments(mq->queue, bouncesz / 512);
+ blk_queue_max_hw_segments(mq->queue, bouncesz / 512);
+ blk_queue_max_segment_size(mq->queue, bouncesz);
+
+ mq->sg = kmalloc(sizeof(struct scatterlist),
+ GFP_KERNEL);
+ if (!mq->sg) {
+ ret = -ENOMEM;
+ goto cleanup_queue;
+ }
+ sg_init_table(mq->sg, 1);
+
+ mq->bounce_sg = kmalloc(sizeof(struct scatterlist) *
+ bouncesz / 512, GFP_KERNEL);
+ if (!mq->bounce_sg) {
+ ret = -ENOMEM;
+ goto cleanup_queue;
+ }
+ sg_init_table(mq->bounce_sg, bouncesz / 512);
+ }
+ }
+#endif
+
+ if (!mq->bounce_buf) {
+ blk_queue_bounce_limit(mq->queue, limit);
+ blk_queue_max_sectors(mq->queue,
+ min(host->max_blk_count, host->max_req_size / 512));
+ blk_queue_max_phys_segments(mq->queue, host->max_phys_segs);
+ blk_queue_max_hw_segments(mq->queue, host->max_hw_segs);
+ blk_queue_max_segment_size(mq->queue, host->max_seg_size);
+
+ mq->sg = kmalloc(sizeof(struct scatterlist) *
+ host->max_phys_segs, GFP_KERNEL);
+ if (!mq->sg) {
+ ret = -ENOMEM;
+ goto cleanup_queue;
+ }
+ sg_init_table(mq->sg, host->max_phys_segs);
+ }
+
+ init_MUTEX(&mq->thread_sem);
+
+ mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd");
+ if (IS_ERR(mq->thread)) {
+ ret = PTR_ERR(mq->thread);
+ goto free_bounce_sg;
+ }
+
+ return 0;
+ free_bounce_sg:
+ if (mq->bounce_sg)
+ kfree(mq->bounce_sg);
+ mq->bounce_sg = NULL;
+ cleanup_queue:
+ if (mq->sg)
+ kfree(mq->sg);
+ mq->sg = NULL;
+ if (mq->bounce_buf)
+ kfree(mq->bounce_buf);
+ mq->bounce_buf = NULL;
+ blk_cleanup_queue(mq->queue);
+ return ret;
+}
+
+void mmc_cleanup_queue(struct mmc_queue *mq)
+{
+ struct request_queue *q = mq->queue;
+ unsigned long flags;
+
+ /* Mark that we should start throwing out stragglers */
+ spin_lock_irqsave(q->queue_lock, flags);
+ q->queuedata = NULL;
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ /* Make sure the queue isn't suspended, as that will deadlock */
+ mmc_queue_resume(mq);
+
+ /* Then terminate our worker thread */
+ kthread_stop(mq->thread);
+
+ if (mq->bounce_sg)
+ kfree(mq->bounce_sg);
+ mq->bounce_sg = NULL;
+
+ kfree(mq->sg);
+ mq->sg = NULL;
+
+ if (mq->bounce_buf)
+ kfree(mq->bounce_buf);
+ mq->bounce_buf = NULL;
+
+ blk_cleanup_queue(mq->queue);
+
+ mq->card = NULL;
+}
+EXPORT_SYMBOL(mmc_cleanup_queue);
+
+/**
+ * mmc_queue_suspend - suspend a MMC request queue
+ * @mq: MMC queue to suspend
+ *
+ * Stop the block request queue, and wait for our thread to
+ * complete any outstanding requests. This ensures that we
+ * won't suspend while a request is being processed.
+ */
+void mmc_queue_suspend(struct mmc_queue *mq)
+{
+ struct request_queue *q = mq->queue;
+ unsigned long flags;
+
+ if (!(mq->flags & MMC_QUEUE_SUSPENDED)) {
+ mq->flags |= MMC_QUEUE_SUSPENDED;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_stop_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ down(&mq->thread_sem);
+ }
+}
+
+/**
+ * mmc_queue_resume - resume a previously suspended MMC request queue
+ * @mq: MMC queue to resume
+ */
+void mmc_queue_resume(struct mmc_queue *mq)
+{
+ struct request_queue *q = mq->queue;
+ unsigned long flags;
+
+ if (mq->flags & MMC_QUEUE_SUSPENDED) {
+ mq->flags &= ~MMC_QUEUE_SUSPENDED;
+
+ up(&mq->thread_sem);
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_start_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ }
+}
+
+/*
+ * Prepare the sg list(s) to be handed of to the host driver
+ */
+unsigned int mmc_queue_map_sg(struct mmc_queue *mq)
+{
+ unsigned int sg_len;
+ size_t buflen;
+ struct scatterlist *sg;
+ int i;
+
+ if (!mq->bounce_buf)
+ return blk_rq_map_sg(mq->queue, mq->req, mq->sg);
+
+ BUG_ON(!mq->bounce_sg);
+
+ sg_len = blk_rq_map_sg(mq->queue, mq->req, mq->bounce_sg);
+
+ mq->bounce_sg_len = sg_len;
+
+ buflen = 0;
+ for_each_sg(mq->bounce_sg, sg, sg_len, i)
+ buflen += sg->length;
+
+ sg_init_one(mq->sg, mq->bounce_buf, buflen);
+
+ return 1;
+}
+
+/*
+ * If writing, bounce the data to the buffer before the request
+ * is sent to the host driver
+ */
+void mmc_queue_bounce_pre(struct mmc_queue *mq)
+{
+ unsigned long flags;
+
+ if (!mq->bounce_buf)
+ return;
+
+ if (rq_data_dir(mq->req) != WRITE)
+ return;
+
+ local_irq_save(flags);
+ sg_copy_to_buffer(mq->bounce_sg, mq->bounce_sg_len,
+ mq->bounce_buf, mq->sg[0].length);
+ local_irq_restore(flags);
+}
+
+/*
+ * If reading, bounce the data from the buffer after the request
+ * has been handled by the host driver
+ */
+void mmc_queue_bounce_post(struct mmc_queue *mq)
+{
+ unsigned long flags;
+
+ if (!mq->bounce_buf)
+ return;
+
+ if (rq_data_dir(mq->req) != READ)
+ return;
+
+ local_irq_save(flags);
+ sg_copy_from_buffer(mq->bounce_sg, mq->bounce_sg_len,
+ mq->bounce_buf, mq->sg[0].length);
+ local_irq_restore(flags);
+}
+
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
new file mode 100644
index 0000000..64e66e0
--- /dev/null
+++ b/drivers/mmc/card/queue.h
@@ -0,0 +1,31 @@
+#ifndef MMC_QUEUE_H
+#define MMC_QUEUE_H
+
+struct request;
+struct task_struct;
+
+struct mmc_queue {
+ struct mmc_card *card;
+ struct task_struct *thread;
+ struct semaphore thread_sem;
+ unsigned int flags;
+ struct request *req;
+ int (*issue_fn)(struct mmc_queue *, struct request *);
+ void *data;
+ struct request_queue *queue;
+ struct scatterlist *sg;
+ char *bounce_buf;
+ struct scatterlist *bounce_sg;
+ unsigned int bounce_sg_len;
+};
+
+extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *);
+extern void mmc_cleanup_queue(struct mmc_queue *);
+extern void mmc_queue_suspend(struct mmc_queue *);
+extern void mmc_queue_resume(struct mmc_queue *);
+
+extern unsigned int mmc_queue_map_sg(struct mmc_queue *);
+extern void mmc_queue_bounce_pre(struct mmc_queue *);
+extern void mmc_queue_bounce_post(struct mmc_queue *);
+
+#endif
diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
new file mode 100644
index 0000000..78ad487
--- /dev/null
+++ b/drivers/mmc/card/sdio_uart.c
@@ -0,0 +1,1161 @@
+/*
+ * linux/drivers/mmc/card/sdio_uart.c - SDIO UART/GPS driver
+ *
+ * Based on drivers/serial/8250.c and drivers/serial/serial_core.c
+ * by Russell King.
+ *
+ * Author: Nicolas Pitre
+ * Created: June 15, 2007
+ * Copyright: MontaVista Software, Inc.
+ *
+ * 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.
+ */
+
+/*
+ * Note: Although this driver assumes a 16550A-like UART implementation,
+ * it is not possible to leverage the common 8250/16550 driver, nor the
+ * core UART infrastructure, as they assumes direct access to the hardware
+ * registers, often under a spinlock. This is not possible in the SDIO
+ * context as SDIO access functions must be able to sleep.
+ *
+ * Because we need to lock the SDIO host to ensure an exclusive access to
+ * the card, we simply rely on that lock to also prevent and serialize
+ * concurrent access to the same port.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/serial_reg.h>
+#include <linux/circ_buf.h>
+#include <linux/gfp.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+
+
+#define UART_NR 8 /* Number of UARTs this driver can handle */
+
+
+#define UART_XMIT_SIZE PAGE_SIZE
+#define WAKEUP_CHARS 256
+
+#define circ_empty(circ) ((circ)->head == (circ)->tail)
+#define circ_clear(circ) ((circ)->head = (circ)->tail = 0)
+
+#define circ_chars_pending(circ) \
+ (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
+
+#define circ_chars_free(circ) \
+ (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
+
+
+struct uart_icount {
+ __u32 cts;
+ __u32 dsr;
+ __u32 rng;
+ __u32 dcd;
+ __u32 rx;
+ __u32 tx;
+ __u32 frame;
+ __u32 overrun;
+ __u32 parity;
+ __u32 brk;
+};
+
+struct sdio_uart_port {
+ struct kref kref;
+ struct tty_struct *tty;
+ unsigned int index;
+ unsigned int opened;
+ struct mutex open_lock;
+ struct sdio_func *func;
+ struct mutex func_lock;
+ struct task_struct *in_sdio_uart_irq;
+ unsigned int regs_offset;
+ struct circ_buf xmit;
+ spinlock_t write_lock;
+ struct uart_icount icount;
+ unsigned int uartclk;
+ unsigned int mctrl;
+ unsigned int read_status_mask;
+ unsigned int ignore_status_mask;
+ unsigned char x_char;
+ unsigned char ier;
+ unsigned char lcr;
+};
+
+static struct sdio_uart_port *sdio_uart_table[UART_NR];
+static DEFINE_SPINLOCK(sdio_uart_table_lock);
+
+static int sdio_uart_add_port(struct sdio_uart_port *port)
+{
+ int index, ret = -EBUSY;
+
+ kref_init(&port->kref);
+ mutex_init(&port->open_lock);
+ mutex_init(&port->func_lock);
+ spin_lock_init(&port->write_lock);
+
+ spin_lock(&sdio_uart_table_lock);
+ for (index = 0; index < UART_NR; index++) {
+ if (!sdio_uart_table[index]) {
+ port->index = index;
+ sdio_uart_table[index] = port;
+ ret = 0;
+ break;
+ }
+ }
+ spin_unlock(&sdio_uart_table_lock);
+
+ return ret;
+}
+
+static struct sdio_uart_port *sdio_uart_port_get(unsigned index)
+{
+ struct sdio_uart_port *port;
+
+ if (index >= UART_NR)
+ return NULL;
+
+ spin_lock(&sdio_uart_table_lock);
+ port = sdio_uart_table[index];
+ if (port)
+ kref_get(&port->kref);
+ spin_unlock(&sdio_uart_table_lock);
+
+ return port;
+}
+
+static void sdio_uart_port_destroy(struct kref *kref)
+{
+ struct sdio_uart_port *port =
+ container_of(kref, struct sdio_uart_port, kref);
+ kfree(port);
+}
+
+static void sdio_uart_port_put(struct sdio_uart_port *port)
+{
+ kref_put(&port->kref, sdio_uart_port_destroy);
+}
+
+static void sdio_uart_port_remove(struct sdio_uart_port *port)
+{
+ struct sdio_func *func;
+
+ BUG_ON(sdio_uart_table[port->index] != port);
+
+ spin_lock(&sdio_uart_table_lock);
+ sdio_uart_table[port->index] = NULL;
+ spin_unlock(&sdio_uart_table_lock);
+
+ /*
+ * We're killing a port that potentially still is in use by
+ * the tty layer. Be careful to prevent any further access
+ * to the SDIO function and arrange for the tty layer to
+ * give up on that port ASAP.
+ * Beware: the lock ordering is critical.
+ */
+ mutex_lock(&port->open_lock);
+ mutex_lock(&port->func_lock);
+ func = port->func;
+ sdio_claim_host(func);
+ port->func = NULL;
+ mutex_unlock(&port->func_lock);
+ if (port->opened)
+ tty_hangup(port->tty);
+ mutex_unlock(&port->open_lock);
+ sdio_release_irq(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+
+ sdio_uart_port_put(port);
+}
+
+static int sdio_uart_claim_func(struct sdio_uart_port *port)
+{
+ mutex_lock(&port->func_lock);
+ if (unlikely(!port->func)) {
+ mutex_unlock(&port->func_lock);
+ return -ENODEV;
+ }
+ if (likely(port->in_sdio_uart_irq != current))
+ sdio_claim_host(port->func);
+ mutex_unlock(&port->func_lock);
+ return 0;
+}
+
+static inline void sdio_uart_release_func(struct sdio_uart_port *port)
+{
+ if (likely(port->in_sdio_uart_irq != current))
+ sdio_release_host(port->func);
+}
+
+static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset)
+{
+ unsigned char c;
+ c = sdio_readb(port->func, port->regs_offset + offset, NULL);
+ return c;
+}
+
+static inline void sdio_out(struct sdio_uart_port *port, int offset, int value)
+{
+ sdio_writeb(port->func, value, port->regs_offset + offset, NULL);
+}
+
+static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port)
+{
+ unsigned char status;
+ unsigned int ret;
+
+ status = sdio_in(port, UART_MSR);
+
+ ret = 0;
+ if (status & UART_MSR_DCD)
+ ret |= TIOCM_CAR;
+ if (status & UART_MSR_RI)
+ ret |= TIOCM_RNG;
+ if (status & UART_MSR_DSR)
+ ret |= TIOCM_DSR;
+ if (status & UART_MSR_CTS)
+ ret |= TIOCM_CTS;
+ return ret;
+}
+
+static void sdio_uart_write_mctrl(struct sdio_uart_port *port, unsigned int mctrl)
+{
+ unsigned char mcr = 0;
+
+ if (mctrl & TIOCM_RTS)
+ mcr |= UART_MCR_RTS;
+ if (mctrl & TIOCM_DTR)
+ mcr |= UART_MCR_DTR;
+ if (mctrl & TIOCM_OUT1)
+ mcr |= UART_MCR_OUT1;
+ if (mctrl & TIOCM_OUT2)
+ mcr |= UART_MCR_OUT2;
+ if (mctrl & TIOCM_LOOP)
+ mcr |= UART_MCR_LOOP;
+
+ sdio_out(port, UART_MCR, mcr);
+}
+
+static inline void sdio_uart_update_mctrl(struct sdio_uart_port *port,
+ unsigned int set, unsigned int clear)
+{
+ unsigned int old;
+
+ old = port->mctrl;
+ port->mctrl = (old & ~clear) | set;
+ if (old != port->mctrl)
+ sdio_uart_write_mctrl(port, port->mctrl);
+}
+
+#define sdio_uart_set_mctrl(port, x) sdio_uart_update_mctrl(port, x, 0)
+#define sdio_uart_clear_mctrl(port, x) sdio_uart_update_mctrl(port, 0, x)
+
+static void sdio_uart_change_speed(struct sdio_uart_port *port,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ unsigned char cval, fcr = 0;
+ unsigned int baud, quot;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+ cval = UART_LCR_WLEN5;
+ break;
+ case CS6:
+ cval = UART_LCR_WLEN6;
+ break;
+ case CS7:
+ cval = UART_LCR_WLEN7;
+ break;
+ default:
+ case CS8:
+ cval = UART_LCR_WLEN8;
+ break;
+ }
+
+ if (termios->c_cflag & CSTOPB)
+ cval |= UART_LCR_STOP;
+ if (termios->c_cflag & PARENB)
+ cval |= UART_LCR_PARITY;
+ if (!(termios->c_cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+
+ for (;;) {
+ baud = tty_termios_baud_rate(termios);
+ if (baud == 0)
+ baud = 9600; /* Special case: B0 rate. */
+ if (baud <= port->uartclk)
+ break;
+ /*
+ * Oops, the quotient was zero. Try again with the old
+ * baud rate if possible, otherwise default to 9600.
+ */
+ termios->c_cflag &= ~CBAUD;
+ if (old) {
+ termios->c_cflag |= old->c_cflag & CBAUD;
+ old = NULL;
+ } else
+ termios->c_cflag |= B9600;
+ }
+ quot = (2 * port->uartclk + baud) / (2 * baud);
+
+ if (baud < 2400)
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+ else
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10;
+
+ port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+ if (termios->c_iflag & INPCK)
+ port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+ if (termios->c_iflag & (BRKINT | PARMRK))
+ port->read_status_mask |= UART_LSR_BI;
+
+ /*
+ * Characters to ignore
+ */
+ port->ignore_status_mask = 0;
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+ if (termios->c_iflag & IGNBRK) {
+ port->ignore_status_mask |= UART_LSR_BI;
+ /*
+ * If we're ignoring parity and break indicators,
+ * ignore overruns too (for real raw support).
+ */
+ if (termios->c_iflag & IGNPAR)
+ port->ignore_status_mask |= UART_LSR_OE;
+ }
+
+ /*
+ * ignore all characters if CREAD is not set
+ */
+ if ((termios->c_cflag & CREAD) == 0)
+ port->ignore_status_mask |= UART_LSR_DR;
+
+ /*
+ * CTS flow control flag and modem status interrupts
+ */
+ port->ier &= ~UART_IER_MSI;
+ if ((termios->c_cflag & CRTSCTS) || !(termios->c_cflag & CLOCAL))
+ port->ier |= UART_IER_MSI;
+
+ port->lcr = cval;
+
+ sdio_out(port, UART_IER, port->ier);
+ sdio_out(port, UART_LCR, cval | UART_LCR_DLAB);
+ sdio_out(port, UART_DLL, quot & 0xff);
+ sdio_out(port, UART_DLM, quot >> 8);
+ sdio_out(port, UART_LCR, cval);
+ sdio_out(port, UART_FCR, fcr);
+
+ sdio_uart_write_mctrl(port, port->mctrl);
+}
+
+static void sdio_uart_start_tx(struct sdio_uart_port *port)
+{
+ if (!(port->ier & UART_IER_THRI)) {
+ port->ier |= UART_IER_THRI;
+ sdio_out(port, UART_IER, port->ier);
+ }
+}
+
+static void sdio_uart_stop_tx(struct sdio_uart_port *port)
+{
+ if (port->ier & UART_IER_THRI) {
+ port->ier &= ~UART_IER_THRI;
+ sdio_out(port, UART_IER, port->ier);
+ }
+}
+
+static void sdio_uart_stop_rx(struct sdio_uart_port *port)
+{
+ port->ier &= ~UART_IER_RLSI;
+ port->read_status_mask &= ~UART_LSR_DR;
+ sdio_out(port, UART_IER, port->ier);
+}
+
+static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *status)
+{
+ struct tty_struct *tty = port->tty;
+ unsigned int ch, flag;
+ int max_count = 256;
+
+ do {
+ ch = sdio_in(port, UART_RX);
+ flag = TTY_NORMAL;
+ port->icount.rx++;
+
+ if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+ UART_LSR_FE | UART_LSR_OE))) {
+ /*
+ * For statistics only
+ */
+ if (*status & UART_LSR_BI) {
+ *status &= ~(UART_LSR_FE | UART_LSR_PE);
+ port->icount.brk++;
+ } else if (*status & UART_LSR_PE)
+ port->icount.parity++;
+ else if (*status & UART_LSR_FE)
+ port->icount.frame++;
+ if (*status & UART_LSR_OE)
+ port->icount.overrun++;
+
+ /*
+ * Mask off conditions which should be ignored.
+ */
+ *status &= port->read_status_mask;
+ if (*status & UART_LSR_BI) {
+ flag = TTY_BREAK;
+ } else if (*status & UART_LSR_PE)
+ flag = TTY_PARITY;
+ else if (*status & UART_LSR_FE)
+ flag = TTY_FRAME;
+ }
+
+ if ((*status & port->ignore_status_mask & ~UART_LSR_OE) == 0)
+ tty_insert_flip_char(tty, ch, flag);
+
+ /*
+ * Overrun is special. Since it's reported immediately,
+ * it doesn't affect the current character.
+ */
+ if (*status & ~port->ignore_status_mask & UART_LSR_OE)
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+
+ *status = sdio_in(port, UART_LSR);
+ } while ((*status & UART_LSR_DR) && (max_count-- > 0));
+ tty_flip_buffer_push(tty);
+}
+
+static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
+{
+ struct circ_buf *xmit = &port->xmit;
+ int count;
+
+ if (port->x_char) {
+ sdio_out(port, UART_TX, port->x_char);
+ port->icount.tx++;
+ port->x_char = 0;
+ return;
+ }
+ if (circ_empty(xmit) || port->tty->stopped || port->tty->hw_stopped) {
+ sdio_uart_stop_tx(port);
+ return;
+ }
+
+ count = 16;
+ do {
+ sdio_out(port, UART_TX, xmit->buf[xmit->tail]);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ port->icount.tx++;
+ if (circ_empty(xmit))
+ break;
+ } while (--count > 0);
+
+ if (circ_chars_pending(xmit) < WAKEUP_CHARS)
+ tty_wakeup(port->tty);
+
+ if (circ_empty(xmit))
+ sdio_uart_stop_tx(port);
+}
+
+static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
+{
+ int status;
+
+ status = sdio_in(port, UART_MSR);
+
+ if ((status & UART_MSR_ANY_DELTA) == 0)
+ return;
+
+ if (status & UART_MSR_TERI)
+ port->icount.rng++;
+ if (status & UART_MSR_DDSR)
+ port->icount.dsr++;
+ if (status & UART_MSR_DDCD)
+ port->icount.dcd++;
+ if (status & UART_MSR_DCTS) {
+ port->icount.cts++;
+ if (port->tty->termios->c_cflag & CRTSCTS) {
+ int cts = (status & UART_MSR_CTS);
+ if (port->tty->hw_stopped) {
+ if (cts) {
+ port->tty->hw_stopped = 0;
+ sdio_uart_start_tx(port);
+ tty_wakeup(port->tty);
+ }
+ } else {
+ if (!cts) {
+ port->tty->hw_stopped = 1;
+ sdio_uart_stop_tx(port);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static void sdio_uart_irq(struct sdio_func *func)
+{
+ struct sdio_uart_port *port = sdio_get_drvdata(func);
+ unsigned int iir, lsr;
+
+ /*
+ * In a few places sdio_uart_irq() is called directly instead of
+ * waiting for the actual interrupt to be raised and the SDIO IRQ
+ * thread scheduled in order to reduce latency. However, some
+ * interaction with the tty core may end up calling us back
+ * (serial echo, flow control, etc.) through those same places
+ * causing undesirable effects. Let's stop the recursion here.
+ */
+ if (unlikely(port->in_sdio_uart_irq == current))
+ return;
+
+ iir = sdio_in(port, UART_IIR);
+ if (iir & UART_IIR_NO_INT)
+ return;
+
+ port->in_sdio_uart_irq = current;
+ lsr = sdio_in(port, UART_LSR);
+ if (lsr & UART_LSR_DR)
+ sdio_uart_receive_chars(port, &lsr);
+ sdio_uart_check_modem_status(port);
+ if (lsr & UART_LSR_THRE)
+ sdio_uart_transmit_chars(port);
+ port->in_sdio_uart_irq = NULL;
+}
+
+static int sdio_uart_startup(struct sdio_uart_port *port)
+{
+ unsigned long page;
+ int ret;
+
+ /*
+ * Set the TTY IO error marker - we will only clear this
+ * once we have successfully opened the port.
+ */
+ set_bit(TTY_IO_ERROR, &port->tty->flags);
+
+ /* Initialise and allocate the transmit buffer. */
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ port->xmit.buf = (unsigned char *)page;
+ circ_clear(&port->xmit);
+
+ ret = sdio_uart_claim_func(port);
+ if (ret)
+ goto err1;
+ ret = sdio_enable_func(port->func);
+ if (ret)
+ goto err2;
+ ret = sdio_claim_irq(port->func, sdio_uart_irq);
+ if (ret)
+ goto err3;
+
+ /*
+ * Clear the FIFO buffers and disable them.
+ * (they will be reenabled in sdio_change_speed())
+ */
+ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO);
+ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ sdio_out(port, UART_FCR, 0);
+
+ /*
+ * Clear the interrupt registers.
+ */
+ (void) sdio_in(port, UART_LSR);
+ (void) sdio_in(port, UART_RX);
+ (void) sdio_in(port, UART_IIR);
+ (void) sdio_in(port, UART_MSR);
+
+ /*
+ * Now, initialize the UART
+ */
+ sdio_out(port, UART_LCR, UART_LCR_WLEN8);
+
+ port->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
+ port->mctrl = TIOCM_OUT2;
+
+ sdio_uart_change_speed(port, port->tty->termios, NULL);
+
+ if (port->tty->termios->c_cflag & CBAUD)
+ sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
+
+ if (port->tty->termios->c_cflag & CRTSCTS)
+ if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS))
+ port->tty->hw_stopped = 1;
+
+ clear_bit(TTY_IO_ERROR, &port->tty->flags);
+
+ /* Kick the IRQ handler once while we're still holding the host lock */
+ sdio_uart_irq(port->func);
+
+ sdio_uart_release_func(port);
+ return 0;
+
+err3:
+ sdio_disable_func(port->func);
+err2:
+ sdio_uart_release_func(port);
+err1:
+ free_page((unsigned long)port->xmit.buf);
+ return ret;
+}
+
+static void sdio_uart_shutdown(struct sdio_uart_port *port)
+{
+ int ret;
+
+ ret = sdio_uart_claim_func(port);
+ if (ret)
+ goto skip;
+
+ sdio_uart_stop_rx(port);
+
+ /* TODO: wait here for TX FIFO to drain */
+
+ /* Turn off DTR and RTS early. */
+ if (port->tty->termios->c_cflag & HUPCL)
+ sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+
+ /* Disable interrupts from this port */
+ sdio_release_irq(port->func);
+ port->ier = 0;
+ sdio_out(port, UART_IER, 0);
+
+ sdio_uart_clear_mctrl(port, TIOCM_OUT2);
+
+ /* Disable break condition and FIFOs. */
+ port->lcr &= ~UART_LCR_SBC;
+ sdio_out(port, UART_LCR, port->lcr);
+ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
+ UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT);
+ sdio_out(port, UART_FCR, 0);
+
+ sdio_disable_func(port->func);
+
+ sdio_uart_release_func(port);
+
+skip:
+ /* Free the transmit buffer page. */
+ free_page((unsigned long)port->xmit.buf);
+}
+
+static int sdio_uart_open (struct tty_struct *tty, struct file * filp)
+{
+ struct sdio_uart_port *port;
+ int ret;
+
+ port = sdio_uart_port_get(tty->index);
+ if (!port)
+ return -ENODEV;
+
+ mutex_lock(&port->open_lock);
+
+ /*
+ * Make sure not to mess up with a dead port
+ * which has not been closed yet.
+ */
+ if (tty->driver_data && tty->driver_data != port) {
+ mutex_unlock(&port->open_lock);
+ sdio_uart_port_put(port);
+ return -EBUSY;
+ }
+
+ if (!port->opened) {
+ tty->driver_data = port;
+ port->tty = tty;
+ ret = sdio_uart_startup(port);
+ if (ret) {
+ tty->driver_data = NULL;
+ port->tty = NULL;
+ mutex_unlock(&port->open_lock);
+ sdio_uart_port_put(port);
+ return ret;
+ }
+ }
+ port->opened++;
+ mutex_unlock(&port->open_lock);
+ return 0;
+}
+
+static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+
+ if (!port)
+ return;
+
+ mutex_lock(&port->open_lock);
+ BUG_ON(!port->opened);
+
+ /*
+ * This is messy. The tty layer calls us even when open()
+ * returned an error. Ignore this close request if tty->count
+ * is larger than port->count.
+ */
+ if (tty->count > port->opened) {
+ mutex_unlock(&port->open_lock);
+ return;
+ }
+
+ if (--port->opened == 0) {
+ tty->closing = 1;
+ sdio_uart_shutdown(port);
+ tty_ldisc_flush(tty);
+ port->tty = NULL;
+ tty->driver_data = NULL;
+ tty->closing = 0;
+ }
+ mutex_unlock(&port->open_lock);
+ sdio_uart_port_put(port);
+}
+
+static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,
+ int count)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+ struct circ_buf *circ = &port->xmit;
+ int c, ret = 0;
+
+ if (!port->func)
+ return -ENODEV;
+
+ spin_lock(&port->write_lock);
+ while (1) {
+ c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
+ if (count < c)
+ c = count;
+ if (c <= 0)
+ break;
+ memcpy(circ->buf + circ->head, buf, c);
+ circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
+ buf += c;
+ count -= c;
+ ret += c;
+ }
+ spin_unlock(&port->write_lock);
+
+ if ( !(port->ier & UART_IER_THRI)) {
+ int err = sdio_uart_claim_func(port);
+ if (!err) {
+ sdio_uart_start_tx(port);
+ sdio_uart_irq(port->func);
+ sdio_uart_release_func(port);
+ } else
+ ret = err;
+ }
+
+ return ret;
+}
+
+static int sdio_uart_write_room(struct tty_struct *tty)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+ return port ? circ_chars_free(&port->xmit) : 0;
+}
+
+static int sdio_uart_chars_in_buffer(struct tty_struct *tty)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+ return port ? circ_chars_pending(&port->xmit) : 0;
+}
+
+static void sdio_uart_send_xchar(struct tty_struct *tty, char ch)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+
+ port->x_char = ch;
+ if (ch && !(port->ier & UART_IER_THRI)) {
+ if (sdio_uart_claim_func(port) != 0)
+ return;
+ sdio_uart_start_tx(port);
+ sdio_uart_irq(port->func);
+ sdio_uart_release_func(port);
+ }
+}
+
+static void sdio_uart_throttle(struct tty_struct *tty)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+
+ if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))
+ return;
+
+ if (sdio_uart_claim_func(port) != 0)
+ return;
+
+ if (I_IXOFF(tty)) {
+ port->x_char = STOP_CHAR(tty);
+ sdio_uart_start_tx(port);
+ }
+
+ if (tty->termios->c_cflag & CRTSCTS)
+ sdio_uart_clear_mctrl(port, TIOCM_RTS);
+
+ sdio_uart_irq(port->func);
+ sdio_uart_release_func(port);
+}
+
+static void sdio_uart_unthrottle(struct tty_struct *tty)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+
+ if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))
+ return;
+
+ if (sdio_uart_claim_func(port) != 0)
+ return;
+
+ if (I_IXOFF(tty)) {
+ if (port->x_char) {
+ port->x_char = 0;
+ } else {
+ port->x_char = START_CHAR(tty);
+ sdio_uart_start_tx(port);
+ }
+ }
+
+ if (tty->termios->c_cflag & CRTSCTS)
+ sdio_uart_set_mctrl(port, TIOCM_RTS);
+
+ sdio_uart_irq(port->func);
+ sdio_uart_release_func(port);
+}
+
+static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+ unsigned int cflag = tty->termios->c_cflag;
+
+#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+ if ((cflag ^ old_termios->c_cflag) == 0 &&
+ RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
+ return;
+
+ if (sdio_uart_claim_func(port) != 0)
+ return;
+
+ sdio_uart_change_speed(port, tty->termios, old_termios);
+
+ /* Handle transition to B0 status */
+ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
+ sdio_uart_clear_mctrl(port, TIOCM_RTS | TIOCM_DTR);
+
+ /* Handle transition away from B0 status */
+ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
+ unsigned int mask = TIOCM_DTR;
+ if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags))
+ mask |= TIOCM_RTS;
+ sdio_uart_set_mctrl(port, mask);
+ }
+
+ /* Handle turning off CRTSCTS */
+ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ sdio_uart_start_tx(port);
+ }
+
+ /* Handle turning on CRTSCTS */
+ if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
+ if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) {
+ tty->hw_stopped = 1;
+ sdio_uart_stop_tx(port);
+ }
+ }
+
+ sdio_uart_release_func(port);
+}
+
+static int sdio_uart_break_ctl(struct tty_struct *tty, int break_state)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+ int result;
+
+ result = sdio_uart_claim_func(port);
+ if (result != 0)
+ return result;
+
+ if (break_state == -1)
+ port->lcr |= UART_LCR_SBC;
+ else
+ port->lcr &= ~UART_LCR_SBC;
+ sdio_out(port, UART_LCR, port->lcr);
+
+ sdio_uart_release_func(port);
+ return 0;
+}
+
+static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+ int result;
+
+ result = sdio_uart_claim_func(port);
+ if (!result) {
+ result = port->mctrl | sdio_uart_get_mctrl(port);
+ sdio_uart_release_func(port);
+ }
+
+ return result;
+}
+
+static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear)
+{
+ struct sdio_uart_port *port = tty->driver_data;
+ int result;
+
+ result =sdio_uart_claim_func(port);
+ if(!result) {
+ sdio_uart_update_mctrl(port, set, clear);
+ sdio_uart_release_func(port);
+ }
+
+ return result;
+}
+
+static int sdio_uart_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int i, len = 0;
+ off_t begin = 0;
+
+ len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n",
+ "", "", "");
+ for (i = 0; i < UART_NR && len < PAGE_SIZE - 96; i++) {
+ struct sdio_uart_port *port = sdio_uart_port_get(i);
+ if (port) {
+ len += sprintf(page+len, "%d: uart:SDIO", i);
+ if(capable(CAP_SYS_ADMIN)) {
+ len += sprintf(page + len, " tx:%d rx:%d",
+ port->icount.tx, port->icount.rx);
+ if (port->icount.frame)
+ len += sprintf(page + len, " fe:%d",
+ port->icount.frame);
+ if (port->icount.parity)
+ len += sprintf(page + len, " pe:%d",
+ port->icount.parity);
+ if (port->icount.brk)
+ len += sprintf(page + len, " brk:%d",
+ port->icount.brk);
+ if (port->icount.overrun)
+ len += sprintf(page + len, " oe:%d",
+ port->icount.overrun);
+ if (port->icount.cts)
+ len += sprintf(page + len, " cts:%d",
+ port->icount.cts);
+ if (port->icount.dsr)
+ len += sprintf(page + len, " dsr:%d",
+ port->icount.dsr);
+ if (port->icount.rng)
+ len += sprintf(page + len, " rng:%d",
+ port->icount.rng);
+ if (port->icount.dcd)
+ len += sprintf(page + len, " dcd:%d",
+ port->icount.dcd);
+ }
+ strcat(page, "\n");
+ len++;
+ sdio_uart_port_put(port);
+ }
+
+ if (len + begin > off + count)
+ goto done;
+ if (len + begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ *eof = 1;
+
+done:
+ if (off >= len + begin)
+ return 0;
+ *start = page + (off - begin);
+ return (count < begin + len - off) ? count : (begin + len - off);
+}
+
+static const struct tty_operations sdio_uart_ops = {
+ .open = sdio_uart_open,
+ .close = sdio_uart_close,
+ .write = sdio_uart_write,
+ .write_room = sdio_uart_write_room,
+ .chars_in_buffer = sdio_uart_chars_in_buffer,
+ .send_xchar = sdio_uart_send_xchar,
+ .throttle = sdio_uart_throttle,
+ .unthrottle = sdio_uart_unthrottle,
+ .set_termios = sdio_uart_set_termios,
+ .break_ctl = sdio_uart_break_ctl,
+ .tiocmget = sdio_uart_tiocmget,
+ .tiocmset = sdio_uart_tiocmset,
+ .read_proc = sdio_uart_read_proc,
+};
+
+static struct tty_driver *sdio_uart_tty_driver;
+
+static int sdio_uart_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct sdio_uart_port *port;
+ int ret;
+
+ port = kzalloc(sizeof(struct sdio_uart_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ if (func->class == SDIO_CLASS_UART) {
+ printk(KERN_WARNING "%s: need info on UART class basic setup\n",
+ sdio_func_id(func));
+ kfree(port);
+ return -ENOSYS;
+ } else if (func->class == SDIO_CLASS_GPS) {
+ /*
+ * We need tuple 0x91. It contains SUBTPL_SIOREG
+ * and SUBTPL_RCVCAPS.
+ */
+ struct sdio_func_tuple *tpl;
+ for (tpl = func->tuples; tpl; tpl = tpl->next) {
+ if (tpl->code != 0x91)
+ continue;
+ if (tpl->size < 10)
+ continue;
+ if (tpl->data[1] == 0) /* SUBTPL_SIOREG */
+ break;
+ }
+ if (!tpl) {
+ printk(KERN_WARNING
+ "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
+ sdio_func_id(func));
+ kfree(port);
+ return -EINVAL;
+ }
+ printk(KERN_DEBUG "%s: Register ID = 0x%02x, Exp ID = 0x%02x\n",
+ sdio_func_id(func), tpl->data[2], tpl->data[3]);
+ port->regs_offset = (tpl->data[4] << 0) |
+ (tpl->data[5] << 8) |
+ (tpl->data[6] << 16);
+ printk(KERN_DEBUG "%s: regs offset = 0x%x\n",
+ sdio_func_id(func), port->regs_offset);
+ port->uartclk = tpl->data[7] * 115200;
+ if (port->uartclk == 0)
+ port->uartclk = 115200;
+ printk(KERN_DEBUG "%s: clk %d baudcode %u 4800-div %u\n",
+ sdio_func_id(func), port->uartclk,
+ tpl->data[7], tpl->data[8] | (tpl->data[9] << 8));
+ } else {
+ kfree(port);
+ return -EINVAL;
+ }
+
+ port->func = func;
+ sdio_set_drvdata(func, port);
+
+ ret = sdio_uart_add_port(port);
+ if (ret) {
+ kfree(port);
+ } else {
+ struct device *dev;
+ dev = tty_register_device(sdio_uart_tty_driver, port->index, &func->dev);
+ if (IS_ERR(dev)) {
+ sdio_uart_port_remove(port);
+ ret = PTR_ERR(dev);
+ }
+ }
+
+ return ret;
+}
+
+static void sdio_uart_remove(struct sdio_func *func)
+{
+ struct sdio_uart_port *port = sdio_get_drvdata(func);
+
+ tty_unregister_device(sdio_uart_tty_driver, port->index);
+ sdio_uart_port_remove(port);
+}
+
+static const struct sdio_device_id sdio_uart_ids[] = {
+ { SDIO_DEVICE_CLASS(SDIO_CLASS_UART) },
+ { SDIO_DEVICE_CLASS(SDIO_CLASS_GPS) },
+ { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(sdio, sdio_uart_ids);
+
+static struct sdio_driver sdio_uart_driver = {
+ .probe = sdio_uart_probe,
+ .remove = sdio_uart_remove,
+ .name = "sdio_uart",
+ .id_table = sdio_uart_ids,
+};
+
+static int __init sdio_uart_init(void)
+{
+ int ret;
+ struct tty_driver *tty_drv;
+
+ sdio_uart_tty_driver = tty_drv = alloc_tty_driver(UART_NR);
+ if (!tty_drv)
+ return -ENOMEM;
+
+ tty_drv->owner = THIS_MODULE;
+ tty_drv->driver_name = "sdio_uart";
+ tty_drv->name = "ttySDIO";
+ tty_drv->major = 0; /* dynamically allocated */
+ tty_drv->minor_start = 0;
+ tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
+ tty_drv->subtype = SERIAL_TYPE_NORMAL;
+ tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ tty_drv->init_termios = tty_std_termios;
+ tty_drv->init_termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL;
+ tty_drv->init_termios.c_ispeed = 4800;
+ tty_drv->init_termios.c_ospeed = 4800;
+ tty_set_operations(tty_drv, &sdio_uart_ops);
+
+ ret = tty_register_driver(tty_drv);
+ if (ret)
+ goto err1;
+
+ ret = sdio_register_driver(&sdio_uart_driver);
+ if (ret)
+ goto err2;
+
+ return 0;
+
+err2:
+ tty_unregister_driver(tty_drv);
+err1:
+ put_tty_driver(tty_drv);
+ return ret;
+}
+
+static void __exit sdio_uart_exit(void)
+{
+ sdio_unregister_driver(&sdio_uart_driver);
+ tty_unregister_driver(sdio_uart_tty_driver);
+ put_tty_driver(sdio_uart_tty_driver);
+}
+
+module_init(sdio_uart_init);
+module_exit(sdio_uart_exit);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
new file mode 100644
index 0000000..ab37a6d
--- /dev/null
+++ b/drivers/mmc/core/Kconfig
@@ -0,0 +1,16 @@
+#
+# MMC core configuration
+#
+
+config MMC_UNSAFE_RESUME
+ bool "Allow unsafe resume (DANGEROUS)"
+ help
+ If you say Y here, the MMC layer will assume that all cards
+ stayed in their respective slots during the suspend. The
+ normal behaviour is to remove them at suspend and
+ redetecting them at resume. Breaking this assumption will
+ in most cases result in data corruption.
+
+ This option is usually just for embedded systems which use
+ a MMC/SD card for rootfs. Most people should say N here.
+
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
new file mode 100644
index 0000000..889e5f8
--- /dev/null
+++ b/drivers/mmc/core/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the kernel mmc core.
+#
+
+ifeq ($(CONFIG_MMC_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
+obj-$(CONFIG_MMC) += mmc_core.o
+mmc_core-y := core.o bus.o host.o \
+ mmc.o mmc_ops.o sd.o sd_ops.o \
+ sdio.o sdio_ops.o sdio_bus.o \
+ sdio_cis.o sdio_io.o sdio_irq.o
+
+mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
new file mode 100644
index 0000000..f210a8e
--- /dev/null
+++ b/drivers/mmc/core/bus.c
@@ -0,0 +1,286 @@
+/*
+ * linux/drivers/mmc/core/bus.c
+ *
+ * Copyright (C) 2003 Russell King, All Rights Reserved.
+ * Copyright (C) 2007 Pierre Ossman
+ *
+ * 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.
+ *
+ * MMC card bus driver model
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+
+#include "core.h"
+#include "sdio_cis.h"
+#include "bus.h"
+
+#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev)
+#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv)
+
+static ssize_t mmc_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_card *card = dev_to_mmc_card(dev);
+
+ switch (card->type) {
+ case MMC_TYPE_MMC:
+ return sprintf(buf, "MMC\n");
+ case MMC_TYPE_SD:
+ return sprintf(buf, "SD\n");
+ case MMC_TYPE_SDIO:
+ return sprintf(buf, "SDIO\n");
+ default:
+ return -EFAULT;
+ }
+}
+
+static struct device_attribute mmc_dev_attrs[] = {
+ __ATTR(type, S_IRUGO, mmc_type_show, NULL),
+ __ATTR_NULL,
+};
+
+/*
+ * This currently matches any MMC driver to any MMC card - drivers
+ * themselves make the decision whether to drive this card in their
+ * probe method.
+ */
+static int mmc_bus_match(struct device *dev, struct device_driver *drv)
+{
+ return 1;
+}
+
+static int
+mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct mmc_card *card = dev_to_mmc_card(dev);
+ const char *type;
+ int retval = 0;
+
+ switch (card->type) {
+ case MMC_TYPE_MMC:
+ type = "MMC";
+ break;
+ case MMC_TYPE_SD:
+ type = "SD";
+ break;
+ case MMC_TYPE_SDIO:
+ type = "SDIO";
+ break;
+ default:
+ type = NULL;
+ }
+
+ if (type) {
+ retval = add_uevent_var(env, "MMC_TYPE=%s", type);
+ if (retval)
+ return retval;
+ }
+
+ retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
+
+ return retval;
+}
+
+static int mmc_bus_probe(struct device *dev)
+{
+ struct mmc_driver *drv = to_mmc_driver(dev->driver);
+ struct mmc_card *card = dev_to_mmc_card(dev);
+
+ return drv->probe(card);
+}
+
+static int mmc_bus_remove(struct device *dev)
+{
+ struct mmc_driver *drv = to_mmc_driver(dev->driver);
+ struct mmc_card *card = dev_to_mmc_card(dev);
+
+ drv->remove(card);
+
+ return 0;
+}
+
+static int mmc_bus_suspend(struct device *dev, pm_message_t state)
+{
+ struct mmc_driver *drv = to_mmc_driver(dev->driver);
+ struct mmc_card *card = dev_to_mmc_card(dev);
+ int ret = 0;
+
+ if (dev->driver && drv->suspend)
+ ret = drv->suspend(card, state);
+ return ret;
+}
+
+static int mmc_bus_resume(struct device *dev)
+{
+ struct mmc_driver *drv = to_mmc_driver(dev->driver);
+ struct mmc_card *card = dev_to_mmc_card(dev);
+ int ret = 0;
+
+ if (dev->driver && drv->resume)
+ ret = drv->resume(card);
+ return ret;
+}
+
+static struct bus_type mmc_bus_type = {
+ .name = "mmc",
+ .dev_attrs = mmc_dev_attrs,
+ .match = mmc_bus_match,
+ .uevent = mmc_bus_uevent,
+ .probe = mmc_bus_probe,
+ .remove = mmc_bus_remove,
+ .suspend = mmc_bus_suspend,
+ .resume = mmc_bus_resume,
+};
+
+int mmc_register_bus(void)
+{
+ return bus_register(&mmc_bus_type);
+}
+
+void mmc_unregister_bus(void)
+{
+ bus_unregister(&mmc_bus_type);
+}
+
+/**
+ * mmc_register_driver - register a media driver
+ * @drv: MMC media driver
+ */
+int mmc_register_driver(struct mmc_driver *drv)
+{
+ drv->drv.bus = &mmc_bus_type;
+ return driver_register(&drv->drv);
+}
+
+EXPORT_SYMBOL(mmc_register_driver);
+
+/**
+ * mmc_unregister_driver - unregister a media driver
+ * @drv: MMC media driver
+ */
+void mmc_unregister_driver(struct mmc_driver *drv)
+{
+ drv->drv.bus = &mmc_bus_type;
+ driver_unregister(&drv->drv);
+}
+
+EXPORT_SYMBOL(mmc_unregister_driver);
+
+static void mmc_release_card(struct device *dev)
+{
+ struct mmc_card *card = dev_to_mmc_card(dev);
+
+ sdio_free_common_cis(card);
+
+ if (card->info)
+ kfree(card->info);
+
+ kfree(card);
+}
+
+/*
+ * Allocate and initialise a new MMC card structure.
+ */
+struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
+{
+ struct mmc_card *card;
+
+ card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);
+ if (!card)
+ return ERR_PTR(-ENOMEM);
+
+ card->host = host;
+
+ device_initialize(&card->dev);
+
+ card->dev.parent = mmc_classdev(host);
+ card->dev.bus = &mmc_bus_type;
+ card->dev.release = mmc_release_card;
+ card->dev.type = type;
+
+ return card;
+}
+
+/*
+ * Register a new MMC card with the driver model.
+ */
+int mmc_add_card(struct mmc_card *card)
+{
+ int ret;
+ const char *type;
+
+ dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);
+
+ switch (card->type) {
+ case MMC_TYPE_MMC:
+ type = "MMC";
+ break;
+ case MMC_TYPE_SD:
+ type = "SD";
+ if (mmc_card_blockaddr(card))
+ type = "SDHC";
+ break;
+ case MMC_TYPE_SDIO:
+ type = "SDIO";
+ break;
+ default:
+ type = "?";
+ break;
+ }
+
+ if (mmc_host_is_spi(card->host)) {
+ printk(KERN_INFO "%s: new %s%s card on SPI\n",
+ mmc_hostname(card->host),
+ mmc_card_highspeed(card) ? "high speed " : "",
+ type);
+ } else {
+ printk(KERN_INFO "%s: new %s%s card at address %04x\n",
+ mmc_hostname(card->host),
+ mmc_card_highspeed(card) ? "high speed " : "",
+ type, card->rca);
+ }
+
+ ret = device_add(&card->dev);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_DEBUG_FS
+ mmc_add_card_debugfs(card);
+#endif
+
+ mmc_card_set_present(card);
+
+ return 0;
+}
+
+/*
+ * Unregister a new MMC card with the driver model, and
+ * (eventually) free it.
+ */
+void mmc_remove_card(struct mmc_card *card)
+{
+#ifdef CONFIG_DEBUG_FS
+ mmc_remove_card_debugfs(card);
+#endif
+
+ if (mmc_card_present(card)) {
+ if (mmc_host_is_spi(card->host)) {
+ printk(KERN_INFO "%s: SPI card removed\n",
+ mmc_hostname(card->host));
+ } else {
+ printk(KERN_INFO "%s: card %04x removed\n",
+ mmc_hostname(card->host), card->rca);
+ }
+ device_del(&card->dev);
+ }
+
+ put_device(&card->dev);
+}
+
diff --git a/drivers/mmc/core/bus.h b/drivers/mmc/core/bus.h
new file mode 100644
index 0000000..1817876
--- /dev/null
+++ b/drivers/mmc/core/bus.h
@@ -0,0 +1,31 @@
+/*
+ * linux/drivers/mmc/core/bus.h
+ *
+ * Copyright (C) 2003 Russell King, All Rights Reserved.
+ * Copyright 2007 Pierre Ossman
+ *
+ * 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 _MMC_CORE_BUS_H
+#define _MMC_CORE_BUS_H
+
+#define MMC_DEV_ATTR(name, fmt, args...) \
+static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct mmc_card *card = container_of(dev, struct mmc_card, dev); \
+ return sprintf(buf, fmt, args); \
+} \
+static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
+
+struct mmc_card *mmc_alloc_card(struct mmc_host *host,
+ struct device_type *type);
+int mmc_add_card(struct mmc_card *card);
+void mmc_remove_card(struct mmc_card *card);
+
+int mmc_register_bus(void);
+void mmc_unregister_bus(void);
+
+#endif
+
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
new file mode 100644
index 0000000..f7284b9
--- /dev/null
+++ b/drivers/mmc/core/core.c
@@ -0,0 +1,862 @@
+/*
+ * linux/drivers/mmc/core/core.c
+ *
+ * Copyright (C) 2003-2004 Russell King, All Rights Reserved.
+ * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
+ * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
+ * MMCv4 support Copyright (C) 2006 Philip Langdale, 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.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/pagemap.h>
+#include <linux/err.h>
+#include <linux/leds.h>
+#include <linux/scatterlist.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+
+#include "core.h"
+#include "bus.h"
+#include "host.h"
+#include "sdio_bus.h"
+
+#include "mmc_ops.h"
+#include "sd_ops.h"
+#include "sdio_ops.h"
+
+static struct workqueue_struct *workqueue;
+
+/*
+ * Enabling software CRCs on the data blocks can be a significant (30%)
+ * performance cost, and for other reasons may not always be desired.
+ * So we allow it it to be disabled.
+ */
+int use_spi_crc = 1;
+module_param(use_spi_crc, bool, 0);
+
+/*
+ * Internal function. Schedule delayed work in the MMC work queue.
+ */
+static int mmc_schedule_delayed_work(struct delayed_work *work,
+ unsigned long delay)
+{
+ return queue_delayed_work(workqueue, work, delay);
+}
+
+/*
+ * Internal function. Flush all scheduled work from the MMC work queue.
+ */
+static void mmc_flush_scheduled_work(void)
+{
+ flush_workqueue(workqueue);
+}
+
+/**
+ * mmc_request_done - finish processing an MMC request
+ * @host: MMC host which completed request
+ * @mrq: MMC request which request
+ *
+ * MMC drivers should call this function when they have completed
+ * their processing of a request.
+ */
+void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
+{
+ struct mmc_command *cmd = mrq->cmd;
+ int err = cmd->error;
+
+ if (err && cmd->retries && mmc_host_is_spi(host)) {
+ if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
+ cmd->retries = 0;
+ }
+
+ if (err && cmd->retries) {
+ pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
+ mmc_hostname(host), cmd->opcode, err);
+
+ cmd->retries--;
+ cmd->error = 0;
+ host->ops->request(host, mrq);
+ } else {
+ led_trigger_event(host->led, LED_OFF);
+
+ pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
+ mmc_hostname(host), cmd->opcode, err,
+ cmd->resp[0], cmd->resp[1],
+ cmd->resp[2], cmd->resp[3]);
+
+ if (mrq->data) {
+ pr_debug("%s: %d bytes transferred: %d\n",
+ mmc_hostname(host),
+ mrq->data->bytes_xfered, mrq->data->error);
+ }
+
+ if (mrq->stop) {
+ pr_debug("%s: (CMD%u): %d: %08x %08x %08x %08x\n",
+ mmc_hostname(host), mrq->stop->opcode,
+ mrq->stop->error,
+ mrq->stop->resp[0], mrq->stop->resp[1],
+ mrq->stop->resp[2], mrq->stop->resp[3]);
+ }
+
+ if (mrq->done)
+ mrq->done(mrq);
+ }
+}
+
+EXPORT_SYMBOL(mmc_request_done);
+
+static void
+mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
+{
+#ifdef CONFIG_MMC_DEBUG
+ unsigned int i, sz;
+ struct scatterlist *sg;
+#endif
+
+ pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
+ mmc_hostname(host), mrq->cmd->opcode,
+ mrq->cmd->arg, mrq->cmd->flags);
+
+ if (mrq->data) {
+ pr_debug("%s: blksz %d blocks %d flags %08x "
+ "tsac %d ms nsac %d\n",
+ mmc_hostname(host), mrq->data->blksz,
+ mrq->data->blocks, mrq->data->flags,
+ mrq->data->timeout_ns / 1000000,
+ mrq->data->timeout_clks);
+ }
+
+ if (mrq->stop) {
+ pr_debug("%s: CMD%u arg %08x flags %08x\n",
+ mmc_hostname(host), mrq->stop->opcode,
+ mrq->stop->arg, mrq->stop->flags);
+ }
+
+ WARN_ON(!host->claimed);
+
+ led_trigger_event(host->led, LED_FULL);
+
+ mrq->cmd->error = 0;
+ mrq->cmd->mrq = mrq;
+ if (mrq->data) {
+ BUG_ON(mrq->data->blksz > host->max_blk_size);
+ BUG_ON(mrq->data->blocks > host->max_blk_count);
+ BUG_ON(mrq->data->blocks * mrq->data->blksz >
+ host->max_req_size);
+
+#ifdef CONFIG_MMC_DEBUG
+ sz = 0;
+ for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i)
+ sz += sg->length;
+ BUG_ON(sz != mrq->data->blocks * mrq->data->blksz);
+#endif
+
+ mrq->cmd->data = mrq->data;
+ mrq->data->error = 0;
+ mrq->data->mrq = mrq;
+ if (mrq->stop) {
+ mrq->data->stop = mrq->stop;
+ mrq->stop->error = 0;
+ mrq->stop->mrq = mrq;
+ }
+ }
+ host->ops->request(host, mrq);
+}
+
+static void mmc_wait_done(struct mmc_request *mrq)
+{
+ complete(mrq->done_data);
+}
+
+/**
+ * mmc_wait_for_req - start a request and wait for completion
+ * @host: MMC host to start command
+ * @mrq: MMC request to start
+ *
+ * Start a new MMC custom command request for a host, and wait
+ * for the command to complete. Does not attempt to parse the
+ * response.
+ */
+void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
+{
+ DECLARE_COMPLETION_ONSTACK(complete);
+
+ mrq->done_data = &complete;
+ mrq->done = mmc_wait_done;
+
+ mmc_start_request(host, mrq);
+
+ wait_for_completion(&complete);
+}
+
+EXPORT_SYMBOL(mmc_wait_for_req);
+
+/**
+ * mmc_wait_for_cmd - start a command and wait for completion
+ * @host: MMC host to start command
+ * @cmd: MMC command to start
+ * @retries: maximum number of retries
+ *
+ * Start a new MMC command for a host, and wait for the command
+ * to complete. Return any error that occurred while the command
+ * was executing. Do not attempt to parse the response.
+ */
+int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
+{
+ struct mmc_request mrq;
+
+ WARN_ON(!host->claimed);
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+
+ memset(cmd->resp, 0, sizeof(cmd->resp));
+ cmd->retries = retries;
+
+ mrq.cmd = cmd;
+ cmd->data = NULL;
+
+ mmc_wait_for_req(host, &mrq);
+
+ return cmd->error;
+}
+
+EXPORT_SYMBOL(mmc_wait_for_cmd);
+
+/**
+ * mmc_set_data_timeout - set the timeout for a data command
+ * @data: data phase for command
+ * @card: the MMC card associated with the data transfer
+ *
+ * Computes the data timeout parameters according to the
+ * correct algorithm given the card type.
+ */
+void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
+{
+ unsigned int mult;
+
+ /*
+ * SDIO cards only define an upper 1 s limit on access.
+ */
+ if (mmc_card_sdio(card)) {
+ data->timeout_ns = 1000000000;
+ data->timeout_clks = 0;
+ return;
+ }
+
+ /*
+ * SD cards use a 100 multiplier rather than 10
+ */
+ mult = mmc_card_sd(card) ? 100 : 10;
+
+ /*
+ * Scale up the multiplier (and therefore the timeout) by
+ * the r2w factor for writes.
+ */
+ if (data->flags & MMC_DATA_WRITE)
+ mult <<= card->csd.r2w_factor;
+
+ data->timeout_ns = card->csd.tacc_ns * mult;
+ data->timeout_clks = card->csd.tacc_clks * mult;
+
+ /*
+ * SD cards also have an upper limit on the timeout.
+ */
+ if (mmc_card_sd(card)) {
+ unsigned int timeout_us, limit_us;
+
+ timeout_us = data->timeout_ns / 1000;
+ timeout_us += data->timeout_clks * 1000 /
+ (card->host->ios.clock / 1000);
+
+ if (data->flags & MMC_DATA_WRITE)
+ /*
+ * The limit is really 250 ms, but that is
+ * insufficient for some crappy cards.
+ */
+ limit_us = 300000;
+ else
+ limit_us = 100000;
+
+ /*
+ * SDHC cards always use these fixed values.
+ */
+ if (timeout_us > limit_us || mmc_card_blockaddr(card)) {
+ data->timeout_ns = limit_us * 1000;
+ data->timeout_clks = 0;
+ }
+ }
+}
+EXPORT_SYMBOL(mmc_set_data_timeout);
+
+/**
+ * mmc_align_data_size - pads a transfer size to a more optimal value
+ * @card: the MMC card associated with the data transfer
+ * @sz: original transfer size
+ *
+ * Pads the original data size with a number of extra bytes in
+ * order to avoid controller bugs and/or performance hits
+ * (e.g. some controllers revert to PIO for certain sizes).
+ *
+ * Returns the improved size, which might be unmodified.
+ *
+ * Note that this function is only relevant when issuing a
+ * single scatter gather entry.
+ */
+unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
+{
+ /*
+ * FIXME: We don't have a system for the controller to tell
+ * the core about its problems yet, so for now we just 32-bit
+ * align the size.
+ */
+ sz = ((sz + 3) / 4) * 4;
+
+ return sz;
+}
+EXPORT_SYMBOL(mmc_align_data_size);
+
+/**
+ * __mmc_claim_host - exclusively claim a host
+ * @host: mmc host to claim
+ * @abort: whether or not the operation should be aborted
+ *
+ * Claim a host for a set of operations. If @abort is non null and
+ * dereference a non-zero value then this will return prematurely with
+ * that non-zero value without acquiring the lock. Returns zero
+ * with the lock held otherwise.
+ */
+int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long flags;
+ int stop;
+
+ might_sleep();
+
+ add_wait_queue(&host->wq, &wait);
+ spin_lock_irqsave(&host->lock, flags);
+ while (1) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ stop = abort ? atomic_read(abort) : 0;
+ if (stop || !host->claimed)
+ break;
+ spin_unlock_irqrestore(&host->lock, flags);
+ schedule();
+ spin_lock_irqsave(&host->lock, flags);
+ }
+ set_current_state(TASK_RUNNING);
+ if (!stop)
+ host->claimed = 1;
+ else
+ wake_up(&host->wq);
+ spin_unlock_irqrestore(&host->lock, flags);
+ remove_wait_queue(&host->wq, &wait);
+ return stop;
+}
+
+EXPORT_SYMBOL(__mmc_claim_host);
+
+/**
+ * mmc_release_host - release a host
+ * @host: mmc host to release
+ *
+ * Release a MMC host, allowing others to claim the host
+ * for their operations.
+ */
+void mmc_release_host(struct mmc_host *host)
+{
+ unsigned long flags;
+
+ WARN_ON(!host->claimed);
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->claimed = 0;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ wake_up(&host->wq);
+}
+
+EXPORT_SYMBOL(mmc_release_host);
+
+/*
+ * Internal function that does the actual ios call to the host driver,
+ * optionally printing some debug output.
+ */
+static inline void mmc_set_ios(struct mmc_host *host)
+{
+ struct mmc_ios *ios = &host->ios;
+
+ pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u "
+ "width %u timing %u\n",
+ mmc_hostname(host), ios->clock, ios->bus_mode,
+ ios->power_mode, ios->chip_select, ios->vdd,
+ ios->bus_width, ios->timing);
+
+ host->ops->set_ios(host, ios);
+}
+
+/*
+ * Control chip select pin on a host.
+ */
+void mmc_set_chip_select(struct mmc_host *host, int mode)
+{
+ host->ios.chip_select = mode;
+ mmc_set_ios(host);
+}
+
+/*
+ * Sets the host clock to the highest possible frequency that
+ * is below "hz".
+ */
+void mmc_set_clock(struct mmc_host *host, unsigned int hz)
+{
+ WARN_ON(hz < host->f_min);
+
+ if (hz > host->f_max)
+ hz = host->f_max;
+
+ host->ios.clock = hz;
+ mmc_set_ios(host);
+}
+
+/*
+ * Change the bus mode (open drain/push-pull) of a host.
+ */
+void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
+{
+ host->ios.bus_mode = mode;
+ mmc_set_ios(host);
+}
+
+/*
+ * Change data bus width of a host.
+ */
+void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
+{
+ host->ios.bus_width = width;
+ mmc_set_ios(host);
+}
+
+/*
+ * Mask off any voltages we don't support and select
+ * the lowest voltage
+ */
+u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
+{
+ int bit;
+
+ ocr &= host->ocr_avail;
+
+ bit = ffs(ocr);
+ if (bit) {
+ bit -= 1;
+
+ ocr &= 3 << bit;
+
+ host->ios.vdd = bit;
+ mmc_set_ios(host);
+ } else {
+ ocr = 0;
+ }
+
+ return ocr;
+}
+
+/*
+ * Select timing parameters for host.
+ */
+void mmc_set_timing(struct mmc_host *host, unsigned int timing)
+{
+ host->ios.timing = timing;
+ mmc_set_ios(host);
+}
+
+/*
+ * Apply power to the MMC stack. This is a two-stage process.
+ * First, we enable power to the card without the clock running.
+ * We then wait a bit for the power to stabilise. Finally,
+ * enable the bus drivers and clock to the card.
+ *
+ * We must _NOT_ enable the clock prior to power stablising.
+ *
+ * If a host does all the power sequencing itself, ignore the
+ * initial MMC_POWER_UP stage.
+ */
+static void mmc_power_up(struct mmc_host *host)
+{
+ int bit = fls(host->ocr_avail) - 1;
+
+ host->ios.vdd = bit;
+ if (mmc_host_is_spi(host)) {
+ host->ios.chip_select = MMC_CS_HIGH;
+ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+ } else {
+ host->ios.chip_select = MMC_CS_DONTCARE;
+ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+ }
+ host->ios.power_mode = MMC_POWER_UP;
+ host->ios.bus_width = MMC_BUS_WIDTH_1;
+ host->ios.timing = MMC_TIMING_LEGACY;
+ mmc_set_ios(host);
+
+ /*
+ * This delay should be sufficient to allow the power supply
+ * to reach the minimum voltage.
+ */
+ mmc_delay(2);
+
+ host->ios.clock = host->f_min;
+ host->ios.power_mode = MMC_POWER_ON;
+ mmc_set_ios(host);
+
+ /*
+ * This delay must be at least 74 clock sizes, or 1 ms, or the
+ * time required to reach a stable voltage.
+ */
+ mmc_delay(2);
+}
+
+static void mmc_power_off(struct mmc_host *host)
+{
+ host->ios.clock = 0;
+ host->ios.vdd = 0;
+ if (!mmc_host_is_spi(host)) {
+ host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+ host->ios.chip_select = MMC_CS_DONTCARE;
+ }
+ host->ios.power_mode = MMC_POWER_OFF;
+ host->ios.bus_width = MMC_BUS_WIDTH_1;
+ host->ios.timing = MMC_TIMING_LEGACY;
+ mmc_set_ios(host);
+}
+
+/*
+ * Cleanup when the last reference to the bus operator is dropped.
+ */
+static void __mmc_release_bus(struct mmc_host *host)
+{
+ BUG_ON(!host);
+ BUG_ON(host->bus_refs);
+ BUG_ON(!host->bus_dead);
+
+ host->bus_ops = NULL;
+}
+
+/*
+ * Increase reference count of bus operator
+ */
+static inline void mmc_bus_get(struct mmc_host *host)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->bus_refs++;
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/*
+ * Decrease reference count of bus operator and free it if
+ * it is the last reference.
+ */
+static inline void mmc_bus_put(struct mmc_host *host)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->bus_refs--;
+ if ((host->bus_refs == 0) && host->bus_ops)
+ __mmc_release_bus(host);
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/*
+ * Assign a mmc bus handler to a host. Only one bus handler may control a
+ * host at any given time.
+ */
+void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
+{
+ unsigned long flags;
+
+ BUG_ON(!host);
+ BUG_ON(!ops);
+
+ WARN_ON(!host->claimed);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ BUG_ON(host->bus_ops);
+ BUG_ON(host->bus_refs);
+
+ host->bus_ops = ops;
+ host->bus_refs = 1;
+ host->bus_dead = 0;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/*
+ * Remove the current bus handler from a host. Assumes that there are
+ * no interesting cards left, so the bus is powered down.
+ */
+void mmc_detach_bus(struct mmc_host *host)
+{
+ unsigned long flags;
+
+ BUG_ON(!host);
+
+ WARN_ON(!host->claimed);
+ WARN_ON(!host->bus_ops);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ host->bus_dead = 1;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ mmc_power_off(host);
+
+ mmc_bus_put(host);
+}
+
+/**
+ * mmc_detect_change - process change of state on a MMC socket
+ * @host: host which changed state.
+ * @delay: optional delay to wait before detection (jiffies)
+ *
+ * MMC drivers should call this when they detect a card has been
+ * inserted or removed. The MMC layer will confirm that any
+ * present card is still functional, and initialize any newly
+ * inserted.
+ */
+void mmc_detect_change(struct mmc_host *host, unsigned long delay)
+{
+#ifdef CONFIG_MMC_DEBUG
+ unsigned long flags;
+ spin_lock_irqsave(&host->lock, flags);
+ WARN_ON(host->removed);
+ spin_unlock_irqrestore(&host->lock, flags);
+#endif
+
+ mmc_schedule_delayed_work(&host->detect, delay);
+}
+
+EXPORT_SYMBOL(mmc_detect_change);
+
+
+void mmc_rescan(struct work_struct *work)
+{
+ struct mmc_host *host =
+ container_of(work, struct mmc_host, detect.work);
+ u32 ocr;
+ int err;
+
+ mmc_bus_get(host);
+
+ if (host->bus_ops == NULL) {
+ /*
+ * Only we can add a new handler, so it's safe to
+ * release the lock here.
+ */
+ mmc_bus_put(host);
+
+ if (host->ops->get_cd && host->ops->get_cd(host) == 0)
+ goto out;
+
+ mmc_claim_host(host);
+
+ mmc_power_up(host);
+ mmc_go_idle(host);
+
+ mmc_send_if_cond(host, host->ocr_avail);
+
+ /*
+ * First we search for SDIO...
+ */
+ err = mmc_send_io_op_cond(host, 0, &ocr);
+ if (!err) {
+ if (mmc_attach_sdio(host, ocr))
+ mmc_power_off(host);
+ goto out;
+ }
+
+ /*
+ * ...then normal SD...
+ */
+ err = mmc_send_app_op_cond(host, 0, &ocr);
+ if (!err) {
+ if (mmc_attach_sd(host, ocr))
+ mmc_power_off(host);
+ goto out;
+ }
+
+ /*
+ * ...and finally MMC.
+ */
+ err = mmc_send_op_cond(host, 0, &ocr);
+ if (!err) {
+ if (mmc_attach_mmc(host, ocr))
+ mmc_power_off(host);
+ goto out;
+ }
+
+ mmc_release_host(host);
+ mmc_power_off(host);
+ } else {
+ if (host->bus_ops->detect && !host->bus_dead)
+ host->bus_ops->detect(host);
+
+ mmc_bus_put(host);
+ }
+out:
+ if (host->caps & MMC_CAP_NEEDS_POLL)
+ mmc_schedule_delayed_work(&host->detect, HZ);
+}
+
+void mmc_start_host(struct mmc_host *host)
+{
+ mmc_power_off(host);
+ mmc_detect_change(host, 0);
+}
+
+void mmc_stop_host(struct mmc_host *host)
+{
+#ifdef CONFIG_MMC_DEBUG
+ unsigned long flags;
+ spin_lock_irqsave(&host->lock, flags);
+ host->removed = 1;
+ spin_unlock_irqrestore(&host->lock, flags);
+#endif
+
+ mmc_flush_scheduled_work();
+
+ mmc_bus_get(host);
+ if (host->bus_ops && !host->bus_dead) {
+ if (host->bus_ops->remove)
+ host->bus_ops->remove(host);
+
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+ }
+ mmc_bus_put(host);
+
+ BUG_ON(host->card);
+
+ mmc_power_off(host);
+}
+
+#ifdef CONFIG_PM
+
+/**
+ * mmc_suspend_host - suspend a host
+ * @host: mmc host
+ * @state: suspend mode (PM_SUSPEND_xxx)
+ */
+int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
+{
+ mmc_flush_scheduled_work();
+
+ mmc_bus_get(host);
+ if (host->bus_ops && !host->bus_dead) {
+ if (host->bus_ops->suspend)
+ host->bus_ops->suspend(host);
+ if (!host->bus_ops->resume) {
+ if (host->bus_ops->remove)
+ host->bus_ops->remove(host);
+
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+ }
+ }
+ mmc_bus_put(host);
+
+ mmc_power_off(host);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(mmc_suspend_host);
+
+/**
+ * mmc_resume_host - resume a previously suspended host
+ * @host: mmc host
+ */
+int mmc_resume_host(struct mmc_host *host)
+{
+ mmc_bus_get(host);
+ if (host->bus_ops && !host->bus_dead) {
+ mmc_power_up(host);
+ BUG_ON(!host->bus_ops->resume);
+ host->bus_ops->resume(host);
+ }
+ mmc_bus_put(host);
+
+ /*
+ * We add a slight delay here so that resume can progress
+ * in parallel.
+ */
+ mmc_detect_change(host, 1);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(mmc_resume_host);
+
+#endif
+
+static int __init mmc_init(void)
+{
+ int ret;
+
+ workqueue = create_singlethread_workqueue("kmmcd");
+ if (!workqueue)
+ return -ENOMEM;
+
+ ret = mmc_register_bus();
+ if (ret)
+ goto destroy_workqueue;
+
+ ret = mmc_register_host_class();
+ if (ret)
+ goto unregister_bus;
+
+ ret = sdio_register_bus();
+ if (ret)
+ goto unregister_host_class;
+
+ return 0;
+
+unregister_host_class:
+ mmc_unregister_host_class();
+unregister_bus:
+ mmc_unregister_bus();
+destroy_workqueue:
+ destroy_workqueue(workqueue);
+
+ return ret;
+}
+
+static void __exit mmc_exit(void)
+{
+ sdio_unregister_bus();
+ mmc_unregister_host_class();
+ mmc_unregister_bus();
+ destroy_workqueue(workqueue);
+}
+
+subsys_initcall(mmc_init);
+module_exit(mmc_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
new file mode 100644
index 0000000..c819eff
--- /dev/null
+++ b/drivers/mmc/core/core.h
@@ -0,0 +1,63 @@
+/*
+ * linux/drivers/mmc/core/core.h
+ *
+ * Copyright (C) 2003 Russell King, All Rights Reserved.
+ * Copyright 2007 Pierre Ossman
+ *
+ * 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 _MMC_CORE_CORE_H
+#define _MMC_CORE_CORE_H
+
+#include <linux/delay.h>
+
+#define MMC_CMD_RETRIES 3
+
+struct mmc_bus_ops {
+ void (*remove)(struct mmc_host *);
+ void (*detect)(struct mmc_host *);
+ void (*suspend)(struct mmc_host *);
+ void (*resume)(struct mmc_host *);
+};
+
+void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
+void mmc_detach_bus(struct mmc_host *host);
+
+void mmc_set_chip_select(struct mmc_host *host, int mode);
+void mmc_set_clock(struct mmc_host *host, unsigned int hz);
+void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
+void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
+u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
+void mmc_set_timing(struct mmc_host *host, unsigned int timing);
+
+static inline void mmc_delay(unsigned int ms)
+{
+ if (ms < 1000 / HZ) {
+ cond_resched();
+ mdelay(ms);
+ } else {
+ msleep(ms);
+ }
+}
+
+void mmc_rescan(struct work_struct *work);
+void mmc_start_host(struct mmc_host *host);
+void mmc_stop_host(struct mmc_host *host);
+
+int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
+int mmc_attach_sd(struct mmc_host *host, u32 ocr);
+int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
+
+extern int use_spi_crc;
+
+/* Debugfs information for hosts and cards */
+void mmc_add_host_debugfs(struct mmc_host *host);
+void mmc_remove_host_debugfs(struct mmc_host *host);
+
+void mmc_add_card_debugfs(struct mmc_card *card);
+void mmc_remove_card_debugfs(struct mmc_card *card);
+
+#endif
+
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
new file mode 100644
index 0000000..1237bb4
--- /dev/null
+++ b/drivers/mmc/core/debugfs.c
@@ -0,0 +1,225 @@
+/*
+ * Debugfs support for hosts and cards
+ *
+ * Copyright (C) 2008 Atmel Corporation
+ *
+ * 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/debugfs.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/stat.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+
+#include "core.h"
+#include "mmc_ops.h"
+
+/* The debugfs functions are optimized away when CONFIG_DEBUG_FS isn't set. */
+static int mmc_ios_show(struct seq_file *s, void *data)
+{
+ static const char *vdd_str[] = {
+ [8] = "2.0",
+ [9] = "2.1",
+ [10] = "2.2",
+ [11] = "2.3",
+ [12] = "2.4",
+ [13] = "2.5",
+ [14] = "2.6",
+ [15] = "2.7",
+ [16] = "2.8",
+ [17] = "2.9",
+ [18] = "3.0",
+ [19] = "3.1",
+ [20] = "3.2",
+ [21] = "3.3",
+ [22] = "3.4",
+ [23] = "3.5",
+ [24] = "3.6",
+ };
+ struct mmc_host *host = s->private;
+ struct mmc_ios *ios = &host->ios;
+ const char *str;
+
+ seq_printf(s, "clock:\t\t%u Hz\n", ios->clock);
+ seq_printf(s, "vdd:\t\t%u ", ios->vdd);
+ if ((1 << ios->vdd) & MMC_VDD_165_195)
+ seq_printf(s, "(1.65 - 1.95 V)\n");
+ else if (ios->vdd < (ARRAY_SIZE(vdd_str) - 1)
+ && vdd_str[ios->vdd] && vdd_str[ios->vdd + 1])
+ seq_printf(s, "(%s ~ %s V)\n", vdd_str[ios->vdd],
+ vdd_str[ios->vdd + 1]);
+ else
+ seq_printf(s, "(invalid)\n");
+
+ switch (ios->bus_mode) {
+ case MMC_BUSMODE_OPENDRAIN:
+ str = "open drain";
+ break;
+ case MMC_BUSMODE_PUSHPULL:
+ str = "push-pull";
+ break;
+ default:
+ str = "invalid";
+ break;
+ }
+ seq_printf(s, "bus mode:\t%u (%s)\n", ios->bus_mode, str);
+
+ switch (ios->chip_select) {
+ case MMC_CS_DONTCARE:
+ str = "don't care";
+ break;
+ case MMC_CS_HIGH:
+ str = "active high";
+ break;
+ case MMC_CS_LOW:
+ str = "active low";
+ break;
+ default:
+ str = "invalid";
+ break;
+ }
+ seq_printf(s, "chip select:\t%u (%s)\n", ios->chip_select, str);
+
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ str = "off";
+ break;
+ case MMC_POWER_UP:
+ str = "up";
+ break;
+ case MMC_POWER_ON:
+ str = "on";
+ break;
+ default:
+ str = "invalid";
+ break;
+ }
+ seq_printf(s, "power mode:\t%u (%s)\n", ios->power_mode, str);
+ seq_printf(s, "bus width:\t%u (%u bits)\n",
+ ios->bus_width, 1 << ios->bus_width);
+
+ switch (ios->timing) {
+ case MMC_TIMING_LEGACY:
+ str = "legacy";
+ break;
+ case MMC_TIMING_MMC_HS:
+ str = "mmc high-speed";
+ break;
+ case MMC_TIMING_SD_HS:
+ str = "sd high-speed";
+ break;
+ default:
+ str = "invalid";
+ break;
+ }
+ seq_printf(s, "timing spec:\t%u (%s)\n", ios->timing, str);
+
+ return 0;
+}
+
+static int mmc_ios_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mmc_ios_show, inode->i_private);
+}
+
+static const struct file_operations mmc_ios_fops = {
+ .open = mmc_ios_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void mmc_add_host_debugfs(struct mmc_host *host)
+{
+ struct dentry *root;
+
+ root = debugfs_create_dir(mmc_hostname(host), NULL);
+ if (IS_ERR(root))
+ /* Don't complain -- debugfs just isn't enabled */
+ return;
+ if (!root)
+ /* Complain -- debugfs is enabled, but it failed to
+ * create the directory. */
+ goto err_root;
+
+ host->debugfs_root = root;
+
+ if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
+ goto err_ios;
+
+ return;
+
+err_ios:
+ debugfs_remove_recursive(root);
+ host->debugfs_root = NULL;
+err_root:
+ dev_err(&host->class_dev, "failed to initialize debugfs\n");
+}
+
+void mmc_remove_host_debugfs(struct mmc_host *host)
+{
+ debugfs_remove_recursive(host->debugfs_root);
+}
+
+static int mmc_dbg_card_status_get(void *data, u64 *val)
+{
+ struct mmc_card *card = data;
+ u32 status;
+ int ret;
+
+ mmc_claim_host(card->host);
+
+ ret = mmc_send_status(data, &status);
+ if (!ret)
+ *val = status;
+
+ mmc_release_host(card->host);
+
+ return ret;
+}
+DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
+ NULL, "%08llx\n");
+
+void mmc_add_card_debugfs(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ struct dentry *root;
+
+ if (!host->debugfs_root)
+ return;
+
+ root = debugfs_create_dir(mmc_card_id(card), host->debugfs_root);
+ if (IS_ERR(root))
+ /* Don't complain -- debugfs just isn't enabled */
+ return;
+ if (!root)
+ /* Complain -- debugfs is enabled, but it failed to
+ * create the directory. */
+ goto err;
+
+ card->debugfs_root = root;
+
+ if (!debugfs_create_x32("state", S_IRUSR, root, &card->state))
+ goto err;
+
+ if (mmc_card_mmc(card) || mmc_card_sd(card))
+ if (!debugfs_create_file("status", S_IRUSR, root, card,
+ &mmc_dbg_card_status_fops))
+ goto err;
+
+ return;
+
+err:
+ debugfs_remove_recursive(root);
+ card->debugfs_root = NULL;
+ dev_err(&card->dev, "failed to initialize debugfs\n");
+}
+
+void mmc_remove_card_debugfs(struct mmc_card *card)
+{
+ debugfs_remove_recursive(card->debugfs_root);
+}
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
new file mode 100644
index 0000000..5e945e6
--- /dev/null
+++ b/drivers/mmc/core/host.c
@@ -0,0 +1,179 @@
+/*
+ * linux/drivers/mmc/core/host.c
+ *
+ * Copyright (C) 2003 Russell King, All Rights Reserved.
+ * Copyright (C) 2007-2008 Pierre Ossman
+ *
+ * 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.
+ *
+ * MMC host class device management
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/idr.h>
+#include <linux/pagemap.h>
+#include <linux/leds.h>
+
+#include <linux/mmc/host.h>
+
+#include "core.h"
+#include "host.h"
+
+#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
+
+static void mmc_host_classdev_release(struct device *dev)
+{
+ struct mmc_host *host = cls_dev_to_mmc_host(dev);
+ kfree(host);
+}
+
+static struct class mmc_host_class = {
+ .name = "mmc_host",
+ .dev_release = mmc_host_classdev_release,
+};
+
+int mmc_register_host_class(void)
+{
+ return class_register(&mmc_host_class);
+}
+
+void mmc_unregister_host_class(void)
+{
+ class_unregister(&mmc_host_class);
+}
+
+static DEFINE_IDR(mmc_host_idr);
+static DEFINE_SPINLOCK(mmc_host_lock);
+
+/**
+ * mmc_alloc_host - initialise the per-host structure.
+ * @extra: sizeof private data structure
+ * @dev: pointer to host device model structure
+ *
+ * Initialise the per-host structure.
+ */
+struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
+{
+ int err;
+ struct mmc_host *host;
+
+ if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
+ return NULL;
+
+ host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
+ if (!host)
+ return NULL;
+
+ spin_lock(&mmc_host_lock);
+ err = idr_get_new(&mmc_host_idr, host, &host->index);
+ spin_unlock(&mmc_host_lock);
+ if (err)
+ goto free;
+
+ dev_set_name(&host->class_dev, "mmc%d", host->index);
+
+ host->parent = dev;
+ host->class_dev.parent = dev;
+ host->class_dev.class = &mmc_host_class;
+ device_initialize(&host->class_dev);
+
+ spin_lock_init(&host->lock);
+ init_waitqueue_head(&host->wq);
+ INIT_DELAYED_WORK(&host->detect, mmc_rescan);
+
+ /*
+ * By default, hosts do not support SGIO or large requests.
+ * They have to set these according to their abilities.
+ */
+ host->max_hw_segs = 1;
+ host->max_phys_segs = 1;
+ host->max_seg_size = PAGE_CACHE_SIZE;
+
+ host->max_req_size = PAGE_CACHE_SIZE;
+ host->max_blk_size = 512;
+ host->max_blk_count = PAGE_CACHE_SIZE / 512;
+
+ return host;
+
+free:
+ kfree(host);
+ return NULL;
+}
+
+EXPORT_SYMBOL(mmc_alloc_host);
+
+/**
+ * mmc_add_host - initialise host hardware
+ * @host: mmc host
+ *
+ * Register the host with the driver model. The host must be
+ * prepared to start servicing requests before this function
+ * completes.
+ */
+int mmc_add_host(struct mmc_host *host)
+{
+ int err;
+
+ WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
+ !host->ops->enable_sdio_irq);
+
+ led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
+
+ err = device_add(&host->class_dev);
+ if (err)
+ return err;
+
+#ifdef CONFIG_DEBUG_FS
+ mmc_add_host_debugfs(host);
+#endif
+
+ mmc_start_host(host);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(mmc_add_host);
+
+/**
+ * mmc_remove_host - remove host hardware
+ * @host: mmc host
+ *
+ * Unregister and remove all cards associated with this host,
+ * and power down the MMC bus. No new requests will be issued
+ * after this function has returned.
+ */
+void mmc_remove_host(struct mmc_host *host)
+{
+ mmc_stop_host(host);
+
+#ifdef CONFIG_DEBUG_FS
+ mmc_remove_host_debugfs(host);
+#endif
+
+ device_del(&host->class_dev);
+
+ led_trigger_unregister_simple(host->led);
+}
+
+EXPORT_SYMBOL(mmc_remove_host);
+
+/**
+ * mmc_free_host - free the host structure
+ * @host: mmc host
+ *
+ * Free the host once all references to it have been dropped.
+ */
+void mmc_free_host(struct mmc_host *host)
+{
+ spin_lock(&mmc_host_lock);
+ idr_remove(&mmc_host_idr, host->index);
+ spin_unlock(&mmc_host_lock);
+
+ put_device(&host->class_dev);
+}
+
+EXPORT_SYMBOL(mmc_free_host);
+
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
new file mode 100644
index 0000000..c2dc3d2
--- /dev/null
+++ b/drivers/mmc/core/host.h
@@ -0,0 +1,18 @@
+/*
+ * linux/drivers/mmc/core/host.h
+ *
+ * Copyright (C) 2003 Russell King, All Rights Reserved.
+ * Copyright 2007 Pierre Ossman
+ *
+ * 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 _MMC_CORE_HOST_H
+#define _MMC_CORE_HOST_H
+
+int mmc_register_host_class(void);
+void mmc_unregister_host_class(void);
+
+#endif
+
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
new file mode 100644
index 0000000..1928a42
--- /dev/null
+++ b/drivers/mmc/core/mmc.c
@@ -0,0 +1,716 @@
+/*
+ * linux/drivers/mmc/core/mmc.c
+ *
+ * Copyright (C) 2003-2004 Russell King, All Rights Reserved.
+ * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
+ * MMCv4 support Copyright (C) 2006 Philip Langdale, 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.
+ */
+
+#include <linux/err.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+
+#include "core.h"
+#include "bus.h"
+#include "mmc_ops.h"
+
+static const unsigned int tran_exp[] = {
+ 10000, 100000, 1000000, 10000000,
+ 0, 0, 0, 0
+};
+
+static const unsigned char tran_mant[] = {
+ 0, 10, 12, 13, 15, 20, 25, 30,
+ 35, 40, 45, 50, 55, 60, 70, 80,
+};
+
+static const unsigned int tacc_exp[] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
+};
+
+static const unsigned int tacc_mant[] = {
+ 0, 10, 12, 13, 15, 20, 25, 30,
+ 35, 40, 45, 50, 55, 60, 70, 80,
+};
+
+#define UNSTUFF_BITS(resp,start,size) \
+ ({ \
+ const int __size = size; \
+ const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \
+ const int __off = 3 - ((start) / 32); \
+ const int __shft = (start) & 31; \
+ u32 __res; \
+ \
+ __res = resp[__off] >> __shft; \
+ if (__size + __shft > 32) \
+ __res |= resp[__off-1] << ((32 - __shft) % 32); \
+ __res & __mask; \
+ })
+
+/*
+ * Given the decoded CSD structure, decode the raw CID to our CID structure.
+ */
+static int mmc_decode_cid(struct mmc_card *card)
+{
+ u32 *resp = card->raw_cid;
+
+ /*
+ * The selection of the format here is based upon published
+ * specs from sandisk and from what people have reported.
+ */
+ switch (card->csd.mmca_vsn) {
+ case 0: /* MMC v1.0 - v1.2 */
+ case 1: /* MMC v1.4 */
+ card->cid.manfid = UNSTUFF_BITS(resp, 104, 24);
+ card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
+ card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
+ card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
+ card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
+ card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
+ card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8);
+ card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8);
+ card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4);
+ card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4);
+ card->cid.serial = UNSTUFF_BITS(resp, 16, 24);
+ card->cid.month = UNSTUFF_BITS(resp, 12, 4);
+ card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997;
+ break;
+
+ case 2: /* MMC v2.0 - v2.2 */
+ case 3: /* MMC v3.1 - v3.3 */
+ case 4: /* MMC v4 */
+ card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
+ card->cid.oemid = UNSTUFF_BITS(resp, 104, 16);
+ card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
+ card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
+ card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
+ card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
+ card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
+ card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8);
+ card->cid.serial = UNSTUFF_BITS(resp, 16, 32);
+ card->cid.month = UNSTUFF_BITS(resp, 12, 4);
+ card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997;
+ break;
+
+ default:
+ printk(KERN_ERR "%s: card has unknown MMCA version %d\n",
+ mmc_hostname(card->host), card->csd.mmca_vsn);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Given a 128-bit response, decode to our card CSD structure.
+ */
+static int mmc_decode_csd(struct mmc_card *card)
+{
+ struct mmc_csd *csd = &card->csd;
+ unsigned int e, m;
+ u32 *resp = card->raw_csd;
+
+ /*
+ * We only understand CSD structure v1.1 and v1.2.
+ * v1.2 has extra information in bits 15, 11 and 10.
+ * We also support eMMC v4.4 & v4.41.
+ */
+#if 0
+ csd_struct = UNSTUFF_BITS(resp, 126, 2);
+ if (csd_struct != 1 && csd_struct != 2) {
+ printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
+ mmc_hostname(card->host), csd_struct);
+ return -EINVAL;
+ }
+#else
+ csd->structure = UNSTUFF_BITS(resp, 126, 2);
+ if (csd->structure == 0) {
+ printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
+ mmc_hostname(card->host), csd->structure);
+ return -EINVAL;
+ }
+#endif
+ csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4);
+ m = UNSTUFF_BITS(resp, 115, 4);
+ e = UNSTUFF_BITS(resp, 112, 3);
+ csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
+ csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100;
+
+ m = UNSTUFF_BITS(resp, 99, 4);
+ e = UNSTUFF_BITS(resp, 96, 3);
+ csd->max_dtr = tran_exp[e] * tran_mant[m];
+ csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);
+
+ e = UNSTUFF_BITS(resp, 47, 3);
+ m = UNSTUFF_BITS(resp, 62, 12);
+ csd->capacity = (1 + m) << (e + 2);
+
+ csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
+ csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
+ csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
+ csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
+ csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
+ csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
+ csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
+
+ return 0;
+}
+
+/*
+ * Read and decode extended CSD.
+ */
+static int mmc_read_ext_csd(struct mmc_card *card)
+{
+ int err;
+ u8 *ext_csd;
+ unsigned int ext_csd_struct;
+
+ BUG_ON(!card);
+
+ if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
+ return 0;
+
+ /*
+ * As the ext_csd is so large and mostly unused, we don't store the
+ * raw block in mmc_card.
+ */
+ ext_csd = kmalloc(512, GFP_KERNEL);
+ if (!ext_csd) {
+ printk(KERN_ERR "%s: could not allocate a buffer to "
+ "receive the ext_csd.\n", mmc_hostname(card->host));
+ return -ENOMEM;
+ }
+
+ err = mmc_send_ext_csd(card, ext_csd);
+ if (err) {
+ /*
+ * We all hosts that cannot perform the command
+ * to fail more gracefully
+ */
+ if (err != -EINVAL)
+ goto out;
+
+ /*
+ * High capacity cards should have this "magic" size
+ * stored in their CSD.
+ */
+ if (card->csd.capacity == (4096 * 512)) {
+ printk(KERN_ERR "%s: unable to read EXT_CSD "
+ "on a possible high capacity card. "
+ "Card will be ignored.\n",
+ mmc_hostname(card->host));
+ } else {
+ printk(KERN_WARNING "%s: unable to read "
+ "EXT_CSD, performance might "
+ "suffer.\n",
+ mmc_hostname(card->host));
+ err = 0;
+ }
+
+ goto out;
+ }
+
+#if 0
+ ext_csd_struct = ext_csd[EXT_CSD_REV];
+ if (ext_csd_struct > 2) {
+ printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
+ "version %d\n", mmc_hostname(card->host),
+ ext_csd_struct);
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (ext_csd_struct >= 2) {
+ card->ext_csd.sectors =
+ ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
+ ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
+ ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
+ ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
+ if (card->ext_csd.sectors)
+ mmc_card_set_blockaddr(card);
+ }
+
+
+ switch (ext_csd[EXT_CSD_CARD_TYPE]) {
+ case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+ card->ext_csd.hs_max_dtr = 52000000;
+ break;
+ case EXT_CSD_CARD_TYPE_26:
+ card->ext_csd.hs_max_dtr = 26000000;
+ break;
+ default:
+ /* MMC v4 spec says this cannot happen */
+ printk(KERN_WARNING "%s: card is mmc v4 but doesn't "
+ "support any high-speed modes.\n",
+ mmc_hostname(card->host));
+ goto out;
+ }
+#else
+ /* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
+ if (card->csd.structure == 3) {
+ int ext_csd_struct = ext_csd[EXT_CSD_STRUCTURE];
+ if (ext_csd_struct > 2) {
+ printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
+ "version %d\n", mmc_hostname(card->host),
+ ext_csd_struct);
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ card->ext_csd.rev = ext_csd[EXT_CSD_REV];
+ if (card->ext_csd.rev > 6) {
+ printk(KERN_ERR "%s: unrecognised EXT_CSD revision %d\n",
+ mmc_hostname(card->host), card->ext_csd.rev);
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (card->ext_csd.rev >= 2) {
+ card->ext_csd.sectors =
+ ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
+ ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
+ ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
+ ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
+
+ /* Cards with density > 2GiB are sector addressed */
+ if (card->ext_csd.sectors > (2u * 1024 * 1024 * 1024) / 512)
+ mmc_card_set_blockaddr(card);
+ }
+
+ switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
+ case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+ card->ext_csd.hs_max_dtr = 52000000;
+ break;
+ case EXT_CSD_CARD_TYPE_26:
+ card->ext_csd.hs_max_dtr = 26000000;
+ break;
+ default:
+ /* MMC v4 spec says this cannot happen */
+ printk(KERN_WARNING "%s: card is mmc v4 but doesn't "
+ "support any high-speed modes.\n",
+ mmc_hostname(card->host));
+ }
+
+ if (card->ext_csd.rev >= 3) {
+ u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT];
+
+ /* Sleep / awake timeout in 100ns units */
+ if (sa_shift > 0 && sa_shift <= 0x17)
+ card->ext_csd.sa_timeout =
+ 1 << ext_csd[EXT_CSD_S_A_TIMEOUT];
+ card->ext_csd.erase_group_def =
+ ext_csd[EXT_CSD_ERASE_GROUP_DEF];
+ card->ext_csd.hc_erase_timeout = 300 *
+ ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT];
+ card->ext_csd.hc_erase_size =
+ ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] << 10;
+ }
+
+ if (card->ext_csd.rev >= 4) {
+ card->ext_csd.sec_trim_mult =
+ ext_csd[EXT_CSD_SEC_TRIM_MULT];
+ card->ext_csd.sec_erase_mult =
+ ext_csd[EXT_CSD_SEC_ERASE_MULT];
+ card->ext_csd.sec_feature_support =
+ ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT];
+ card->ext_csd.trim_timeout = 300 *
+ ext_csd[EXT_CSD_TRIM_MULT];
+ }
+
+ if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
+ card->erased_byte = 0xFF;
+ else
+ card->erased_byte = 0x0;
+#endif
+
+out:
+ kfree(ext_csd);
+
+ return err;
+}
+
+MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
+ card->raw_cid[2], card->raw_cid[3]);
+MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
+ card->raw_csd[2], card->raw_csd[3]);
+MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
+MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
+MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
+MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
+MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
+MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
+MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
+
+static struct attribute *mmc_std_attrs[] = {
+ &dev_attr_cid.attr,
+ &dev_attr_csd.attr,
+ &dev_attr_date.attr,
+ &dev_attr_fwrev.attr,
+ &dev_attr_hwrev.attr,
+ &dev_attr_manfid.attr,
+ &dev_attr_name.attr,
+ &dev_attr_oemid.attr,
+ &dev_attr_serial.attr,
+ NULL,
+};
+
+static struct attribute_group mmc_std_attr_group = {
+ .attrs = mmc_std_attrs,
+};
+
+static struct attribute_group *mmc_attr_groups[] = {
+ &mmc_std_attr_group,
+ NULL,
+};
+
+static struct device_type mmc_type = {
+ .groups = mmc_attr_groups,
+};
+
+/*
+ * Handle the detection and initialisation of a card.
+ *
+ * In the case of a resume, "oldcard" will contain the card
+ * we're trying to reinitialise.
+ */
+static int mmc_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *oldcard)
+{
+ struct mmc_card *card;
+ int err;
+ u32 cid[4];
+ unsigned int max_dtr;
+
+ BUG_ON(!host);
+ WARN_ON(!host->claimed);
+
+ /*
+ * Since we're changing the OCR value, we seem to
+ * need to tell some cards to go back to the idle
+ * state. We wait 1ms to give cards time to
+ * respond.
+ */
+ mmc_go_idle(host);
+
+ /* The extra bit indicates that we support high capacity */
+ err = mmc_send_op_cond(host, ocr | (1 << 30), NULL);
+ if (err)
+ goto err;
+
+ /*
+ * For SPI, enable CRC as appropriate.
+ */
+ if (mmc_host_is_spi(host)) {
+ err = mmc_spi_set_crc(host, use_spi_crc);
+ if (err)
+ goto err;
+ }
+
+ /*
+ * Fetch CID from card.
+ */
+ if (mmc_host_is_spi(host))
+ err = mmc_send_cid(host, cid);
+ else
+ err = mmc_all_send_cid(host, cid);
+ if (err)
+ goto err;
+
+ if (oldcard) {
+ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
+ err = -ENOENT;
+ goto err;
+ }
+
+ card = oldcard;
+ } else {
+ /*
+ * Allocate card structure.
+ */
+ card = mmc_alloc_card(host, &mmc_type);
+ if (IS_ERR(card)) {
+ err = PTR_ERR(card);
+ goto err;
+ }
+
+ card->type = MMC_TYPE_MMC;
+ card->rca = 1;
+ memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ }
+
+ /*
+ * For native busses: set card RCA and quit open drain mode.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_set_relative_addr(card);
+ if (err)
+ goto free_card;
+
+ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+ }
+
+ if (!oldcard) {
+ /*
+ * Fetch CSD from card.
+ */
+ err = mmc_send_csd(card, card->raw_csd);
+ if (err)
+ goto free_card;
+
+ err = mmc_decode_csd(card);
+ if (err)
+ goto free_card;
+ err = mmc_decode_cid(card);
+ if (err)
+ goto free_card;
+ }
+
+ /*
+ * Select card, as all following commands rely on that.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_select_card(card);
+ if (err)
+ goto free_card;
+ }
+
+ if (!oldcard) {
+ /*
+ * Fetch and process extended CSD.
+ */
+ err = mmc_read_ext_csd(card);
+ if (err)
+ goto free_card;
+ }
+
+ /*
+ * Activate high speed (if supported)
+ */
+ if ((card->ext_csd.hs_max_dtr != 0) &&
+ (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, 1);
+ if (err)
+ goto free_card;
+
+ mmc_card_set_highspeed(card);
+
+ mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
+ }
+
+ /*
+ * Compute bus speed.
+ */
+ max_dtr = (unsigned int)-1;
+
+ if (mmc_card_highspeed(card)) {
+ if (max_dtr > card->ext_csd.hs_max_dtr)
+ max_dtr = card->ext_csd.hs_max_dtr;
+ } else if (max_dtr > card->csd.max_dtr) {
+ max_dtr = card->csd.max_dtr;
+ }
+
+ mmc_set_clock(host, max_dtr);
+
+ /*
+ * Activate wide bus (if supported).
+ */
+ if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
+ (host->caps & MMC_CAP_4_BIT_DATA)) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
+ if (err)
+ goto free_card;
+
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+ }
+
+ if (!oldcard)
+ host->card = card;
+
+ return 0;
+
+free_card:
+ if (!oldcard)
+ mmc_remove_card(card);
+err:
+
+ return err;
+}
+
+/*
+ * Host is being removed. Free up the current card.
+ */
+static void mmc_remove(struct mmc_host *host)
+{
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_remove_card(host->card);
+ host->card = NULL;
+}
+
+/*
+ * Card detection callback from host.
+ */
+static void mmc_detect(struct mmc_host *host)
+{
+ int err;
+
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+
+ /*
+ * Just check if our card has been removed.
+ */
+ err = mmc_send_status(host->card, NULL);
+
+ mmc_release_host(host);
+
+ if (err) {
+ mmc_remove(host);
+
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+ }
+}
+
+#ifdef CONFIG_MMC_UNSAFE_RESUME
+
+/*
+ * Suspend callback from host.
+ */
+static void mmc_suspend(struct mmc_host *host)
+{
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+ if (!mmc_host_is_spi(host))
+ mmc_deselect_cards(host);
+ host->card->state &= ~MMC_STATE_HIGHSPEED;
+ mmc_release_host(host);
+}
+
+/*
+ * Resume callback from host.
+ *
+ * This function tries to determine if the same card is still present
+ * and, if so, restore all state to it.
+ */
+static void mmc_resume(struct mmc_host *host)
+{
+ int err;
+
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+ err = mmc_init_card(host, host->ocr, host->card);
+ mmc_release_host(host);
+
+ if (err) {
+ mmc_remove(host);
+
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+ }
+
+}
+
+#else
+
+#define mmc_suspend NULL
+#define mmc_resume NULL
+
+#endif
+
+static const struct mmc_bus_ops mmc_ops = {
+ .remove = mmc_remove,
+ .detect = mmc_detect,
+ .suspend = mmc_suspend,
+ .resume = mmc_resume,
+};
+
+/*
+ * Starting point for MMC card init.
+ */
+int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
+{
+ int err;
+
+ BUG_ON(!host);
+ WARN_ON(!host->claimed);
+
+ mmc_attach_bus(host, &mmc_ops);
+
+ /*
+ * We need to get OCR a different way for SPI.
+ */
+ if (mmc_host_is_spi(host)) {
+ err = mmc_spi_read_ocr(host, 1, &ocr);
+ if (err)
+ goto err;
+ }
+
+ /*
+ * Sanity check the voltages that the card claims to
+ * support.
+ */
+ if (ocr & 0x7F) {
+ printk(KERN_WARNING "%s: card claims to support voltages "
+ "below the defined range. These will be ignored.\n",
+ mmc_hostname(host));
+ ocr &= ~0x7F;
+ }
+
+ host->ocr = mmc_select_voltage(host, ocr);
+
+ /*
+ * Can we support the voltage of the card?
+ */
+ if (!host->ocr) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ /*
+ * Detect and init the card.
+ */
+ err = mmc_init_card(host, host->ocr, NULL);
+ if (err)
+ goto err;
+
+ mmc_release_host(host);
+
+ err = mmc_add_card(host->card);
+ if (err)
+ goto remove_card;
+
+ return 0;
+
+remove_card:
+ mmc_remove_card(host->card);
+ host->card = NULL;
+ mmc_claim_host(host);
+err:
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+
+ printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
+ mmc_hostname(host), err);
+
+ return err;
+}
+
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
new file mode 100644
index 0000000..34ce270
--- /dev/null
+++ b/drivers/mmc/core/mmc_ops.c
@@ -0,0 +1,404 @@
+/*
+ * linux/drivers/mmc/core/mmc_ops.h
+ *
+ * Copyright 2006-2007 Pierre Ossman
+ *
+ * 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/types.h>
+#include <linux/scatterlist.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+
+#include "core.h"
+#include "mmc_ops.h"
+
+static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
+{
+ int err;
+ struct mmc_command cmd;
+
+ BUG_ON(!host);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SELECT_CARD;
+
+ if (card) {
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ } else {
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
+ }
+
+ err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+int mmc_select_card(struct mmc_card *card)
+{
+ BUG_ON(!card);
+
+ return _mmc_select_card(card->host, card);
+}
+
+int mmc_deselect_cards(struct mmc_host *host)
+{
+ return _mmc_select_card(host, NULL);
+}
+
+int mmc_go_idle(struct mmc_host *host)
+{
+ int err;
+ struct mmc_command cmd;
+
+ /*
+ * Non-SPI hosts need to prevent chipselect going active during
+ * GO_IDLE; that would put chips into SPI mode. Remind them of
+ * that in case of hardware that won't pull up DAT3/nCS otherwise.
+ *
+ * SPI hosts ignore ios.chip_select; it's managed according to
+ * rules that must accomodate non-MMC slaves which this layer
+ * won't even know about.
+ */
+ if (!mmc_host_is_spi(host)) {
+ mmc_set_chip_select(host, MMC_CS_HIGH);
+ mmc_delay(1);
+ }
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_GO_IDLE_STATE;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+
+ mmc_delay(1);
+
+ if (!mmc_host_is_spi(host)) {
+ mmc_set_chip_select(host, MMC_CS_DONTCARE);
+ mmc_delay(1);
+ }
+
+ host->use_spi_crc = 0;
+
+ return err;
+}
+
+int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
+{
+ struct mmc_command cmd;
+ int i, err = 0;
+
+ BUG_ON(!host);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SEND_OP_COND;
+ cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
+
+ for (i = 100; i; i--) {
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err)
+ break;
+
+ /* if we're just probing, do a single pass */
+ if (ocr == 0)
+ break;
+
+ /* otherwise wait until reset completes */
+ if (mmc_host_is_spi(host)) {
+ if (!(cmd.resp[0] & R1_SPI_IDLE))
+ break;
+ } else {
+ if (cmd.resp[0] & MMC_CARD_BUSY)
+ break;
+ }
+
+ err = -ETIMEDOUT;
+
+ mmc_delay(10);
+ }
+
+ if (rocr && !mmc_host_is_spi(host))
+ *rocr = cmd.resp[0];
+
+ return err;
+}
+
+int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
+{
+ int err;
+ struct mmc_command cmd;
+
+ BUG_ON(!host);
+ BUG_ON(!cid);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_ALL_SEND_CID;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
+
+ err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ memcpy(cid, cmd.resp, sizeof(u32) * 4);
+
+ return 0;
+}
+
+int mmc_set_relative_addr(struct mmc_card *card)
+{
+ int err;
+ struct mmc_command cmd;
+
+ BUG_ON(!card);
+ BUG_ON(!card->host);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SET_RELATIVE_ADDR;
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int
+mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
+{
+ int err;
+ struct mmc_command cmd;
+
+ BUG_ON(!host);
+ BUG_ON(!cxd);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = opcode;
+ cmd.arg = arg;
+ cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ memcpy(cxd, cmd.resp, sizeof(u32) * 4);
+
+ return 0;
+}
+
+static int
+mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
+ u32 opcode, void *buf, unsigned len)
+{
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_data data;
+ struct scatterlist sg;
+ void *data_buf;
+
+ /* dma onto stack is unsafe/nonportable, but callers to this
+ * routine normally provide temporary on-stack buffers ...
+ */
+ data_buf = kmalloc(len, GFP_KERNEL);
+ if (data_buf == NULL)
+ return -ENOMEM;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&data, 0, sizeof(struct mmc_data));
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ cmd.opcode = opcode;
+ cmd.arg = 0;
+
+ /* NOTE HACK: the MMC_RSP_SPI_R1 is always correct here, but we
+ * rely on callers to never use this with "native" calls for reading
+ * CSD or CID. Native versions of those commands use the R2 type,
+ * not R1 plus a data block.
+ */
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ data.blksz = len;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.sg = &sg;
+ data.sg_len = 1;
+
+ sg_init_one(&sg, data_buf, len);
+
+ if (opcode == MMC_SEND_CSD || opcode == MMC_SEND_CID) {
+ /*
+ * The spec states that CSR and CID accesses have a timeout
+ * of 64 clock cycles.
+ */
+ data.timeout_ns = 0;
+ data.timeout_clks = 64;
+ } else
+ mmc_set_data_timeout(&data, card);
+
+ mmc_wait_for_req(host, &mrq);
+
+ memcpy(buf, data_buf, len);
+ kfree(data_buf);
+
+ if (cmd.error)
+ return cmd.error;
+ if (data.error)
+ return data.error;
+
+ return 0;
+}
+
+int mmc_send_csd(struct mmc_card *card, u32 *csd)
+{
+ int ret, i;
+
+ if (!mmc_host_is_spi(card->host))
+ return mmc_send_cxd_native(card->host, card->rca << 16,
+ csd, MMC_SEND_CSD);
+
+ ret = mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd, 16);
+ if (ret)
+ return ret;
+
+ for (i = 0;i < 4;i++)
+ csd[i] = be32_to_cpu(csd[i]);
+
+ return 0;
+}
+
+int mmc_send_cid(struct mmc_host *host, u32 *cid)
+{
+ int ret, i;
+
+ if (!mmc_host_is_spi(host)) {
+ if (!host->card)
+ return -EINVAL;
+ return mmc_send_cxd_native(host, host->card->rca << 16,
+ cid, MMC_SEND_CID);
+ }
+
+ ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid, 16);
+ if (ret)
+ return ret;
+
+ for (i = 0;i < 4;i++)
+ cid[i] = be32_to_cpu(cid[i]);
+
+ return 0;
+}
+
+int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
+{
+ return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
+ ext_csd, 512);
+}
+
+int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
+{
+ struct mmc_command cmd;
+ int err;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SPI_READ_OCR;
+ cmd.arg = highcap ? (1 << 30) : 0;
+ cmd.flags = MMC_RSP_SPI_R3;
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+
+ *ocrp = cmd.resp[1];
+ return err;
+}
+
+int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
+{
+ struct mmc_command cmd;
+ int err;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SPI_CRC_ON_OFF;
+ cmd.flags = MMC_RSP_SPI_R1;
+ cmd.arg = use_crc;
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (!err)
+ host->use_spi_crc = use_crc;
+ return err;
+}
+
+int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
+{
+ int err;
+ struct mmc_command cmd;
+
+ BUG_ON(!card);
+ BUG_ON(!card->host);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SWITCH;
+ cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+ (index << 16) |
+ (value << 8) |
+ set;
+ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+int mmc_send_status(struct mmc_card *card, u32 *status)
+{
+ int err;
+ struct mmc_command cmd;
+
+ BUG_ON(!card);
+ BUG_ON(!card->host);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SEND_STATUS;
+ if (!mmc_host_is_spi(card->host))
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ /* NOTE: callers are required to understand the difference
+ * between "native" and SPI format status words!
+ */
+ if (status)
+ *status = cmd.resp[0];
+
+ return 0;
+}
+
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
new file mode 100644
index 0000000..17854bf
--- /dev/null
+++ b/drivers/mmc/core/mmc_ops.h
@@ -0,0 +1,30 @@
+/*
+ * linux/drivers/mmc/core/mmc_ops.h
+ *
+ * Copyright 2006-2007 Pierre Ossman
+ *
+ * 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.
+ */
+
+#ifndef _MMC_MMC_OPS_H
+#define _MMC_MMC_OPS_H
+
+int mmc_select_card(struct mmc_card *card);
+int mmc_deselect_cards(struct mmc_host *host);
+int mmc_go_idle(struct mmc_host *host);
+int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
+int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
+int mmc_set_relative_addr(struct mmc_card *card);
+int mmc_send_csd(struct mmc_card *card, u32 *csd);
+int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
+int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
+int mmc_send_status(struct mmc_card *card, u32 *status);
+int mmc_send_cid(struct mmc_host *host, u32 *cid);
+int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
+int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
+
+#endif
+
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
new file mode 100644
index 0000000..26fc098
--- /dev/null
+++ b/drivers/mmc/core/sd.c
@@ -0,0 +1,698 @@
+/*
+ * linux/drivers/mmc/core/sd.c
+ *
+ * Copyright (C) 2003-2004 Russell King, All Rights Reserved.
+ * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
+ * Copyright (C) 2005-2007 Pierre Ossman, 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.
+ */
+
+#include <linux/err.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+
+#include "core.h"
+#include "bus.h"
+#include "mmc_ops.h"
+#include "sd_ops.h"
+
+static const unsigned int tran_exp[] = {
+ 10000, 100000, 1000000, 10000000,
+ 0, 0, 0, 0
+};
+
+static const unsigned char tran_mant[] = {
+ 0, 10, 12, 13, 15, 20, 25, 30,
+ 35, 40, 45, 50, 55, 60, 70, 80,
+};
+
+static const unsigned int tacc_exp[] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
+};
+
+static const unsigned int tacc_mant[] = {
+ 0, 10, 12, 13, 15, 20, 25, 30,
+ 35, 40, 45, 50, 55, 60, 70, 80,
+};
+
+#define UNSTUFF_BITS(resp,start,size) \
+ ({ \
+ const int __size = size; \
+ const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \
+ const int __off = 3 - ((start) / 32); \
+ const int __shft = (start) & 31; \
+ u32 __res; \
+ \
+ __res = resp[__off] >> __shft; \
+ if (__size + __shft > 32) \
+ __res |= resp[__off-1] << ((32 - __shft) % 32); \
+ __res & __mask; \
+ })
+
+/*
+ * Given the decoded CSD structure, decode the raw CID to our CID structure.
+ */
+static void mmc_decode_cid(struct mmc_card *card)
+{
+ u32 *resp = card->raw_cid;
+
+ memset(&card->cid, 0, sizeof(struct mmc_cid));
+
+ /*
+ * SD doesn't currently have a version field so we will
+ * have to assume we can parse this.
+ */
+ card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
+ card->cid.oemid = UNSTUFF_BITS(resp, 104, 16);
+ card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
+ card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
+ card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
+ card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
+ card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
+ card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4);
+ card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4);
+ card->cid.serial = UNSTUFF_BITS(resp, 24, 32);
+ card->cid.year = UNSTUFF_BITS(resp, 12, 8);
+ card->cid.month = UNSTUFF_BITS(resp, 8, 4);
+
+ card->cid.year += 2000; /* SD cards year offset */
+}
+
+/*
+ * Given a 128-bit response, decode to our card CSD structure.
+ */
+static int mmc_decode_csd(struct mmc_card *card)
+{
+ struct mmc_csd *csd = &card->csd;
+ unsigned int e, m, csd_struct;
+ u32 *resp = card->raw_csd;
+
+ csd_struct = UNSTUFF_BITS(resp, 126, 2);
+
+ switch (csd_struct) {
+ case 0:
+ m = UNSTUFF_BITS(resp, 115, 4);
+ e = UNSTUFF_BITS(resp, 112, 3);
+ csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
+ csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100;
+
+ m = UNSTUFF_BITS(resp, 99, 4);
+ e = UNSTUFF_BITS(resp, 96, 3);
+ csd->max_dtr = tran_exp[e] * tran_mant[m];
+ csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);
+
+ e = UNSTUFF_BITS(resp, 47, 3);
+ m = UNSTUFF_BITS(resp, 62, 12);
+ csd->capacity = (1 + m) << (e + 2);
+
+ csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
+ csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
+ csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
+ csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
+ csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
+ csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
+ csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
+ break;
+ case 1:
+ /*
+ * This is a block-addressed SDHC card. Most
+ * interesting fields are unused and have fixed
+ * values. To avoid getting tripped by buggy cards,
+ * we assume those fixed values ourselves.
+ */
+ mmc_card_set_blockaddr(card);
+
+ csd->tacc_ns = 0; /* Unused */
+ csd->tacc_clks = 0; /* Unused */
+
+ m = UNSTUFF_BITS(resp, 99, 4);
+ e = UNSTUFF_BITS(resp, 96, 3);
+ csd->max_dtr = tran_exp[e] * tran_mant[m];
+ csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);
+
+ m = UNSTUFF_BITS(resp, 48, 22);
+ csd->capacity = (1 + m) << 10;
+
+ csd->read_blkbits = 9;
+ csd->read_partial = 0;
+ csd->write_misalign = 0;
+ csd->read_misalign = 0;
+ csd->r2w_factor = 4; /* Unused */
+ csd->write_blkbits = 9;
+ csd->write_partial = 0;
+ break;
+ default:
+ printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
+ mmc_hostname(card->host), csd_struct);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Given a 64-bit response, decode to our card SCR structure.
+ */
+static int mmc_decode_scr(struct mmc_card *card)
+{
+ struct sd_scr *scr = &card->scr;
+ unsigned int scr_struct;
+ u32 resp[4];
+
+ resp[3] = card->raw_scr[1];
+ resp[2] = card->raw_scr[0];
+
+ scr_struct = UNSTUFF_BITS(resp, 60, 4);
+ if (scr_struct != 0) {
+ printk(KERN_ERR "%s: unrecognised SCR structure version %d\n",
+ mmc_hostname(card->host), scr_struct);
+ return -EINVAL;
+ }
+
+ scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4);
+ scr->bus_widths = UNSTUFF_BITS(resp, 48, 4);
+
+ return 0;
+}
+
+/*
+ * Fetches and decodes switch information
+ */
+static int mmc_read_switch(struct mmc_card *card)
+{
+ int err;
+ u8 *status;
+
+ if (card->scr.sda_vsn < SCR_SPEC_VER_1)
+ return 0;
+
+ if (!(card->csd.cmdclass & CCC_SWITCH)) {
+ printk(KERN_WARNING "%s: card lacks mandatory switch "
+ "function, performance might suffer.\n",
+ mmc_hostname(card->host));
+ return 0;
+ }
+
+ err = -EIO;
+
+ status = kmalloc(64, GFP_KERNEL);
+ if (!status) {
+ printk(KERN_ERR "%s: could not allocate a buffer for "
+ "switch capabilities.\n", mmc_hostname(card->host));
+ return -ENOMEM;
+ }
+
+ err = mmc_sd_switch(card, 0, 0, 1, status);
+ if (err) {
+ /*
+ * We all hosts that cannot perform the command
+ * to fail more gracefully
+ */
+ if (err != -EINVAL)
+ goto out;
+
+ printk(KERN_WARNING "%s: problem reading switch "
+ "capabilities, performance might suffer.\n",
+ mmc_hostname(card->host));
+ err = 0;
+
+ goto out;
+ }
+
+ if (status[13] & 0x02)
+ card->sw_caps.hs_max_dtr = 50000000;
+
+out:
+ kfree(status);
+
+ return err;
+}
+
+/*
+ * Test if the card supports high-speed mode and, if so, switch to it.
+ */
+static int mmc_switch_hs(struct mmc_card *card)
+{
+ int err;
+ u8 *status;
+
+ if (card->scr.sda_vsn < SCR_SPEC_VER_1)
+ return 0;
+
+ if (!(card->csd.cmdclass & CCC_SWITCH))
+ return 0;
+
+ if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
+ return 0;
+
+ if (card->sw_caps.hs_max_dtr == 0)
+ return 0;
+
+ err = -EIO;
+
+ status = kmalloc(64, GFP_KERNEL);
+ if (!status) {
+ printk(KERN_ERR "%s: could not allocate a buffer for "
+ "switch capabilities.\n", mmc_hostname(card->host));
+ return -ENOMEM;
+ }
+
+ err = mmc_sd_switch(card, 1, 0, 1, status);
+ if (err)
+ goto out;
+
+ if ((status[16] & 0xF) != 1) {
+ printk(KERN_WARNING "%s: Problem switching card "
+ "into high-speed mode!\n",
+ mmc_hostname(card->host));
+ } else {
+ mmc_card_set_highspeed(card);
+ mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+ }
+
+out:
+ kfree(status);
+
+ return err;
+}
+
+MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
+ card->raw_cid[2], card->raw_cid[3]);
+MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
+ card->raw_csd[2], card->raw_csd[3]);
+MMC_DEV_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]);
+MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
+MMC_DEV_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
+MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
+MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
+MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
+MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
+MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
+
+
+static struct attribute *sd_std_attrs[] = {
+ &dev_attr_cid.attr,
+ &dev_attr_csd.attr,
+ &dev_attr_scr.attr,
+ &dev_attr_date.attr,
+ &dev_attr_fwrev.attr,
+ &dev_attr_hwrev.attr,
+ &dev_attr_manfid.attr,
+ &dev_attr_name.attr,
+ &dev_attr_oemid.attr,
+ &dev_attr_serial.attr,
+ NULL,
+};
+
+static struct attribute_group sd_std_attr_group = {
+ .attrs = sd_std_attrs,
+};
+
+static struct attribute_group *sd_attr_groups[] = {
+ &sd_std_attr_group,
+ NULL,
+};
+
+static struct device_type sd_type = {
+ .groups = sd_attr_groups,
+};
+
+/*
+ * Handle the detection and initialisation of a card.
+ *
+ * In the case of a resume, "oldcard" will contain the card
+ * we're trying to reinitialise.
+ */
+static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *oldcard)
+{
+ struct mmc_card *card;
+ int err;
+ u32 cid[4];
+ unsigned int max_dtr;
+
+ BUG_ON(!host);
+ WARN_ON(!host->claimed);
+
+ /*
+ * Since we're changing the OCR value, we seem to
+ * need to tell some cards to go back to the idle
+ * state. We wait 1ms to give cards time to
+ * respond.
+ */
+ mmc_go_idle(host);
+
+ /*
+ * If SD_SEND_IF_COND indicates an SD 2.0
+ * compliant card and we should set bit 30
+ * of the ocr to indicate that we can handle
+ * block-addressed SDHC cards.
+ */
+ err = mmc_send_if_cond(host, ocr);
+ if (!err)
+ ocr |= 1 << 30;
+
+ err = mmc_send_app_op_cond(host, ocr, NULL);
+ if (err)
+ goto err;
+
+ /*
+ * For SPI, enable CRC as appropriate.
+ */
+ if (mmc_host_is_spi(host)) {
+ err = mmc_spi_set_crc(host, use_spi_crc);
+ if (err)
+ goto err;
+ }
+
+ /*
+ * Fetch CID from card.
+ */
+ if (mmc_host_is_spi(host))
+ err = mmc_send_cid(host, cid);
+ else
+ err = mmc_all_send_cid(host, cid);
+ if (err)
+ goto err;
+
+ if (oldcard) {
+ if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
+ err = -ENOENT;
+ goto err;
+ }
+
+ card = oldcard;
+ } else {
+ /*
+ * Allocate card structure.
+ */
+ card = mmc_alloc_card(host, &sd_type);
+ if (IS_ERR(card)) {
+ err = PTR_ERR(card);
+ goto err;
+ }
+
+ card->type = MMC_TYPE_SD;
+ memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+ }
+
+ /*
+ * For native busses: get card RCA and quit open drain mode.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_send_relative_addr(host, &card->rca);
+ if (err)
+ goto free_card;
+
+ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+ }
+
+ if (!oldcard) {
+ /*
+ * Fetch CSD from card.
+ */
+ err = mmc_send_csd(card, card->raw_csd);
+ if (err)
+ goto free_card;
+
+ err = mmc_decode_csd(card);
+ if (err)
+ goto free_card;
+
+ mmc_decode_cid(card);
+ }
+
+ /*
+ * Select card, as all following commands rely on that.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_select_card(card);
+ if (err)
+ goto free_card;
+ }
+
+ if (!oldcard) {
+ /*
+ * Fetch SCR from card.
+ */
+ err = mmc_app_send_scr(card, card->raw_scr);
+ if (err)
+ goto free_card;
+
+ err = mmc_decode_scr(card);
+ if (err < 0)
+ goto free_card;
+
+ /*
+ * Fetch switch information from card.
+ */
+ err = mmc_read_switch(card);
+ if (err)
+ goto free_card;
+ }
+
+ /*
+ * Attempt to change to high-speed (if supported)
+ */
+ err = mmc_switch_hs(card);
+ if (err)
+ goto free_card;
+
+ /*
+ * Compute bus speed.
+ */
+ max_dtr = (unsigned int)-1;
+
+ if (mmc_card_highspeed(card)) {
+ if (max_dtr > card->sw_caps.hs_max_dtr)
+ max_dtr = card->sw_caps.hs_max_dtr;
+ } else if (max_dtr > card->csd.max_dtr) {
+ max_dtr = card->csd.max_dtr;
+ }
+
+ mmc_set_clock(host, max_dtr);
+
+ /*
+ * Switch to wider bus (if supported).
+ */
+ if ((host->caps & MMC_CAP_4_BIT_DATA) &&
+ (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
+ err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
+ if (err)
+ goto free_card;
+
+ mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
+ }
+
+ /*
+ * Check if read-only switch is active.
+ */
+ if (!oldcard) {
+ if (!host->ops->get_ro || host->ops->get_ro(host) < 0) {
+ printk(KERN_WARNING "%s: host does not "
+ "support reading read-only "
+ "switch. assuming write-enable.\n",
+ mmc_hostname(host));
+ } else {
+ if (host->ops->get_ro(host) > 0)
+ mmc_card_set_readonly(card);
+ }
+ }
+
+ if (!oldcard)
+ host->card = card;
+
+ return 0;
+
+free_card:
+ if (!oldcard)
+ mmc_remove_card(card);
+err:
+
+ return err;
+}
+
+/*
+ * Host is being removed. Free up the current card.
+ */
+static void mmc_sd_remove(struct mmc_host *host)
+{
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_remove_card(host->card);
+ host->card = NULL;
+}
+
+/*
+ * Card detection callback from host.
+ */
+static void mmc_sd_detect(struct mmc_host *host)
+{
+ int err;
+
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+
+ /*
+ * Just check if our card has been removed.
+ */
+ err = mmc_send_status(host->card, NULL);
+
+ mmc_release_host(host);
+
+ if (err) {
+ mmc_sd_remove(host);
+
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+ }
+}
+
+#ifdef CONFIG_MMC_UNSAFE_RESUME
+
+/*
+ * Suspend callback from host.
+ */
+static void mmc_sd_suspend(struct mmc_host *host)
+{
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+ if (!mmc_host_is_spi(host))
+ mmc_deselect_cards(host);
+ host->card->state &= ~MMC_STATE_HIGHSPEED;
+ mmc_release_host(host);
+}
+
+/*
+ * Resume callback from host.
+ *
+ * This function tries to determine if the same card is still present
+ * and, if so, restore all state to it.
+ */
+static void mmc_sd_resume(struct mmc_host *host)
+{
+ int err;
+
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+ err = mmc_sd_init_card(host, host->ocr, host->card);
+ mmc_release_host(host);
+
+ if (err) {
+ mmc_sd_remove(host);
+
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+ }
+
+}
+
+#else
+
+#define mmc_sd_suspend NULL
+#define mmc_sd_resume NULL
+
+#endif
+
+static const struct mmc_bus_ops mmc_sd_ops = {
+ .remove = mmc_sd_remove,
+ .detect = mmc_sd_detect,
+ .suspend = mmc_sd_suspend,
+ .resume = mmc_sd_resume,
+};
+
+/*
+ * Starting point for SD card init.
+ */
+int mmc_attach_sd(struct mmc_host *host, u32 ocr)
+{
+ int err;
+
+ BUG_ON(!host);
+ WARN_ON(!host->claimed);
+
+ mmc_attach_bus(host, &mmc_sd_ops);
+
+ /*
+ * We need to get OCR a different way for SPI.
+ */
+ if (mmc_host_is_spi(host)) {
+ mmc_go_idle(host);
+
+ err = mmc_spi_read_ocr(host, 0, &ocr);
+ if (err)
+ goto err;
+ }
+
+ /*
+ * Sanity check the voltages that the card claims to
+ * support.
+ */
+ if (ocr & 0x7F) {
+ printk(KERN_WARNING "%s: card claims to support voltages "
+ "below the defined range. These will be ignored.\n",
+ mmc_hostname(host));
+ ocr &= ~0x7F;
+ }
+
+ if (ocr & MMC_VDD_165_195) {
+ printk(KERN_WARNING "%s: SD card claims to support the "
+ "incompletely defined 'low voltage range'. This "
+ "will be ignored.\n", mmc_hostname(host));
+ ocr &= ~MMC_VDD_165_195;
+ }
+
+ host->ocr = mmc_select_voltage(host, ocr);
+
+ /*
+ * Can we support the voltage(s) of the card(s)?
+ */
+ if (!host->ocr) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ /*
+ * Detect and init the card.
+ */
+ err = mmc_sd_init_card(host, host->ocr, NULL);
+ if (err)
+ goto err;
+
+ mmc_release_host(host);
+
+ err = mmc_add_card(host->card);
+ if (err)
+ goto remove_card;
+
+ return 0;
+
+remove_card:
+ mmc_remove_card(host->card);
+ host->card = NULL;
+ mmc_claim_host(host);
+err:
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+
+ printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
+ mmc_hostname(host), err);
+
+ return err;
+}
+
diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c
new file mode 100644
index 0000000..0d96080
--- /dev/null
+++ b/drivers/mmc/core/sd_ops.c
@@ -0,0 +1,350 @@
+/*
+ * linux/drivers/mmc/core/sd_ops.h
+ *
+ * Copyright 2006-2007 Pierre Ossman
+ *
+ * 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/types.h>
+#include <linux/scatterlist.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+
+#include "core.h"
+#include "sd_ops.h"
+
+static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
+{
+ int err;
+ struct mmc_command cmd;
+
+ BUG_ON(!host);
+ BUG_ON(card && (card->host != host));
+
+ cmd.opcode = MMC_APP_CMD;
+
+ if (card) {
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ } else {
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR;
+ }
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err)
+ return err;
+
+ /* Check that card supported application commands */
+ if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+/**
+ * mmc_wait_for_app_cmd - start an application command and wait for
+ completion
+ * @host: MMC host to start command
+ * @card: Card to send MMC_APP_CMD to
+ * @cmd: MMC command to start
+ * @retries: maximum number of retries
+ *
+ * Sends a MMC_APP_CMD, checks the card response, sends the command
+ * in the parameter and waits for it to complete. Return any error
+ * that occurred while the command was executing. Do not attempt to
+ * parse the response.
+ */
+int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
+ struct mmc_command *cmd, int retries)
+{
+ struct mmc_request mrq;
+
+ int i, err;
+
+ BUG_ON(!cmd);
+ BUG_ON(retries < 0);
+
+ err = -EIO;
+
+ /*
+ * We have to resend MMC_APP_CMD for each attempt so
+ * we cannot use the retries field in mmc_command.
+ */
+ for (i = 0;i <= retries;i++) {
+ memset(&mrq, 0, sizeof(struct mmc_request));
+
+ err = mmc_app_cmd(host, card);
+ if (err) {
+ /* no point in retrying; no APP commands allowed */
+ if (mmc_host_is_spi(host)) {
+ if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
+ break;
+ }
+ continue;
+ }
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+
+ memset(cmd->resp, 0, sizeof(cmd->resp));
+ cmd->retries = 0;
+
+ mrq.cmd = cmd;
+ cmd->data = NULL;
+
+ mmc_wait_for_req(host, &mrq);
+
+ err = cmd->error;
+ if (!cmd->error)
+ break;
+
+ /* no point in retrying illegal APP commands */
+ if (mmc_host_is_spi(host)) {
+ if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
+ break;
+ }
+ }
+
+ return err;
+}
+
+EXPORT_SYMBOL(mmc_wait_for_app_cmd);
+
+int mmc_app_set_bus_width(struct mmc_card *card, int width)
+{
+ int err;
+ struct mmc_command cmd;
+
+ BUG_ON(!card);
+ BUG_ON(!card->host);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = SD_APP_SET_BUS_WIDTH;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ switch (width) {
+ case MMC_BUS_WIDTH_1:
+ cmd.arg = SD_BUS_WIDTH_1;
+ break;
+ case MMC_BUS_WIDTH_4:
+ cmd.arg = SD_BUS_WIDTH_4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
+{
+ struct mmc_command cmd;
+ int i, err = 0;
+
+ BUG_ON(!host);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = SD_APP_OP_COND;
+ if (mmc_host_is_spi(host))
+ cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
+ else
+ cmd.arg = ocr;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
+
+ for (i = 100; i; i--) {
+ err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
+ if (err)
+ break;
+
+ /* if we're just probing, do a single pass */
+ if (ocr == 0)
+ break;
+
+ /* otherwise wait until reset completes */
+ if (mmc_host_is_spi(host)) {
+ if (!(cmd.resp[0] & R1_SPI_IDLE))
+ break;
+ } else {
+ if (cmd.resp[0] & MMC_CARD_BUSY)
+ break;
+ }
+
+ err = -ETIMEDOUT;
+
+ mmc_delay(10);
+ }
+
+ if (rocr && !mmc_host_is_spi(host))
+ *rocr = cmd.resp[0];
+
+ return err;
+}
+
+int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
+{
+ struct mmc_command cmd;
+ int err;
+ static const u8 test_pattern = 0xAA;
+ u8 result_pattern;
+
+ /*
+ * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
+ * before SD_APP_OP_COND. This command will harmlessly fail for
+ * SD 1.0 cards.
+ */
+ cmd.opcode = SD_SEND_IF_COND;
+ cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
+ cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;
+
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err)
+ return err;
+
+ if (mmc_host_is_spi(host))
+ result_pattern = cmd.resp[1] & 0xFF;
+ else
+ result_pattern = cmd.resp[0] & 0xFF;
+
+ if (result_pattern != test_pattern)
+ return -EIO;
+
+ return 0;
+}
+
+int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
+{
+ int err;
+ struct mmc_command cmd;
+
+ BUG_ON(!host);
+ BUG_ON(!rca);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = SD_SEND_RELATIVE_ADDR;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
+
+ err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+ if (err)
+ return err;
+
+ *rca = cmd.resp[0] >> 16;
+
+ return 0;
+}
+
+int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
+{
+ int err;
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_data data;
+ struct scatterlist sg;
+
+ BUG_ON(!card);
+ BUG_ON(!card->host);
+ BUG_ON(!scr);
+
+ /* NOTE: caller guarantees scr is heap-allocated */
+
+ err = mmc_app_cmd(card->host, card);
+ if (err)
+ return err;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&data, 0, sizeof(struct mmc_data));
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ cmd.opcode = SD_APP_SEND_SCR;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ data.blksz = 8;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.sg = &sg;
+ data.sg_len = 1;
+
+ sg_init_one(&sg, scr, 8);
+
+ mmc_set_data_timeout(&data, card);
+
+ mmc_wait_for_req(card->host, &mrq);
+
+ if (cmd.error)
+ return cmd.error;
+ if (data.error)
+ return data.error;
+
+ scr[0] = be32_to_cpu(scr[0]);
+ scr[1] = be32_to_cpu(scr[1]);
+
+ return 0;
+}
+
+int mmc_sd_switch(struct mmc_card *card, int mode, int group,
+ u8 value, u8 *resp)
+{
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_data data;
+ struct scatterlist sg;
+
+ BUG_ON(!card);
+ BUG_ON(!card->host);
+
+ /* NOTE: caller guarantees resp is heap-allocated */
+
+ mode = !!mode;
+ value &= 0xF;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&data, 0, sizeof(struct mmc_data));
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ cmd.opcode = SD_SWITCH;
+ cmd.arg = mode << 31 | 0x00FFFFFF;
+ cmd.arg &= ~(0xF << (group * 4));
+ cmd.arg |= value << (group * 4);
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ data.blksz = 64;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.sg = &sg;
+ data.sg_len = 1;
+
+ sg_init_one(&sg, resp, 64);
+
+ mmc_set_data_timeout(&data, card);
+
+ mmc_wait_for_req(card->host, &mrq);
+
+ if (cmd.error)
+ return cmd.error;
+ if (data.error)
+ return data.error;
+
+ return 0;
+}
+
diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h
new file mode 100644
index 0000000..9742d8a
--- /dev/null
+++ b/drivers/mmc/core/sd_ops.h
@@ -0,0 +1,24 @@
+/*
+ * linux/drivers/mmc/core/sd_ops.h
+ *
+ * Copyright 2006-2007 Pierre Ossman
+ *
+ * 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.
+ */
+
+#ifndef _MMC_SD_OPS_H
+#define _MMC_SD_OPS_H
+
+int mmc_app_set_bus_width(struct mmc_card *card, int width);
+int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
+int mmc_send_if_cond(struct mmc_host *host, u32 ocr);
+int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca);
+int mmc_app_send_scr(struct mmc_card *card, u32 *scr);
+int mmc_sd_switch(struct mmc_card *card, int mode, int group,
+ u8 value, u8 *resp);
+
+#endif
+
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
new file mode 100644
index 0000000..fb99ccf
--- /dev/null
+++ b/drivers/mmc/core/sdio.c
@@ -0,0 +1,441 @@
+/*
+ * linux/drivers/mmc/sdio.c
+ *
+ * Copyright 2006-2007 Pierre Ossman
+ *
+ * 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/err.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "core.h"
+#include "bus.h"
+#include "sdio_bus.h"
+#include "mmc_ops.h"
+#include "sd_ops.h"
+#include "sdio_ops.h"
+#include "sdio_cis.h"
+
+static int sdio_read_fbr(struct sdio_func *func)
+{
+ int ret;
+ unsigned char data;
+
+ ret = mmc_io_rw_direct(func->card, 0, 0,
+ SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data);
+ if (ret)
+ goto out;
+
+ data &= 0x0f;
+
+ if (data == 0x0f) {
+ ret = mmc_io_rw_direct(func->card, 0, 0,
+ SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF_EXT, 0, &data);
+ if (ret)
+ goto out;
+ }
+
+ func->class = data;
+
+out:
+ return ret;
+}
+
+static int sdio_init_func(struct mmc_card *card, unsigned int fn)
+{
+ int ret;
+ struct sdio_func *func;
+
+ BUG_ON(fn > SDIO_MAX_FUNCS);
+
+ func = sdio_alloc_func(card);
+ if (IS_ERR(func))
+ return PTR_ERR(func);
+
+ func->num = fn;
+
+ ret = sdio_read_fbr(func);
+ if (ret)
+ goto fail;
+
+ ret = sdio_read_func_cis(func);
+ if (ret)
+ goto fail;
+
+ card->sdio_func[fn - 1] = func;
+
+ return 0;
+
+fail:
+ /*
+ * It is okay to remove the function here even though we hold
+ * the host lock as we haven't registered the device yet.
+ */
+ sdio_remove_func(func);
+ return ret;
+}
+
+static int sdio_read_cccr(struct mmc_card *card)
+{
+ int ret;
+ int cccr_vsn;
+ unsigned char data;
+
+ memset(&card->cccr, 0, sizeof(struct sdio_cccr));
+
+ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data);
+ if (ret)
+ goto out;
+
+ cccr_vsn = data & 0x0f;
+
+ if (cccr_vsn > SDIO_CCCR_REV_1_20) {
+ printk(KERN_ERR "%s: unrecognised CCCR structure version %d\n",
+ mmc_hostname(card->host), cccr_vsn);
+ return -EINVAL;
+ }
+
+ card->cccr.sdio_vsn = (data & 0xf0) >> 4;
+
+ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CAPS, 0, &data);
+ if (ret)
+ goto out;
+
+ if (data & SDIO_CCCR_CAP_SMB)
+ card->cccr.multi_block = 1;
+ if (data & SDIO_CCCR_CAP_LSC)
+ card->cccr.low_speed = 1;
+ if (data & SDIO_CCCR_CAP_4BLS)
+ card->cccr.wide_bus = 1;
+
+ if (cccr_vsn >= SDIO_CCCR_REV_1_10) {
+ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_POWER, 0, &data);
+ if (ret)
+ goto out;
+
+ if (data & SDIO_POWER_SMPC)
+ card->cccr.high_power = 1;
+ }
+
+ if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
+ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data);
+ if (ret)
+ goto out;
+
+ if (data & SDIO_SPEED_SHS)
+ card->cccr.high_speed = 1;
+ }
+
+out:
+ return ret;
+}
+
+static int sdio_enable_wide(struct mmc_card *card)
+{
+ int ret;
+ u8 ctrl;
+
+ if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
+ return 0;
+
+ if (card->cccr.low_speed && !card->cccr.wide_bus)
+ return 0;
+
+ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
+ if (ret)
+ return ret;
+
+ ctrl |= SDIO_BUS_WIDTH_4BIT;
+
+ ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
+ if (ret)
+ return ret;
+
+ mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+
+ return 0;
+}
+
+/*
+ * Test if the card supports high-speed mode and, if so, switch to it.
+ */
+static int sdio_enable_hs(struct mmc_card *card)
+{
+ int ret;
+ u8 speed;
+
+ if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
+ return 0;
+
+ if (!card->cccr.high_speed)
+ return 0;
+
+ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
+ if (ret)
+ return ret;
+
+ speed |= SDIO_SPEED_EHS;
+
+ ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
+ if (ret)
+ return ret;
+
+ mmc_card_set_highspeed(card);
+ mmc_set_timing(card->host, MMC_TIMING_SD_HS);
+
+ return 0;
+}
+
+/*
+ * Host is being removed. Free up the current card.
+ */
+static void mmc_sdio_remove(struct mmc_host *host)
+{
+ int i;
+
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ for (i = 0;i < host->card->sdio_funcs;i++) {
+ if (host->card->sdio_func[i]) {
+ sdio_remove_func(host->card->sdio_func[i]);
+ host->card->sdio_func[i] = NULL;
+ }
+ }
+
+ mmc_remove_card(host->card);
+ host->card = NULL;
+}
+
+/*
+ * Card detection callback from host.
+ */
+static void mmc_sdio_detect(struct mmc_host *host)
+{
+ int err;
+
+ BUG_ON(!host);
+ BUG_ON(!host->card);
+
+ mmc_claim_host(host);
+
+ /*
+ * Just check if our card has been removed.
+ */
+ err = mmc_select_card(host->card);
+
+ mmc_release_host(host);
+
+ if (err) {
+ mmc_sdio_remove(host);
+
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+ }
+}
+
+
+static const struct mmc_bus_ops mmc_sdio_ops = {
+ .remove = mmc_sdio_remove,
+ .detect = mmc_sdio_detect,
+};
+
+
+/*
+ * Starting point for SDIO card init.
+ */
+int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
+{
+ int err;
+ int i, funcs;
+ struct mmc_card *card;
+
+ BUG_ON(!host);
+ WARN_ON(!host->claimed);
+
+ mmc_attach_bus(host, &mmc_sdio_ops);
+
+ /*
+ * Sanity check the voltages that the card claims to
+ * support.
+ */
+ if (ocr & 0x7F) {
+ printk(KERN_WARNING "%s: card claims to support voltages "
+ "below the defined range. These will be ignored.\n",
+ mmc_hostname(host));
+ ocr &= ~0x7F;
+ }
+
+ if (ocr & MMC_VDD_165_195) {
+ printk(KERN_WARNING "%s: SDIO card claims to support the "
+ "incompletely defined 'low voltage range'. This "
+ "will be ignored.\n", mmc_hostname(host));
+ ocr &= ~MMC_VDD_165_195;
+ }
+
+ host->ocr = mmc_select_voltage(host, ocr);
+
+ /*
+ * Can we support the voltage(s) of the card(s)?
+ */
+ if (!host->ocr) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ /*
+ * Inform the card of the voltage
+ */
+ err = mmc_send_io_op_cond(host, host->ocr, &ocr);
+ if (err)
+ goto err;
+
+ /*
+ * For SPI, enable CRC as appropriate.
+ */
+ if (mmc_host_is_spi(host)) {
+ err = mmc_spi_set_crc(host, use_spi_crc);
+ if (err)
+ goto err;
+ }
+
+ /*
+ * The number of functions on the card is encoded inside
+ * the ocr.
+ */
+ funcs = (ocr & 0x70000000) >> 28;
+
+ /*
+ * Allocate card structure.
+ */
+ card = mmc_alloc_card(host, NULL);
+ if (IS_ERR(card)) {
+ err = PTR_ERR(card);
+ goto err;
+ }
+
+ card->type = MMC_TYPE_SDIO;
+ card->sdio_funcs = funcs;
+
+ host->card = card;
+
+ /*
+ * For native busses: set card RCA and quit open drain mode.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_send_relative_addr(host, &card->rca);
+ if (err)
+ goto remove;
+
+ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+ }
+
+ /*
+ * Select card, as all following commands rely on that.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_select_card(card);
+ if (err)
+ goto remove;
+ }
+
+ /*
+ * Read the common registers.
+ */
+ err = sdio_read_cccr(card);
+ if (err)
+ goto remove;
+
+ /*
+ * Read the common CIS tuples.
+ */
+ err = sdio_read_common_cis(card);
+ if (err)
+ goto remove;
+
+ /*
+ * Switch to high-speed (if supported).
+ */
+ err = sdio_enable_hs(card);
+ if (err)
+ goto remove;
+
+ /*
+ * Change to the card's maximum speed.
+ */
+ if (mmc_card_highspeed(card)) {
+ /*
+ * The SDIO specification doesn't mention how
+ * the CIS transfer speed register relates to
+ * high-speed, but it seems that 50 MHz is
+ * mandatory.
+ */
+ mmc_set_clock(host, 50000000);
+ } else {
+ mmc_set_clock(host, card->cis.max_dtr);
+ }
+
+ /*
+ * Switch to wider bus (if supported).
+ */
+ err = sdio_enable_wide(card);
+ if (err)
+ goto remove;
+
+ /*
+ * Initialize (but don't add) all present functions.
+ */
+ for (i = 0;i < funcs;i++) {
+ err = sdio_init_func(host->card, i + 1);
+ if (err)
+ goto remove;
+ }
+
+ mmc_release_host(host);
+
+ /*
+ * First add the card to the driver model...
+ */
+ err = mmc_add_card(host->card);
+ if (err)
+ goto remove_added;
+
+ /*
+ * ...then the SDIO functions.
+ */
+ for (i = 0;i < funcs;i++) {
+ err = sdio_add_func(host->card->sdio_func[i]);
+ if (err)
+ goto remove_added;
+ }
+
+ return 0;
+
+
+remove_added:
+ /* Remove without lock if the device has been added. */
+ mmc_sdio_remove(host);
+ mmc_claim_host(host);
+remove:
+ /* And with lock if it hasn't been added. */
+ if (host->card)
+ mmc_sdio_remove(host);
+err:
+ mmc_detach_bus(host);
+ mmc_release_host(host);
+
+ printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
+ mmc_hostname(host), err);
+
+ return err;
+}
+
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
new file mode 100644
index 0000000..46284b5
--- /dev/null
+++ b/drivers/mmc/core/sdio_bus.c
@@ -0,0 +1,262 @@
+/*
+ * linux/drivers/mmc/core/sdio_bus.c
+ *
+ * Copyright 2007 Pierre Ossman
+ *
+ * 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.
+ *
+ * SDIO function driver model
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "sdio_cis.h"
+#include "sdio_bus.h"
+
+#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev)
+#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
+
+/* show configuration fields */
+#define sdio_config_attr(field, format_string) \
+static ssize_t \
+field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct sdio_func *func; \
+ \
+ func = dev_to_sdio_func (dev); \
+ return sprintf (buf, format_string, func->field); \
+}
+
+sdio_config_attr(class, "0x%02x\n");
+sdio_config_attr(vendor, "0x%04x\n");
+sdio_config_attr(device, "0x%04x\n");
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sdio_func *func = dev_to_sdio_func (dev);
+
+ return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n",
+ func->class, func->vendor, func->device);
+}
+
+static struct device_attribute sdio_dev_attrs[] = {
+ __ATTR_RO(class),
+ __ATTR_RO(vendor),
+ __ATTR_RO(device),
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
+static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)
+ return NULL;
+ if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)
+ return NULL;
+ if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)
+ return NULL;
+ return id;
+}
+
+static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
+ struct sdio_driver *sdrv)
+{
+ const struct sdio_device_id *ids;
+
+ ids = sdrv->id_table;
+
+ if (ids) {
+ while (ids->class || ids->vendor || ids->device) {
+ if (sdio_match_one(func, ids))
+ return ids;
+ ids++;
+ }
+ }
+
+ return NULL;
+}
+
+static int sdio_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct sdio_driver *sdrv = to_sdio_driver(drv);
+
+ if (sdio_match_device(func, sdrv))
+ return 1;
+
+ return 0;
+}
+
+static int
+sdio_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+
+ if (add_uevent_var(env,
+ "SDIO_CLASS=%02X", func->class))
+ return -ENOMEM;
+
+ if (add_uevent_var(env,
+ "SDIO_ID=%04X:%04X", func->vendor, func->device))
+ return -ENOMEM;
+
+ if (add_uevent_var(env,
+ "MODALIAS=sdio:c%02Xv%04Xd%04X",
+ func->class, func->vendor, func->device))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int sdio_bus_probe(struct device *dev)
+{
+ struct sdio_driver *drv = to_sdio_driver(dev->driver);
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ const struct sdio_device_id *id;
+ int ret;
+
+ id = sdio_match_device(func, drv);
+ if (!id)
+ return -ENODEV;
+
+ /* Set the default block size so the driver is sure it's something
+ * sensible. */
+ sdio_claim_host(func);
+ ret = sdio_set_block_size(func, 0);
+ sdio_release_host(func);
+ if (ret)
+ return ret;
+
+ return drv->probe(func, id);
+}
+
+static int sdio_bus_remove(struct device *dev)
+{
+ struct sdio_driver *drv = to_sdio_driver(dev->driver);
+ struct sdio_func *func = dev_to_sdio_func(dev);
+
+ drv->remove(func);
+
+ if (func->irq_handler) {
+ printk(KERN_WARNING "WARNING: driver %s did not remove "
+ "its interrupt handler!\n", drv->name);
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_release_host(func);
+ }
+
+ return 0;
+}
+
+static struct bus_type sdio_bus_type = {
+ .name = "sdio",
+ .dev_attrs = sdio_dev_attrs,
+ .match = sdio_bus_match,
+ .uevent = sdio_bus_uevent,
+ .probe = sdio_bus_probe,
+ .remove = sdio_bus_remove,
+};
+
+int sdio_register_bus(void)
+{
+ return bus_register(&sdio_bus_type);
+}
+
+void sdio_unregister_bus(void)
+{
+ bus_unregister(&sdio_bus_type);
+}
+
+/**
+ * sdio_register_driver - register a function driver
+ * @drv: SDIO function driver
+ */
+int sdio_register_driver(struct sdio_driver *drv)
+{
+ drv->drv.name = drv->name;
+ drv->drv.bus = &sdio_bus_type;
+ return driver_register(&drv->drv);
+}
+EXPORT_SYMBOL_GPL(sdio_register_driver);
+
+/**
+ * sdio_unregister_driver - unregister a function driver
+ * @drv: SDIO function driver
+ */
+void sdio_unregister_driver(struct sdio_driver *drv)
+{
+ drv->drv.bus = &sdio_bus_type;
+ driver_unregister(&drv->drv);
+}
+EXPORT_SYMBOL_GPL(sdio_unregister_driver);
+
+static void sdio_release_func(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+
+ sdio_free_func_cis(func);
+
+ if (func->info)
+ kfree(func->info);
+
+ kfree(func);
+}
+
+/*
+ * Allocate and initialise a new SDIO function structure.
+ */
+struct sdio_func *sdio_alloc_func(struct mmc_card *card)
+{
+ struct sdio_func *func;
+
+ func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL);
+ if (!func)
+ return ERR_PTR(-ENOMEM);
+
+ func->card = card;
+
+ device_initialize(&func->dev);
+
+ func->dev.parent = &card->dev;
+ func->dev.bus = &sdio_bus_type;
+ func->dev.release = sdio_release_func;
+
+ return func;
+}
+
+/*
+ * Register a new SDIO function with the driver model.
+ */
+int sdio_add_func(struct sdio_func *func)
+{
+ int ret;
+
+ dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num);
+
+ ret = device_add(&func->dev);
+ if (ret == 0)
+ sdio_func_set_present(func);
+
+ return ret;
+}
+
+/*
+ * Unregister a SDIO function with the driver model, and
+ * (eventually) free it.
+ */
+void sdio_remove_func(struct sdio_func *func)
+{
+ if (sdio_func_present(func))
+ device_del(&func->dev);
+
+ put_device(&func->dev);
+}
+
diff --git a/drivers/mmc/core/sdio_bus.h b/drivers/mmc/core/sdio_bus.h
new file mode 100644
index 0000000..567a768
--- /dev/null
+++ b/drivers/mmc/core/sdio_bus.h
@@ -0,0 +1,22 @@
+/*
+ * linux/drivers/mmc/core/sdio_bus.h
+ *
+ * Copyright 2007 Pierre Ossman
+ *
+ * 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.
+ */
+#ifndef _MMC_CORE_SDIO_BUS_H
+#define _MMC_CORE_SDIO_BUS_H
+
+struct sdio_func *sdio_alloc_func(struct mmc_card *card);
+int sdio_add_func(struct sdio_func *func);
+void sdio_remove_func(struct sdio_func *func);
+
+int sdio_register_bus(void);
+void sdio_unregister_bus(void);
+
+#endif
+
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
new file mode 100644
index 0000000..956bd76
--- /dev/null
+++ b/drivers/mmc/core/sdio_cis.c
@@ -0,0 +1,352 @@
+/*
+ * linux/drivers/mmc/core/sdio_cis.c
+ *
+ * Author: Nicolas Pitre
+ * Created: June 11, 2007
+ * Copyright: MontaVista Software Inc.
+ *
+ * Copyright 2007 Pierre Ossman
+ *
+ * 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/kernel.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "sdio_cis.h"
+#include "sdio_ops.h"
+
+static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func,
+ const unsigned char *buf, unsigned size)
+{
+ unsigned i, nr_strings;
+ char **buffer, *string;
+
+ buf += 2;
+ size -= 2;
+
+ nr_strings = 0;
+ for (i = 0; i < size; i++) {
+ if (buf[i] == 0xff)
+ break;
+ if (buf[i] == 0)
+ nr_strings++;
+ }
+
+ if (buf[i-1] != '\0') {
+ printk(KERN_WARNING "SDIO: ignoring broken CISTPL_VERS_1\n");
+ return 0;
+ }
+
+ size = i;
+
+ buffer = kzalloc(sizeof(char*) * nr_strings + size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ string = (char*)(buffer + nr_strings);
+
+ for (i = 0; i < nr_strings; i++) {
+ buffer[i] = string;
+ strcpy(string, buf);
+ string += strlen(string) + 1;
+ buf += strlen(buf) + 1;
+ }
+
+ if (func) {
+ func->num_info = nr_strings;
+ func->info = (const char**)buffer;
+ } else {
+ card->num_info = nr_strings;
+ card->info = (const char**)buffer;
+ }
+
+ return 0;
+}
+
+static int cistpl_manfid(struct mmc_card *card, struct sdio_func *func,
+ const unsigned char *buf, unsigned size)
+{
+ unsigned int vendor, device;
+
+ /* TPLMID_MANF */
+ vendor = buf[0] | (buf[1] << 8);
+
+ /* TPLMID_CARD */
+ device = buf[2] | (buf[3] << 8);
+
+ if (func) {
+ func->vendor = vendor;
+ func->device = device;
+ } else {
+ card->cis.vendor = vendor;
+ card->cis.device = device;
+ }
+
+ return 0;
+}
+
+static const unsigned char speed_val[16] =
+ { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
+static const unsigned int speed_unit[8] =
+ { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
+
+static int cistpl_funce_common(struct mmc_card *card,
+ const unsigned char *buf, unsigned size)
+{
+ if (size < 0x04 || buf[0] != 0)
+ return -EINVAL;
+
+ /* TPLFE_FN0_BLK_SIZE */
+ card->cis.blksize = buf[1] | (buf[2] << 8);
+
+ /* TPLFE_MAX_TRAN_SPEED */
+ card->cis.max_dtr = speed_val[(buf[3] >> 3) & 15] *
+ speed_unit[buf[3] & 7];
+
+ return 0;
+}
+
+static int cistpl_funce_func(struct sdio_func *func,
+ const unsigned char *buf, unsigned size)
+{
+ unsigned vsn;
+ unsigned min_size;
+
+ vsn = func->card->cccr.sdio_vsn;
+ min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;
+
+ if (size < min_size || buf[0] != 1)
+ return -EINVAL;
+
+ /* TPLFE_MAX_BLK_SIZE */
+ func->max_blksize = buf[12] | (buf[13] << 8);
+
+ /* TPLFE_ENABLE_TIMEOUT_VAL, present in ver 1.1 and above */
+ if (vsn > SDIO_SDIO_REV_1_00)
+ func->enable_timeout = (buf[28] | (buf[29] << 8)) * 10;
+ else
+ func->enable_timeout = jiffies_to_msecs(HZ);
+
+ return 0;
+}
+
+static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,
+ const unsigned char *buf, unsigned size)
+{
+ int ret;
+
+ /*
+ * There should be two versions of the CISTPL_FUNCE tuple,
+ * one for the common CIS (function 0) and a version used by
+ * the individual function's CIS (1-7). Yet, the later has a
+ * different length depending on the SDIO spec version.
+ */
+ if (func)
+ ret = cistpl_funce_func(func, buf, size);
+ else
+ ret = cistpl_funce_common(card, buf, size);
+
+ if (ret) {
+ printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u "
+ "type %u\n", mmc_hostname(card->host), size, buf[0]);
+ return ret;
+ }
+
+ return 0;
+}
+
+typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
+ const unsigned char *, unsigned);
+
+struct cis_tpl {
+ unsigned char code;
+ unsigned char min_size;
+ tpl_parse_t *parse;
+};
+
+static const struct cis_tpl cis_tpl_list[] = {
+ { 0x15, 3, cistpl_vers_1 },
+ { 0x20, 4, cistpl_manfid },
+ { 0x21, 2, /* cistpl_funcid */ },
+ { 0x22, 0, cistpl_funce },
+};
+
+static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
+{
+ int ret;
+ struct sdio_func_tuple *this, **prev;
+ unsigned i, ptr = 0;
+
+ /*
+ * Note that this works for the common CIS (function number 0) as
+ * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS
+ * have the same offset.
+ */
+ for (i = 0; i < 3; i++) {
+ unsigned char x, fn;
+
+ if (func)
+ fn = func->num;
+ else
+ fn = 0;
+
+ ret = mmc_io_rw_direct(card, 0, 0,
+ SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x);
+ if (ret)
+ return ret;
+ ptr |= x << (i * 8);
+ }
+
+ if (func)
+ prev = &func->tuples;
+ else
+ prev = &card->tuples;
+
+ BUG_ON(*prev);
+
+ do {
+ unsigned char tpl_code, tpl_link;
+
+ ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code);
+ if (ret)
+ break;
+
+ /* 0xff means we're done */
+ if (tpl_code == 0xff)
+ break;
+
+ ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
+ if (ret)
+ break;
+
+ this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
+ if (!this)
+ return -ENOMEM;
+
+ for (i = 0; i < tpl_link; i++) {
+ ret = mmc_io_rw_direct(card, 0, 0,
+ ptr + i, 0, &this->data[i]);
+ if (ret)
+ break;
+ }
+ if (ret) {
+ kfree(this);
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
+ if (cis_tpl_list[i].code == tpl_code)
+ break;
+ if (i >= ARRAY_SIZE(cis_tpl_list)) {
+ /* this tuple is unknown to the core */
+ this->next = NULL;
+ this->code = tpl_code;
+ this->size = tpl_link;
+ *prev = this;
+ prev = &this->next;
+ printk(KERN_DEBUG
+ "%s: queuing CIS tuple 0x%02x length %u\n",
+ mmc_hostname(card->host), tpl_code, tpl_link);
+ } else {
+ const struct cis_tpl *tpl = cis_tpl_list + i;
+ if (tpl_link < tpl->min_size) {
+ printk(KERN_ERR
+ "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n",
+ mmc_hostname(card->host),
+ tpl_code, tpl_link, tpl->min_size);
+ ret = -EINVAL;
+ } else if (tpl->parse) {
+ ret = tpl->parse(card, func,
+ this->data, tpl_link);
+ }
+ kfree(this);
+ }
+
+ ptr += tpl_link;
+ } while (!ret);
+
+ /*
+ * Link in all unknown tuples found in the common CIS so that
+ * drivers don't have to go digging in two places.
+ */
+ if (func)
+ *prev = card->tuples;
+
+ return ret;
+}
+
+int sdio_read_common_cis(struct mmc_card *card)
+{
+ return sdio_read_cis(card, NULL);
+}
+
+void sdio_free_common_cis(struct mmc_card *card)
+{
+ struct sdio_func_tuple *tuple, *victim;
+
+ tuple = card->tuples;
+
+ while (tuple) {
+ victim = tuple;
+ tuple = tuple->next;
+ kfree(victim);
+ }
+
+ card->tuples = NULL;
+}
+
+int sdio_read_func_cis(struct sdio_func *func)
+{
+ int ret;
+
+ ret = sdio_read_cis(func->card, func);
+ if (ret)
+ return ret;
+
+ /*
+ * Since we've linked to tuples in the card structure,
+ * we must make sure we have a reference to it.
+ */
+ get_device(&func->card->dev);
+
+ /*
+ * Vendor/device id is optional for function CIS, so
+ * copy it from the card structure as needed.
+ */
+ if (func->vendor == 0) {
+ func->vendor = func->card->cis.vendor;
+ func->device = func->card->cis.device;
+ }
+
+ return 0;
+}
+
+void sdio_free_func_cis(struct sdio_func *func)
+{
+ struct sdio_func_tuple *tuple, *victim;
+
+ tuple = func->tuples;
+
+ while (tuple && tuple != func->card->tuples) {
+ victim = tuple;
+ tuple = tuple->next;
+ kfree(victim);
+ }
+
+ func->tuples = NULL;
+
+ /*
+ * We have now removed the link to the tuples in the
+ * card structure, so remove the reference.
+ */
+ put_device(&func->card->dev);
+}
+
diff --git a/drivers/mmc/core/sdio_cis.h b/drivers/mmc/core/sdio_cis.h
new file mode 100644
index 0000000..4d903c2
--- /dev/null
+++ b/drivers/mmc/core/sdio_cis.h
@@ -0,0 +1,23 @@
+/*
+ * linux/drivers/mmc/core/sdio_cis.h
+ *
+ * Author: Nicolas Pitre
+ * Created: June 11, 2007
+ * Copyright: MontaVista Software Inc.
+ *
+ * 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.
+ */
+
+#ifndef _MMC_SDIO_CIS_H
+#define _MMC_SDIO_CIS_H
+
+int sdio_read_common_cis(struct mmc_card *card);
+void sdio_free_common_cis(struct mmc_card *card);
+
+int sdio_read_func_cis(struct sdio_func *func);
+void sdio_free_func_cis(struct sdio_func *func);
+
+#endif
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
new file mode 100644
index 0000000..f61fc2d
--- /dev/null
+++ b/drivers/mmc/core/sdio_io.c
@@ -0,0 +1,637 @@
+/*
+ * linux/drivers/mmc/core/sdio_io.c
+ *
+ * Copyright 2007-2008 Pierre Ossman
+ *
+ * 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/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "sdio_ops.h"
+
+/**
+ * sdio_claim_host - exclusively claim a bus for a certain SDIO function
+ * @func: SDIO function that will be accessed
+ *
+ * Claim a bus for a set of operations. The SDIO function given
+ * is used to figure out which bus is relevant.
+ */
+void sdio_claim_host(struct sdio_func *func)
+{
+ BUG_ON(!func);
+ BUG_ON(!func->card);
+
+ mmc_claim_host(func->card->host);
+}
+EXPORT_SYMBOL_GPL(sdio_claim_host);
+
+/**
+ * sdio_release_host - release a bus for a certain SDIO function
+ * @func: SDIO function that was accessed
+ *
+ * Release a bus, allowing others to claim the bus for their
+ * operations.
+ */
+void sdio_release_host(struct sdio_func *func)
+{
+ BUG_ON(!func);
+ BUG_ON(!func->card);
+
+ mmc_release_host(func->card->host);
+}
+EXPORT_SYMBOL_GPL(sdio_release_host);
+
+/**
+ * sdio_enable_func - enables a SDIO function for usage
+ * @func: SDIO function to enable
+ *
+ * Powers up and activates a SDIO function so that register
+ * access is possible.
+ */
+int sdio_enable_func(struct sdio_func *func)
+{
+ int ret;
+ unsigned char reg;
+ unsigned long timeout;
+
+ BUG_ON(!func);
+ BUG_ON(!func->card);
+
+ pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func));
+
+ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg);
+ if (ret)
+ goto err;
+
+ reg |= 1 << func->num;
+
+ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
+ if (ret)
+ goto err;
+
+ timeout = jiffies + msecs_to_jiffies(func->enable_timeout);
+
+ while (1) {
+ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, &reg);
+ if (ret)
+ goto err;
+ if (reg & (1 << func->num))
+ break;
+ ret = -ETIME;
+ if (time_after(jiffies, timeout))
+ goto err;
+ }
+
+ pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func));
+
+ return 0;
+
+err:
+ pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func));
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdio_enable_func);
+
+/**
+ * sdio_disable_func - disable a SDIO function
+ * @func: SDIO function to disable
+ *
+ * Powers down and deactivates a SDIO function. Register access
+ * to this function will fail until the function is reenabled.
+ */
+int sdio_disable_func(struct sdio_func *func)
+{
+ int ret;
+ unsigned char reg;
+
+ BUG_ON(!func);
+ BUG_ON(!func->card);
+
+ pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func));
+
+ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg);
+ if (ret)
+ goto err;
+
+ reg &= ~(1 << func->num);
+
+ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
+ if (ret)
+ goto err;
+
+ pr_debug("SDIO: Disabled device %s\n", sdio_func_id(func));
+
+ return 0;
+
+err:
+ pr_debug("SDIO: Failed to disable device %s\n", sdio_func_id(func));
+ return -EIO;
+}
+EXPORT_SYMBOL_GPL(sdio_disable_func);
+
+/**
+ * sdio_set_block_size - set the block size of an SDIO function
+ * @func: SDIO function to change
+ * @blksz: new block size or 0 to use the default.
+ *
+ * The default block size is the largest supported by both the function
+ * and the host, with a maximum of 512 to ensure that arbitrarily sized
+ * data transfer use the optimal (least) number of commands.
+ *
+ * A driver may call this to override the default block size set by the
+ * core. This can be used to set a block size greater than the maximum
+ * that reported by the card; it is the driver's responsibility to ensure
+ * it uses a value that the card supports.
+ *
+ * Returns 0 on success, -EINVAL if the host does not support the
+ * requested block size, or -EIO (etc.) if one of the resultant FBR block
+ * size register writes failed.
+ *
+ */
+int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
+{
+ int ret;
+
+ if (blksz > func->card->host->max_blk_size)
+ return -EINVAL;
+
+ if (blksz == 0) {
+ blksz = min(func->max_blksize, func->card->host->max_blk_size);
+ blksz = min(blksz, 512u);
+ }
+
+ ret = mmc_io_rw_direct(func->card, 1, 0,
+ SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
+ blksz & 0xff, NULL);
+ if (ret)
+ return ret;
+ ret = mmc_io_rw_direct(func->card, 1, 0,
+ SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1,
+ (blksz >> 8) & 0xff, NULL);
+ if (ret)
+ return ret;
+ func->cur_blksize = blksz;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sdio_set_block_size);
+
+/*
+ * Calculate the maximum byte mode transfer size
+ */
+static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
+{
+ unsigned mval = min(func->card->host->max_seg_size,
+ func->card->host->max_blk_size);
+ mval = min(mval, func->max_blksize);
+ return min(mval, 512u); /* maximum size for byte mode */
+}
+
+/**
+ * sdio_align_size - pads a transfer size to a more optimal value
+ * @func: SDIO function
+ * @sz: original transfer size
+ *
+ * Pads the original data size with a number of extra bytes in
+ * order to avoid controller bugs and/or performance hits
+ * (e.g. some controllers revert to PIO for certain sizes).
+ *
+ * If possible, it will also adjust the size so that it can be
+ * handled in just a single request.
+ *
+ * Returns the improved size, which might be unmodified.
+ */
+unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
+{
+ unsigned int orig_sz;
+ unsigned int blk_sz, byte_sz;
+ unsigned chunk_sz;
+
+ orig_sz = sz;
+
+ /*
+ * Do a first check with the controller, in case it
+ * wants to increase the size up to a point where it
+ * might need more than one block.
+ */
+ sz = mmc_align_data_size(func->card, sz);
+
+ /*
+ * If we can still do this with just a byte transfer, then
+ * we're done.
+ */
+ if (sz <= sdio_max_byte_size(func))
+ return sz;
+
+ if (func->card->cccr.multi_block) {
+ /*
+ * Check if the transfer is already block aligned
+ */
+ if ((sz % func->cur_blksize) == 0)
+ return sz;
+
+ /*
+ * Realign it so that it can be done with one request,
+ * and recheck if the controller still likes it.
+ */
+ blk_sz = ((sz + func->cur_blksize - 1) /
+ func->cur_blksize) * func->cur_blksize;
+ blk_sz = mmc_align_data_size(func->card, blk_sz);
+
+ /*
+ * This value is only good if it is still just
+ * one request.
+ */
+ if ((blk_sz % func->cur_blksize) == 0)
+ return blk_sz;
+
+ /*
+ * We failed to do one request, but at least try to
+ * pad the remainder properly.
+ */
+ byte_sz = mmc_align_data_size(func->card,
+ sz % func->cur_blksize);
+ if (byte_sz <= sdio_max_byte_size(func)) {
+ blk_sz = sz / func->cur_blksize;
+ return blk_sz * func->cur_blksize + byte_sz;
+ }
+ } else {
+ /*
+ * We need multiple requests, so first check that the
+ * controller can handle the chunk size;
+ */
+ chunk_sz = mmc_align_data_size(func->card,
+ sdio_max_byte_size(func));
+ if (chunk_sz == sdio_max_byte_size(func)) {
+ /*
+ * Fix up the size of the remainder (if any)
+ */
+ byte_sz = orig_sz % chunk_sz;
+ if (byte_sz) {
+ byte_sz = mmc_align_data_size(func->card,
+ byte_sz);
+ }
+
+ return (orig_sz / chunk_sz) * chunk_sz + byte_sz;
+ }
+ }
+
+ /*
+ * The controller is simply incapable of transferring the size
+ * we want in decent manner, so just return the original size.
+ */
+ return orig_sz;
+}
+EXPORT_SYMBOL_GPL(sdio_align_size);
+
+/* Split an arbitrarily sized data transfer into several
+ * IO_RW_EXTENDED commands. */
+static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
+ unsigned addr, int incr_addr, u8 *buf, unsigned size)
+{
+ unsigned remainder = size;
+ unsigned max_blocks;
+ int ret;
+
+ /* Do the bulk of the transfer using block mode (if supported). */
+ if (func->card->cccr.multi_block && (size > sdio_max_byte_size(func))) {
+ /* Blocks per command is limited by host count, host transfer
+ * size (we only use a single sg entry) and the maximum for
+ * IO_RW_EXTENDED of 511 blocks. */
+ max_blocks = min(func->card->host->max_blk_count,
+ func->card->host->max_seg_size / func->cur_blksize);
+ max_blocks = min(max_blocks, 511u);
+
+ while (remainder > func->cur_blksize) {
+ unsigned blocks;
+
+ blocks = remainder / func->cur_blksize;
+ if (blocks > max_blocks)
+ blocks = max_blocks;
+ size = blocks * func->cur_blksize;
+
+ ret = mmc_io_rw_extended(func->card, write,
+ func->num, addr, incr_addr, buf,
+ blocks, func->cur_blksize);
+ if (ret)
+ return ret;
+
+ remainder -= size;
+ buf += size;
+ if (incr_addr)
+ addr += size;
+ }
+ }
+
+ /* Write the remainder using byte mode. */
+ while (remainder > 0) {
+ size = min(remainder, sdio_max_byte_size(func));
+
+ ret = mmc_io_rw_extended(func->card, write, func->num, addr,
+ incr_addr, buf, 1, size);
+ if (ret)
+ return ret;
+
+ remainder -= size;
+ buf += size;
+ if (incr_addr)
+ addr += size;
+ }
+ return 0;
+}
+
+/**
+ * sdio_readb - read a single byte from a SDIO function
+ * @func: SDIO function to access
+ * @addr: address to read
+ * @err_ret: optional status value from transfer
+ *
+ * Reads a single byte from the address space of a given SDIO
+ * function. If there is a problem reading the address, 0xff
+ * is returned and @err_ret will contain the error code.
+ */
+u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret)
+{
+ int ret;
+ u8 val;
+
+ BUG_ON(!func);
+
+ if (err_ret)
+ *err_ret = 0;
+
+ ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val);
+ if (ret) {
+ if (err_ret)
+ *err_ret = ret;
+ return 0xFF;
+ }
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(sdio_readb);
+
+/**
+ * sdio_writeb - write a single byte to a SDIO function
+ * @func: SDIO function to access
+ * @b: byte to write
+ * @addr: address to write to
+ * @err_ret: optional status value from transfer
+ *
+ * Writes a single byte to the address space of a given SDIO
+ * function. @err_ret will contain the status of the actual
+ * transfer.
+ */
+void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret)
+{
+ int ret;
+
+ BUG_ON(!func);
+
+ ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL);
+ if (err_ret)
+ *err_ret = ret;
+}
+EXPORT_SYMBOL_GPL(sdio_writeb);
+
+/**
+ * sdio_memcpy_fromio - read a chunk of memory from a SDIO function
+ * @func: SDIO function to access
+ * @dst: buffer to store the data
+ * @addr: address to begin reading from
+ * @count: number of bytes to read
+ *
+ * Reads from the address space of a given SDIO function. Return
+ * value indicates if the transfer succeeded or not.
+ */
+int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
+ unsigned int addr, int count)
+{
+ return sdio_io_rw_ext_helper(func, 0, addr, 1, dst, count);
+}
+EXPORT_SYMBOL_GPL(sdio_memcpy_fromio);
+
+/**
+ * sdio_memcpy_toio - write a chunk of memory to a SDIO function
+ * @func: SDIO function to access
+ * @addr: address to start writing to
+ * @src: buffer that contains the data to write
+ * @count: number of bytes to write
+ *
+ * Writes to the address space of a given SDIO function. Return
+ * value indicates if the transfer succeeded or not.
+ */
+int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
+ void *src, int count)
+{
+ return sdio_io_rw_ext_helper(func, 1, addr, 1, src, count);
+}
+EXPORT_SYMBOL_GPL(sdio_memcpy_toio);
+
+/**
+ * sdio_readsb - read from a FIFO on a SDIO function
+ * @func: SDIO function to access
+ * @dst: buffer to store the data
+ * @addr: address of (single byte) FIFO
+ * @count: number of bytes to read
+ *
+ * Reads from the specified FIFO of a given SDIO function. Return
+ * value indicates if the transfer succeeded or not.
+ */
+int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr,
+ int count)
+{
+ return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count);
+}
+EXPORT_SYMBOL_GPL(sdio_readsb);
+
+/**
+ * sdio_writesb - write to a FIFO of a SDIO function
+ * @func: SDIO function to access
+ * @addr: address of (single byte) FIFO
+ * @src: buffer that contains the data to write
+ * @count: number of bytes to write
+ *
+ * Writes to the specified FIFO of a given SDIO function. Return
+ * value indicates if the transfer succeeded or not.
+ */
+int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src,
+ int count)
+{
+ return sdio_io_rw_ext_helper(func, 1, addr, 0, src, count);
+}
+EXPORT_SYMBOL_GPL(sdio_writesb);
+
+/**
+ * sdio_readw - read a 16 bit integer from a SDIO function
+ * @func: SDIO function to access
+ * @addr: address to read
+ * @err_ret: optional status value from transfer
+ *
+ * Reads a 16 bit integer from the address space of a given SDIO
+ * function. If there is a problem reading the address, 0xffff
+ * is returned and @err_ret will contain the error code.
+ */
+u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret)
+{
+ int ret;
+
+ if (err_ret)
+ *err_ret = 0;
+
+ ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 2);
+ if (ret) {
+ if (err_ret)
+ *err_ret = ret;
+ return 0xFFFF;
+ }
+
+ return le16_to_cpup((__le16 *)func->tmpbuf);
+}
+EXPORT_SYMBOL_GPL(sdio_readw);
+
+/**
+ * sdio_writew - write a 16 bit integer to a SDIO function
+ * @func: SDIO function to access
+ * @b: integer to write
+ * @addr: address to write to
+ * @err_ret: optional status value from transfer
+ *
+ * Writes a 16 bit integer to the address space of a given SDIO
+ * function. @err_ret will contain the status of the actual
+ * transfer.
+ */
+void sdio_writew(struct sdio_func *func, u16 b, unsigned int addr, int *err_ret)
+{
+ int ret;
+
+ *(__le16 *)func->tmpbuf = cpu_to_le16(b);
+
+ ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2);
+ if (err_ret)
+ *err_ret = ret;
+}
+EXPORT_SYMBOL_GPL(sdio_writew);
+
+/**
+ * sdio_readl - read a 32 bit integer from a SDIO function
+ * @func: SDIO function to access
+ * @addr: address to read
+ * @err_ret: optional status value from transfer
+ *
+ * Reads a 32 bit integer from the address space of a given SDIO
+ * function. If there is a problem reading the address,
+ * 0xffffffff is returned and @err_ret will contain the error
+ * code.
+ */
+u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret)
+{
+ int ret;
+
+ if (err_ret)
+ *err_ret = 0;
+
+ ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 4);
+ if (ret) {
+ if (err_ret)
+ *err_ret = ret;
+ return 0xFFFFFFFF;
+ }
+
+ return le32_to_cpup((__le32 *)func->tmpbuf);
+}
+EXPORT_SYMBOL_GPL(sdio_readl);
+
+/**
+ * sdio_writel - write a 32 bit integer to a SDIO function
+ * @func: SDIO function to access
+ * @b: integer to write
+ * @addr: address to write to
+ * @err_ret: optional status value from transfer
+ *
+ * Writes a 32 bit integer to the address space of a given SDIO
+ * function. @err_ret will contain the status of the actual
+ * transfer.
+ */
+void sdio_writel(struct sdio_func *func, u32 b, unsigned int addr, int *err_ret)
+{
+ int ret;
+
+ *(__le32 *)func->tmpbuf = cpu_to_le32(b);
+
+ ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4);
+ if (err_ret)
+ *err_ret = ret;
+}
+EXPORT_SYMBOL_GPL(sdio_writel);
+
+/**
+ * sdio_f0_readb - read a single byte from SDIO function 0
+ * @func: an SDIO function of the card
+ * @addr: address to read
+ * @err_ret: optional status value from transfer
+ *
+ * Reads a single byte from the address space of SDIO function 0.
+ * If there is a problem reading the address, 0xff is returned
+ * and @err_ret will contain the error code.
+ */
+unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr,
+ int *err_ret)
+{
+ int ret;
+ unsigned char val;
+
+ BUG_ON(!func);
+
+ if (err_ret)
+ *err_ret = 0;
+
+ ret = mmc_io_rw_direct(func->card, 0, 0, addr, 0, &val);
+ if (ret) {
+ if (err_ret)
+ *err_ret = ret;
+ return 0xFF;
+ }
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(sdio_f0_readb);
+
+/**
+ * sdio_f0_writeb - write a single byte to SDIO function 0
+ * @func: an SDIO function of the card
+ * @b: byte to write
+ * @addr: address to write to
+ * @err_ret: optional status value from transfer
+ *
+ * Writes a single byte to the address space of SDIO function 0.
+ * @err_ret will contain the status of the actual transfer.
+ *
+ * Only writes to the vendor specific CCCR registers (0xF0 -
+ * 0xFF) are permiited; @err_ret will be set to -EINVAL for *
+ * writes outside this range.
+ */
+void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
+ int *err_ret)
+{
+ int ret;
+
+ BUG_ON(!func);
+
+ if (addr < 0xF0 || addr > 0xFF) {
+ if (err_ret)
+ *err_ret = -EINVAL;
+ return;
+ }
+
+ ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL);
+ if (err_ret)
+ *err_ret = ret;
+}
+EXPORT_SYMBOL_GPL(sdio_f0_writeb);
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
new file mode 100644
index 0000000..bb192f9
--- /dev/null
+++ b/drivers/mmc/core/sdio_irq.c
@@ -0,0 +1,273 @@
+/*
+ * linux/drivers/mmc/core/sdio_irq.c
+ *
+ * Author: Nicolas Pitre
+ * Created: June 18, 2007
+ * Copyright: MontaVista Software Inc.
+ *
+ * Copyright 2008 Pierre Ossman
+ *
+ * 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/kernel.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "sdio_ops.h"
+
+static int process_sdio_pending_irqs(struct mmc_card *card)
+{
+ int i, ret, count;
+ unsigned char pending;
+
+ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
+ if (ret) {
+ printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n",
+ mmc_card_id(card), ret);
+ return ret;
+ }
+
+ count = 0;
+ for (i = 1; i <= 7; i++) {
+ if (pending & (1 << i)) {
+ struct sdio_func *func = card->sdio_func[i - 1];
+ if (!func) {
+ printk(KERN_WARNING "%s: pending IRQ for "
+ "non-existant function\n",
+ mmc_card_id(card));
+ ret = -EINVAL;
+ } else if (func->irq_handler) {
+ func->irq_handler(func);
+ count++;
+ } else {
+ printk(KERN_WARNING "%s: pending IRQ with no handler\n",
+ sdio_func_id(func));
+ ret = -EINVAL;
+ }
+ }
+ }
+
+ if (count)
+ return count;
+
+ return ret;
+}
+
+static int sdio_irq_thread(void *_host)
+{
+ struct mmc_host *host = _host;
+ struct sched_param param = { .sched_priority = 1 };
+ unsigned long period, idle_period;
+ int ret;
+
+ sched_setscheduler(current, SCHED_FIFO, &param);
+
+ /*
+ * We want to allow for SDIO cards to work even on non SDIO
+ * aware hosts. One thing that non SDIO host cannot do is
+ * asynchronous notification of pending SDIO card interrupts
+ * hence we poll for them in that case.
+ */
+ idle_period = msecs_to_jiffies(10);
+ period = (host->caps & MMC_CAP_SDIO_IRQ) ?
+ MAX_SCHEDULE_TIMEOUT : idle_period;
+
+ pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n",
+ mmc_hostname(host), period);
+
+ do {
+ /*
+ * We claim the host here on drivers behalf for a couple
+ * reasons:
+ *
+ * 1) it is already needed to retrieve the CCCR_INTx;
+ * 2) we want the driver(s) to clear the IRQ condition ASAP;
+ * 3) we need to control the abort condition locally.
+ *
+ * Just like traditional hard IRQ handlers, we expect SDIO
+ * IRQ handlers to be quick and to the point, so that the
+ * holding of the host lock does not cover too much work
+ * that doesn't require that lock to be held.
+ */
+ ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
+ if (ret)
+ break;
+ ret = process_sdio_pending_irqs(host->card);
+ mmc_release_host(host);
+
+ /*
+ * Give other threads a chance to run in the presence of
+ * errors.
+ */
+ if (ret < 0) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!kthread_should_stop())
+ schedule_timeout(HZ);
+ set_current_state(TASK_RUNNING);
+ }
+
+ /*
+ * Adaptive polling frequency based on the assumption
+ * that an interrupt will be closely followed by more.
+ * This has a substantial benefit for network devices.
+ */
+ if (!(host->caps & MMC_CAP_SDIO_IRQ)) {
+ if (ret > 0)
+ period /= 2;
+ else {
+ period++;
+ if (period > idle_period)
+ period = idle_period;
+ }
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (host->caps & MMC_CAP_SDIO_IRQ)
+ host->ops->enable_sdio_irq(host, 1);
+ if (!kthread_should_stop())
+ schedule_timeout(period);
+ set_current_state(TASK_RUNNING);
+ } while (!kthread_should_stop());
+
+ if (host->caps & MMC_CAP_SDIO_IRQ)
+ host->ops->enable_sdio_irq(host, 0);
+
+ pr_debug("%s: IRQ thread exiting with code %d\n",
+ mmc_hostname(host), ret);
+
+ return ret;
+}
+
+static int sdio_card_irq_get(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+
+ WARN_ON(!host->claimed);
+
+ if (!host->sdio_irqs++) {
+ atomic_set(&host->sdio_irq_thread_abort, 0);
+ host->sdio_irq_thread =
+ kthread_run(sdio_irq_thread, host, "ksdioirqd/%s",
+ mmc_hostname(host));
+ if (IS_ERR(host->sdio_irq_thread)) {
+ int err = PTR_ERR(host->sdio_irq_thread);
+ host->sdio_irqs--;
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int sdio_card_irq_put(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+
+ WARN_ON(!host->claimed);
+ BUG_ON(host->sdio_irqs < 1);
+
+ if (!--host->sdio_irqs) {
+ atomic_set(&host->sdio_irq_thread_abort, 1);
+ kthread_stop(host->sdio_irq_thread);
+ }
+
+ return 0;
+}
+
+/**
+ * sdio_claim_irq - claim the IRQ for a SDIO function
+ * @func: SDIO function
+ * @handler: IRQ handler callback
+ *
+ * Claim and activate the IRQ for the given SDIO function. The provided
+ * handler will be called when that IRQ is asserted. The host is always
+ * claimed already when the handler is called so the handler must not
+ * call sdio_claim_host() nor sdio_release_host().
+ */
+int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
+{
+ int ret;
+ unsigned char reg;
+
+ BUG_ON(!func);
+ BUG_ON(!func->card);
+
+ pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));
+
+ if (func->irq_handler) {
+ pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func));
+ return -EBUSY;
+ }
+
+ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
+ if (ret)
+ return ret;
+
+ reg |= 1 << func->num;
+
+ reg |= 1; /* Master interrupt enable */
+
+ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
+ if (ret)
+ return ret;
+
+ func->irq_handler = handler;
+ ret = sdio_card_irq_get(func->card);
+ if (ret)
+ func->irq_handler = NULL;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sdio_claim_irq);
+
+/**
+ * sdio_release_irq - release the IRQ for a SDIO function
+ * @func: SDIO function
+ *
+ * Disable and release the IRQ for the given SDIO function.
+ */
+int sdio_release_irq(struct sdio_func *func)
+{
+ int ret;
+ unsigned char reg;
+
+ BUG_ON(!func);
+ BUG_ON(!func->card);
+
+ pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));
+
+ if (func->irq_handler) {
+ func->irq_handler = NULL;
+ sdio_card_irq_put(func->card);
+ }
+
+ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
+ if (ret)
+ return ret;
+
+ reg &= ~(1 << func->num);
+
+ /* Disable master interrupt with the last function interrupt */
+ if (!(reg & 0xFE))
+ reg = 0;
+
+ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sdio_release_irq);
+
diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c
new file mode 100644
index 0000000..c8fa095
--- /dev/null
+++ b/drivers/mmc/core/sdio_ops.c
@@ -0,0 +1,176 @@
+/*
+ * linux/drivers/mmc/sdio_ops.c
+ *
+ * Copyright 2006-2007 Pierre Ossman
+ *
+ * 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/scatterlist.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+
+#include "core.h"
+#include "sdio_ops.h"
+
+int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
+{
+ struct mmc_command cmd;
+ int i, err = 0;
+
+ BUG_ON(!host);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = SD_IO_SEND_OP_COND;
+ cmd.arg = ocr;
+ cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR;
+
+ for (i = 100; i; i--) {
+ err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+ if (err)
+ break;
+
+ /* if we're just probing, do a single pass */
+ if (ocr == 0)
+ break;
+
+ /* otherwise wait until reset completes */
+ if (mmc_host_is_spi(host)) {
+ /*
+ * Both R1_SPI_IDLE and MMC_CARD_BUSY indicate
+ * an initialized card under SPI, but some cards
+ * (Marvell's) only behave when looking at this
+ * one.
+ */
+ if (cmd.resp[1] & MMC_CARD_BUSY)
+ break;
+ } else {
+ if (cmd.resp[0] & MMC_CARD_BUSY)
+ break;
+ }
+
+ err = -ETIMEDOUT;
+
+ mmc_delay(10);
+ }
+
+ if (rocr)
+ *rocr = cmd.resp[mmc_host_is_spi(host) ? 1 : 0];
+
+ return err;
+}
+
+int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
+ unsigned addr, u8 in, u8* out)
+{
+ struct mmc_command cmd;
+ int err;
+
+ BUG_ON(!card);
+ BUG_ON(fn > 7);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = SD_IO_RW_DIRECT;
+ cmd.arg = write ? 0x80000000 : 0x00000000;
+ cmd.arg |= fn << 28;
+ cmd.arg |= (write && out) ? 0x08000000 : 0x00000000;
+ cmd.arg |= addr << 9;
+ cmd.arg |= in;
+ cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err)
+ return err;
+
+ if (mmc_host_is_spi(card->host)) {
+ /* host driver already reported errors */
+ } else {
+ if (cmd.resp[0] & R5_ERROR)
+ return -EIO;
+ if (cmd.resp[0] & R5_FUNCTION_NUMBER)
+ return -EINVAL;
+ if (cmd.resp[0] & R5_OUT_OF_RANGE)
+ return -ERANGE;
+ }
+
+ if (out) {
+ if (mmc_host_is_spi(card->host))
+ *out = (cmd.resp[0] >> 8) & 0xFF;
+ else
+ *out = cmd.resp[0] & 0xFF;
+ }
+
+ return 0;
+}
+
+int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
+ unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
+{
+ struct mmc_request mrq;
+ struct mmc_command cmd;
+ struct mmc_data data;
+ struct scatterlist sg;
+
+ BUG_ON(!card);
+ BUG_ON(fn > 7);
+ BUG_ON(blocks == 1 && blksz > 512);
+ WARN_ON(blocks == 0);
+ WARN_ON(blksz == 0);
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ memset(&data, 0, sizeof(struct mmc_data));
+
+ mrq.cmd = &cmd;
+ mrq.data = &data;
+
+ cmd.opcode = SD_IO_RW_EXTENDED;
+ cmd.arg = write ? 0x80000000 : 0x00000000;
+ cmd.arg |= fn << 28;
+ cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
+ cmd.arg |= addr << 9;
+ if (blocks == 1 && blksz <= 512)
+ cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */
+ else
+ cmd.arg |= 0x08000000 | blocks; /* block mode */
+ cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+
+ data.blksz = blksz;
+ data.blocks = blocks;
+ data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+ data.sg = &sg;
+ data.sg_len = 1;
+
+ sg_init_one(&sg, buf, blksz * blocks);
+
+ mmc_set_data_timeout(&data, card);
+
+ mmc_wait_for_req(card->host, &mrq);
+
+ if (cmd.error)
+ return cmd.error;
+ if (data.error)
+ return data.error;
+
+ if (mmc_host_is_spi(card->host)) {
+ /* host driver already reported errors */
+ } else {
+ if (cmd.resp[0] & R5_ERROR)
+ return -EIO;
+ if (cmd.resp[0] & R5_FUNCTION_NUMBER)
+ return -EINVAL;
+ if (cmd.resp[0] & R5_OUT_OF_RANGE)
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
diff --git a/drivers/mmc/core/sdio_ops.h b/drivers/mmc/core/sdio_ops.h
new file mode 100644
index 0000000..e2e74b0
--- /dev/null
+++ b/drivers/mmc/core/sdio_ops.h
@@ -0,0 +1,22 @@
+/*
+ * linux/drivers/mmc/sdio_ops.c
+ *
+ * Copyright 2006-2007 Pierre Ossman
+ *
+ * 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.
+ */
+
+#ifndef _MMC_SDIO_OPS_H
+#define _MMC_SDIO_OPS_H
+
+int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
+int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
+ unsigned addr, u8 in, u8* out);
+int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
+ unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz);
+
+#endif
+
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
new file mode 100644
index 0000000..ce66df5
--- /dev/null
+++ b/drivers/mmc/host/Kconfig
@@ -0,0 +1,205 @@
+#
+# MMC/SD host controller drivers
+#
+
+comment "MMC/SD/SDIO Host Controller Drivers"
+
+config MMC_ARMMMCI
+ tristate "ARM AMBA Multimedia Card Interface support"
+ depends on ARM_AMBA
+ help
+ This selects the ARM(R) AMBA(R) PrimeCell Multimedia Card
+ Interface (PL180 and PL181) support. If you have an ARM(R)
+ platform with a Multimedia Card slot, say Y or M here.
+
+ If unsure, say N.
+
+config MMC_PXA
+ tristate "Intel PXA25x/26x/27x Multimedia Card Interface support"
+ depends on ARCH_PXA
+ help
+ This selects the Intel(R) PXA(R) Multimedia card Interface.
+ If you have a PXA(R) platform with a Multimedia Card slot,
+ say Y or M here.
+
+ If unsure, say N.
+
+config MMC_SDHCI
+ tristate "Secure Digital Host Controller Interface support"
+ depends on HAS_DMA
+ help
+ This selects the generic Secure Digital Host Controller Interface.
+ It is used by manufacturers such as Texas Instruments(R), Ricoh(R)
+ and Toshiba(R). Most controllers found in laptops are of this type.
+
+ If you have a controller with this interface, say Y or M here. You
+ also need to enable an appropriate bus interface.
+
+ If unsure, say N.
+
+config MMC_AST
+ tristate "ASPEED Secure Digital Host Controller Interface support"
+ depends on HAS_DMA
+ help
+ This selects the ASPEED Secure Digital Host Controller Interface.
+
+ If you have a controller with this interface, say Y or M here. You
+ also need to enable an appropriate bus interface.
+
+ If unsure, say N.
+
+config MMC_SDHCI_PCI
+ tristate "SDHCI support on PCI bus"
+ depends on MMC_SDHCI && PCI
+ help
+ This selects the PCI Secure Digital Host Controller Interface.
+ Most controllers found today are PCI devices.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
+config MMC_RICOH_MMC
+ tristate "Ricoh MMC Controller Disabler (EXPERIMENTAL)"
+ depends on MMC_SDHCI_PCI
+ help
+ This selects the disabler for the Ricoh MMC Controller. This
+ proprietary controller is unnecessary because the SDHCI driver
+ supports MMC cards on the SD controller, but if it is not
+ disabled, it will steal the MMC cards away - rendering them
+ useless. It is safe to select this driver even if you don't
+ have a Ricoh based card reader.
+
+
+ To compile this driver as a module, choose M here:
+ the module will be called ricoh_mmc.
+
+ If unsure, say Y.
+
+config MMC_OMAP
+ tristate "TI OMAP Multimedia Card Interface support"
+ depends on ARCH_OMAP
+ select TPS65010 if MACH_OMAP_H2
+ help
+ This selects the TI OMAP Multimedia card Interface.
+ If you have an OMAP board with a Multimedia Card slot,
+ say Y or M here.
+
+ If unsure, say N.
+
+config MMC_WBSD
+ tristate "Winbond W83L51xD SD/MMC Card Interface support"
+ depends on ISA_DMA_API
+ help
+ This selects the Winbond(R) W83L51xD Secure digital and
+ Multimedia card Interface.
+ If you have a machine with a integrated W83L518D or W83L519D
+ SD/MMC card reader, say Y or M here.
+
+ If unsure, say N.
+
+config MMC_AU1X
+ tristate "Alchemy AU1XX0 MMC Card Interface support"
+ depends on SOC_AU1200
+ help
+ This selects the AMD Alchemy(R) Multimedia card interface.
+ If you have a Alchemy platform with a MMC slot, say Y or M here.
+
+ If unsure, say N.
+
+config MMC_AT91
+ tristate "AT91 SD/MMC Card Interface support"
+ depends on ARCH_AT91
+ help
+ This selects the AT91 MCI controller.
+
+ If unsure, say N.
+
+config MMC_ATMELMCI
+ tristate "Atmel Multimedia Card Interface support"
+ depends on AVR32
+ help
+ This selects the Atmel Multimedia Card Interface driver. If
+ you have an AT32 (AVR32) platform with a Multimedia Card
+ slot, say Y or M here.
+
+ If unsure, say N.
+
+config MMC_ATMELMCI_DMA
+ bool "Atmel MCI DMA support (EXPERIMENTAL)"
+ depends on MMC_ATMELMCI && DMA_ENGINE && EXPERIMENTAL
+ help
+ Say Y here to have the Atmel MCI driver use a DMA engine to
+ do data transfers and thus increase the throughput and
+ reduce the CPU utilization. Note that this is highly
+ experimental and may cause the driver to lock up.
+
+ If unsure, say N.
+
+config MMC_IMX
+ tristate "Motorola i.MX Multimedia Card Interface support"
+ depends on ARCH_IMX
+ help
+ This selects the Motorola i.MX Multimedia card Interface.
+ If you have a i.MX platform with a Multimedia Card slot,
+ say Y or M here.
+
+ If unsure, say N.
+
+config MMC_TIFM_SD
+ tristate "TI Flash Media MMC/SD Interface support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && PCI
+ select TIFM_CORE
+ help
+ Say Y here if you want to be able to access MMC/SD cards with
+ the Texas Instruments(R) Flash Media card reader, found in many
+ laptops.
+ This option 'selects' (turns on, enables) 'TIFM_CORE', but you
+ probably also need appropriate card reader host adapter, such as
+ 'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support
+ (TIFM_7XX1)'.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tifm_sd.
+
+config MMC_SPI
+ tristate "MMC/SD/SDIO over SPI"
+ depends on SPI_MASTER && !HIGHMEM && HAS_DMA
+ select CRC7
+ select CRC_ITU_T
+ help
+ Some systems accss MMC/SD/SDIO cards using a SPI controller
+ instead of using a "native" MMC/SD/SDIO controller. This has a
+ disadvantage of being relatively high overhead, but a compensating
+ advantage of working on many systems without dedicated MMC/SD/SDIO
+ controllers.
+
+ If unsure, or if your system has no SPI master driver, say N.
+
+config MMC_S3C
+ tristate "Samsung S3C SD/MMC Card Interface support"
+ depends on ARCH_S3C2410
+ help
+ This selects a driver for the MCI interface found in
+ Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
+ If you have a board based on one of those and a MMC/SD
+ slot, say Y or M here.
+
+ If unsure, say N.
+
+config MMC_SDRICOH_CS
+ tristate "MMC/SD driver for Ricoh Bay1Controllers (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && PCI && PCMCIA
+ help
+ Say Y here if your Notebook reports a Ricoh Bay1Controller PCMCIA
+ card whenever you insert a MMC or SD card into the card slot.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sdricoh_cs.
+
+config MMC_TMIO
+ tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support"
+ depends on MFD_TMIO
+ help
+ This provides support for the SD/MMC cell found in TC6393XB,
+ T7L66XB and also ipaq ASIC3
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
new file mode 100644
index 0000000..5078ba2
--- /dev/null
+++ b/drivers/mmc/host/Makefile
@@ -0,0 +1,26 @@
+#
+# Makefile for MMC/SD host controller drivers
+#
+
+ifeq ($(CONFIG_MMC_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
+obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
+obj-$(CONFIG_MMC_PXA) += pxamci.o
+obj-$(CONFIG_MMC_IMX) += imxmmc.o
+obj-$(CONFIG_MMC_SDHCI) += sdhci.o
+obj-$(CONFIG_MMC_AST) += ast_sdhci.o
+obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
+obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o
+obj-$(CONFIG_MMC_WBSD) += wbsd.o
+obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
+obj-$(CONFIG_MMC_OMAP) += omap.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_SPI) += mmc_spi.o
+obj-$(CONFIG_MMC_S3C) += s3cmci.o
+obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
+obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
+
diff --git a/drivers/mmc/host/ast_sdhci.c b/drivers/mmc/host/ast_sdhci.c
new file mode 100644
index 0000000..8b5d80d
--- /dev/null
+++ b/drivers/mmc/host/ast_sdhci.c
@@ -0,0 +1,1929 @@
+/*
+ * aspeed_sdhci.c - ASPEED Secure Digital Host Controller Interface driver
+ *
+ * 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/delay.h>
+#include <linux/highmem.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/platform_device.h>
+#include <mach/hardware.h>
+#include <mach/platform.h>
+
+#include <linux/leds.h>
+
+#include <linux/mmc/host.h>
+
+#include <plat/ast_sdhci.h>
+
+
+#define DRIVER_NAME "ast_sdhci"
+
+#define DBG(f, x...) \
+ pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x)
+
+static unsigned int debug_quirks = 0;
+
+struct ast_sdhc_platform_data *ast_sdhc_info;
+
+static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
+static void sdhci_finish_data(struct sdhci_host *);
+
+static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
+static void sdhci_finish_command(struct sdhci_host *);
+
+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));
+ 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));
+ printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_ARGUMENT),
+ readw(host->ioaddr + 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));
+ 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));
+ 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));
+ 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));
+ 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));
+ 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));
+ printk(KERN_DEBUG DRIVER_NAME ": Caps: 0x%08x | Max curr: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_CAPABILITIES),
+ readl(host->ioaddr + SDHCI_MAX_CURRENT));
+
+ printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n");
+}
+
+/*****************************************************************************\
+ * *
+ * Low level functions *
+ * *
+\*****************************************************************************/
+
+static void sdhci_reset(struct sdhci_host *host, u8 mask)
+{
+ unsigned long timeout;
+
+ if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
+ if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
+ SDHCI_CARD_PRESENT))
+ return;
+ }
+
+ writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET);
+
+ if (mask & SDHCI_RESET_ALL)
+ host->clock = 0;
+
+ /* Wait max 100 ms */
+ timeout = 100;
+
+ /* hw clears the bit when it's done */
+ while (readb(host->ioaddr + SDHCI_SOFTWARE_RESET) & mask) {
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
+ mmc_hostname(host->mmc), (int)mask);
+ sdhci_dumpregs(host);
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+}
+
+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_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;
+
+ writel(intmask, host->ioaddr + SDHCI_INT_ENABLE);
+ writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+}
+
+static void sdhci_activate_led(struct sdhci_host *host)
+{
+ u8 ctrl;
+
+ ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+ ctrl |= SDHCI_CTRL_LED;
+ writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+}
+
+static void sdhci_deactivate_led(struct sdhci_host *host)
+{
+ u8 ctrl;
+
+ ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+ ctrl &= ~SDHCI_CTRL_LED;
+ writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+}
+
+#ifdef CONFIG_LEDS_CLASS
+static void sdhci_led_control(struct led_classdev *led,
+ enum led_brightness brightness)
+{
+ struct sdhci_host *host = container_of(led, struct sdhci_host, led);
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (brightness == LED_OFF)
+ sdhci_deactivate_led(host);
+ else
+ sdhci_activate_led(host);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+#endif
+
+/*****************************************************************************\
+ * *
+ * Core functions *
+ * *
+\*****************************************************************************/
+
+static void sdhci_read_block_pio(struct sdhci_host *host)
+{
+ unsigned long flags;
+ size_t blksize, len, chunk;
+ u32 uninitialized_var(scratch);
+ u8 *buf;
+
+ DBG("PIO reading\n");
+
+ blksize = host->data->blksz;
+ chunk = 0;
+
+ local_irq_save(flags);
+
+ while (blksize) {
+ if (!sg_miter_next(&host->sg_miter))
+ BUG();
+
+ len = min(host->sg_miter.length, blksize);
+
+ blksize -= len;
+ host->sg_miter.consumed = len;
+
+ buf = host->sg_miter.addr;
+
+ while (len) {
+ if (chunk == 0) {
+ scratch = readl(host->ioaddr + SDHCI_BUFFER);
+ chunk = 4;
+ }
+
+ *buf = scratch & 0xFF;
+
+ buf++;
+ scratch >>= 8;
+ chunk--;
+ len--;
+ }
+ }
+
+ sg_miter_stop(&host->sg_miter);
+
+ local_irq_restore(flags);
+}
+
+static void sdhci_write_block_pio(struct sdhci_host *host)
+{
+ unsigned long flags;
+ size_t blksize, len, chunk;
+ u32 scratch;
+ u8 *buf;
+
+ DBG("PIO writing\n");
+
+ blksize = host->data->blksz;
+ chunk = 0;
+ scratch = 0;
+
+ local_irq_save(flags);
+
+ while (blksize) {
+ if (!sg_miter_next(&host->sg_miter))
+ BUG();
+
+ len = min(host->sg_miter.length, blksize);
+
+ blksize -= len;
+ host->sg_miter.consumed = len;
+
+ buf = host->sg_miter.addr;
+
+ while (len) {
+ scratch |= (u32)*buf << (chunk * 8);
+
+ buf++;
+ chunk++;
+ len--;
+
+ if ((chunk == 4) || ((len == 0) && (blksize == 0))) {
+ writel(scratch, host->ioaddr + SDHCI_BUFFER);
+ chunk = 0;
+ scratch = 0;
+ }
+ }
+ }
+
+ sg_miter_stop(&host->sg_miter);
+
+ local_irq_restore(flags);
+}
+
+static void sdhci_transfer_pio(struct sdhci_host *host)
+{
+ u32 mask;
+
+ BUG_ON(!host->data);
+
+ if (host->blocks == 0)
+ return;
+
+ if (host->data->flags & MMC_DATA_READ)
+ mask = SDHCI_DATA_AVAILABLE;
+ else
+ mask = SDHCI_SPACE_AVAILABLE;
+
+ /*
+ * Some controllers (JMicron JMB38x) mess up the buffer bits
+ * for transfers < 4 bytes. As long as it is just one block,
+ * we can ignore the bits.
+ */
+ if ((host->quirks & SDHCI_QUIRK_BROKEN_SMALL_PIO) &&
+ (host->data->blocks == 1))
+ mask = ~0;
+
+ while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+ if (host->data->flags & MMC_DATA_READ)
+ sdhci_read_block_pio(host);
+ else
+ sdhci_write_block_pio(host);
+
+ host->blocks--;
+ if (host->blocks == 0)
+ break;
+ }
+
+ DBG("PIO transfer complete.\n");
+}
+
+static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags)
+{
+ local_irq_save(*flags);
+ return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+}
+
+static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
+{
+ kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+ local_irq_restore(*flags);
+}
+
+static int sdhci_adma_table_pre(struct sdhci_host *host,
+ struct mmc_data *data)
+{
+ int direction;
+
+ u8 *desc;
+ u8 *align;
+ dma_addr_t addr;
+ dma_addr_t align_addr;
+ int len, offset;
+
+ struct scatterlist *sg;
+ int i;
+ char *buffer;
+ unsigned long flags;
+
+ /*
+ * The spec does not specify endianness of descriptor table.
+ * We currently guess that it is LE.
+ */
+
+ if (data->flags & MMC_DATA_READ)
+ direction = DMA_FROM_DEVICE;
+ else
+ direction = DMA_TO_DEVICE;
+
+ /*
+ * The ADMA descriptor table is mapped further down as we
+ * need to fill it with data first.
+ */
+
+ host->align_addr = dma_map_single(mmc_dev(host->mmc),
+ host->align_buffer, 128 * 4, direction);
+ if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
+ goto fail;
+ BUG_ON(host->align_addr & 0x3);
+
+ host->sg_count = dma_map_sg(mmc_dev(host->mmc),
+ data->sg, data->sg_len, direction);
+ if (host->sg_count == 0)
+ goto unmap_align;
+
+ desc = host->adma_desc;
+ align = host->align_buffer;
+
+ align_addr = host->align_addr;
+
+ for_each_sg(data->sg, sg, host->sg_count, i) {
+ addr = sg_dma_address(sg);
+ len = sg_dma_len(sg);
+
+ /*
+ * The SDHCI specification states that ADMA
+ * addresses must be 32-bit aligned. If they
+ * aren't, then we use a bounce buffer for
+ * the (up to three) bytes that screw up the
+ * alignment.
+ */
+ offset = (4 - (addr & 0x3)) & 0x3;
+ if (offset) {
+ if (data->flags & MMC_DATA_WRITE) {
+ buffer = sdhci_kmap_atomic(sg, &flags);
+ WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
+ memcpy(align, buffer, offset);
+ sdhci_kunmap_atomic(buffer, &flags);
+ }
+
+ desc[7] = (align_addr >> 24) & 0xff;
+ desc[6] = (align_addr >> 16) & 0xff;
+ desc[5] = (align_addr >> 8) & 0xff;
+ desc[4] = (align_addr >> 0) & 0xff;
+
+ BUG_ON(offset > 65536);
+
+ desc[3] = (offset >> 8) & 0xff;
+ desc[2] = (offset >> 0) & 0xff;
+
+ desc[1] = 0x00;
+ desc[0] = 0x21; /* tran, valid */
+
+ align += 4;
+ align_addr += 4;
+
+ desc += 8;
+
+ addr += offset;
+ len -= offset;
+ }
+
+ desc[7] = (addr >> 24) & 0xff;
+ desc[6] = (addr >> 16) & 0xff;
+ desc[5] = (addr >> 8) & 0xff;
+ desc[4] = (addr >> 0) & 0xff;
+
+ BUG_ON(len > 65536);
+
+ desc[3] = (len >> 8) & 0xff;
+ desc[2] = (len >> 0) & 0xff;
+
+ desc[1] = 0x00;
+ desc[0] = 0x21; /* tran, valid */
+
+ desc += 8;
+
+ /*
+ * If this triggers then we have a calculation bug
+ * somewhere. :/
+ */
+ WARN_ON((desc - host->adma_desc) > (128 * 2 + 1) * 4);
+ }
+
+ /*
+ * Add a terminating entry.
+ */
+ desc[7] = 0;
+ desc[6] = 0;
+ desc[5] = 0;
+ desc[4] = 0;
+
+ desc[3] = 0;
+ desc[2] = 0;
+
+ desc[1] = 0x00;
+ desc[0] = 0x03; /* nop, end, valid */
+
+ /*
+ * Resync align buffer as we might have changed it.
+ */
+ if (data->flags & MMC_DATA_WRITE) {
+ dma_sync_single_for_device(mmc_dev(host->mmc),
+ host->align_addr, 128 * 4, direction);
+ }
+
+ host->adma_addr = dma_map_single(mmc_dev(host->mmc),
+ host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE);
+ if (dma_mapping_error(mmc_dev(host->mmc), host->adma_addr))
+ goto unmap_entries;
+ BUG_ON(host->adma_addr & 0x3);
+
+ return 0;
+
+unmap_entries:
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, direction);
+unmap_align:
+ dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
+ 128 * 4, direction);
+fail:
+ return -EINVAL;
+}
+
+static void sdhci_adma_table_post(struct sdhci_host *host,
+ struct mmc_data *data)
+{
+ int direction;
+
+ struct scatterlist *sg;
+ int i, size;
+ u8 *align;
+ char *buffer;
+ unsigned long flags;
+
+ if (data->flags & MMC_DATA_READ)
+ direction = DMA_FROM_DEVICE;
+ else
+ direction = DMA_TO_DEVICE;
+
+ dma_unmap_single(mmc_dev(host->mmc), host->adma_addr,
+ (128 * 2 + 1) * 4, DMA_TO_DEVICE);
+
+ dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
+ 128 * 4, direction);
+
+ if (data->flags & MMC_DATA_READ) {
+ dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg,
+ data->sg_len, direction);
+
+ align = host->align_buffer;
+
+ for_each_sg(data->sg, sg, host->sg_count, i) {
+ if (sg_dma_address(sg) & 0x3) {
+ size = 4 - (sg_dma_address(sg) & 0x3);
+
+ buffer = sdhci_kmap_atomic(sg, &flags);
+ WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
+ memcpy(buffer, align, size);
+ sdhci_kunmap_atomic(buffer, &flags);
+
+ align += 4;
+ }
+ }
+ }
+
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, direction);
+}
+
+static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data)
+{
+ u8 count;
+ unsigned target_timeout, current_timeout;
+
+ /*
+ * If the host controller provides us with an incorrect timeout
+ * value, just skip the check and use 0xE. The hardware may take
+ * longer to time out, but that's much better than having a too-short
+ * timeout value.
+ */
+ if ((host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL))
+ return 0xE;
+
+ /* timeout in us */
+ target_timeout = data->timeout_ns / 1000 +
+ data->timeout_clks / host->clock;
+
+ /*
+ * Figure out needed cycles.
+ * We do this in steps in order to fit inside a 32 bit int.
+ * The first step is the minimum timeout, which will have a
+ * minimum resolution of 6 bits:
+ * (1) 2^13*1000 > 2^22,
+ * (2) host->timeout_clk < 2^16
+ * =>
+ * (1) / (2) > 2^6
+ */
+ count = 0;
+ current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+ while (current_timeout < target_timeout) {
+ count++;
+ current_timeout <<= 1;
+ if (count >= 0xF)
+ break;
+ }
+
+ if (count >= 0xF) {
+ printk(KERN_WARNING "%s: Too large timeout requested!\n",
+ mmc_hostname(host->mmc));
+ count = 0xE;
+ }
+
+ return count;
+}
+
+static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
+{
+ u8 count;
+ u8 ctrl;
+ int ret;
+
+ WARN_ON(host->data);
+
+ if (data == NULL)
+ return;
+
+ /* Sanity checks */
+ BUG_ON(data->blksz * data->blocks > 524288);
+ BUG_ON(data->blksz > host->mmc->max_blk_size);
+ BUG_ON(data->blocks > 65535);
+
+ host->data = data;
+ host->data_early = 0;
+
+ count = sdhci_calc_timeout(host, data);
+ writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
+
+ if (host->flags & SDHCI_USE_DMA)
+ host->flags |= SDHCI_REQ_USE_DMA;
+
+ /*
+ * FIXME: This doesn't account for merging when mapping the
+ * scatterlist.
+ */
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ int broken, i;
+ struct scatterlist *sg;
+
+ broken = 0;
+ if (host->flags & SDHCI_USE_ADMA) {
+ if (host->quirks & SDHCI_QUIRK_32BIT_ADMA_SIZE)
+ broken = 1;
+ } else {
+ if (host->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE)
+ broken = 1;
+ }
+
+ if (unlikely(broken)) {
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ if (sg->length & 0x3) {
+ DBG("Reverting to PIO because of "
+ "transfer size (%d)\n",
+ sg->length);
+ host->flags &= ~SDHCI_REQ_USE_DMA;
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * The assumption here being that alignment is the same after
+ * translation to device address space.
+ */
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ int broken, i;
+ struct scatterlist *sg;
+
+ broken = 0;
+ if (host->flags & SDHCI_USE_ADMA) {
+ /*
+ * As we use 3 byte chunks to work around
+ * alignment problems, we need to check this
+ * quirk.
+ */
+ if (host->quirks & SDHCI_QUIRK_32BIT_ADMA_SIZE)
+ broken = 1;
+ } else {
+ if (host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR)
+ broken = 1;
+ }
+
+ if (unlikely(broken)) {
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ if (sg->offset & 0x3) {
+ DBG("Reverting to PIO because of "
+ "bad alignment\n");
+ host->flags &= ~SDHCI_REQ_USE_DMA;
+ break;
+ }
+ }
+ }
+ }
+
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ if (host->flags & SDHCI_USE_ADMA) {
+ ret = sdhci_adma_table_pre(host, data);
+ if (ret) {
+ /*
+ * This only happens when someone fed
+ * us an invalid request.
+ */
+ WARN_ON(1);
+ host->flags &= ~SDHCI_REQ_USE_DMA;
+ } else {
+ writel(host->adma_addr,
+ host->ioaddr + SDHCI_ADMA_ADDRESS);
+ }
+ } else {
+ int sg_cnt;
+
+ sg_cnt = dma_map_sg(mmc_dev(host->mmc),
+ data->sg, data->sg_len,
+ (data->flags & MMC_DATA_READ) ?
+ DMA_FROM_DEVICE :
+ DMA_TO_DEVICE);
+ if (sg_cnt == 0) {
+ /*
+ * This only happens when someone fed
+ * us an invalid request.
+ */
+ WARN_ON(1);
+ host->flags &= ~SDHCI_REQ_USE_DMA;
+ } else {
+ WARN_ON(sg_cnt != 1);
+ writel(sg_dma_address(data->sg),
+ host->ioaddr + SDHCI_DMA_ADDRESS);
+ }
+ }
+ }
+
+ /*
+ * Always adjust the DMA selection as some controllers
+ * (e.g. JMicron) can't do PIO properly when the selection
+ * is ADMA.
+ */
+ if (host->version >= SDHCI_SPEC_200) {
+ ctrl = readb(host->ioaddr + 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);
+ }
+
+ if (!(host->flags & SDHCI_REQ_USE_DMA)) {
+ sg_miter_start(&host->sg_miter,
+ data->sg, data->sg_len, SG_MITER_ATOMIC);
+ host->blocks = data->blocks;
+ }
+
+ /* 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);
+}
+
+static void sdhci_set_transfer_mode(struct sdhci_host *host,
+ struct mmc_data *data)
+{
+ u16 mode;
+
+ if (data == NULL)
+ return;
+
+ WARN_ON(!host->data);
+
+ mode = SDHCI_TRNS_BLK_CNT_EN;
+ if (data->blocks > 1)
+ mode |= SDHCI_TRNS_MULTI | SDHCI_TRNS_ACMD12;
+// mode |= SDHCI_TRNS_MULTI;
+ if (data->flags & MMC_DATA_READ)
+ mode |= SDHCI_TRNS_READ;
+ if (host->flags & SDHCI_REQ_USE_DMA)
+ mode |= SDHCI_TRNS_DMA;
+
+ writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE);
+}
+
+static void sdhci_finish_data(struct sdhci_host *host)
+{
+ struct mmc_data *data;
+
+ BUG_ON(!host->data);
+
+ data = host->data;
+ host->data = NULL;
+
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ if (host->flags & SDHCI_USE_ADMA)
+ sdhci_adma_table_post(host, data);
+ else {
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, (data->flags & MMC_DATA_READ) ?
+ DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ }
+ }
+
+ /*
+ * The specification states that the block count register must
+ * be updated, but it does not specify at what point in the
+ * data flow. That makes the register entirely useless to read
+ * back so we have to assume that nothing made it to the card
+ * in the event of an error.
+ */
+ if (data->error)
+ data->bytes_xfered = 0;
+ else
+ data->bytes_xfered = data->blksz * data->blocks;
+
+ if (data->stop) {
+ /*
+ * The controller needs a reset of internal state machines
+ * upon error conditions.
+ */
+ if (data->error) {
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+ }
+
+ sdhci_send_command(host, data->stop);
+ } else
+ tasklet_schedule(&host->finish_tasklet);
+}
+
+static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ int flags;
+ u32 mask;
+ unsigned long timeout;
+
+ WARN_ON(host->cmd);
+
+ /* Wait max 10 ms */
+ timeout = 10;
+
+ mask = SDHCI_CMD_INHIBIT;
+ if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
+ mask |= SDHCI_DATA_INHIBIT;
+
+ /* We shouldn't wait for data inihibit for stop commands, even
+ though they might use busy signaling */
+ if (host->mrq->data && (cmd == host->mrq->data->stop))
+ mask &= ~SDHCI_DATA_INHIBIT;
+
+ while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: Controller never released "
+ "inhibit bit(s).\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ cmd->error = -EIO;
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ mod_timer(&host->timer, jiffies + 10 * HZ);
+
+ host->cmd = cmd;
+
+ sdhci_prepare_data(host, cmd->data);
+
+ writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);
+
+ sdhci_set_transfer_mode(host, cmd->data);
+
+ if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
+ printk(KERN_ERR "%s: Unsupported response type!\n",
+ mmc_hostname(host->mmc));
+ cmd->error = -EINVAL;
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ }
+
+ if (!(cmd->flags & MMC_RSP_PRESENT))
+ flags = SDHCI_CMD_RESP_NONE;
+ else if (cmd->flags & MMC_RSP_136)
+ flags = SDHCI_CMD_RESP_LONG;
+ else if (cmd->flags & MMC_RSP_BUSY)
+ flags = SDHCI_CMD_RESP_SHORT_BUSY;
+ else
+ flags = SDHCI_CMD_RESP_SHORT;
+
+ if (cmd->flags & MMC_RSP_CRC)
+ flags |= SDHCI_CMD_CRC;
+ if (cmd->flags & MMC_RSP_OPCODE)
+ flags |= SDHCI_CMD_INDEX;
+ if (cmd->data)
+ flags |= SDHCI_CMD_DATA;
+
+ writew(SDHCI_MAKE_CMD(cmd->opcode, flags),
+ host->ioaddr + SDHCI_COMMAND);
+}
+
+static void sdhci_finish_command(struct sdhci_host *host)
+{
+ int i;
+
+ BUG_ON(host->cmd == NULL);
+
+ if (host->cmd->flags & MMC_RSP_PRESENT) {
+ 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 +
+ SDHCI_RESPONSE + (3-i)*4) << 8;
+ if (i != 3)
+ host->cmd->resp[i] |=
+ readb(host->ioaddr +
+ SDHCI_RESPONSE + (3-i)*4-1);
+ }
+ } else {
+ host->cmd->resp[0] = readl(host->ioaddr + SDHCI_RESPONSE);
+ }
+ }
+
+ host->cmd->error = 0;
+
+ if (host->data && host->data_early)
+ sdhci_finish_data(host);
+
+ if (!host->cmd->data)
+ tasklet_schedule(&host->finish_tasklet);
+
+ host->cmd = NULL;
+}
+
+static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ int div;
+ u16 clk;
+ unsigned long timeout;
+
+ if (clock == host->clock)
+ return;
+
+ writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ if (clock == 0)
+ goto out;
+
+ for (div = 1;div < 256;div *= 2) {
+ if ((host->max_clk / div) <= clock)
+ break;
+ }
+ div >>= 1;
+
+ //Issue : For ast2300, ast2400 couldn't set div = 0 means /1 , so set source is ~50Mhz up
+
+ clk = div << SDHCI_DIVIDER_SHIFT;
+ clk |= SDHCI_CLOCK_INT_EN;
+ writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ /* Wait max 10 ms */
+ timeout = 10;
+ while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL))
+ & SDHCI_CLOCK_INT_STABLE)) {
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: Internal clock never "
+ "stabilised.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+out:
+ host->clock = clock;
+}
+
+static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
+{
+ u8 pwr;
+
+ if (host->power == power)
+ return;
+
+ if (power == (unsigned short)-1) {
+ writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
+ goto out;
+ }
+
+ /*
+ * Spec says that we should clear the power reg before setting
+ * 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);
+
+ pwr = SDHCI_POWER_ON;
+
+ switch (1 << power) {
+ case MMC_VDD_165_195:
+ pwr |= SDHCI_POWER_180;
+ break;
+ case MMC_VDD_29_30:
+ case MMC_VDD_30_31:
+ pwr |= SDHCI_POWER_300;
+ break;
+ case MMC_VDD_32_33:
+ case MMC_VDD_33_34:
+ pwr |= SDHCI_POWER_330;
+ break;
+ default:
+ BUG();
+ }
+
+ /*
+ * At least the Marvell CaFe chip gets confused if we set the voltage
+ * 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);
+
+ writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL);
+
+out:
+ host->power = power;
+}
+
+/*****************************************************************************\
+ * *
+ * MMC callbacks *
+ * *
+\*****************************************************************************/
+
+static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+
+ host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ WARN_ON(host->mrq != NULL);
+
+#ifndef CONFIG_LEDS_CLASS
+ sdhci_activate_led(host);
+#endif
+
+ host->mrq = mrq;
+
+ if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
+ || (host->flags & SDHCI_DEVICE_DEAD)) {
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ } else
+ sdhci_send_command(host, mrq->cmd);
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+ u8 ctrl;
+
+ host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->flags & SDHCI_DEVICE_DEAD)
+ goto out;
+
+ /*
+ * Reset the chip on each power off.
+ * Should clear out any weird states.
+ */
+ if (ios->power_mode == MMC_POWER_OFF) {
+ writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ sdhci_init(host);
+ }
+
+ sdhci_set_clock(host, ios->clock);
+
+ if (ios->power_mode == MMC_POWER_OFF)
+ sdhci_set_power(host, -1);
+ else
+ sdhci_set_power(host, ios->vdd);
+
+ ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+
+ if (ios->bus_width == MMC_BUS_WIDTH_4)
+ ctrl |= SDHCI_CTRL_4BITBUS;
+ else
+ ctrl &= ~SDHCI_CTRL_4BITBUS;
+
+ if (ios->timing == MMC_TIMING_SD_HS)
+ ctrl |= SDHCI_CTRL_HISPD;
+ else
+ ctrl &= ~SDHCI_CTRL_HISPD;
+
+ writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+
+ /*
+ * Some (ENE) controllers go apeshit on some ios operation,
+ * signalling timeout and CRC errors even on CMD0. Resetting
+ * it on each ios seems to solve the problem.
+ */
+ if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
+ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+out:
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int sdhci_get_ro(struct mmc_host *mmc)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+ int present;
+
+ host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->flags & SDHCI_DEVICE_DEAD)
+ present = 0;
+ else
+ present = readl(host->ioaddr + SDHCI_PRESENT_STATE);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return !(present & SDHCI_WRITE_PROTECT);
+}
+
+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);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ 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);
+
+out:
+ mmiowb();
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static const struct mmc_host_ops sdhci_ops = {
+ .request = sdhci_request,
+ .set_ios = sdhci_set_ios,
+ .get_ro = sdhci_get_ro,
+ .enable_sdio_irq = sdhci_enable_sdio_irq,
+};
+
+/*****************************************************************************\
+ * *
+ * Tasklets *
+ * *
+\*****************************************************************************/
+
+static void sdhci_tasklet_card(unsigned long param)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+
+ host = (struct sdhci_host*)param;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+ if (host->mrq) {
+ printk(KERN_ERR "%s: Card removed during transfer!\n",
+ mmc_hostname(host->mmc));
+ printk(KERN_ERR "%s: Resetting controller.\n",
+ mmc_hostname(host->mmc));
+
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ }
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ mmc_detect_change(host->mmc, msecs_to_jiffies(200));
+}
+
+static void sdhci_tasklet_finish(unsigned long param)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+ struct mmc_request *mrq;
+
+ host = (struct sdhci_host*)param;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ del_timer(&host->timer);
+
+ mrq = host->mrq;
+
+ /*
+ * The controller needs a reset of internal state machines
+ * upon error conditions.
+ */
+ if (!(host->flags & SDHCI_DEVICE_DEAD) &&
+ (mrq->cmd->error ||
+ (mrq->data && (mrq->data->error ||
+ (mrq->data->stop && mrq->data->stop->error))) ||
+ (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
+
+ /* Some controllers need this kick or reset won't work here */
+ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
+ unsigned int clock;
+
+ /* This is to force an update */
+ clock = host->clock;
+ host->clock = 0;
+ sdhci_set_clock(host, clock);
+ }
+
+ /* Spec says we should do both at the same time, but Ricoh
+ controllers do not like that. */
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+ }
+
+ host->mrq = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+
+#ifndef CONFIG_LEDS_CLASS
+ sdhci_deactivate_led(host);
+#endif
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ mmc_request_done(host->mmc, mrq);
+}
+
+static void sdhci_timeout_timer(unsigned long data)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+
+ host = (struct sdhci_host*)data;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->mrq) {
+ printk(KERN_ERR "%s: Timeout waiting for hardware "
+ "interrupt.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+
+ if (host->data) {
+ host->data->error = -ETIMEDOUT;
+ sdhci_finish_data(host);
+ } else {
+ if (host->cmd)
+ host->cmd->error = -ETIMEDOUT;
+ else
+ host->mrq->cmd->error = -ETIMEDOUT;
+
+ tasklet_schedule(&host->finish_tasklet);
+ }
+ }
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/*****************************************************************************\
+ * *
+ * Interrupt handling *
+ * *
+\*****************************************************************************/
+
+static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
+{
+ BUG_ON(intmask == 0);
+
+ if (!host->cmd) {
+ printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
+ "though no command operation was in progress.\n",
+ mmc_hostname(host->mmc), (unsigned)intmask);
+ sdhci_dumpregs(host);
+ return;
+ }
+
+ if (intmask & SDHCI_INT_TIMEOUT)
+ host->cmd->error = -ETIMEDOUT;
+ else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
+ SDHCI_INT_INDEX))
+ host->cmd->error = -EILSEQ;
+
+ if (host->cmd->error) {
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ }
+
+ /*
+ * The host can send and interrupt when the busy state has
+ * ended, allowing us to wait without wasting CPU cycles.
+ * Unfortunately this is overloaded on the "data complete"
+ * interrupt, so we need to take some care when handling
+ * it.
+ *
+ * Note: The 1.0 specification is a bit ambiguous about this
+ * feature so there might be some problems with older
+ * controllers.
+ */
+ if (host->cmd->flags & MMC_RSP_BUSY) {
+ if (host->cmd->data)
+ DBG("Cannot wait for busy signal when also "
+ "doing a data transfer");
+ else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ))
+ return;
+
+ /* The controller does not support the end-of-busy IRQ,
+ * fall through and take the SDHCI_INT_RESPONSE */
+ }
+
+ if (intmask & SDHCI_INT_RESPONSE)
+ sdhci_finish_command(host);
+}
+
+static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
+{
+ BUG_ON(intmask == 0);
+
+ if (!host->data) {
+ /*
+ * The "data complete" interrupt is also used to
+ * indicate that a busy state has ended. See comment
+ * above in sdhci_cmd_irq().
+ */
+ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) {
+ if (intmask & SDHCI_INT_DATA_END) {
+ sdhci_finish_command(host);
+ return;
+ }
+ }
+
+ printk(KERN_ERR "%s: Got data interrupt 0x%08x even "
+ "though no data operation was in progress.\n",
+ mmc_hostname(host->mmc), (unsigned)intmask);
+ sdhci_dumpregs(host);
+
+ return;
+ }
+
+ if (intmask & SDHCI_INT_DATA_TIMEOUT)
+ host->data->error = -ETIMEDOUT;
+ else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT))
+ host->data->error = -EILSEQ;
+ else if (intmask & SDHCI_INT_ADMA_ERROR)
+ host->data->error = -EIO;
+
+ if (host->data->error)
+ sdhci_finish_data(host);
+ else {
+ if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
+ sdhci_transfer_pio(host);
+
+ /*
+ * We currently don't do anything fancy with DMA
+ * boundaries, but as we can't disable the feature
+ * 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);
+
+ if (intmask & SDHCI_INT_DATA_END) {
+ if (host->cmd) {
+ /*
+ * Data managed to finish before the
+ * command completed. Make sure we do
+ * things in the proper order.
+ */
+ host->data_early = 1;
+ } else {
+ sdhci_finish_data(host);
+ }
+ }
+ }
+}
+
+static irqreturn_t sdhci_irq(int irq, void *dev_id)
+{
+ irqreturn_t result;
+ struct sdhci_host* host = dev_id;
+ u32 intmask;
+ int cardint = 0;
+
+ spin_lock(&host->lock);
+
+ intmask = readl(host->ioaddr + SDHCI_INT_STATUS);
+
+ if (!intmask || intmask == 0xffffffff) {
+ result = IRQ_NONE;
+ goto out;
+ }
+
+ DBG("*** %s got interrupt: 0x%08x\n",
+ 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);
+ 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_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_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
+ }
+
+ intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
+
+ intmask &= ~SDHCI_INT_ERROR;
+
+ 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);
+ }
+
+ intmask &= ~SDHCI_INT_BUS_POWER;
+
+ if (intmask & SDHCI_INT_CARD_INT)
+ cardint = 1;
+
+ intmask &= ~SDHCI_INT_CARD_INT;
+
+ if (intmask) {
+ printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
+ mmc_hostname(host->mmc), intmask);
+ sdhci_dumpregs(host);
+
+ writel(intmask, host->ioaddr + SDHCI_INT_STATUS);
+ }
+
+ result = IRQ_HANDLED;
+
+ mmiowb();
+out:
+ spin_unlock(&host->lock);
+
+ /*
+ * We have to delay this as it calls back into the driver.
+ */
+ if (cardint)
+ mmc_signal_sdio_irq(host->mmc);
+
+ return result;
+}
+
+/*****************************************************************************\
+ * *
+ * Suspend/resume *
+ * *
+\*****************************************************************************/
+
+#ifdef CONFIG_PM
+
+int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
+{
+ int ret;
+
+ ret = mmc_suspend_host(host->mmc, state);
+ if (ret)
+ return ret;
+
+ free_irq(host->irq, host);
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(sdhci_suspend_host);
+
+int sdhci_resume_host(struct sdhci_host *host)
+{
+ int ret;
+
+ if (host->flags & SDHCI_USE_DMA) {
+/*
+ if (host->ops->enable_dma)
+ host->ops->enable_dma(host);
+*/
+ }
+
+ ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
+ mmc_hostname(host->mmc), host);
+ if (ret)
+ return ret;
+
+ sdhci_init(host);
+ mmiowb();
+
+ ret = mmc_resume_host(host->mmc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(sdhci_resume_host);
+
+#endif /* CONFIG_PM */
+
+/*****************************************************************************\
+ * *
+ * Device allocation/registration *
+ * *
+\*****************************************************************************/
+
+struct sdhci_host *sdhci_alloc_host(struct device *dev,
+ size_t priv_size)
+{
+ struct mmc_host *mmc;
+ struct sdhci_host *host;
+
+ WARN_ON(dev == NULL);
+
+ mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);
+ if (!mmc)
+ return ERR_PTR(-ENOMEM);
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+
+ return host;
+}
+
+//EXPORT_SYMBOL_GPL(sdhci_alloc_host);
+
+int sdhci_add_host(struct sdhci_host *host)
+{
+ struct mmc_host *mmc;
+ unsigned int caps, temp;
+ int ret;
+
+ WARN_ON(host == NULL);
+ if (host == NULL)
+ return -EINVAL;
+#if 0
+ //TODO
+//Both ports's capabilities are 0, software needs to reset SDIO
+#define SDIO000 0x1e740000
+#define SDIO004 0x1e740004
+
+#define SDIO_ALL_SOFTWARE_RESET 0x01
+
+ if ((*(unsigned int*)(IO_ADDRESS(0x1E740140)) == 0) && (*(unsigned int*)(IO_ADDRESS(0x1E740240)) == 0)) {
+ temp = *(unsigned int*)(IO_ADDRESS(SDIO000));
+ *(unsigned int*)(IO_ADDRESS(SDIO000)) = temp | SDIO_ALL_SOFTWARE_RESET;
+ barrier();
+ do {
+ temp = (*(unsigned int*)(IO_ADDRESS(SDIO000)) & SDIO_ALL_SOFTWARE_RESET);
+ } while (temp == SDIO_ALL_SOFTWARE_RESET);
+ }
+ //Card detect debounce timing
+ *(unsigned int*)(IO_ADDRESS(SDIO004)) = 0x1000;
+#endif
+ ///////////////////////////////////////////////////////////////////
+
+ mmc = host->mmc;
+
+ if (debug_quirks)
+ host->quirks = debug_quirks;
+
+ sdhci_reset(host, SDHCI_RESET_ALL);
+
+ host->version = readw(host->ioaddr + SDHCI_HOST_VERSION);
+ host->version = (host->version & SDHCI_SPEC_VER_MASK)
+ >> SDHCI_SPEC_VER_SHIFT;
+ if (host->version > SDHCI_SPEC_200) {
+ printk(KERN_ERR "%s: Unknown controller version (%d). "
+ "You may experience problems.\n", mmc_hostname(mmc),
+ host->version);
+ }
+
+ caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
+
+ //Ryan Add for timeout
+ host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
+// host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+
+ if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
+ host->flags |= SDHCI_USE_DMA;
+ else if (!(caps & SDHCI_CAN_DO_DMA))
+ DBG("Controller doesn't have DMA capability\n");
+ else
+ host->flags |= SDHCI_USE_DMA;
+
+ if ((host->quirks & SDHCI_QUIRK_BROKEN_DMA) &&
+ (host->flags & SDHCI_USE_DMA)) {
+ DBG("Disabling DMA as it is marked broken\n");
+ host->flags &= ~SDHCI_USE_DMA;
+ }
+
+ if (host->flags & SDHCI_USE_DMA) {
+ if ((host->version >= SDHCI_SPEC_200) &&
+ (caps & SDHCI_CAN_DO_ADMA2))
+ host->flags |= SDHCI_USE_ADMA;
+ }
+
+ if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
+ (host->flags & SDHCI_USE_ADMA)) {
+ DBG("Disabling ADMA as it is marked broken\n");
+ host->flags &= ~SDHCI_USE_ADMA;
+ }
+
+ if (host->flags & SDHCI_USE_DMA) {
+/*
+ if (host->ops->enable_dma) {
+ if (host->ops->enable_dma(host)) {
+ printk(KERN_WARNING "%s: No suitable DMA "
+ "available. Falling back to PIO.\n",
+ mmc_hostname(mmc));
+ host->flags &= ~(SDHCI_USE_DMA | SDHCI_USE_ADMA);
+ }
+ }
+*/
+ }
+
+ if (host->flags & SDHCI_USE_ADMA) {
+ /*
+ * We need to allocate descriptors for all sg entries
+ * (128) and potentially one alignment transfer for
+ * each of those entries.
+ */
+ host->adma_desc = kmalloc((128 * 2 + 1) * 4, GFP_KERNEL);
+ host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);
+ if (!host->adma_desc || !host->align_buffer) {
+ kfree(host->adma_desc);
+ kfree(host->align_buffer);
+ printk(KERN_WARNING "%s: Unable to allocate ADMA "
+ "buffers. Falling back to standard DMA.\n",
+ mmc_hostname(mmc));
+ host->flags &= ~SDHCI_USE_ADMA;
+ }
+ }
+
+ /*
+ * If we use DMA, then it's up to the caller to set the DMA
+ * mask, but PIO does not need the hw shim so we set a new
+ * mask here in that case.
+ */
+ if (!(host->flags & SDHCI_USE_DMA)) {
+ host->dma_mask = DMA_BIT_MASK(64);
+ mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
+ }
+
+// host->max_clk =
+// (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
+
+ host->max_clk = ast_sdhc_info->sd_clock_src_get()/1000000;
+// printk("host->max_clk = %d Mhz\n",host->max_clk);
+
+ if (host->max_clk == 0) {
+ printk(KERN_ERR "%s: Hardware doesn't specify base clock "
+ "frequency.\n", mmc_hostname(mmc));
+ return -ENODEV;
+ }
+ host->max_clk *= 1000000;
+
+ //Ryan modify for calc timeout issue
+ host->timeout_clk = ast_sdhc_info->sd_clock_src_get()/1000000;
+// (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 (caps & SDHCI_TIMEOUT_CLK_UNIT)
+ host->timeout_clk *= 1000;
+
+ /*
+ * Set host parameters.
+ */
+ mmc->ops = &sdhci_ops;
+ mmc->f_min = host->max_clk / 256;
+ mmc->f_max = host->max_clk;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
+
+ if ((caps & SDHCI_CAN_DO_HISPD) ||
+ (host->quirks & SDHCI_QUIRK_FORCE_HIGHSPEED))
+ mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+
+ mmc->caps |= MMC_CAP_MMC_HIGHSPEED;
+
+ mmc->ocr_avail = 0;
+ if (caps & SDHCI_CAN_VDD_330)
+ mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
+ if (caps & SDHCI_CAN_VDD_300)
+ mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31;
+ if (caps & SDHCI_CAN_VDD_180)
+ mmc->ocr_avail |= MMC_VDD_165_195;
+
+ if (mmc->ocr_avail == 0) {
+ printk(KERN_ERR "%s: Hardware doesn't report any "
+ "support voltages.\n", mmc_hostname(mmc));
+ return -ENODEV;
+ }
+
+ spin_lock_init(&host->lock);
+
+ /*
+ * Maximum number of segments. Depends on if the hardware
+ * can do scatter/gather or not.
+ */
+ if (host->flags & SDHCI_USE_ADMA)
+ mmc->max_hw_segs = 128;
+ else if (host->flags & SDHCI_USE_DMA)
+ mmc->max_hw_segs = 1;
+ else /* PIO */
+ mmc->max_hw_segs = 128;
+ mmc->max_phys_segs = 128;
+
+ /*
+ * Maximum number of sectors in one transfer. Limited by DMA boundary
+ * size (512KiB).
+ */
+ mmc->max_req_size = 524288;
+
+ /*
+ * Maximum segment size. Could be one segment with the maximum number
+ * of bytes. When doing hardware scatter/gather, each entry cannot
+ * be larger than 64 KiB though.
+ */
+ if (host->flags & SDHCI_USE_ADMA)
+ mmc->max_seg_size = 65536;
+ else
+ mmc->max_seg_size = mmc->max_req_size;
+
+ /*
+ * 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;
+
+ /*
+ * Maximum block count.
+ */
+ mmc->max_blk_count = 65535;
+
+ /*
+ * Init tasklets.
+ */
+ tasklet_init(&host->card_tasklet,
+ sdhci_tasklet_card, (unsigned long)host);
+ tasklet_init(&host->finish_tasklet,
+ sdhci_tasklet_finish, (unsigned long)host);
+
+ setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
+
+ ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
+ mmc_hostname(mmc), host);
+ if (ret)
+ goto untasklet;
+
+ sdhci_init(host);
+
+#ifdef CONFIG_MMC_DEBUG
+ sdhci_dumpregs(host);
+#endif
+
+#ifdef CONFIG_LEDS_CLASS
+ snprintf(host->led_name, sizeof(host->led_name),
+ "%s::", mmc_hostname(mmc));
+ host->led.name = host->led_name;
+ host->led.brightness = LED_OFF;
+ host->led.default_trigger = mmc_hostname(mmc);
+ host->led.brightness_set = sdhci_led_control;
+
+ ret = led_classdev_register(mmc_dev(mmc), &host->led);
+ if (ret)
+ goto reset;
+#endif
+
+ mmiowb();
+
+ mmc_add_host(mmc);
+
+ printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s%s\n",
+ mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
+ (host->flags & SDHCI_USE_ADMA)?"A":"",
+ (host->flags & SDHCI_USE_DMA)?"DMA":"PIO");
+
+ return 0;
+
+#ifdef CONFIG_LEDS_CLASS
+reset:
+ sdhci_reset(host, SDHCI_RESET_ALL);
+ free_irq(host->irq, host);
+#endif
+untasklet:
+ tasklet_kill(&host->card_tasklet);
+ tasklet_kill(&host->finish_tasklet);
+
+ return ret;
+}
+
+//EXPORT_SYMBOL_GPL(sdhci_add_host);
+
+static int sdhci_probe(struct platform_device *pdev)
+{
+ struct sdhci_host *host;
+ struct resource *res;
+ int ret;
+ ast_sdhc_info = pdev->dev.platform_data;
+
+ host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_host));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (NULL == res) {
+ dev_err(&pdev->dev, "cannot get IORESOURCE_MEM\n");
+ ret = -ENOENT;
+ return ret;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), res->name)) {
+ dev_err(&pdev->dev, "cannot reserved region\n");
+ ret = -ENXIO;
+ return ret;
+ }
+
+ host->ioaddr = ioremap(res->start, resource_size(res));
+ if (!host->ioaddr) {
+ ret = -EIO;
+ return ret;
+ }
+
+ host->hw_name = res->name;
+ host->irq = platform_get_irq(pdev, 0);
+ if (host->irq < 0) {
+ dev_err(&pdev->dev, "no irq specified\n");
+ ret = -ENOENT;
+ return ret;
+ }
+
+ ret = sdhci_add_host(host);
+
+ return ret;
+}
+
+void sdhci_remove_host(struct sdhci_host *host, int dead)
+{
+ unsigned long flags;
+
+ if (dead) {
+ spin_lock_irqsave(&host->lock, flags);
+
+ host->flags |= SDHCI_DEVICE_DEAD;
+
+ if (host->mrq) {
+ printk(KERN_ERR "%s: Controller removed during "
+ " transfer!\n", mmc_hostname(host->mmc));
+
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+
+ mmc_remove_host(host->mmc);
+
+#ifdef CONFIG_LEDS_CLASS
+ led_classdev_unregister(&host->led);
+#endif
+
+ if (!dead)
+ sdhci_reset(host, SDHCI_RESET_ALL);
+
+ free_irq(host->irq, host);
+
+ del_timer_sync(&host->timer);
+
+ tasklet_kill(&host->card_tasklet);
+ tasklet_kill(&host->finish_tasklet);
+
+ kfree(host->adma_desc);
+ kfree(host->align_buffer);
+
+ host->adma_desc = NULL;
+ host->align_buffer = NULL;
+}
+
+EXPORT_SYMBOL_GPL(sdhci_remove_host);
+
+void sdhci_free_host(struct sdhci_host *host)
+{
+ mmc_free_host(host->mmc);
+}
+
+EXPORT_SYMBOL_GPL(sdhci_free_host);
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static struct platform_driver ast_sdhci_driver = {
+ .driver.name = "ast_sdhci",
+ .driver.owner = THIS_MODULE,
+ .probe = sdhci_probe,
+ .remove = __exit_p(sdhci_remove_host),
+#ifdef CONFIG_PM
+ .resume = sdhci_resume_host,
+ .suspend = sdhci_suspend_host,
+#endif
+};
+
+static int __init ast_sdhci_drv_init(void)
+{
+ return platform_driver_register(&ast_sdhci_driver);
+}
+
+static void __exit ast_sdhci_drv_exit(void)
+{
+ platform_driver_unregister(&ast_sdhci_driver);
+}
+
+module_init(ast_sdhci_drv_init);
+module_exit(ast_sdhci_drv_exit);
+
+module_param(debug_quirks, uint, 0444);
+
+MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx> & River Huang");
+MODULE_DESCRIPTION("ASPEED Secure Digital Host Controller Interface core driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
new file mode 100644
index 0000000..1f8b5b3
--- /dev/null
+++ b/drivers/mmc/host/at91_mci.c
@@ -0,0 +1,1250 @@
+/*
+ * linux/drivers/mmc/host/at91_mci.c - ATMEL AT91 MCI Driver
+ *
+ * Copyright (C) 2005 Cougar Creek Computing Devices Ltd, All Rights Reserved
+ *
+ * Copyright (C) 2006 Malcolm Noyes
+ *
+ * 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.
+ */
+
+/*
+ This is the AT91 MCI driver that has been tested with both MMC cards
+ and SD-cards. Boards that support write protect are now supported.
+ The CCAT91SBC001 board does not support SD cards.
+
+ The three entry points are at91_mci_request, at91_mci_set_ios
+ and at91_mci_get_ro.
+
+ SET IOS
+ This configures the device to put it into the correct mode and clock speed
+ required.
+
+ MCI REQUEST
+ MCI request processes the commands sent in the mmc_request structure. This
+ can consist of a processing command and a stop command in the case of
+ multiple block transfers.
+
+ There are three main types of request, commands, reads and writes.
+
+ Commands are straight forward. The command is submitted to the controller and
+ the request function returns. When the controller generates an interrupt to indicate
+ the command is finished, the response to the command are read and the mmc_request_done
+ function called to end the request.
+
+ Reads and writes work in a similar manner to normal commands but involve the PDC (DMA)
+ controller to manage the transfers.
+
+ A read is done from the controller directly to the scatterlist passed in from the request.
+ Due to a bug in the AT91RM9200 controller, when a read is completed, all the words are byte
+ swapped in the scatterlist buffers. AT91SAM926x are not affected by this bug.
+
+ The sequence of read interrupts is: ENDRX, RXBUFF, CMDRDY
+
+ A write is slightly different in that the bytes to write are read from the scatterlist
+ into a dma memory buffer (this is in case the source buffer should be read only). The
+ entire write buffer is then done from this single dma memory buffer.
+
+ The sequence of write interrupts is: ENDTX, TXBUFE, NOTBUSY, CMDRDY
+
+ GET RO
+ Gets the status of the write protect pin, if available.
+*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/atmel_pdc.h>
+
+#include <linux/mmc/host.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/gpio.h>
+
+#include <asm/mach/mmc.h>
+#include <mach/board.h>
+#include <mach/cpu.h>
+#include <mach/at91_mci.h>
+
+#define DRIVER_NAME "at91_mci"
+
+#define FL_SENT_COMMAND (1 << 0)
+#define FL_SENT_STOP (1 << 1)
+
+#define AT91_MCI_ERRORS (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE \
+ | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE \
+ | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)
+
+#define at91_mci_read(host, reg) __raw_readl((host)->baseaddr + (reg))
+#define at91_mci_write(host, reg, val) __raw_writel((val), (host)->baseaddr + (reg))
+
+
+/*
+ * Low level type for this driver
+ */
+struct at91mci_host
+{
+ struct mmc_host *mmc;
+ struct mmc_command *cmd;
+ struct mmc_request *request;
+
+ void __iomem *baseaddr;
+ int irq;
+
+ struct at91_mmc_data *board;
+ int present;
+
+ struct clk *mci_clk;
+
+ /*
+ * Flag indicating when the command has been sent. This is used to
+ * work out whether or not to send the stop
+ */
+ unsigned int flags;
+ /* flag for current bus settings */
+ u32 bus_mode;
+
+ /* DMA buffer used for transmitting */
+ unsigned int* buffer;
+ dma_addr_t physical_address;
+ unsigned int total_length;
+
+ /* Latest in the scatterlist that has been enabled for transfer, but not freed */
+ int in_use_index;
+
+ /* Latest in the scatterlist that has been enabled for transfer */
+ int transfer_index;
+
+ /* Timer for timeouts */
+ struct timer_list timer;
+};
+
+/*
+ * Reset the controller and restore most of the state
+ */
+static void at91_reset_host(struct at91mci_host *host)
+{
+ unsigned long flags;
+ u32 mr;
+ u32 sdcr;
+ u32 dtor;
+ u32 imr;
+
+ local_irq_save(flags);
+ imr = at91_mci_read(host, AT91_MCI_IMR);
+
+ at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
+
+ /* save current state */
+ mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;
+ sdcr = at91_mci_read(host, AT91_MCI_SDCR);
+ dtor = at91_mci_read(host, AT91_MCI_DTOR);
+
+ /* reset the controller */
+ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST);
+
+ /* restore state */
+ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+ at91_mci_write(host, AT91_MCI_MR, mr);
+ at91_mci_write(host, AT91_MCI_SDCR, sdcr);
+ at91_mci_write(host, AT91_MCI_DTOR, dtor);
+ at91_mci_write(host, AT91_MCI_IER, imr);
+
+ /* make sure sdio interrupts will fire */
+ at91_mci_read(host, AT91_MCI_SR);
+
+ local_irq_restore(flags);
+}
+
+static void at91_timeout_timer(unsigned long data)
+{
+ struct at91mci_host *host;
+
+ host = (struct at91mci_host *)data;
+
+ if (host->request) {
+ dev_err(host->mmc->parent, "Timeout waiting end of packet\n");
+
+ if (host->cmd && host->cmd->data) {
+ host->cmd->data->error = -ETIMEDOUT;
+ } else {
+ if (host->cmd)
+ host->cmd->error = -ETIMEDOUT;
+ else
+ host->request->cmd->error = -ETIMEDOUT;
+ }
+
+ at91_reset_host(host);
+ mmc_request_done(host->mmc, host->request);
+ }
+}
+
+/*
+ * Copy from sg to a dma block - used for transfers
+ */
+static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data)
+{
+ unsigned int len, i, size;
+ unsigned *dmabuf = host->buffer;
+
+ size = data->blksz * data->blocks;
+ len = data->sg_len;
+
+ /* AT91SAM926[0/3] Data Write Operation and number of bytes erratum */
+ if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
+ if (host->total_length == 12)
+ memset(dmabuf, 0, 12);
+
+ /*
+ * Just loop through all entries. Size might not
+ * be the entire list though so make sure that
+ * we do not transfer too much.
+ */
+ for (i = 0; i < len; i++) {
+ struct scatterlist *sg;
+ int amount;
+ unsigned int *sgbuffer;
+
+ sg = &data->sg[i];
+
+ sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+ amount = min(size, sg->length);
+ size -= amount;
+
+ if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */
+ int index;
+
+ for (index = 0; index < (amount / 4); index++)
+ *dmabuf++ = swab32(sgbuffer[index]);
+ } else {
+ memcpy(dmabuf, sgbuffer, amount);
+ dmabuf += amount;
+ }
+
+ kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
+
+ if (size == 0)
+ break;
+ }
+
+ /*
+ * Check that we didn't get a request to transfer
+ * more data than can fit into the SG list.
+ */
+ BUG_ON(size != 0);
+}
+
+/*
+ * Prepare a dma read
+ */
+static void at91_mci_pre_dma_read(struct at91mci_host *host)
+{
+ int i;
+ struct scatterlist *sg;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+
+ pr_debug("pre dma read\n");
+
+ cmd = host->cmd;
+ if (!cmd) {
+ pr_debug("no command\n");
+ return;
+ }
+
+ data = cmd->data;
+ if (!data) {
+ pr_debug("no data\n");
+ return;
+ }
+
+ for (i = 0; i < 2; i++) {
+ /* nothing left to transfer */
+ if (host->transfer_index >= data->sg_len) {
+ pr_debug("Nothing left to transfer (index = %d)\n", host->transfer_index);
+ break;
+ }
+
+ /* Check to see if this needs filling */
+ if (i == 0) {
+ if (at91_mci_read(host, ATMEL_PDC_RCR) != 0) {
+ pr_debug("Transfer active in current\n");
+ continue;
+ }
+ }
+ else {
+ if (at91_mci_read(host, ATMEL_PDC_RNCR) != 0) {
+ pr_debug("Transfer active in next\n");
+ continue;
+ }
+ }
+
+ /* Setup the next transfer */
+ pr_debug("Using transfer index %d\n", host->transfer_index);
+
+ sg = &data->sg[host->transfer_index++];
+ pr_debug("sg = %p\n", sg);
+
+ sg->dma_address = dma_map_page(NULL, sg_page(sg), sg->offset, sg->length, DMA_FROM_DEVICE);
+
+ pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length);
+
+ if (i == 0) {
+ at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address);
+ at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? sg->length : sg->length / 4);
+ }
+ else {
+ at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address);
+ at91_mci_write(host, ATMEL_PDC_RNCR, (data->blksz & 0x3) ? sg->length : sg->length / 4);
+ }
+ }
+
+ pr_debug("pre dma read done\n");
+}
+
+/*
+ * Handle after a dma read
+ */
+static void at91_mci_post_dma_read(struct at91mci_host *host)
+{
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+
+ pr_debug("post dma read\n");
+
+ cmd = host->cmd;
+ if (!cmd) {
+ pr_debug("no command\n");
+ return;
+ }
+
+ data = cmd->data;
+ if (!data) {
+ pr_debug("no data\n");
+ return;
+ }
+
+ while (host->in_use_index < host->transfer_index) {
+ struct scatterlist *sg;
+
+ pr_debug("finishing index %d\n", host->in_use_index);
+
+ sg = &data->sg[host->in_use_index++];
+
+ pr_debug("Unmapping page %08X\n", sg->dma_address);
+
+ dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE);
+
+ if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */
+ unsigned int *buffer;
+ int index;
+
+ /* Swap the contents of the buffer */
+ buffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+ pr_debug("buffer = %p, length = %d\n", buffer, sg->length);
+
+ for (index = 0; index < (sg->length / 4); index++)
+ buffer[index] = swab32(buffer[index]);
+
+ kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+ }
+
+ flush_dcache_page(sg_page(sg));
+
+ data->bytes_xfered += sg->length;
+ }
+
+ /* Is there another transfer to trigger? */
+ if (host->transfer_index < data->sg_len)
+ at91_mci_pre_dma_read(host);
+ else {
+ at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX);
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF);
+ }
+
+ pr_debug("post dma read done\n");
+}
+
+/*
+ * Handle transmitted data
+ */
+static void at91_mci_handle_transmitted(struct at91mci_host *host)
+{
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+
+ pr_debug("Handling the transmit\n");
+
+ /* Disable the transfer */
+ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+
+ /* Now wait for cmd ready */
+ at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE);
+
+ cmd = host->cmd;
+ if (!cmd) return;
+
+ data = cmd->data;
+ if (!data) return;
+
+ if (cmd->data->blocks > 1) {
+ pr_debug("multiple write : wait for BLKE...\n");
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
+ } else
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+}
+
+/*
+ * Update bytes tranfered count during a write operation
+ */
+static void at91_mci_update_bytes_xfered(struct at91mci_host *host)
+{
+ struct mmc_data *data;
+
+ /* always deal with the effective request (and not the current cmd) */
+
+ if (host->request->cmd && host->request->cmd->error != 0)
+ return;
+
+ if (host->request->data) {
+ data = host->request->data;
+ if (data->flags & MMC_DATA_WRITE) {
+ /* card is in IDLE mode now */
+ pr_debug("-> bytes_xfered %d, total_length = %d\n",
+ data->bytes_xfered, host->total_length);
+ data->bytes_xfered = data->blksz * data->blocks;
+ }
+ }
+}
+
+
+/*Handle after command sent ready*/
+static int at91_mci_handle_cmdrdy(struct at91mci_host *host)
+{
+ if (!host->cmd)
+ return 1;
+ else if (!host->cmd->data) {
+ if (host->flags & FL_SENT_STOP) {
+ /*After multi block write, we must wait for NOTBUSY*/
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+ } else return 1;
+ } else if (host->cmd->data->flags & MMC_DATA_WRITE) {
+ /*After sendding multi-block-write command, start DMA transfer*/
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE | AT91_MCI_BLKE);
+ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
+ }
+
+ /* command not completed, have to wait */
+ return 0;
+}
+
+
+/*
+ * Enable the controller
+ */
+static void at91_mci_enable(struct at91mci_host *host)
+{
+ unsigned int mr;
+
+ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+ at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
+ at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC);
+ mr = AT91_MCI_PDCMODE | 0x34a;
+
+ if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
+ mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF;
+
+ at91_mci_write(host, AT91_MCI_MR, mr);
+
+ /* use Slot A or B (only one at same time) */
+ at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b);
+}
+
+/*
+ * Disable the controller
+ */
+static void at91_mci_disable(struct at91mci_host *host)
+{
+ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST);
+}
+
+/*
+ * Send a command
+ */
+static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd)
+{
+ unsigned int cmdr, mr;
+ unsigned int block_length;
+ struct mmc_data *data = cmd->data;
+
+ unsigned int blocks;
+ unsigned int ier = 0;
+
+ host->cmd = cmd;
+
+ /* Needed for leaving busy state before CMD1 */
+ if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) {
+ pr_debug("Clearing timeout\n");
+ at91_mci_write(host, AT91_MCI_ARGR, 0);
+ at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_OPDCMD);
+ while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY)) {
+ /* spin */
+ pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR));
+ }
+ }
+
+ cmdr = cmd->opcode;
+
+ if (mmc_resp_type(cmd) == MMC_RSP_NONE)
+ cmdr |= AT91_MCI_RSPTYP_NONE;
+ else {
+ /* if a response is expected then allow maximum response latancy */
+ cmdr |= AT91_MCI_MAXLAT;
+ /* set 136 bit response for R2, 48 bit response otherwise */
+ if (mmc_resp_type(cmd) == MMC_RSP_R2)
+ cmdr |= AT91_MCI_RSPTYP_136;
+ else
+ cmdr |= AT91_MCI_RSPTYP_48;
+ }
+
+ if (data) {
+
+ if (cpu_is_at91rm9200() || cpu_is_at91sam9261()) {
+ if (data->blksz & 0x3) {
+ pr_debug("Unsupported block size\n");
+ cmd->error = -EINVAL;
+ mmc_request_done(host->mmc, host->request);
+ return;
+ }
+ if (data->flags & MMC_DATA_STREAM) {
+ pr_debug("Stream commands not supported\n");
+ cmd->error = -EINVAL;
+ mmc_request_done(host->mmc, host->request);
+ return;
+ }
+ }
+
+ block_length = data->blksz;
+ blocks = data->blocks;
+
+ /* always set data start - also set direction flag for read */
+ if (data->flags & MMC_DATA_READ)
+ cmdr |= (AT91_MCI_TRDIR | AT91_MCI_TRCMD_START);
+ else if (data->flags & MMC_DATA_WRITE)
+ cmdr |= AT91_MCI_TRCMD_START;
+
+ if (data->flags & MMC_DATA_STREAM)
+ cmdr |= AT91_MCI_TRTYP_STREAM;
+ if (data->blocks > 1)
+ cmdr |= AT91_MCI_TRTYP_MULTIPLE;
+ }
+ else {
+ block_length = 0;
+ blocks = 0;
+ }
+
+ if (host->flags & FL_SENT_STOP)
+ cmdr |= AT91_MCI_TRCMD_STOP;
+
+ if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
+ cmdr |= AT91_MCI_OPDCMD;
+
+ /*
+ * Set the arguments and send the command
+ */
+ pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n",
+ cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR));
+
+ if (!data) {
+ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS);
+ at91_mci_write(host, ATMEL_PDC_RPR, 0);
+ at91_mci_write(host, ATMEL_PDC_RCR, 0);
+ at91_mci_write(host, ATMEL_PDC_RNPR, 0);
+ at91_mci_write(host, ATMEL_PDC_RNCR, 0);
+ at91_mci_write(host, ATMEL_PDC_TPR, 0);
+ at91_mci_write(host, ATMEL_PDC_TCR, 0);
+ at91_mci_write(host, ATMEL_PDC_TNPR, 0);
+ at91_mci_write(host, ATMEL_PDC_TNCR, 0);
+ ier = AT91_MCI_CMDRDY;
+ } else {
+ /* zero block length and PDC mode */
+ mr = at91_mci_read(host, AT91_MCI_MR) & 0x5fff;
+ mr |= (data->blksz & 0x3) ? AT91_MCI_PDCFBYTE : 0;
+ mr |= (block_length << 16);
+ mr |= AT91_MCI_PDCMODE;
+ at91_mci_write(host, AT91_MCI_MR, mr);
+
+ if (!(cpu_is_at91rm9200() || cpu_is_at91sam9261()))
+ at91_mci_write(host, AT91_MCI_BLKR,
+ AT91_MCI_BLKR_BCNT(blocks) |
+ AT91_MCI_BLKR_BLKLEN(block_length));
+
+ /*
+ * Disable the PDC controller
+ */
+ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+
+ if (cmdr & AT91_MCI_TRCMD_START) {
+ data->bytes_xfered = 0;
+ host->transfer_index = 0;
+ host->in_use_index = 0;
+ if (cmdr & AT91_MCI_TRDIR) {
+ /*
+ * Handle a read
+ */
+ host->buffer = NULL;
+ host->total_length = 0;
+
+ at91_mci_pre_dma_read(host);
+ ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
+ }
+ else {
+ /*
+ * Handle a write
+ */
+ host->total_length = block_length * blocks;
+ /*
+ * AT91SAM926[0/3] Data Write Operation and
+ * number of bytes erratum
+ */
+ if (cpu_is_at91sam9260 () || cpu_is_at91sam9263())
+ if (host->total_length < 12)
+ host->total_length = 12;
+
+ host->buffer = kmalloc(host->total_length, GFP_KERNEL);
+ if (!host->buffer) {
+ pr_debug("Can't alloc tx buffer\n");
+ cmd->error = -ENOMEM;
+ mmc_request_done(host->mmc, host->request);
+ return;
+ }
+
+ at91_mci_sg_to_dma(host, data);
+
+ host->physical_address = dma_map_single(NULL,
+ host->buffer, host->total_length,
+ DMA_TO_DEVICE);
+
+ pr_debug("Transmitting %d bytes\n", host->total_length);
+
+ at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
+ at91_mci_write(host, ATMEL_PDC_TCR, (data->blksz & 0x3) ?
+ host->total_length : host->total_length / 4);
+
+ ier = AT91_MCI_CMDRDY;
+ }
+ }
+ }
+
+ /*
+ * Send the command and then enable the PDC - not the other way round as
+ * the data sheet says
+ */
+
+ at91_mci_write(host, AT91_MCI_ARGR, cmd->arg);
+ at91_mci_write(host, AT91_MCI_CMDR, cmdr);
+
+ if (cmdr & AT91_MCI_TRCMD_START) {
+ if (cmdr & AT91_MCI_TRDIR)
+ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN);
+ }
+
+ /* Enable selected interrupts */
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier);
+}
+
+/*
+ * Process the next step in the request
+ */
+static void at91_mci_process_next(struct at91mci_host *host)
+{
+ if (!(host->flags & FL_SENT_COMMAND)) {
+ host->flags |= FL_SENT_COMMAND;
+ at91_mci_send_command(host, host->request->cmd);
+ }
+ else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) {
+ host->flags |= FL_SENT_STOP;
+ at91_mci_send_command(host, host->request->stop);
+ } else {
+ del_timer(&host->timer);
+ /* the at91rm9200 mci controller hangs after some transfers,
+ * and the workaround is to reset it after each transfer.
+ */
+ if (cpu_is_at91rm9200())
+ at91_reset_host(host);
+ mmc_request_done(host->mmc, host->request);
+ }
+}
+
+/*
+ * Handle a command that has been completed
+ */
+static void at91_mci_completed_command(struct at91mci_host *host, unsigned int status)
+{
+ struct mmc_command *cmd = host->cmd;
+ struct mmc_data *data = cmd->data;
+
+ at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));
+
+ cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0));
+ cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1));
+ cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2));
+ cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3));
+
+ if (host->buffer) {
+ dma_unmap_single(NULL,
+ host->physical_address, host->total_length,
+ DMA_TO_DEVICE);
+ kfree(host->buffer);
+ host->buffer = NULL;
+ }
+
+ pr_debug("Status = %08X/%08x [%08X %08X %08X %08X]\n",
+ status, at91_mci_read(host, AT91_MCI_SR),
+ cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
+
+ if (status & AT91_MCI_ERRORS) {
+ if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) {
+ cmd->error = 0;
+ }
+ else {
+ if (status & (AT91_MCI_DTOE | AT91_MCI_DCRCE)) {
+ if (data) {
+ if (status & AT91_MCI_DTOE)
+ data->error = -ETIMEDOUT;
+ else if (status & AT91_MCI_DCRCE)
+ data->error = -EILSEQ;
+ }
+ } else {
+ if (status & AT91_MCI_RTOE)
+ cmd->error = -ETIMEDOUT;
+ else if (status & AT91_MCI_RCRCE)
+ cmd->error = -EILSEQ;
+ else
+ cmd->error = -EIO;
+ }
+
+ pr_debug("Error detected and set to %d/%d (cmd = %d, retries = %d)\n",
+ cmd->error, data ? data->error : 0,
+ cmd->opcode, cmd->retries);
+ }
+ }
+ else
+ cmd->error = 0;
+
+ at91_mci_process_next(host);
+}
+
+/*
+ * Handle an MMC request
+ */
+static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct at91mci_host *host = mmc_priv(mmc);
+ host->request = mrq;
+ host->flags = 0;
+
+ mod_timer(&host->timer, jiffies + HZ);
+
+ at91_mci_process_next(host);
+}
+
+/*
+ * Set the IOS
+ */
+static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ int clkdiv;
+ struct at91mci_host *host = mmc_priv(mmc);
+ unsigned long at91_master_clock = clk_get_rate(host->mci_clk);
+
+ host->bus_mode = ios->bus_mode;
+
+ if (ios->clock == 0) {
+ /* Disable the MCI controller */
+ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS);
+ clkdiv = 0;
+ }
+ else {
+ /* Enable the MCI controller */
+ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+
+ if ((at91_master_clock % (ios->clock * 2)) == 0)
+ clkdiv = ((at91_master_clock / ios->clock) / 2) - 1;
+ else
+ clkdiv = (at91_master_clock / ios->clock) / 2;
+
+ pr_debug("clkdiv = %d. mcck = %ld\n", clkdiv,
+ at91_master_clock / (2 * (clkdiv + 1)));
+ }
+ if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) {
+ pr_debug("MMC: Setting controller bus width to 4\n");
+ at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) | AT91_MCI_SDCBUS);
+ }
+ else {
+ pr_debug("MMC: Setting controller bus width to 1\n");
+ at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS);
+ }
+
+ /* Set the clock divider */
+ at91_mci_write(host, AT91_MCI_MR, (at91_mci_read(host, AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv);
+
+ /* maybe switch power to the card */
+ if (host->board->vcc_pin) {
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ gpio_set_value(host->board->vcc_pin, 0);
+ break;
+ case MMC_POWER_UP:
+ gpio_set_value(host->board->vcc_pin, 1);
+ break;
+ case MMC_POWER_ON:
+ break;
+ default:
+ WARN_ON(1);
+ }
+ }
+}
+
+/*
+ * Handle an interrupt
+ */
+static irqreturn_t at91_mci_irq(int irq, void *devid)
+{
+ struct at91mci_host *host = devid;
+ int completed = 0;
+ unsigned int int_status, int_mask;
+
+ int_status = at91_mci_read(host, AT91_MCI_SR);
+ int_mask = at91_mci_read(host, AT91_MCI_IMR);
+
+ pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask,
+ int_status & int_mask);
+
+ int_status = int_status & int_mask;
+
+ if (int_status & AT91_MCI_ERRORS) {
+ completed = 1;
+
+ if (int_status & AT91_MCI_UNRE)
+ pr_debug("MMC: Underrun error\n");
+ if (int_status & AT91_MCI_OVRE)
+ pr_debug("MMC: Overrun error\n");
+ if (int_status & AT91_MCI_DTOE)
+ pr_debug("MMC: Data timeout\n");
+ if (int_status & AT91_MCI_DCRCE)
+ pr_debug("MMC: CRC error in data\n");
+ if (int_status & AT91_MCI_RTOE)
+ pr_debug("MMC: Response timeout\n");
+ if (int_status & AT91_MCI_RENDE)
+ pr_debug("MMC: Response end bit error\n");
+ if (int_status & AT91_MCI_RCRCE)
+ pr_debug("MMC: Response CRC error\n");
+ if (int_status & AT91_MCI_RDIRE)
+ pr_debug("MMC: Response direction error\n");
+ if (int_status & AT91_MCI_RINDE)
+ pr_debug("MMC: Response index error\n");
+ } else {
+ /* Only continue processing if no errors */
+
+ if (int_status & AT91_MCI_TXBUFE) {
+ pr_debug("TX buffer empty\n");
+ at91_mci_handle_transmitted(host);
+ }
+
+ if (int_status & AT91_MCI_ENDRX) {
+ pr_debug("ENDRX\n");
+ at91_mci_post_dma_read(host);
+ }
+
+ if (int_status & AT91_MCI_RXBUFF) {
+ pr_debug("RX buffer full\n");
+ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+ at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXBUFF | AT91_MCI_ENDRX);
+ completed = 1;
+ }
+
+ if (int_status & AT91_MCI_ENDTX)
+ pr_debug("Transmit has ended\n");
+
+ if (int_status & AT91_MCI_NOTBUSY) {
+ pr_debug("Card is ready\n");
+ at91_mci_update_bytes_xfered(host);
+ completed = 1;
+ }
+
+ if (int_status & AT91_MCI_DTIP)
+ pr_debug("Data transfer in progress\n");
+
+ if (int_status & AT91_MCI_BLKE) {
+ pr_debug("Block transfer has ended\n");
+ if (host->request->data && host->request->data->blocks > 1) {
+ /* multi block write : complete multi write
+ * command and send stop */
+ completed = 1;
+ } else {
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+ }
+ }
+
+ if (int_status & AT91_MCI_SDIOIRQA)
+ mmc_signal_sdio_irq(host->mmc);
+
+ if (int_status & AT91_MCI_SDIOIRQB)
+ mmc_signal_sdio_irq(host->mmc);
+
+ if (int_status & AT91_MCI_TXRDY)
+ pr_debug("Ready to transmit\n");
+
+ if (int_status & AT91_MCI_RXRDY)
+ pr_debug("Ready to receive\n");
+
+ if (int_status & AT91_MCI_CMDRDY) {
+ pr_debug("Command ready\n");
+ completed = at91_mci_handle_cmdrdy(host);
+ }
+ }
+
+ if (completed) {
+ pr_debug("Completed command\n");
+ at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));
+ at91_mci_completed_command(host, int_status);
+ } else
+ at91_mci_write(host, AT91_MCI_IDR, int_status & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB));
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t at91_mmc_det_irq(int irq, void *_host)
+{
+ struct at91mci_host *host = _host;
+ int present = !gpio_get_value(irq_to_gpio(irq));
+
+ /*
+ * we expect this irq on both insert and remove,
+ * and use a short delay to debounce.
+ */
+ if (present != host->present) {
+ host->present = present;
+ pr_debug("%s: card %s\n", mmc_hostname(host->mmc),
+ present ? "insert" : "remove");
+ if (!present) {
+ pr_debug("****** Resetting SD-card bus width ******\n");
+ at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS);
+ }
+ mmc_detect_change(host->mmc, msecs_to_jiffies(100));
+ }
+ return IRQ_HANDLED;
+}
+
+static int at91_mci_get_ro(struct mmc_host *mmc)
+{
+ struct at91mci_host *host = mmc_priv(mmc);
+
+ if (host->board->wp_pin)
+ return !!gpio_get_value(host->board->wp_pin);
+ /*
+ * Board doesn't support read only detection; let the mmc core
+ * decide what to do.
+ */
+ return -ENOSYS;
+}
+
+static void at91_mci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct at91mci_host *host = mmc_priv(mmc);
+
+ pr_debug("%s: sdio_irq %c : %s\n", mmc_hostname(host->mmc),
+ host->board->slot_b ? 'B':'A', enable ? "enable" : "disable");
+ at91_mci_write(host, enable ? AT91_MCI_IER : AT91_MCI_IDR,
+ host->board->slot_b ? AT91_MCI_SDIOIRQB : AT91_MCI_SDIOIRQA);
+
+}
+
+static const struct mmc_host_ops at91_mci_ops = {
+ .request = at91_mci_request,
+ .set_ios = at91_mci_set_ios,
+ .get_ro = at91_mci_get_ro,
+ .enable_sdio_irq = at91_mci_enable_sdio_irq,
+};
+
+/*
+ * Probe for the device
+ */
+static int __init at91_mci_probe(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct at91mci_host *host;
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
+ if (!request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME))
+ return -EBUSY;
+
+ mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "couldn't allocate mmc host\n");
+ goto fail6;
+ }
+
+ mmc->ops = &at91_mci_ops;
+ mmc->f_min = 375000;
+ mmc->f_max = 25000000;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_SDIO_IRQ;
+
+ mmc->max_blk_size = 4095;
+ mmc->max_blk_count = mmc->max_req_size;
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->buffer = NULL;
+ host->bus_mode = 0;
+ host->board = pdev->dev.platform_data;
+ if (host->board->wire4) {
+ if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+ else
+ dev_warn(&pdev->dev, "4 wire bus mode not supported"
+ " - using 1 wire\n");
+ }
+
+ /*
+ * Reserve GPIOs ... board init code makes sure these pins are set
+ * up as GPIOs with the right direction (input, except for vcc)
+ */
+ if (host->board->det_pin) {
+ ret = gpio_request(host->board->det_pin, "mmc_detect");
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "couldn't claim card detect pin\n");
+ goto fail5;
+ }
+ }
+ if (host->board->wp_pin) {
+ ret = gpio_request(host->board->wp_pin, "mmc_wp");
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "couldn't claim wp sense pin\n");
+ goto fail4;
+ }
+ }
+ if (host->board->vcc_pin) {
+ ret = gpio_request(host->board->vcc_pin, "mmc_vcc");
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "couldn't claim vcc switch pin\n");
+ goto fail3;
+ }
+ }
+
+ /*
+ * Get Clock
+ */
+ host->mci_clk = clk_get(&pdev->dev, "mci_clk");
+ if (IS_ERR(host->mci_clk)) {
+ ret = -ENODEV;
+ dev_dbg(&pdev->dev, "no mci_clk?\n");
+ goto fail2;
+ }
+
+ /*
+ * Map I/O region
+ */
+ host->baseaddr = ioremap(res->start, res->end - res->start + 1);
+ if (!host->baseaddr) {
+ ret = -ENOMEM;
+ goto fail1;
+ }
+
+ /*
+ * Reset hardware
+ */
+ clk_enable(host->mci_clk); /* Enable the peripheral clock */
+ at91_mci_disable(host);
+ at91_mci_enable(host);
+
+ /*
+ * Allocate the MCI interrupt
+ */
+ host->irq = platform_get_irq(pdev, 0);
+ ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED,
+ mmc_hostname(mmc), host);
+ if (ret) {
+ dev_dbg(&pdev->dev, "request MCI interrupt failed\n");
+ goto fail0;
+ }
+
+ platform_set_drvdata(pdev, mmc);
+
+ /*
+ * Add host to MMC layer
+ */
+ if (host->board->det_pin) {
+ host->present = !gpio_get_value(host->board->det_pin);
+ }
+ else
+ host->present = -1;
+
+ mmc_add_host(mmc);
+
+ setup_timer(&host->timer, at91_timeout_timer, (unsigned long)host);
+
+ /*
+ * monitor card insertion/removal if we can
+ */
+ if (host->board->det_pin) {
+ ret = request_irq(gpio_to_irq(host->board->det_pin),
+ at91_mmc_det_irq, 0, mmc_hostname(mmc), host);
+ if (ret)
+ dev_warn(&pdev->dev, "request MMC detect irq failed\n");
+ else
+ device_init_wakeup(&pdev->dev, 1);
+ }
+
+ pr_debug("Added MCI driver\n");
+
+ return 0;
+
+fail0:
+ clk_disable(host->mci_clk);
+ iounmap(host->baseaddr);
+fail1:
+ clk_put(host->mci_clk);
+fail2:
+ if (host->board->vcc_pin)
+ gpio_free(host->board->vcc_pin);
+fail3:
+ if (host->board->wp_pin)
+ gpio_free(host->board->wp_pin);
+fail4:
+ if (host->board->det_pin)
+ gpio_free(host->board->det_pin);
+fail5:
+ mmc_free_host(mmc);
+fail6:
+ release_mem_region(res->start, res->end - res->start + 1);
+ dev_err(&pdev->dev, "probe failed, err %d\n", ret);
+ return ret;
+}
+
+/*
+ * Remove a device
+ */
+static int __exit at91_mci_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct at91mci_host *host;
+ struct resource *res;
+
+ if (!mmc)
+ return -1;
+
+ host = mmc_priv(mmc);
+
+ if (host->board->det_pin) {
+ if (device_can_wakeup(&pdev->dev))
+ free_irq(gpio_to_irq(host->board->det_pin), host);
+ device_init_wakeup(&pdev->dev, 0);
+ gpio_free(host->board->det_pin);
+ }
+
+ at91_mci_disable(host);
+ del_timer_sync(&host->timer);
+ mmc_remove_host(mmc);
+ free_irq(host->irq, host);
+
+ clk_disable(host->mci_clk); /* Disable the peripheral clock */
+ clk_put(host->mci_clk);
+
+ if (host->board->vcc_pin)
+ gpio_free(host->board->vcc_pin);
+ if (host->board->wp_pin)
+ gpio_free(host->board->wp_pin);
+
+ iounmap(host->baseaddr);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, res->end - res->start + 1);
+
+ mmc_free_host(mmc);
+ platform_set_drvdata(pdev, NULL);
+ pr_debug("MCI Removed\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct at91mci_host *host = mmc_priv(mmc);
+ int ret = 0;
+
+ if (host->board->det_pin && device_may_wakeup(&pdev->dev))
+ enable_irq_wake(host->board->det_pin);
+
+ if (mmc)
+ ret = mmc_suspend_host(mmc, state);
+
+ return ret;
+}
+
+static int at91_mci_resume(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct at91mci_host *host = mmc_priv(mmc);
+ int ret = 0;
+
+ if (host->board->det_pin && device_may_wakeup(&pdev->dev))
+ disable_irq_wake(host->board->det_pin);
+
+ if (mmc)
+ ret = mmc_resume_host(mmc);
+
+ return ret;
+}
+#else
+#define at91_mci_suspend NULL
+#define at91_mci_resume NULL
+#endif
+
+static struct platform_driver at91_mci_driver = {
+ .remove = __exit_p(at91_mci_remove),
+ .suspend = at91_mci_suspend,
+ .resume = at91_mci_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init at91_mci_init(void)
+{
+ return platform_driver_probe(&at91_mci_driver, at91_mci_probe);
+}
+
+static void __exit at91_mci_exit(void)
+{
+ platform_driver_unregister(&at91_mci_driver);
+}
+
+module_init(at91_mci_init);
+module_exit(at91_mci_exit);
+
+MODULE_DESCRIPTION("AT91 Multimedia Card Interface driver");
+MODULE_AUTHOR("Nick Randell");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:at91_mci");
diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h
new file mode 100644
index 0000000..b58364e
--- /dev/null
+++ b/drivers/mmc/host/atmel-mci-regs.h
@@ -0,0 +1,95 @@
+/*
+ * Atmel MultiMedia Card Interface driver
+ *
+ * Copyright (C) 2004-2006 Atmel Corporation
+ *
+ * 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 __DRIVERS_MMC_ATMEL_MCI_H__
+#define __DRIVERS_MMC_ATMEL_MCI_H__
+
+/* MCI Register Definitions */
+#define MCI_CR 0x0000 /* Control */
+# define MCI_CR_MCIEN ( 1 << 0) /* MCI Enable */
+# define MCI_CR_MCIDIS ( 1 << 1) /* MCI Disable */
+# define MCI_CR_SWRST ( 1 << 7) /* Software Reset */
+#define MCI_MR 0x0004 /* Mode */
+# define MCI_MR_CLKDIV(x) ((x) << 0) /* Clock Divider */
+# define MCI_MR_RDPROOF ( 1 << 11) /* Read Proof */
+# define MCI_MR_WRPROOF ( 1 << 12) /* Write Proof */
+#define MCI_DTOR 0x0008 /* Data Timeout */
+# define MCI_DTOCYC(x) ((x) << 0) /* Data Timeout Cycles */
+# define MCI_DTOMUL(x) ((x) << 4) /* Data Timeout Multiplier */
+#define MCI_SDCR 0x000c /* SD Card / SDIO */
+# define MCI_SDCSEL_SLOT_A ( 0 << 0) /* Select SD slot A */
+# define MCI_SDCSEL_SLOT_B ( 1 << 0) /* Select SD slot A */
+# define MCI_SDCSEL_MASK ( 3 << 0)
+# define MCI_SDCBUS_1BIT ( 0 << 6) /* 1-bit data bus */
+# define MCI_SDCBUS_4BIT ( 2 << 6) /* 4-bit data bus */
+# define MCI_SDCBUS_MASK ( 3 << 6)
+#define MCI_ARGR 0x0010 /* Command Argument */
+#define MCI_CMDR 0x0014 /* Command */
+# define MCI_CMDR_CMDNB(x) ((x) << 0) /* Command Opcode */
+# define MCI_CMDR_RSPTYP_NONE ( 0 << 6) /* No response */
+# define MCI_CMDR_RSPTYP_48BIT ( 1 << 6) /* 48-bit response */
+# define MCI_CMDR_RSPTYP_136BIT ( 2 << 6) /* 136-bit response */
+# define MCI_CMDR_SPCMD_INIT ( 1 << 8) /* Initialization command */
+# define MCI_CMDR_SPCMD_SYNC ( 2 << 8) /* Synchronized command */
+# define MCI_CMDR_SPCMD_INT ( 4 << 8) /* Interrupt command */
+# define MCI_CMDR_SPCMD_INTRESP ( 5 << 8) /* Interrupt response */
+# define MCI_CMDR_OPDCMD ( 1 << 11) /* Open Drain */
+# define MCI_CMDR_MAXLAT_5CYC ( 0 << 12) /* Max latency 5 cycles */
+# define MCI_CMDR_MAXLAT_64CYC ( 1 << 12) /* Max latency 64 cycles */
+# define MCI_CMDR_START_XFER ( 1 << 16) /* Start data transfer */
+# define MCI_CMDR_STOP_XFER ( 2 << 16) /* Stop data transfer */
+# define MCI_CMDR_TRDIR_WRITE ( 0 << 18) /* Write data */
+# define MCI_CMDR_TRDIR_READ ( 1 << 18) /* Read data */
+# define MCI_CMDR_BLOCK ( 0 << 19) /* Single-block transfer */
+# define MCI_CMDR_MULTI_BLOCK ( 1 << 19) /* Multi-block transfer */
+# define MCI_CMDR_STREAM ( 2 << 19) /* MMC Stream transfer */
+# define MCI_CMDR_SDIO_BYTE ( 4 << 19) /* SDIO Byte transfer */
+# define MCI_CMDR_SDIO_BLOCK ( 5 << 19) /* SDIO Block transfer */
+# define MCI_CMDR_SDIO_SUSPEND ( 1 << 24) /* SDIO Suspend Command */
+# define MCI_CMDR_SDIO_RESUME ( 2 << 24) /* SDIO Resume Command */
+#define MCI_BLKR 0x0018 /* Block */
+# define MCI_BCNT(x) ((x) << 0) /* Data Block Count */
+# define MCI_BLKLEN(x) ((x) << 16) /* Data Block Length */
+#define MCI_RSPR 0x0020 /* Response 0 */
+#define MCI_RSPR1 0x0024 /* Response 1 */
+#define MCI_RSPR2 0x0028 /* Response 2 */
+#define MCI_RSPR3 0x002c /* Response 3 */
+#define MCI_RDR 0x0030 /* Receive Data */
+#define MCI_TDR 0x0034 /* Transmit Data */
+#define MCI_SR 0x0040 /* Status */
+#define MCI_IER 0x0044 /* Interrupt Enable */
+#define MCI_IDR 0x0048 /* Interrupt Disable */
+#define MCI_IMR 0x004c /* Interrupt Mask */
+# define MCI_CMDRDY ( 1 << 0) /* Command Ready */
+# define MCI_RXRDY ( 1 << 1) /* Receiver Ready */
+# define MCI_TXRDY ( 1 << 2) /* Transmitter Ready */
+# define MCI_BLKE ( 1 << 3) /* Data Block Ended */
+# define MCI_DTIP ( 1 << 4) /* Data Transfer In Progress */
+# define MCI_NOTBUSY ( 1 << 5) /* Data Not Busy */
+# define MCI_SDIOIRQA ( 1 << 8) /* SDIO IRQ in slot A */
+# define MCI_SDIOIRQB ( 1 << 9) /* SDIO IRQ in slot B */
+# define MCI_RINDE ( 1 << 16) /* Response Index Error */
+# define MCI_RDIRE ( 1 << 17) /* Response Direction Error */
+# define MCI_RCRCE ( 1 << 18) /* Response CRC Error */
+# define MCI_RENDE ( 1 << 19) /* Response End Bit Error */
+# define MCI_RTOE ( 1 << 20) /* Response Time-Out Error */
+# define MCI_DCRCE ( 1 << 21) /* Data CRC Error */
+# define MCI_DTOE ( 1 << 22) /* Data Time-Out Error */
+# define MCI_OVRE ( 1 << 30) /* RX Overrun Error */
+# define MCI_UNRE ( 1 << 31) /* TX Underrun Error */
+
+#define MCI_REGS_SIZE 0x100
+
+/* Register access macros */
+#define mci_readl(port,reg) \
+ __raw_readl((port)->regs + MCI_##reg)
+#define mci_writel(port,reg,value) \
+ __raw_writel((value), (port)->regs + MCI_##reg)
+
+#endif /* __DRIVERS_MMC_ATMEL_MCI_H__ */
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
new file mode 100644
index 0000000..7a3f243
--- /dev/null
+++ b/drivers/mmc/host/atmel-mci.c
@@ -0,0 +1,1769 @@
+/*
+ * Atmel MultiMedia Card Interface driver
+ *
+ * Copyright (C) 2004-2008 Atmel Corporation
+ *
+ * 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/blkdev.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/seq_file.h>
+#include <linux/stat.h>
+
+#include <linux/mmc/host.h>
+
+#include <asm/atmel-mci.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+
+#include <mach/board.h>
+
+#include "atmel-mci-regs.h"
+
+#define ATMCI_DATA_ERROR_FLAGS (MCI_DCRCE | MCI_DTOE | MCI_OVRE | MCI_UNRE)
+#define ATMCI_DMA_THRESHOLD 16
+
+enum {
+ EVENT_CMD_COMPLETE = 0,
+ EVENT_XFER_COMPLETE,
+ EVENT_DATA_COMPLETE,
+ EVENT_DATA_ERROR,
+};
+
+enum atmel_mci_state {
+ STATE_IDLE = 0,
+ STATE_SENDING_CMD,
+ STATE_SENDING_DATA,
+ STATE_DATA_BUSY,
+ STATE_SENDING_STOP,
+ STATE_DATA_ERROR,
+};
+
+struct atmel_mci_dma {
+#ifdef CONFIG_MMC_ATMELMCI_DMA
+ struct dma_client client;
+ struct dma_chan *chan;
+ struct dma_async_tx_descriptor *data_desc;
+#endif
+};
+
+/**
+ * struct atmel_mci - MMC controller state shared between all slots
+ * @lock: Spinlock protecting the queue and associated data.
+ * @regs: Pointer to MMIO registers.
+ * @sg: Scatterlist entry currently being processed by PIO code, if any.
+ * @pio_offset: Offset into the current scatterlist entry.
+ * @cur_slot: The slot which is currently using the controller.
+ * @mrq: The request currently being processed on @cur_slot,
+ * or NULL if the controller is idle.
+ * @cmd: The command currently being sent to the card, or NULL.
+ * @data: The data currently being transferred, or NULL if no data
+ * transfer is in progress.
+ * @dma: DMA client state.
+ * @data_chan: DMA channel being used for the current data transfer.
+ * @cmd_status: Snapshot of SR taken upon completion of the current
+ * command. Only valid when EVENT_CMD_COMPLETE is pending.
+ * @data_status: Snapshot of SR taken upon completion of the current
+ * data transfer. Only valid when EVENT_DATA_COMPLETE or
+ * EVENT_DATA_ERROR is pending.
+ * @stop_cmdr: Value to be loaded into CMDR when the stop command is
+ * to be sent.
+ * @tasklet: Tasklet running the request state machine.
+ * @pending_events: Bitmask of events flagged by the interrupt handler
+ * to be processed by the tasklet.
+ * @completed_events: Bitmask of events which the state machine has
+ * processed.
+ * @state: Tasklet state.
+ * @queue: List of slots waiting for access to the controller.
+ * @need_clock_update: Update the clock rate before the next request.
+ * @need_reset: Reset controller before next request.
+ * @mode_reg: Value of the MR register.
+ * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus
+ * rate and timeout calculations.
+ * @mapbase: Physical address of the MMIO registers.
+ * @mck: The peripheral bus clock hooked up to the MMC controller.
+ * @pdev: Platform device associated with the MMC controller.
+ * @slot: Slots sharing this MMC controller.
+ *
+ * Locking
+ * =======
+ *
+ * @lock is a softirq-safe spinlock protecting @queue as well as
+ * @cur_slot, @mrq and @state. These must always be updated
+ * at the same time while holding @lock.
+ *
+ * @lock also protects mode_reg and need_clock_update since these are
+ * used to synchronize mode register updates with the queue
+ * processing.
+ *
+ * The @mrq field of struct atmel_mci_slot is also protected by @lock,
+ * and must always be written at the same time as the slot is added to
+ * @queue.
+ *
+ * @pending_events and @completed_events are accessed using atomic bit
+ * operations, so they don't need any locking.
+ *
+ * None of the fields touched by the interrupt handler need any
+ * locking. However, ordering is important: Before EVENT_DATA_ERROR or
+ * EVENT_DATA_COMPLETE is set in @pending_events, all data-related
+ * interrupts must be disabled and @data_status updated with a
+ * snapshot of SR. Similarly, before EVENT_CMD_COMPLETE is set, the
+ * CMDRDY interupt must be disabled and @cmd_status updated with a
+ * snapshot of SR, and before EVENT_XFER_COMPLETE can be set, the
+ * bytes_xfered field of @data must be written. This is ensured by
+ * using barriers.
+ */
+struct atmel_mci {
+ spinlock_t lock;
+ void __iomem *regs;
+
+ struct scatterlist *sg;
+ unsigned int pio_offset;
+
+ struct atmel_mci_slot *cur_slot;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+
+ struct atmel_mci_dma dma;
+ struct dma_chan *data_chan;
+
+ u32 cmd_status;
+ u32 data_status;
+ u32 stop_cmdr;
+
+ struct tasklet_struct tasklet;
+ unsigned long pending_events;
+ unsigned long completed_events;
+ enum atmel_mci_state state;
+ struct list_head queue;
+
+ bool need_clock_update;
+ bool need_reset;
+ u32 mode_reg;
+ unsigned long bus_hz;
+ unsigned long mapbase;
+ struct clk *mck;
+ struct platform_device *pdev;
+
+ struct atmel_mci_slot *slot[ATMEL_MCI_MAX_NR_SLOTS];
+};
+
+/**
+ * struct atmel_mci_slot - MMC slot state
+ * @mmc: The mmc_host representing this slot.
+ * @host: The MMC controller this slot is using.
+ * @sdc_reg: Value of SDCR to be written before using this slot.
+ * @mrq: mmc_request currently being processed or waiting to be
+ * processed, or NULL when the slot is idle.
+ * @queue_node: List node for placing this node in the @queue list of
+ * &struct atmel_mci.
+ * @clock: Clock rate configured by set_ios(). Protected by host->lock.
+ * @flags: Random state bits associated with the slot.
+ * @detect_pin: GPIO pin used for card detection, or negative if not
+ * available.
+ * @wp_pin: GPIO pin used for card write protect sending, or negative
+ * if not available.
+ * @detect_timer: Timer used for debouncing @detect_pin interrupts.
+ */
+struct atmel_mci_slot {
+ struct mmc_host *mmc;
+ struct atmel_mci *host;
+
+ u32 sdc_reg;
+
+ struct mmc_request *mrq;
+ struct list_head queue_node;
+
+ unsigned int clock;
+ unsigned long flags;
+#define ATMCI_CARD_PRESENT 0
+#define ATMCI_CARD_NEED_INIT 1
+#define ATMCI_SHUTDOWN 2
+
+ int detect_pin;
+ int wp_pin;
+
+ struct timer_list detect_timer;
+};
+
+#define atmci_test_and_clear_pending(host, event) \
+ test_and_clear_bit(event, &host->pending_events)
+#define atmci_set_completed(host, event) \
+ set_bit(event, &host->completed_events)
+#define atmci_set_pending(host, event) \
+ set_bit(event, &host->pending_events)
+
+/*
+ * The debugfs stuff below is mostly optimized away when
+ * CONFIG_DEBUG_FS is not set.
+ */
+static int atmci_req_show(struct seq_file *s, void *v)
+{
+ struct atmel_mci_slot *slot = s->private;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_command *stop;
+ struct mmc_data *data;
+
+ /* Make sure we get a consistent snapshot */
+ spin_lock_bh(&slot->host->lock);
+ mrq = slot->mrq;
+
+ if (mrq) {
+ cmd = mrq->cmd;
+ data = mrq->data;
+ stop = mrq->stop;
+
+ if (cmd)
+ seq_printf(s,
+ "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+ cmd->opcode, cmd->arg, cmd->flags,
+ cmd->resp[0], cmd->resp[1], cmd->resp[2],
+ cmd->resp[2], cmd->error);
+ if (data)
+ seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
+ data->bytes_xfered, data->blocks,
+ data->blksz, data->flags, data->error);
+ if (stop)
+ seq_printf(s,
+ "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
+ stop->opcode, stop->arg, stop->flags,
+ stop->resp[0], stop->resp[1], stop->resp[2],
+ stop->resp[2], stop->error);
+ }
+
+ spin_unlock_bh(&slot->host->lock);
+
+ return 0;
+}
+
+static int atmci_req_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, atmci_req_show, inode->i_private);
+}
+
+static const struct file_operations atmci_req_fops = {
+ .owner = THIS_MODULE,
+ .open = atmci_req_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void atmci_show_status_reg(struct seq_file *s,
+ const char *regname, u32 value)
+{
+ static const char *sr_bit[] = {
+ [0] = "CMDRDY",
+ [1] = "RXRDY",
+ [2] = "TXRDY",
+ [3] = "BLKE",
+ [4] = "DTIP",
+ [5] = "NOTBUSY",
+ [8] = "SDIOIRQA",
+ [9] = "SDIOIRQB",
+ [16] = "RINDE",
+ [17] = "RDIRE",
+ [18] = "RCRCE",
+ [19] = "RENDE",
+ [20] = "RTOE",
+ [21] = "DCRCE",
+ [22] = "DTOE",
+ [30] = "OVRE",
+ [31] = "UNRE",
+ };
+ unsigned int i;
+
+ seq_printf(s, "%s:\t0x%08x", regname, value);
+ for (i = 0; i < ARRAY_SIZE(sr_bit); i++) {
+ if (value & (1 << i)) {
+ if (sr_bit[i])
+ seq_printf(s, " %s", sr_bit[i]);
+ else
+ seq_puts(s, " UNKNOWN");
+ }
+ }
+ seq_putc(s, '\n');
+}
+
+static int atmci_regs_show(struct seq_file *s, void *v)
+{
+ struct atmel_mci *host = s->private;
+ u32 *buf;
+
+ buf = kmalloc(MCI_REGS_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /*
+ * Grab a more or less consistent snapshot. Note that we're
+ * not disabling interrupts, so IMR and SR may not be
+ * consistent.
+ */
+ spin_lock_bh(&host->lock);
+ clk_enable(host->mck);
+ memcpy_fromio(buf, host->regs, MCI_REGS_SIZE);
+ clk_disable(host->mck);
+ spin_unlock_bh(&host->lock);
+
+ seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n",
+ buf[MCI_MR / 4],
+ buf[MCI_MR / 4] & MCI_MR_RDPROOF ? " RDPROOF" : "",
+ buf[MCI_MR / 4] & MCI_MR_WRPROOF ? " WRPROOF" : "",
+ buf[MCI_MR / 4] & 0xff);
+ seq_printf(s, "DTOR:\t0x%08x\n", buf[MCI_DTOR / 4]);
+ seq_printf(s, "SDCR:\t0x%08x\n", buf[MCI_SDCR / 4]);
+ seq_printf(s, "ARGR:\t0x%08x\n", buf[MCI_ARGR / 4]);
+ seq_printf(s, "BLKR:\t0x%08x BCNT=%u BLKLEN=%u\n",
+ buf[MCI_BLKR / 4],
+ buf[MCI_BLKR / 4] & 0xffff,
+ (buf[MCI_BLKR / 4] >> 16) & 0xffff);
+
+ /* Don't read RSPR and RDR; it will consume the data there */
+
+ atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]);
+ atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]);
+
+ kfree(buf);
+
+ return 0;
+}
+
+static int atmci_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, atmci_regs_show, inode->i_private);
+}
+
+static const struct file_operations atmci_regs_fops = {
+ .owner = THIS_MODULE,
+ .open = atmci_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void atmci_init_debugfs(struct atmel_mci_slot *slot)
+{
+ struct mmc_host *mmc = slot->mmc;
+ struct atmel_mci *host = slot->host;
+ struct dentry *root;
+ struct dentry *node;
+
+ root = mmc->debugfs_root;
+ if (!root)
+ return;
+
+ node = debugfs_create_file("regs", S_IRUSR, root, host,
+ &atmci_regs_fops);
+ if (IS_ERR(node))
+ return;
+ if (!node)
+ goto err;
+
+ node = debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops);
+ if (!node)
+ goto err;
+
+ node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
+ if (!node)
+ goto err;
+
+ node = debugfs_create_x32("pending_events", S_IRUSR, root,
+ (u32 *)&host->pending_events);
+ if (!node)
+ goto err;
+
+ node = debugfs_create_x32("completed_events", S_IRUSR, root,
+ (u32 *)&host->completed_events);
+ if (!node)
+ goto err;
+
+ return;
+
+err:
+ dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n");
+}
+
+static inline unsigned int ns_to_clocks(struct atmel_mci *host,
+ unsigned int ns)
+{
+ return (ns * (host->bus_hz / 1000000) + 999) / 1000;
+}
+
+static void atmci_set_timeout(struct atmel_mci *host,
+ struct atmel_mci_slot *slot, struct mmc_data *data)
+{
+ static unsigned dtomul_to_shift[] = {
+ 0, 4, 7, 8, 10, 12, 16, 20
+ };
+ unsigned timeout;
+ unsigned dtocyc;
+ unsigned dtomul;
+
+ timeout = ns_to_clocks(host, data->timeout_ns) + data->timeout_clks;
+
+ for (dtomul = 0; dtomul < 8; dtomul++) {
+ unsigned shift = dtomul_to_shift[dtomul];
+ dtocyc = (timeout + (1 << shift) - 1) >> shift;
+ if (dtocyc < 15)
+ break;
+ }
+
+ if (dtomul >= 8) {
+ dtomul = 7;
+ dtocyc = 15;
+ }
+
+ dev_vdbg(&slot->mmc->class_dev, "setting timeout to %u cycles\n",
+ dtocyc << dtomul_to_shift[dtomul]);
+ mci_writel(host, DTOR, (MCI_DTOMUL(dtomul) | MCI_DTOCYC(dtocyc)));
+}
+
+/*
+ * Return mask with command flags to be enabled for this command.
+ */
+static u32 atmci_prepare_command(struct mmc_host *mmc,
+ struct mmc_command *cmd)
+{
+ struct mmc_data *data;
+ u32 cmdr;
+
+ cmd->error = -EINPROGRESS;
+
+ cmdr = MCI_CMDR_CMDNB(cmd->opcode);
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136)
+ cmdr |= MCI_CMDR_RSPTYP_136BIT;
+ else
+ cmdr |= MCI_CMDR_RSPTYP_48BIT;
+ }
+
+ /*
+ * This should really be MAXLAT_5 for CMD2 and ACMD41, but
+ * it's too difficult to determine whether this is an ACMD or
+ * not. Better make it 64.
+ */
+ cmdr |= MCI_CMDR_MAXLAT_64CYC;
+
+ if (mmc->ios.bus_mode == MMC_BUSMODE_OPENDRAIN)
+ cmdr |= MCI_CMDR_OPDCMD;
+
+ data = cmd->data;
+ if (data) {
+ cmdr |= MCI_CMDR_START_XFER;
+ if (data->flags & MMC_DATA_STREAM)
+ cmdr |= MCI_CMDR_STREAM;
+ else if (data->blocks > 1)
+ cmdr |= MCI_CMDR_MULTI_BLOCK;
+ else
+ cmdr |= MCI_CMDR_BLOCK;
+
+ if (data->flags & MMC_DATA_READ)
+ cmdr |= MCI_CMDR_TRDIR_READ;
+ }
+
+ return cmdr;
+}
+
+static void atmci_start_command(struct atmel_mci *host,
+ struct mmc_command *cmd, u32 cmd_flags)
+{
+ WARN_ON(host->cmd);
+ host->cmd = cmd;
+
+ dev_vdbg(&host->pdev->dev,
+ "start command: ARGR=0x%08x CMDR=0x%08x\n",
+ cmd->arg, cmd_flags);
+
+ mci_writel(host, ARGR, cmd->arg);
+ mci_writel(host, CMDR, cmd_flags);
+}
+
+static void send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
+{
+ atmci_start_command(host, data->stop, host->stop_cmdr);
+ mci_writel(host, IER, MCI_CMDRDY);
+}
+
+#ifdef CONFIG_MMC_ATMELMCI_DMA
+static void atmci_dma_cleanup(struct atmel_mci *host)
+{
+ struct mmc_data *data = host->data;
+
+ dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len,
+ ((data->flags & MMC_DATA_WRITE)
+ ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
+}
+
+static void atmci_stop_dma(struct atmel_mci *host)
+{
+ struct dma_chan *chan = host->data_chan;
+
+ if (chan) {
+ chan->device->device_terminate_all(chan);
+ atmci_dma_cleanup(host);
+ } else {
+ /* Data transfer was stopped by the interrupt handler */
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
+ mci_writel(host, IER, MCI_NOTBUSY);
+ }
+}
+
+/* This function is called by the DMA driver from tasklet context. */
+static void atmci_dma_complete(void *arg)
+{
+ struct atmel_mci *host = arg;
+ struct mmc_data *data = host->data;
+
+ dev_vdbg(&host->pdev->dev, "DMA complete\n");
+
+ atmci_dma_cleanup(host);
+
+ /*
+ * If the card was removed, data will be NULL. No point trying
+ * to send the stop command or waiting for NBUSY in this case.
+ */
+ if (data) {
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
+ tasklet_schedule(&host->tasklet);
+
+ /*
+ * Regardless of what the documentation says, we have
+ * to wait for NOTBUSY even after block read
+ * operations.
+ *
+ * When the DMA transfer is complete, the controller
+ * may still be reading the CRC from the card, i.e.
+ * the data transfer is still in progress and we
+ * haven't seen all the potential error bits yet.
+ *
+ * The interrupt handler will schedule a different
+ * tasklet to finish things up when the data transfer
+ * is completely done.
+ *
+ * We may not complete the mmc request here anyway
+ * because the mmc layer may call back and cause us to
+ * violate the "don't submit new operations from the
+ * completion callback" rule of the dma engine
+ * framework.
+ */
+ mci_writel(host, IER, MCI_NOTBUSY);
+ }
+}
+
+static int
+atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
+{
+ struct dma_chan *chan;
+ struct dma_async_tx_descriptor *desc;
+ struct scatterlist *sg;
+ unsigned int i;
+ enum dma_data_direction direction;
+
+ /*
+ * We don't do DMA on "complex" transfers, i.e. with
+ * non-word-aligned buffers or lengths. Also, we don't bother
+ * with all the DMA setup overhead for short transfers.
+ */
+ if (data->blocks * data->blksz < ATMCI_DMA_THRESHOLD)
+ return -EINVAL;
+ if (data->blksz & 3)
+ return -EINVAL;
+
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ if (sg->offset & 3 || sg->length & 3)
+ return -EINVAL;
+ }
+
+ /* If we don't have a channel, we can't do DMA */
+ chan = host->dma.chan;
+ if (chan) {
+ dma_chan_get(chan);
+ host->data_chan = chan;
+ }
+
+ if (!chan)
+ return -ENODEV;
+
+ if (data->flags & MMC_DATA_READ)
+ direction = DMA_FROM_DEVICE;
+ else
+ direction = DMA_TO_DEVICE;
+
+ desc = chan->device->device_prep_slave_sg(chan,
+ data->sg, data->sg_len, direction,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc)
+ return -ENOMEM;
+
+ host->dma.data_desc = desc;
+ desc->callback = atmci_dma_complete;
+ desc->callback_param = host;
+ desc->tx_submit(desc);
+
+ /* Go! */
+ chan->device->device_issue_pending(chan);
+
+ return 0;
+}
+
+#else /* CONFIG_MMC_ATMELMCI_DMA */
+
+static int atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data)
+{
+ return -ENOSYS;
+}
+
+static void atmci_stop_dma(struct atmel_mci *host)
+{
+ /* Data transfer was stopped by the interrupt handler */
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
+ mci_writel(host, IER, MCI_NOTBUSY);
+}
+
+#endif /* CONFIG_MMC_ATMELMCI_DMA */
+
+/*
+ * Returns a mask of interrupt flags to be enabled after the whole
+ * request has been prepared.
+ */
+static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data)
+{
+ u32 iflags;
+
+ data->error = -EINPROGRESS;
+
+ WARN_ON(host->data);
+ host->sg = NULL;
+ host->data = data;
+
+ iflags = ATMCI_DATA_ERROR_FLAGS;
+ if (atmci_submit_data_dma(host, data)) {
+ host->data_chan = NULL;
+
+ /*
+ * Errata: MMC data write operation with less than 12
+ * bytes is impossible.
+ *
+ * Errata: MCI Transmit Data Register (TDR) FIFO
+ * corruption when length is not multiple of 4.
+ */
+ if (data->blocks * data->blksz < 12
+ || (data->blocks * data->blksz) & 3)
+ host->need_reset = true;
+
+ host->sg = data->sg;
+ host->pio_offset = 0;
+ if (data->flags & MMC_DATA_READ)
+ iflags |= MCI_RXRDY;
+ else
+ iflags |= MCI_TXRDY;
+ }
+
+ return iflags;
+}
+
+static void atmci_start_request(struct atmel_mci *host,
+ struct atmel_mci_slot *slot)
+{
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ u32 iflags;
+ u32 cmdflags;
+
+ mrq = slot->mrq;
+ host->cur_slot = slot;
+ host->mrq = mrq;
+
+ host->pending_events = 0;
+ host->completed_events = 0;
+ host->data_status = 0;
+
+ if (host->need_reset) {
+ mci_writel(host, CR, MCI_CR_SWRST);
+ mci_writel(host, CR, MCI_CR_MCIEN);
+ mci_writel(host, MR, host->mode_reg);
+ host->need_reset = false;
+ }
+ mci_writel(host, SDCR, slot->sdc_reg);
+
+ iflags = mci_readl(host, IMR);
+ if (iflags)
+ dev_warn(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n",
+ iflags);
+
+ if (unlikely(test_and_clear_bit(ATMCI_CARD_NEED_INIT, &slot->flags))) {
+ /* Send init sequence (74 clock cycles) */
+ mci_writel(host, CMDR, MCI_CMDR_SPCMD_INIT);
+ while (!(mci_readl(host, SR) & MCI_CMDRDY))
+ cpu_relax();
+ }
+ data = mrq->data;
+ if (data) {
+ atmci_set_timeout(host, slot, data);
+
+ /* Must set block count/size before sending command */
+ mci_writel(host, BLKR, MCI_BCNT(data->blocks)
+ | MCI_BLKLEN(data->blksz));
+ dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n",
+ MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz));
+ }
+
+ iflags = MCI_CMDRDY;
+ cmd = mrq->cmd;
+ cmdflags = atmci_prepare_command(slot->mmc, cmd);
+ atmci_start_command(host, cmd, cmdflags);
+
+ if (data)
+ iflags |= atmci_submit_data(host, data);
+
+ if (mrq->stop) {
+ host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
+ host->stop_cmdr |= MCI_CMDR_STOP_XFER;
+ if (!(data->flags & MMC_DATA_WRITE))
+ host->stop_cmdr |= MCI_CMDR_TRDIR_READ;
+ if (data->flags & MMC_DATA_STREAM)
+ host->stop_cmdr |= MCI_CMDR_STREAM;
+ else
+ host->stop_cmdr |= MCI_CMDR_MULTI_BLOCK;
+ }
+
+ /*
+ * We could have enabled interrupts earlier, but I suspect
+ * that would open up a nice can of interesting race
+ * conditions (e.g. command and data complete, but stop not
+ * prepared yet.)
+ */
+ mci_writel(host, IER, iflags);
+}
+
+static void atmci_queue_request(struct atmel_mci *host,
+ struct atmel_mci_slot *slot, struct mmc_request *mrq)
+{
+ dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n",
+ host->state);
+
+ spin_lock_bh(&host->lock);
+ slot->mrq = mrq;
+ if (host->state == STATE_IDLE) {
+ host->state = STATE_SENDING_CMD;
+ atmci_start_request(host, slot);
+ } else {
+ list_add_tail(&slot->queue_node, &host->queue);
+ }
+ spin_unlock_bh(&host->lock);
+}
+
+static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct atmel_mci_slot *slot = mmc_priv(mmc);
+ struct atmel_mci *host = slot->host;
+ struct mmc_data *data;
+
+ WARN_ON(slot->mrq);
+
+ /*
+ * We may "know" the card is gone even though there's still an
+ * electrical connection. If so, we really need to communicate
+ * this to the MMC core since there won't be any more
+ * interrupts as the card is completely removed. Otherwise,
+ * the MMC core might believe the card is still there even
+ * though the card was just removed very slowly.
+ */
+ if (!test_bit(ATMCI_CARD_PRESENT, &slot->flags)) {
+ mrq->cmd->error = -ENOMEDIUM;
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
+ /* We don't support multiple blocks of weird lengths. */
+ data = mrq->data;
+ if (data && data->blocks > 1 && data->blksz & 3) {
+ mrq->cmd->error = -EINVAL;
+ mmc_request_done(mmc, mrq);
+ }
+
+ atmci_queue_request(host, slot, mrq);
+}
+
+static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct atmel_mci_slot *slot = mmc_priv(mmc);
+ struct atmel_mci *host = slot->host;
+ unsigned int i;
+
+ slot->sdc_reg &= ~MCI_SDCBUS_MASK;
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_1:
+ slot->sdc_reg |= MCI_SDCBUS_1BIT;
+ break;
+ case MMC_BUS_WIDTH_4:
+ slot->sdc_reg = MCI_SDCBUS_4BIT;
+ break;
+ }
+
+ if (ios->clock) {
+ unsigned int clock_min = ~0U;
+ u32 clkdiv;
+
+ spin_lock_bh(&host->lock);
+ if (!host->mode_reg) {
+ clk_enable(host->mck);
+ mci_writel(host, CR, MCI_CR_SWRST);
+ mci_writel(host, CR, MCI_CR_MCIEN);
+ }
+
+ /*
+ * Use mirror of ios->clock to prevent race with mmc
+ * core ios update when finding the minimum.
+ */
+ slot->clock = ios->clock;
+ for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
+ if (host->slot[i] && host->slot[i]->clock
+ && host->slot[i]->clock < clock_min)
+ clock_min = host->slot[i]->clock;
+ }
+
+ /* Calculate clock divider */
+ clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * clock_min) - 1;
+ if (clkdiv > 255) {
+ dev_warn(&mmc->class_dev,
+ "clock %u too slow; using %lu\n",
+ clock_min, host->bus_hz / (2 * 256));
+ clkdiv = 255;
+ }
+
+ /*
+ * WRPROOF and RDPROOF prevent overruns/underruns by
+ * stopping the clock when the FIFO is full/empty.
+ * This state is not expected to last for long.
+ */
+ host->mode_reg = MCI_MR_CLKDIV(clkdiv) | MCI_MR_WRPROOF
+ | MCI_MR_RDPROOF;
+
+ if (list_empty(&host->queue))
+ mci_writel(host, MR, host->mode_reg);
+ else
+ host->need_clock_update = true;
+
+ spin_unlock_bh(&host->lock);
+ } else {
+ bool any_slot_active = false;
+
+ spin_lock_bh(&host->lock);
+ slot->clock = 0;
+ for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
+ if (host->slot[i] && host->slot[i]->clock) {
+ any_slot_active = true;
+ break;
+ }
+ }
+ if (!any_slot_active) {
+ mci_writel(host, CR, MCI_CR_MCIDIS);
+ if (host->mode_reg) {
+ mci_readl(host, MR);
+ clk_disable(host->mck);
+ }
+ host->mode_reg = 0;
+ }
+ spin_unlock_bh(&host->lock);
+ }
+
+ switch (ios->power_mode) {
+ case MMC_POWER_UP:
+ set_bit(ATMCI_CARD_NEED_INIT, &slot->flags);
+ break;
+ default:
+ /*
+ * TODO: None of the currently available AVR32-based
+ * boards allow MMC power to be turned off. Implement
+ * power control when this can be tested properly.
+ *
+ * We also need to hook this into the clock management
+ * somehow so that newly inserted cards aren't
+ * subjected to a fast clock before we have a chance
+ * to figure out what the maximum rate is. Currently,
+ * there's no way to avoid this, and there never will
+ * be for boards that don't support power control.
+ */
+ break;
+ }
+}
+
+static int atmci_get_ro(struct mmc_host *mmc)
+{
+ int read_only = -ENOSYS;
+ struct atmel_mci_slot *slot = mmc_priv(mmc);
+
+ if (gpio_is_valid(slot->wp_pin)) {
+ read_only = gpio_get_value(slot->wp_pin);
+ dev_dbg(&mmc->class_dev, "card is %s\n",
+ read_only ? "read-only" : "read-write");
+ }
+
+ return read_only;
+}
+
+static int atmci_get_cd(struct mmc_host *mmc)
+{
+ int present = -ENOSYS;
+ struct atmel_mci_slot *slot = mmc_priv(mmc);
+
+ if (gpio_is_valid(slot->detect_pin)) {
+ present = !gpio_get_value(slot->detect_pin);
+ dev_dbg(&mmc->class_dev, "card is %spresent\n",
+ present ? "" : "not ");
+ }
+
+ return present;
+}
+
+static const struct mmc_host_ops atmci_ops = {
+ .request = atmci_request,
+ .set_ios = atmci_set_ios,
+ .get_ro = atmci_get_ro,
+ .get_cd = atmci_get_cd,
+};
+
+/* Called with host->lock held */
+static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
+ __releases(&host->lock)
+ __acquires(&host->lock)
+{
+ struct atmel_mci_slot *slot = NULL;
+ struct mmc_host *prev_mmc = host->cur_slot->mmc;
+
+ WARN_ON(host->cmd || host->data);
+
+ /*
+ * Update the MMC clock rate if necessary. This may be
+ * necessary if set_ios() is called when a different slot is
+ * busy transfering data.
+ */
+ if (host->need_clock_update)
+ mci_writel(host, MR, host->mode_reg);
+
+ host->cur_slot->mrq = NULL;
+ host->mrq = NULL;
+ if (!list_empty(&host->queue)) {
+ slot = list_entry(host->queue.next,
+ struct atmel_mci_slot, queue_node);
+ list_del(&slot->queue_node);
+ dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n",
+ mmc_hostname(slot->mmc));
+ host->state = STATE_SENDING_CMD;
+ atmci_start_request(host, slot);
+ } else {
+ dev_vdbg(&host->pdev->dev, "list empty\n");
+ host->state = STATE_IDLE;
+ }
+
+ spin_unlock(&host->lock);
+ mmc_request_done(prev_mmc, mrq);
+ spin_lock(&host->lock);
+}
+
+static void atmci_command_complete(struct atmel_mci *host,
+ struct mmc_command *cmd)
+{
+ u32 status = host->cmd_status;
+
+ /* Read the response from the card (up to 16 bytes) */
+ cmd->resp[0] = mci_readl(host, RSPR);
+ cmd->resp[1] = mci_readl(host, RSPR);
+ cmd->resp[2] = mci_readl(host, RSPR);
+ cmd->resp[3] = mci_readl(host, RSPR);
+
+ if (status & MCI_RTOE)
+ cmd->error = -ETIMEDOUT;
+ else if ((cmd->flags & MMC_RSP_CRC) && (status & MCI_RCRCE))
+ cmd->error = -EILSEQ;
+ else if (status & (MCI_RINDE | MCI_RDIRE | MCI_RENDE))
+ cmd->error = -EIO;
+ else
+ cmd->error = 0;
+
+ if (cmd->error) {
+ dev_dbg(&host->pdev->dev,
+ "command error: status=0x%08x\n", status);
+
+ if (cmd->data) {
+ host->data = NULL;
+ atmci_stop_dma(host);
+ mci_writel(host, IDR, MCI_NOTBUSY
+ | MCI_TXRDY | MCI_RXRDY
+ | ATMCI_DATA_ERROR_FLAGS);
+ }
+ }
+}
+
+static void atmci_detect_change(unsigned long data)
+{
+ struct atmel_mci_slot *slot = (struct atmel_mci_slot *)data;
+ bool present;
+ bool present_old;
+
+ /*
+ * atmci_cleanup_slot() sets the ATMCI_SHUTDOWN flag before
+ * freeing the interrupt. We must not re-enable the interrupt
+ * if it has been freed, and if we're shutting down, it
+ * doesn't really matter whether the card is present or not.
+ */
+ smp_rmb();
+ if (test_bit(ATMCI_SHUTDOWN, &slot->flags))
+ return;
+
+ enable_irq(gpio_to_irq(slot->detect_pin));
+ present = !gpio_get_value(slot->detect_pin);
+ present_old = test_bit(ATMCI_CARD_PRESENT, &slot->flags);
+
+ dev_vdbg(&slot->mmc->class_dev, "detect change: %d (was %d)\n",
+ present, present_old);
+
+ if (present != present_old) {
+ struct atmel_mci *host = slot->host;
+ struct mmc_request *mrq;
+
+ dev_dbg(&slot->mmc->class_dev, "card %s\n",
+ present ? "inserted" : "removed");
+
+ spin_lock(&host->lock);
+
+ if (!present)
+ clear_bit(ATMCI_CARD_PRESENT, &slot->flags);
+ else
+ set_bit(ATMCI_CARD_PRESENT, &slot->flags);
+
+ /* Clean up queue if present */
+ mrq = slot->mrq;
+ if (mrq) {
+ if (mrq == host->mrq) {
+ /*
+ * Reset controller to terminate any ongoing
+ * commands or data transfers.
+ */
+ mci_writel(host, CR, MCI_CR_SWRST);
+ mci_writel(host, CR, MCI_CR_MCIEN);
+ mci_writel(host, MR, host->mode_reg);
+
+ host->data = NULL;
+ host->cmd = NULL;
+
+ switch (host->state) {
+ case STATE_IDLE:
+ break;
+ case STATE_SENDING_CMD:
+ mrq->cmd->error = -ENOMEDIUM;
+ if (!mrq->data)
+ break;
+ /* fall through */
+ case STATE_SENDING_DATA:
+ mrq->data->error = -ENOMEDIUM;
+ atmci_stop_dma(host);
+ break;
+ case STATE_DATA_BUSY:
+ case STATE_DATA_ERROR:
+ if (mrq->data->error == -EINPROGRESS)
+ mrq->data->error = -ENOMEDIUM;
+ if (!mrq->stop)
+ break;
+ /* fall through */
+ case STATE_SENDING_STOP:
+ mrq->stop->error = -ENOMEDIUM;
+ break;
+ }
+
+ atmci_request_end(host, mrq);
+ } else {
+ list_del(&slot->queue_node);
+ mrq->cmd->error = -ENOMEDIUM;
+ if (mrq->data)
+ mrq->data->error = -ENOMEDIUM;
+ if (mrq->stop)
+ mrq->stop->error = -ENOMEDIUM;
+
+ spin_unlock(&host->lock);
+ mmc_request_done(slot->mmc, mrq);
+ spin_lock(&host->lock);
+ }
+ }
+ spin_unlock(&host->lock);
+
+ mmc_detect_change(slot->mmc, 0);
+ }
+}
+
+static void atmci_tasklet_func(unsigned long priv)
+{
+ struct atmel_mci *host = (struct atmel_mci *)priv;
+ struct mmc_request *mrq = host->mrq;
+ struct mmc_data *data = host->data;
+ struct mmc_command *cmd = host->cmd;
+ enum atmel_mci_state state = host->state;
+ enum atmel_mci_state prev_state;
+ u32 status;
+
+ spin_lock(&host->lock);
+
+ state = host->state;
+
+ dev_vdbg(&host->pdev->dev,
+ "tasklet: state %u pending/completed/mask %lx/%lx/%x\n",
+ state, host->pending_events, host->completed_events,
+ mci_readl(host, IMR));
+
+ do {
+ prev_state = state;
+
+ switch (state) {
+ case STATE_IDLE:
+ break;
+
+ case STATE_SENDING_CMD:
+ if (!atmci_test_and_clear_pending(host,
+ EVENT_CMD_COMPLETE))
+ break;
+
+ host->cmd = NULL;
+ atmci_set_completed(host, EVENT_CMD_COMPLETE);
+ atmci_command_complete(host, mrq->cmd);
+ if (!mrq->data || cmd->error) {
+ atmci_request_end(host, host->mrq);
+ goto unlock;
+ }
+
+ prev_state = state = STATE_SENDING_DATA;
+ /* fall through */
+
+ case STATE_SENDING_DATA:
+ if (atmci_test_and_clear_pending(host,
+ EVENT_DATA_ERROR)) {
+ atmci_stop_dma(host);
+ if (data->stop)
+ send_stop_cmd(host, data);
+ state = STATE_DATA_ERROR;
+ break;
+ }
+
+ if (!atmci_test_and_clear_pending(host,
+ EVENT_XFER_COMPLETE))
+ break;
+
+ atmci_set_completed(host, EVENT_XFER_COMPLETE);
+ prev_state = state = STATE_DATA_BUSY;
+ /* fall through */
+
+ case STATE_DATA_BUSY:
+ if (!atmci_test_and_clear_pending(host,
+ EVENT_DATA_COMPLETE))
+ break;
+
+ host->data = NULL;
+ atmci_set_completed(host, EVENT_DATA_COMPLETE);
+ status = host->data_status;
+ if (unlikely(status & ATMCI_DATA_ERROR_FLAGS)) {
+ if (status & MCI_DTOE) {
+ dev_dbg(&host->pdev->dev,
+ "data timeout error\n");
+ data->error = -ETIMEDOUT;
+ } else if (status & MCI_DCRCE) {
+ dev_dbg(&host->pdev->dev,
+ "data CRC error\n");
+ data->error = -EILSEQ;
+ } else {
+ dev_dbg(&host->pdev->dev,
+ "data FIFO error (status=%08x)\n",
+ status);
+ data->error = -EIO;
+ }
+ } else {
+ data->bytes_xfered = data->blocks * data->blksz;
+ data->error = 0;
+ }
+
+ if (!data->stop) {
+ atmci_request_end(host, host->mrq);
+ goto unlock;
+ }
+
+ prev_state = state = STATE_SENDING_STOP;
+ if (!data->error)
+ send_stop_cmd(host, data);
+ /* fall through */
+
+ case STATE_SENDING_STOP:
+ if (!atmci_test_and_clear_pending(host,
+ EVENT_CMD_COMPLETE))
+ break;
+
+ host->cmd = NULL;
+ atmci_command_complete(host, mrq->stop);
+ atmci_request_end(host, host->mrq);
+ goto unlock;
+
+ case STATE_DATA_ERROR:
+ if (!atmci_test_and_clear_pending(host,
+ EVENT_XFER_COMPLETE))
+ break;
+
+ state = STATE_DATA_BUSY;
+ break;
+ }
+ } while (state != prev_state);
+
+ host->state = state;
+
+unlock:
+ spin_unlock(&host->lock);
+}
+
+static void atmci_read_data_pio(struct atmel_mci *host)
+{
+ struct scatterlist *sg = host->sg;
+ void *buf = sg_virt(sg);
+ unsigned int offset = host->pio_offset;
+ struct mmc_data *data = host->data;
+ u32 value;
+ u32 status;
+ unsigned int nbytes = 0;
+
+ do {
+ value = mci_readl(host, RDR);
+ if (likely(offset + 4 <= sg->length)) {
+ put_unaligned(value, (u32 *)(buf + offset));
+
+ offset += 4;
+ nbytes += 4;
+
+ if (offset == sg->length) {
+ flush_dcache_page(sg_page(sg));
+ host->sg = sg = sg_next(sg);
+ if (!sg)
+ goto done;
+
+ offset = 0;
+ buf = sg_virt(sg);
+ }
+ } else {
+ unsigned int remaining = sg->length - offset;
+ memcpy(buf + offset, &value, remaining);
+ nbytes += remaining;
+
+ flush_dcache_page(sg_page(sg));
+ host->sg = sg = sg_next(sg);
+ if (!sg)
+ goto done;
+
+ offset = 4 - remaining;
+ buf = sg_virt(sg);
+ memcpy(buf, (u8 *)&value + remaining, offset);
+ nbytes += offset;
+ }
+
+ status = mci_readl(host, SR);
+ if (status & ATMCI_DATA_ERROR_FLAGS) {
+ mci_writel(host, IDR, (MCI_NOTBUSY | MCI_RXRDY
+ | ATMCI_DATA_ERROR_FLAGS));
+ host->data_status = status;
+ data->bytes_xfered += nbytes;
+ smp_wmb();
+ atmci_set_pending(host, EVENT_DATA_ERROR);
+ tasklet_schedule(&host->tasklet);
+ return;
+ }
+ } while (status & MCI_RXRDY);
+
+ host->pio_offset = offset;
+ data->bytes_xfered += nbytes;
+
+ return;
+
+done:
+ mci_writel(host, IDR, MCI_RXRDY);
+ mci_writel(host, IER, MCI_NOTBUSY);
+ data->bytes_xfered += nbytes;
+ smp_wmb();
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
+}
+
+static void atmci_write_data_pio(struct atmel_mci *host)
+{
+ struct scatterlist *sg = host->sg;
+ void *buf = sg_virt(sg);
+ unsigned int offset = host->pio_offset;
+ struct mmc_data *data = host->data;
+ u32 value;
+ u32 status;
+ unsigned int nbytes = 0;
+
+ do {
+ if (likely(offset + 4 <= sg->length)) {
+ value = get_unaligned((u32 *)(buf + offset));
+ mci_writel(host, TDR, value);
+
+ offset += 4;
+ nbytes += 4;
+ if (offset == sg->length) {
+ host->sg = sg = sg_next(sg);
+ if (!sg)
+ goto done;
+
+ offset = 0;
+ buf = sg_virt(sg);
+ }
+ } else {
+ unsigned int remaining = sg->length - offset;
+
+ value = 0;
+ memcpy(&value, buf + offset, remaining);
+ nbytes += remaining;
+
+ host->sg = sg = sg_next(sg);
+ if (!sg) {
+ mci_writel(host, TDR, value);
+ goto done;
+ }
+
+ offset = 4 - remaining;
+ buf = sg_virt(sg);
+ memcpy((u8 *)&value + remaining, buf, offset);
+ mci_writel(host, TDR, value);
+ nbytes += offset;
+ }
+
+ status = mci_readl(host, SR);
+ if (status & ATMCI_DATA_ERROR_FLAGS) {
+ mci_writel(host, IDR, (MCI_NOTBUSY | MCI_TXRDY
+ | ATMCI_DATA_ERROR_FLAGS));
+ host->data_status = status;
+ data->bytes_xfered += nbytes;
+ smp_wmb();
+ atmci_set_pending(host, EVENT_DATA_ERROR);
+ tasklet_schedule(&host->tasklet);
+ return;
+ }
+ } while (status & MCI_TXRDY);
+
+ host->pio_offset = offset;
+ data->bytes_xfered += nbytes;
+
+ return;
+
+done:
+ mci_writel(host, IDR, MCI_TXRDY);
+ mci_writel(host, IER, MCI_NOTBUSY);
+ data->bytes_xfered += nbytes;
+ smp_wmb();
+ atmci_set_pending(host, EVENT_XFER_COMPLETE);
+}
+
+static void atmci_cmd_interrupt(struct atmel_mci *host, u32 status)
+{
+ mci_writel(host, IDR, MCI_CMDRDY);
+
+ host->cmd_status = status;
+ smp_wmb();
+ atmci_set_pending(host, EVENT_CMD_COMPLETE);
+ tasklet_schedule(&host->tasklet);
+}
+
+static irqreturn_t atmci_interrupt(int irq, void *dev_id)
+{
+ struct atmel_mci *host = dev_id;
+ u32 status, mask, pending;
+ unsigned int pass_count = 0;
+
+ do {
+ status = mci_readl(host, SR);
+ mask = mci_readl(host, IMR);
+ pending = status & mask;
+ if (!pending)
+ break;
+
+ if (pending & ATMCI_DATA_ERROR_FLAGS) {
+ mci_writel(host, IDR, ATMCI_DATA_ERROR_FLAGS
+ | MCI_RXRDY | MCI_TXRDY);
+ pending &= mci_readl(host, IMR);
+
+ host->data_status = status;
+ smp_wmb();
+ atmci_set_pending(host, EVENT_DATA_ERROR);
+ tasklet_schedule(&host->tasklet);
+ }
+ if (pending & MCI_NOTBUSY) {
+ mci_writel(host, IDR,
+ ATMCI_DATA_ERROR_FLAGS | MCI_NOTBUSY);
+ if (!host->data_status)
+ host->data_status = status;
+ smp_wmb();
+ atmci_set_pending(host, EVENT_DATA_COMPLETE);
+ tasklet_schedule(&host->tasklet);
+ }
+ if (pending & MCI_RXRDY)
+ atmci_read_data_pio(host);
+ if (pending & MCI_TXRDY)
+ atmci_write_data_pio(host);
+
+ if (pending & MCI_CMDRDY)
+ atmci_cmd_interrupt(host, status);
+ } while (pass_count++ < 5);
+
+ return pass_count ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id)
+{
+ struct atmel_mci_slot *slot = dev_id;
+
+ /*
+ * Disable interrupts until the pin has stabilized and check
+ * the state then. Use mod_timer() since we may be in the
+ * middle of the timer routine when this interrupt triggers.
+ */
+ disable_irq_nosync(irq);
+ mod_timer(&slot->detect_timer, jiffies + msecs_to_jiffies(20));
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_MMC_ATMELMCI_DMA
+
+static inline struct atmel_mci *
+dma_client_to_atmel_mci(struct dma_client *client)
+{
+ return container_of(client, struct atmel_mci, dma.client);
+}
+
+static enum dma_state_client atmci_dma_event(struct dma_client *client,
+ struct dma_chan *chan, enum dma_state state)
+{
+ struct atmel_mci *host;
+ enum dma_state_client ret = DMA_NAK;
+
+ host = dma_client_to_atmel_mci(client);
+
+ switch (state) {
+ case DMA_RESOURCE_AVAILABLE:
+ spin_lock_bh(&host->lock);
+ if (!host->dma.chan) {
+ host->dma.chan = chan;
+ ret = DMA_ACK;
+ }
+ spin_unlock_bh(&host->lock);
+
+ if (ret == DMA_ACK)
+ dev_info(&host->pdev->dev,
+ "Using %s for DMA transfers\n",
+ chan->dev.bus_id);
+ break;
+
+ case DMA_RESOURCE_REMOVED:
+ spin_lock_bh(&host->lock);
+ if (host->dma.chan == chan) {
+ host->dma.chan = NULL;
+ ret = DMA_ACK;
+ }
+ spin_unlock_bh(&host->lock);
+
+ if (ret == DMA_ACK)
+ dev_info(&host->pdev->dev,
+ "Lost %s, falling back to PIO\n",
+ chan->dev.bus_id);
+ break;
+
+ default:
+ break;
+ }
+
+
+ return ret;
+}
+#endif /* CONFIG_MMC_ATMELMCI_DMA */
+
+static int __init atmci_init_slot(struct atmel_mci *host,
+ struct mci_slot_pdata *slot_data, unsigned int id,
+ u32 sdc_reg)
+{
+ struct mmc_host *mmc;
+ struct atmel_mci_slot *slot;
+
+ mmc = mmc_alloc_host(sizeof(struct atmel_mci_slot), &host->pdev->dev);
+ if (!mmc)
+ return -ENOMEM;
+
+ slot = mmc_priv(mmc);
+ slot->mmc = mmc;
+ slot->host = host;
+ slot->detect_pin = slot_data->detect_pin;
+ slot->wp_pin = slot_data->wp_pin;
+ slot->sdc_reg = sdc_reg;
+
+ mmc->ops = &atmci_ops;
+ mmc->f_min = DIV_ROUND_UP(host->bus_hz, 512);
+ mmc->f_max = host->bus_hz / 2;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ if (slot_data->bus_width >= 4)
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+ mmc->max_hw_segs = 64;
+ mmc->max_phys_segs = 64;
+ mmc->max_req_size = 32768 * 512;
+ mmc->max_blk_size = 32768;
+ mmc->max_blk_count = 512;
+
+ /* Assume card is present initially */
+ set_bit(ATMCI_CARD_PRESENT, &slot->flags);
+ if (gpio_is_valid(slot->detect_pin)) {
+ if (gpio_request(slot->detect_pin, "mmc_detect")) {
+ dev_dbg(&mmc->class_dev, "no detect pin available\n");
+ slot->detect_pin = -EBUSY;
+ } else if (gpio_get_value(slot->detect_pin)) {
+ clear_bit(ATMCI_CARD_PRESENT, &slot->flags);
+ }
+ }
+
+ if (!gpio_is_valid(slot->detect_pin))
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+ if (gpio_is_valid(slot->wp_pin)) {
+ if (gpio_request(slot->wp_pin, "mmc_wp")) {
+ dev_dbg(&mmc->class_dev, "no WP pin available\n");
+ slot->wp_pin = -EBUSY;
+ }
+ }
+
+ host->slot[id] = slot;
+ mmc_add_host(mmc);
+
+ if (gpio_is_valid(slot->detect_pin)) {
+ int ret;
+
+ setup_timer(&slot->detect_timer, atmci_detect_change,
+ (unsigned long)slot);
+
+ ret = request_irq(gpio_to_irq(slot->detect_pin),
+ atmci_detect_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "mmc-detect", slot);
+ if (ret) {
+ dev_dbg(&mmc->class_dev,
+ "could not request IRQ %d for detect pin\n",
+ gpio_to_irq(slot->detect_pin));
+ gpio_free(slot->detect_pin);
+ slot->detect_pin = -EBUSY;
+ }
+ }
+
+ atmci_init_debugfs(slot);
+
+ return 0;
+}
+
+static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot,
+ unsigned int id)
+{
+ /* Debugfs stuff is cleaned up by mmc core */
+
+ set_bit(ATMCI_SHUTDOWN, &slot->flags);
+ smp_wmb();
+
+ mmc_remove_host(slot->mmc);
+
+ if (gpio_is_valid(slot->detect_pin)) {
+ int pin = slot->detect_pin;
+
+ free_irq(gpio_to_irq(pin), slot);
+ del_timer_sync(&slot->detect_timer);
+ gpio_free(pin);
+ }
+ if (gpio_is_valid(slot->wp_pin))
+ gpio_free(slot->wp_pin);
+
+ slot->host->slot[id] = NULL;
+ mmc_free_host(slot->mmc);
+}
+
+static int __init atmci_probe(struct platform_device *pdev)
+{
+ struct mci_platform_data *pdata;
+ struct atmel_mci *host;
+ struct resource *regs;
+ unsigned int nr_slots;
+ int irq;
+ int ret;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs)
+ return -ENXIO;
+ pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -ENXIO;
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ host = kzalloc(sizeof(struct atmel_mci), GFP_KERNEL);
+ if (!host)
+ return -ENOMEM;
+
+ host->pdev = pdev;
+ spin_lock_init(&host->lock);
+ INIT_LIST_HEAD(&host->queue);
+
+ host->mck = clk_get(&pdev->dev, "mci_clk");
+ if (IS_ERR(host->mck)) {
+ ret = PTR_ERR(host->mck);
+ goto err_clk_get;
+ }
+
+ ret = -ENOMEM;
+ host->regs = ioremap(regs->start, regs->end - regs->start + 1);
+ if (!host->regs)
+ goto err_ioremap;
+
+ clk_enable(host->mck);
+ mci_writel(host, CR, MCI_CR_SWRST);
+ host->bus_hz = clk_get_rate(host->mck);
+ clk_disable(host->mck);
+
+ host->mapbase = regs->start;
+
+ tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host);
+
+ ret = request_irq(irq, atmci_interrupt, 0, pdev->dev.bus_id, host);
+ if (ret)
+ goto err_request_irq;
+
+#ifdef CONFIG_MMC_ATMELMCI_DMA
+ if (pdata->dma_slave) {
+ struct dma_slave *slave = pdata->dma_slave;
+
+ slave->tx_reg = regs->start + MCI_TDR;
+ slave->rx_reg = regs->start + MCI_RDR;
+
+ /* Try to grab a DMA channel */
+ host->dma.client.event_callback = atmci_dma_event;
+ dma_cap_set(DMA_SLAVE, host->dma.client.cap_mask);
+ host->dma.client.slave = slave;
+
+ dma_async_client_register(&host->dma.client);
+ dma_async_client_chan_request(&host->dma.client);
+ } else {
+ dev_notice(&pdev->dev, "DMA not available, using PIO\n");
+ }
+#endif /* CONFIG_MMC_ATMELMCI_DMA */
+
+ platform_set_drvdata(pdev, host);
+
+ /* We need at least one slot to succeed */
+ nr_slots = 0;
+ ret = -ENODEV;
+ if (pdata->slot[0].bus_width) {
+ ret = atmci_init_slot(host, &pdata->slot[0],
+ MCI_SDCSEL_SLOT_A, 0);
+ if (!ret)
+ nr_slots++;
+ }
+ if (pdata->slot[1].bus_width) {
+ ret = atmci_init_slot(host, &pdata->slot[1],
+ MCI_SDCSEL_SLOT_B, 1);
+ if (!ret)
+ nr_slots++;
+ }
+
+ if (!nr_slots)
+ goto err_init_slot;
+
+ dev_info(&pdev->dev,
+ "Atmel MCI controller at 0x%08lx irq %d, %u slots\n",
+ host->mapbase, irq, nr_slots);
+
+ return 0;
+
+err_init_slot:
+#ifdef CONFIG_MMC_ATMELMCI_DMA
+ if (pdata->dma_slave)
+ dma_async_client_unregister(&host->dma.client);
+#endif
+ free_irq(irq, host);
+err_request_irq:
+ iounmap(host->regs);
+err_ioremap:
+ clk_put(host->mck);
+err_clk_get:
+ kfree(host);
+ return ret;
+}
+
+static int __exit atmci_remove(struct platform_device *pdev)
+{
+ struct atmel_mci *host = platform_get_drvdata(pdev);
+ unsigned int i;
+
+ platform_set_drvdata(pdev, NULL);
+
+ for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) {
+ if (host->slot[i])
+ atmci_cleanup_slot(host->slot[i], i);
+ }
+
+ clk_enable(host->mck);
+ mci_writel(host, IDR, ~0UL);
+ mci_writel(host, CR, MCI_CR_MCIDIS);
+ mci_readl(host, SR);
+ clk_disable(host->mck);
+
+#ifdef CONFIG_MMC_ATMELMCI_DMA
+ if (host->dma.client.slave)
+ dma_async_client_unregister(&host->dma.client);
+#endif
+
+ free_irq(platform_get_irq(pdev, 0), host);
+ iounmap(host->regs);
+
+ clk_put(host->mck);
+ kfree(host);
+
+ return 0;
+}
+
+static struct platform_driver atmci_driver = {
+ .remove = __exit_p(atmci_remove),
+ .driver = {
+ .name = "atmel_mci",
+ },
+};
+
+static int __init atmci_init(void)
+{
+ return platform_driver_probe(&atmci_driver, atmci_probe);
+}
+
+static void __exit atmci_exit(void)
+{
+ platform_driver_unregister(&atmci_driver);
+}
+
+module_init(atmci_init);
+module_exit(atmci_exit);
+
+MODULE_DESCRIPTION("Atmel Multimedia Card Interface driver");
+MODULE_AUTHOR("Haavard Skinnemoen <haavard.skinnemoen@atmel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
new file mode 100644
index 0000000..d3f5561
--- /dev/null
+++ b/drivers/mmc/host/au1xmmc.c
@@ -0,0 +1,1206 @@
+/*
+ * linux/drivers/mmc/host/au1xmmc.c - AU1XX0 MMC driver
+ *
+ * Copyright (c) 2005, Advanced Micro Devices, Inc.
+ *
+ * Developed with help from the 2.4.30 MMC AU1XXX controller including
+ * the following copyright notices:
+ * Copyright (c) 2003-2004 Embedded Edge, LLC.
+ * Portions Copyright (C) 2002 Embedix, Inc
+ * Copyright 2002 Hewlett-Packard Company
+
+ * 2.6 version of this driver inspired by:
+ * (drivers/mmc/wbsd.c) Copyright (C) 2004-2005 Pierre Ossman,
+ * All Rights Reserved.
+ * (drivers/mmc/pxa.c) Copyright (C) 2003 Russell King,
+ * 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.
+ */
+
+/* Why don't we use the SD controllers' carddetect feature?
+ *
+ * From the AU1100 MMC application guide:
+ * If the Au1100-based design is intended to support both MultiMediaCards
+ * and 1- or 4-data bit SecureDigital cards, then the solution is to
+ * connect a weak (560KOhm) pull-up resistor to connector pin 1.
+ * In doing so, a MMC card never enters SPI-mode communications,
+ * but now the SecureDigital card-detect feature of CD/DAT3 is ineffective
+ * (the low to high transition will not occur).
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/leds.h>
+#include <linux/mmc/host.h>
+
+#include <asm/io.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1xxx_dbdma.h>
+#include <asm/mach-au1x00/au1100_mmc.h>
+
+#define DRIVER_NAME "au1xxx-mmc"
+
+/* Set this to enable special debugging macros */
+/* #define DEBUG */
+
+#ifdef DEBUG
+#define DBG(fmt, idx, args...) \
+ printk(KERN_DEBUG "au1xmmc(%d): DEBUG: " fmt, idx, ##args)
+#else
+#define DBG(fmt, idx, args...) do {} while (0)
+#endif
+
+/* Hardware definitions */
+#define AU1XMMC_DESCRIPTOR_COUNT 1
+
+/* max DMA seg size: 64KB on Au1100, 4MB on Au1200 */
+#ifdef CONFIG_SOC_AU1100
+#define AU1XMMC_DESCRIPTOR_SIZE 0x0000ffff
+#else /* Au1200 */
+#define AU1XMMC_DESCRIPTOR_SIZE 0x003fffff
+#endif
+
+#define AU1XMMC_OCR (MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 | \
+ MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 | \
+ MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36)
+
+/* This gives us a hard value for the stop command that we can write directly
+ * to the command register.
+ */
+#define STOP_CMD \
+ (SD_CMD_RT_1B | SD_CMD_CT_7 | (0xC << SD_CMD_CI_SHIFT) | SD_CMD_GO)
+
+/* This is the set of interrupts that we configure by default. */
+#define AU1XMMC_INTERRUPTS \
+ (SD_CONFIG_SC | SD_CONFIG_DT | SD_CONFIG_RAT | \
+ SD_CONFIG_CR | SD_CONFIG_I)
+
+/* The poll event (looking for insert/remove events runs twice a second. */
+#define AU1XMMC_DETECT_TIMEOUT (HZ/2)
+
+struct au1xmmc_host {
+ struct mmc_host *mmc;
+ struct mmc_request *mrq;
+
+ u32 flags;
+ u32 iobase;
+ u32 clock;
+ u32 bus_width;
+ u32 power_mode;
+
+ int status;
+
+ struct {
+ int len;
+ int dir;
+ } dma;
+
+ struct {
+ int index;
+ int offset;
+ int len;
+ } pio;
+
+ u32 tx_chan;
+ u32 rx_chan;
+
+ int irq;
+
+ struct tasklet_struct finish_task;
+ struct tasklet_struct data_task;
+ struct au1xmmc_platform_data *platdata;
+ struct platform_device *pdev;
+ struct resource *ioarea;
+};
+
+/* Status flags used by the host structure */
+#define HOST_F_XMIT 0x0001
+#define HOST_F_RECV 0x0002
+#define HOST_F_DMA 0x0010
+#define HOST_F_ACTIVE 0x0100
+#define HOST_F_STOP 0x1000
+
+#define HOST_S_IDLE 0x0001
+#define HOST_S_CMD 0x0002
+#define HOST_S_DATA 0x0003
+#define HOST_S_STOP 0x0004
+
+/* Easy access macros */
+#define HOST_STATUS(h) ((h)->iobase + SD_STATUS)
+#define HOST_CONFIG(h) ((h)->iobase + SD_CONFIG)
+#define HOST_ENABLE(h) ((h)->iobase + SD_ENABLE)
+#define HOST_TXPORT(h) ((h)->iobase + SD_TXPORT)
+#define HOST_RXPORT(h) ((h)->iobase + SD_RXPORT)
+#define HOST_CMDARG(h) ((h)->iobase + SD_CMDARG)
+#define HOST_BLKSIZE(h) ((h)->iobase + SD_BLKSIZE)
+#define HOST_CMD(h) ((h)->iobase + SD_CMD)
+#define HOST_CONFIG2(h) ((h)->iobase + SD_CONFIG2)
+#define HOST_TIMEOUT(h) ((h)->iobase + SD_TIMEOUT)
+#define HOST_DEBUG(h) ((h)->iobase + SD_DEBUG)
+
+#define DMA_CHANNEL(h) \
+ (((h)->flags & HOST_F_XMIT) ? (h)->tx_chan : (h)->rx_chan)
+
+static inline void IRQ_ON(struct au1xmmc_host *host, u32 mask)
+{
+ u32 val = au_readl(HOST_CONFIG(host));
+ val |= mask;
+ au_writel(val, HOST_CONFIG(host));
+ au_sync();
+}
+
+static inline void FLUSH_FIFO(struct au1xmmc_host *host)
+{
+ u32 val = au_readl(HOST_CONFIG2(host));
+
+ au_writel(val | SD_CONFIG2_FF, HOST_CONFIG2(host));
+ au_sync_delay(1);
+
+ /* SEND_STOP will turn off clock control - this re-enables it */
+ val &= ~SD_CONFIG2_DF;
+
+ au_writel(val, HOST_CONFIG2(host));
+ au_sync();
+}
+
+static inline void IRQ_OFF(struct au1xmmc_host *host, u32 mask)
+{
+ u32 val = au_readl(HOST_CONFIG(host));
+ val &= ~mask;
+ au_writel(val, HOST_CONFIG(host));
+ au_sync();
+}
+
+static inline void SEND_STOP(struct au1xmmc_host *host)
+{
+ u32 config2;
+
+ WARN_ON(host->status != HOST_S_DATA);
+ host->status = HOST_S_STOP;
+
+ config2 = au_readl(HOST_CONFIG2(host));
+ au_writel(config2 | SD_CONFIG2_DF, HOST_CONFIG2(host));
+ au_sync();
+
+ /* Send the stop commmand */
+ au_writel(STOP_CMD, HOST_CMD(host));
+}
+
+static void au1xmmc_set_power(struct au1xmmc_host *host, int state)
+{
+ if (host->platdata && host->platdata->set_power)
+ host->platdata->set_power(host->mmc, state);
+}
+
+static int au1xmmc_card_inserted(struct mmc_host *mmc)
+{
+ struct au1xmmc_host *host = mmc_priv(mmc);
+
+ if (host->platdata && host->platdata->card_inserted)
+ return !!host->platdata->card_inserted(host->mmc);
+
+ return -ENOSYS;
+}
+
+static int au1xmmc_card_readonly(struct mmc_host *mmc)
+{
+ struct au1xmmc_host *host = mmc_priv(mmc);
+
+ if (host->platdata && host->platdata->card_readonly)
+ return !!host->platdata->card_readonly(mmc);
+
+ return -ENOSYS;
+}
+
+static void au1xmmc_finish_request(struct au1xmmc_host *host)
+{
+ struct mmc_request *mrq = host->mrq;
+
+ host->mrq = NULL;
+ host->flags &= HOST_F_ACTIVE | HOST_F_DMA;
+
+ host->dma.len = 0;
+ host->dma.dir = 0;
+
+ host->pio.index = 0;
+ host->pio.offset = 0;
+ host->pio.len = 0;
+
+ host->status = HOST_S_IDLE;
+
+ mmc_request_done(host->mmc, mrq);
+}
+
+static void au1xmmc_tasklet_finish(unsigned long param)
+{
+ struct au1xmmc_host *host = (struct au1xmmc_host *) param;
+ au1xmmc_finish_request(host);
+}
+
+static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
+ struct mmc_command *cmd, struct mmc_data *data)
+{
+ u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT);
+
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ break;
+ case MMC_RSP_R1:
+ mmccmd |= SD_CMD_RT_1;
+ break;
+ case MMC_RSP_R1B:
+ mmccmd |= SD_CMD_RT_1B;
+ break;
+ case MMC_RSP_R2:
+ mmccmd |= SD_CMD_RT_2;
+ break;
+ case MMC_RSP_R3:
+ mmccmd |= SD_CMD_RT_3;
+ break;
+ default:
+ printk(KERN_INFO "au1xmmc: unhandled response type %02x\n",
+ mmc_resp_type(cmd));
+ return -EINVAL;
+ }
+
+ if (data) {
+ if (data->flags & MMC_DATA_READ) {
+ if (data->blocks > 1)
+ mmccmd |= SD_CMD_CT_4;
+ else
+ mmccmd |= SD_CMD_CT_2;
+ } else if (data->flags & MMC_DATA_WRITE) {
+ if (data->blocks > 1)
+ mmccmd |= SD_CMD_CT_3;
+ else
+ mmccmd |= SD_CMD_CT_1;
+ }
+ }
+
+ au_writel(cmd->arg, HOST_CMDARG(host));
+ au_sync();
+
+ if (wait)
+ IRQ_OFF(host, SD_CONFIG_CR);
+
+ au_writel((mmccmd | SD_CMD_GO), HOST_CMD(host));
+ au_sync();
+
+ /* Wait for the command to go on the line */
+ while (au_readl(HOST_CMD(host)) & SD_CMD_GO)
+ /* nop */;
+
+ /* Wait for the command to come back */
+ if (wait) {
+ u32 status = au_readl(HOST_STATUS(host));
+
+ while (!(status & SD_STATUS_CR))
+ status = au_readl(HOST_STATUS(host));
+
+ /* Clear the CR status */
+ au_writel(SD_STATUS_CR, HOST_STATUS(host));
+
+ IRQ_ON(host, SD_CONFIG_CR);
+ }
+
+ return 0;
+}
+
+static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
+{
+ struct mmc_request *mrq = host->mrq;
+ struct mmc_data *data;
+ u32 crc;
+
+ WARN_ON((host->status != HOST_S_DATA) && (host->status != HOST_S_STOP));
+
+ if (host->mrq == NULL)
+ return;
+
+ data = mrq->cmd->data;
+
+ if (status == 0)
+ status = au_readl(HOST_STATUS(host));
+
+ /* The transaction is really over when the SD_STATUS_DB bit is clear */
+ while ((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB))
+ status = au_readl(HOST_STATUS(host));
+
+ data->error = 0;
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir);
+
+ /* Process any errors */
+ crc = (status & (SD_STATUS_WC | SD_STATUS_RC));
+ if (host->flags & HOST_F_XMIT)
+ crc |= ((status & 0x07) == 0x02) ? 0 : 1;
+
+ if (crc)
+ data->error = -EILSEQ;
+
+ /* Clear the CRC bits */
+ au_writel(SD_STATUS_WC | SD_STATUS_RC, HOST_STATUS(host));
+
+ data->bytes_xfered = 0;
+
+ if (!data->error) {
+ if (host->flags & HOST_F_DMA) {
+#ifdef CONFIG_SOC_AU1200 /* DBDMA */
+ u32 chan = DMA_CHANNEL(host);
+
+ chan_tab_t *c = *((chan_tab_t **)chan);
+ au1x_dma_chan_t *cp = c->chan_ptr;
+ data->bytes_xfered = cp->ddma_bytecnt;
+#endif
+ } else
+ data->bytes_xfered =
+ (data->blocks * data->blksz) - host->pio.len;
+ }
+
+ au1xmmc_finish_request(host);
+}
+
+static void au1xmmc_tasklet_data(unsigned long param)
+{
+ struct au1xmmc_host *host = (struct au1xmmc_host *)param;
+
+ u32 status = au_readl(HOST_STATUS(host));
+ au1xmmc_data_complete(host, status);
+}
+
+#define AU1XMMC_MAX_TRANSFER 8
+
+static void au1xmmc_send_pio(struct au1xmmc_host *host)
+{
+ struct mmc_data *data;
+ int sg_len, max, count;
+ unsigned char *sg_ptr, val;
+ u32 status;
+ struct scatterlist *sg;
+
+ data = host->mrq->data;
+
+ if (!(host->flags & HOST_F_XMIT))
+ return;
+
+ /* This is the pointer to the data buffer */
+ sg = &data->sg[host->pio.index];
+ sg_ptr = sg_virt(sg) + host->pio.offset;
+
+ /* This is the space left inside the buffer */
+ sg_len = data->sg[host->pio.index].length - host->pio.offset;
+
+ /* Check if we need less than the size of the sg_buffer */
+ max = (sg_len > host->pio.len) ? host->pio.len : sg_len;
+ if (max > AU1XMMC_MAX_TRANSFER)
+ max = AU1XMMC_MAX_TRANSFER;
+
+ for (count = 0; count < max; count++) {
+ status = au_readl(HOST_STATUS(host));
+
+ if (!(status & SD_STATUS_TH))
+ break;
+
+ val = *sg_ptr++;
+
+ au_writel((unsigned long)val, HOST_TXPORT(host));
+ au_sync();
+ }
+
+ host->pio.len -= count;
+ host->pio.offset += count;
+
+ if (count == sg_len) {
+ host->pio.index++;
+ host->pio.offset = 0;
+ }
+
+ if (host->pio.len == 0) {
+ IRQ_OFF(host, SD_CONFIG_TH);
+
+ if (host->flags & HOST_F_STOP)
+ SEND_STOP(host);
+
+ tasklet_schedule(&host->data_task);
+ }
+}
+
+static void au1xmmc_receive_pio(struct au1xmmc_host *host)
+{
+ struct mmc_data *data;
+ int max, count, sg_len = 0;
+ unsigned char *sg_ptr = NULL;
+ u32 status, val;
+ struct scatterlist *sg;
+
+ data = host->mrq->data;
+
+ if (!(host->flags & HOST_F_RECV))
+ return;
+
+ max = host->pio.len;
+
+ if (host->pio.index < host->dma.len) {
+ sg = &data->sg[host->pio.index];
+ sg_ptr = sg_virt(sg) + host->pio.offset;
+
+ /* This is the space left inside the buffer */
+ sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset;
+
+ /* Check if we need less than the size of the sg_buffer */
+ if (sg_len < max)
+ max = sg_len;
+ }
+
+ if (max > AU1XMMC_MAX_TRANSFER)
+ max = AU1XMMC_MAX_TRANSFER;
+
+ for (count = 0; count < max; count++) {
+ status = au_readl(HOST_STATUS(host));
+
+ if (!(status & SD_STATUS_NE))
+ break;
+
+ if (status & SD_STATUS_RC) {
+ DBG("RX CRC Error [%d + %d].\n", host->pdev->id,
+ host->pio.len, count);
+ break;
+ }
+
+ if (status & SD_STATUS_RO) {
+ DBG("RX Overrun [%d + %d]\n", host->pdev->id,
+ host->pio.len, count);
+ break;
+ }
+ else if (status & SD_STATUS_RU) {
+ DBG("RX Underrun [%d + %d]\n", host->pdev->id,
+ host->pio.len, count);
+ break;
+ }
+
+ val = au_readl(HOST_RXPORT(host));
+
+ if (sg_ptr)
+ *sg_ptr++ = (unsigned char)(val & 0xFF);
+ }
+
+ host->pio.len -= count;
+ host->pio.offset += count;
+
+ if (sg_len && count == sg_len) {
+ host->pio.index++;
+ host->pio.offset = 0;
+ }
+
+ if (host->pio.len == 0) {
+ /* IRQ_OFF(host, SD_CONFIG_RA | SD_CONFIG_RF); */
+ IRQ_OFF(host, SD_CONFIG_NE);
+
+ if (host->flags & HOST_F_STOP)
+ SEND_STOP(host);
+
+ tasklet_schedule(&host->data_task);
+ }
+}
+
+/* This is called when a command has been completed - grab the response
+ * and check for errors. Then start the data transfer if it is indicated.
+ */
+static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
+{
+ struct mmc_request *mrq = host->mrq;
+ struct mmc_command *cmd;
+ u32 r[4];
+ int i, trans;
+
+ if (!host->mrq)
+ return;
+
+ cmd = mrq->cmd;
+ cmd->error = 0;
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
+ r[0] = au_readl(host->iobase + SD_RESP3);
+ r[1] = au_readl(host->iobase + SD_RESP2);
+ r[2] = au_readl(host->iobase + SD_RESP1);
+ r[3] = au_readl(host->iobase + SD_RESP0);
+
+ /* The CRC is omitted from the response, so really
+ * we only got 120 bytes, but the engine expects
+ * 128 bits, so we have to shift things up.
+ */
+ for (i = 0; i < 4; i++) {
+ cmd->resp[i] = (r[i] & 0x00FFFFFF) << 8;
+ if (i != 3)
+ cmd->resp[i] |= (r[i + 1] & 0xFF000000) >> 24;
+ }
+ } else {
+ /* Techincally, we should be getting all 48 bits of
+ * the response (SD_RESP1 + SD_RESP2), but because
+ * our response omits the CRC, our data ends up
+ * being shifted 8 bits to the right. In this case,
+ * that means that the OSR data starts at bit 31,
+ * so we can just read RESP0 and return that.
+ */
+ cmd->resp[0] = au_readl(host->iobase + SD_RESP0);
+ }
+ }
+
+ /* Figure out errors */
+ if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC))
+ cmd->error = -EILSEQ;
+
+ trans = host->flags & (HOST_F_XMIT | HOST_F_RECV);
+
+ if (!trans || cmd->error) {
+ IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF);
+ tasklet_schedule(&host->finish_task);
+ return;
+ }
+
+ host->status = HOST_S_DATA;
+
+ if (host->flags & HOST_F_DMA) {
+#ifdef CONFIG_SOC_AU1200 /* DBDMA */
+ u32 channel = DMA_CHANNEL(host);
+
+ /* Start the DMA as soon as the buffer gets something in it */
+
+ if (host->flags & HOST_F_RECV) {
+ u32 mask = SD_STATUS_DB | SD_STATUS_NE;
+
+ while((status & mask) != mask)
+ status = au_readl(HOST_STATUS(host));
+ }
+
+ au1xxx_dbdma_start(channel);
+#endif
+ }
+}
+
+static void au1xmmc_set_clock(struct au1xmmc_host *host, int rate)
+{
+ unsigned int pbus = get_au1x00_speed();
+ unsigned int divisor;
+ u32 config;
+
+ /* From databook:
+ * divisor = ((((cpuclock / sbus_divisor) / 2) / mmcclock) / 2) - 1
+ */
+ pbus /= ((au_readl(SYS_POWERCTRL) & 0x3) + 2);
+ pbus /= 2;
+ divisor = ((pbus / rate) / 2) - 1;
+
+ config = au_readl(HOST_CONFIG(host));
+
+ config &= ~(SD_CONFIG_DIV);
+ config |= (divisor & SD_CONFIG_DIV) | SD_CONFIG_DE;
+
+ au_writel(config, HOST_CONFIG(host));
+ au_sync();
+}
+
+static int au1xmmc_prepare_data(struct au1xmmc_host *host,
+ struct mmc_data *data)
+{
+ int datalen = data->blocks * data->blksz;
+
+ if (data->flags & MMC_DATA_READ)
+ host->flags |= HOST_F_RECV;
+ else
+ host->flags |= HOST_F_XMIT;
+
+ if (host->mrq->stop)
+ host->flags |= HOST_F_STOP;
+
+ host->dma.dir = DMA_BIDIRECTIONAL;
+
+ host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, host->dma.dir);
+
+ if (host->dma.len == 0)
+ return -ETIMEDOUT;
+
+ au_writel(data->blksz - 1, HOST_BLKSIZE(host));
+
+ if (host->flags & HOST_F_DMA) {
+#ifdef CONFIG_SOC_AU1200 /* DBDMA */
+ int i;
+ u32 channel = DMA_CHANNEL(host);
+
+ au1xxx_dbdma_stop(channel);
+
+ for (i = 0; i < host->dma.len; i++) {
+ u32 ret = 0, flags = DDMA_FLAGS_NOIE;
+ struct scatterlist *sg = &data->sg[i];
+ int sg_len = sg->length;
+
+ int len = (datalen > sg_len) ? sg_len : datalen;
+
+ if (i == host->dma.len - 1)
+ flags = DDMA_FLAGS_IE;
+
+ if (host->flags & HOST_F_XMIT) {
+ ret = au1xxx_dbdma_put_source_flags(channel,
+ (void *)sg_virt(sg), len, flags);
+ } else {
+ ret = au1xxx_dbdma_put_dest_flags(channel,
+ (void *)sg_virt(sg), len, flags);
+ }
+
+ if (!ret)
+ goto dataerr;
+
+ datalen -= len;
+ }
+#endif
+ } else {
+ host->pio.index = 0;
+ host->pio.offset = 0;
+ host->pio.len = datalen;
+
+ if (host->flags & HOST_F_XMIT)
+ IRQ_ON(host, SD_CONFIG_TH);
+ else
+ IRQ_ON(host, SD_CONFIG_NE);
+ /* IRQ_ON(host, SD_CONFIG_RA | SD_CONFIG_RF); */
+ }
+
+ return 0;
+
+dataerr:
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ host->dma.dir);
+ return -ETIMEDOUT;
+}
+
+/* This actually starts a command or data transaction */
+static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
+{
+ struct au1xmmc_host *host = mmc_priv(mmc);
+ int ret = 0;
+
+ WARN_ON(irqs_disabled());
+ WARN_ON(host->status != HOST_S_IDLE);
+
+ host->mrq = mrq;
+ host->status = HOST_S_CMD;
+
+ /* fail request immediately if no card is present */
+ if (0 == au1xmmc_card_inserted(mmc)) {
+ mrq->cmd->error = -ENOMEDIUM;
+ au1xmmc_finish_request(host);
+ return;
+ }
+
+ if (mrq->data) {
+ FLUSH_FIFO(host);
+ ret = au1xmmc_prepare_data(host, mrq->data);
+ }
+
+ if (!ret)
+ ret = au1xmmc_send_command(host, 0, mrq->cmd, mrq->data);
+
+ if (ret) {
+ mrq->cmd->error = ret;
+ au1xmmc_finish_request(host);
+ }
+}
+
+static void au1xmmc_reset_controller(struct au1xmmc_host *host)
+{
+ /* Apply the clock */
+ au_writel(SD_ENABLE_CE, HOST_ENABLE(host));
+ au_sync_delay(1);
+
+ au_writel(SD_ENABLE_R | SD_ENABLE_CE, HOST_ENABLE(host));
+ au_sync_delay(5);
+
+ au_writel(~0, HOST_STATUS(host));
+ au_sync();
+
+ au_writel(0, HOST_BLKSIZE(host));
+ au_writel(0x001fffff, HOST_TIMEOUT(host));
+ au_sync();
+
+ au_writel(SD_CONFIG2_EN, HOST_CONFIG2(host));
+ au_sync();
+
+ au_writel(SD_CONFIG2_EN | SD_CONFIG2_FF, HOST_CONFIG2(host));
+ au_sync_delay(1);
+
+ au_writel(SD_CONFIG2_EN, HOST_CONFIG2(host));
+ au_sync();
+
+ /* Configure interrupts */
+ au_writel(AU1XMMC_INTERRUPTS, HOST_CONFIG(host));
+ au_sync();
+}
+
+
+static void au1xmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct au1xmmc_host *host = mmc_priv(mmc);
+ u32 config2;
+
+ if (ios->power_mode == MMC_POWER_OFF)
+ au1xmmc_set_power(host, 0);
+ else if (ios->power_mode == MMC_POWER_ON) {
+ au1xmmc_set_power(host, 1);
+ }
+
+ if (ios->clock && ios->clock != host->clock) {
+ au1xmmc_set_clock(host, ios->clock);
+ host->clock = ios->clock;
+ }
+
+ config2 = au_readl(HOST_CONFIG2(host));
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_4:
+ config2 |= SD_CONFIG2_WB;
+ break;
+ case MMC_BUS_WIDTH_1:
+ config2 &= ~SD_CONFIG2_WB;
+ break;
+ }
+ au_writel(config2, HOST_CONFIG2(host));
+ au_sync();
+}
+
+#define STATUS_TIMEOUT (SD_STATUS_RAT | SD_STATUS_DT)
+#define STATUS_DATA_IN (SD_STATUS_NE)
+#define STATUS_DATA_OUT (SD_STATUS_TH)
+
+static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
+{
+ struct au1xmmc_host *host = dev_id;
+ u32 status;
+
+ status = au_readl(HOST_STATUS(host));
+
+ if (!(status & SD_STATUS_I))
+ return IRQ_NONE; /* not ours */
+
+ if (status & SD_STATUS_SI) /* SDIO */
+ mmc_signal_sdio_irq(host->mmc);
+
+ if (host->mrq && (status & STATUS_TIMEOUT)) {
+ if (status & SD_STATUS_RAT)
+ host->mrq->cmd->error = -ETIMEDOUT;
+ else if (status & SD_STATUS_DT)
+ host->mrq->data->error = -ETIMEDOUT;
+
+ /* In PIO mode, interrupts might still be enabled */
+ IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH);
+
+ /* IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF); */
+ tasklet_schedule(&host->finish_task);
+ }
+#if 0
+ else if (status & SD_STATUS_DD) {
+ /* Sometimes we get a DD before a NE in PIO mode */
+ if (!(host->flags & HOST_F_DMA) && (status & SD_STATUS_NE))
+ au1xmmc_receive_pio(host);
+ else {
+ au1xmmc_data_complete(host, status);
+ /* tasklet_schedule(&host->data_task); */
+ }
+ }
+#endif
+ else if (status & SD_STATUS_CR) {
+ if (host->status == HOST_S_CMD)
+ au1xmmc_cmd_complete(host, status);
+
+ } else if (!(host->flags & HOST_F_DMA)) {
+ if ((host->flags & HOST_F_XMIT) && (status & STATUS_DATA_OUT))
+ au1xmmc_send_pio(host);
+ else if ((host->flags & HOST_F_RECV) && (status & STATUS_DATA_IN))
+ au1xmmc_receive_pio(host);
+
+ } else if (status & 0x203F3C70) {
+ DBG("Unhandled status %8.8x\n", host->pdev->id,
+ status);
+ }
+
+ au_writel(status, HOST_STATUS(host));
+ au_sync();
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_SOC_AU1200
+/* 8bit memory DMA device */
+static dbdev_tab_t au1xmmc_mem_dbdev = {
+ .dev_id = DSCR_CMD0_ALWAYS,
+ .dev_flags = DEV_FLAGS_ANYUSE,
+ .dev_tsize = 0,
+ .dev_devwidth = 8,
+ .dev_physaddr = 0x00000000,
+ .dev_intlevel = 0,
+ .dev_intpolarity = 0,
+};
+static int memid;
+
+static void au1xmmc_dbdma_callback(int irq, void *dev_id)
+{
+ struct au1xmmc_host *host = (struct au1xmmc_host *)dev_id;
+
+ /* Avoid spurious interrupts */
+ if (!host->mrq)
+ return;
+
+ if (host->flags & HOST_F_STOP)
+ SEND_STOP(host);
+
+ tasklet_schedule(&host->data_task);
+}
+
+static int au1xmmc_dbdma_init(struct au1xmmc_host *host)
+{
+ struct resource *res;
+ int txid, rxid;
+
+ res = platform_get_resource(host->pdev, IORESOURCE_DMA, 0);
+ if (!res)
+ return -ENODEV;
+ txid = res->start;
+
+ res = platform_get_resource(host->pdev, IORESOURCE_DMA, 1);
+ if (!res)
+ return -ENODEV;
+ rxid = res->start;
+
+ if (!memid)
+ return -ENODEV;
+
+ host->tx_chan = au1xxx_dbdma_chan_alloc(memid, txid,
+ au1xmmc_dbdma_callback, (void *)host);
+ if (!host->tx_chan) {
+ dev_err(&host->pdev->dev, "cannot allocate TX DMA\n");
+ return -ENODEV;
+ }
+
+ host->rx_chan = au1xxx_dbdma_chan_alloc(rxid, memid,
+ au1xmmc_dbdma_callback, (void *)host);
+ if (!host->rx_chan) {
+ dev_err(&host->pdev->dev, "cannot allocate RX DMA\n");
+ au1xxx_dbdma_chan_free(host->tx_chan);
+ return -ENODEV;
+ }
+
+ au1xxx_dbdma_set_devwidth(host->tx_chan, 8);
+ au1xxx_dbdma_set_devwidth(host->rx_chan, 8);
+
+ au1xxx_dbdma_ring_alloc(host->tx_chan, AU1XMMC_DESCRIPTOR_COUNT);
+ au1xxx_dbdma_ring_alloc(host->rx_chan, AU1XMMC_DESCRIPTOR_COUNT);
+
+ /* DBDMA is good to go */
+ host->flags |= HOST_F_DMA;
+
+ return 0;
+}
+
+static void au1xmmc_dbdma_shutdown(struct au1xmmc_host *host)
+{
+ if (host->flags & HOST_F_DMA) {
+ host->flags &= ~HOST_F_DMA;
+ au1xxx_dbdma_chan_free(host->tx_chan);
+ au1xxx_dbdma_chan_free(host->rx_chan);
+ }
+}
+#endif
+
+static void au1xmmc_enable_sdio_irq(struct mmc_host *mmc, int en)
+{
+ struct au1xmmc_host *host = mmc_priv(mmc);
+
+ if (en)
+ IRQ_ON(host, SD_CONFIG_SI);
+ else
+ IRQ_OFF(host, SD_CONFIG_SI);
+}
+
+static const struct mmc_host_ops au1xmmc_ops = {
+ .request = au1xmmc_request,
+ .set_ios = au1xmmc_set_ios,
+ .get_ro = au1xmmc_card_readonly,
+ .get_cd = au1xmmc_card_inserted,
+ .enable_sdio_irq = au1xmmc_enable_sdio_irq,
+};
+
+static int __devinit au1xmmc_probe(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct au1xmmc_host *host;
+ struct resource *r;
+ int ret;
+
+ mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev);
+ if (!mmc) {
+ dev_err(&pdev->dev, "no memory for mmc_host\n");
+ ret = -ENOMEM;
+ goto out0;
+ }
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->platdata = pdev->dev.platform_data;
+ host->pdev = pdev;
+
+ ret = -ENODEV;
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "no mmio defined\n");
+ goto out1;
+ }
+
+ host->ioarea = request_mem_region(r->start, r->end - r->start + 1,
+ pdev->name);
+ if (!host->ioarea) {
+ dev_err(&pdev->dev, "mmio already in use\n");
+ goto out1;
+ }
+
+ host->iobase = (unsigned long)ioremap(r->start, 0x3c);
+ if (!host->iobase) {
+ dev_err(&pdev->dev, "cannot remap mmio\n");
+ goto out2;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "no IRQ defined\n");
+ goto out3;
+ }
+
+ host->irq = r->start;
+ /* IRQ is shared among both SD controllers */
+ ret = request_irq(host->irq, au1xmmc_irq, IRQF_SHARED,
+ DRIVER_NAME, host);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot grab IRQ\n");
+ goto out3;
+ }
+
+ mmc->ops = &au1xmmc_ops;
+
+ mmc->f_min = 450000;
+ mmc->f_max = 24000000;
+
+ mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE;
+ mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT;
+
+ mmc->max_blk_size = 2048;
+ mmc->max_blk_count = 512;
+
+ mmc->ocr_avail = AU1XMMC_OCR;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
+
+ host->status = HOST_S_IDLE;
+
+ /* board-specific carddetect setup, if any */
+ if (host->platdata && host->platdata->cd_setup) {
+ ret = host->platdata->cd_setup(mmc, 1);
+ if (ret) {
+ dev_warn(&pdev->dev, "board CD setup failed\n");
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+ }
+ } else
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+ tasklet_init(&host->data_task, au1xmmc_tasklet_data,
+ (unsigned long)host);
+
+ tasklet_init(&host->finish_task, au1xmmc_tasklet_finish,
+ (unsigned long)host);
+
+#ifdef CONFIG_SOC_AU1200
+ ret = au1xmmc_dbdma_init(host);
+ if (ret)
+ printk(KERN_INFO DRIVER_NAME ": DBDMA init failed; using PIO\n");
+#endif
+
+#ifdef CONFIG_LEDS_CLASS
+ if (host->platdata && host->platdata->led) {
+ struct led_classdev *led = host->platdata->led;
+ led->name = mmc_hostname(mmc);
+ led->brightness = LED_OFF;
+ led->default_trigger = mmc_hostname(mmc);
+ ret = led_classdev_register(mmc_dev(mmc), led);
+ if (ret)
+ goto out5;
+ }
+#endif
+
+ au1xmmc_reset_controller(host);
+
+ ret = mmc_add_host(mmc);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot add mmc host\n");
+ goto out6;
+ }
+
+ platform_set_drvdata(pdev, host);
+
+ printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X"
+ " (mode=%s)\n", pdev->id, host->iobase,
+ host->flags & HOST_F_DMA ? "dma" : "pio");
+
+ return 0; /* all ok */
+
+out6:
+#ifdef CONFIG_LEDS_CLASS
+ if (host->platdata && host->platdata->led)
+ led_classdev_unregister(host->platdata->led);
+out5:
+#endif
+ au_writel(0, HOST_ENABLE(host));
+ au_writel(0, HOST_CONFIG(host));
+ au_writel(0, HOST_CONFIG2(host));
+ au_sync();
+
+#ifdef CONFIG_SOC_AU1200
+ au1xmmc_dbdma_shutdown(host);
+#endif
+
+ tasklet_kill(&host->data_task);
+ tasklet_kill(&host->finish_task);
+
+ if (host->platdata && host->platdata->cd_setup &&
+ !(mmc->caps & MMC_CAP_NEEDS_POLL))
+ host->platdata->cd_setup(mmc, 0);
+
+ free_irq(host->irq, host);
+out3:
+ iounmap((void *)host->iobase);
+out2:
+ release_resource(host->ioarea);
+ kfree(host->ioarea);
+out1:
+ mmc_free_host(mmc);
+out0:
+ return ret;
+}
+
+static int __devexit au1xmmc_remove(struct platform_device *pdev)
+{
+ struct au1xmmc_host *host = platform_get_drvdata(pdev);
+
+ if (host) {
+ mmc_remove_host(host->mmc);
+
+#ifdef CONFIG_LEDS_CLASS
+ if (host->platdata && host->platdata->led)
+ led_classdev_unregister(host->platdata->led);
+#endif
+
+ if (host->platdata && host->platdata->cd_setup &&
+ !(host->mmc->caps & MMC_CAP_NEEDS_POLL))
+ host->platdata->cd_setup(host->mmc, 0);
+
+ au_writel(0, HOST_ENABLE(host));
+ au_writel(0, HOST_CONFIG(host));
+ au_writel(0, HOST_CONFIG2(host));
+ au_sync();
+
+ tasklet_kill(&host->data_task);
+ tasklet_kill(&host->finish_task);
+
+#ifdef CONFIG_SOC_AU1200
+ au1xmmc_dbdma_shutdown(host);
+#endif
+ au1xmmc_set_power(host, 0);
+
+ free_irq(host->irq, host);
+ iounmap((void *)host->iobase);
+ release_resource(host->ioarea);
+ kfree(host->ioarea);
+
+ mmc_free_host(host->mmc);
+ platform_set_drvdata(pdev, NULL);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct au1xmmc_host *host = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = mmc_suspend_host(host->mmc, state);
+ if (ret)
+ return ret;
+
+ au_writel(0, HOST_CONFIG2(host));
+ au_writel(0, HOST_CONFIG(host));
+ au_writel(0xffffffff, HOST_STATUS(host));
+ au_writel(0, HOST_ENABLE(host));
+ au_sync();
+
+ return 0;
+}
+
+static int au1xmmc_resume(struct platform_device *pdev)
+{
+ struct au1xmmc_host *host = platform_get_drvdata(pdev);
+
+ au1xmmc_reset_controller(host);
+
+ return mmc_resume_host(host->mmc);
+}
+#else
+#define au1xmmc_suspend NULL
+#define au1xmmc_resume NULL
+#endif
+
+static struct platform_driver au1xmmc_driver = {
+ .probe = au1xmmc_probe,
+ .remove = au1xmmc_remove,
+ .suspend = au1xmmc_suspend,
+ .resume = au1xmmc_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init au1xmmc_init(void)
+{
+#ifdef CONFIG_SOC_AU1200
+ /* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride
+ * of 8 bits. And since devices are shared, we need to create
+ * our own to avoid freaking out other devices.
+ */
+ memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev);
+ if (!memid)
+ printk(KERN_ERR "au1xmmc: cannot add memory dbdma dev\n");
+#endif
+ return platform_driver_register(&au1xmmc_driver);
+}
+
+static void __exit au1xmmc_exit(void)
+{
+#ifdef CONFIG_SOC_AU1200
+ if (memid)
+ au1xxx_ddma_del_device(memid);
+#endif
+ platform_driver_unregister(&au1xmmc_driver);
+}
+
+module_init(au1xmmc_init);
+module_exit(au1xmmc_exit);
+
+MODULE_AUTHOR("Advanced Micro Devices, Inc");
+MODULE_DESCRIPTION("MMC/SD driver for the Alchemy Au1XXX");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:au1xxx-mmc");
diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c
new file mode 100644
index 0000000..2f0fcdb
--- /dev/null
+++ b/drivers/mmc/host/imxmmc.c
@@ -0,0 +1,1151 @@
+/*
+ * linux/drivers/mmc/host/imxmmc.c - Motorola i.MX MMCI driver
+ *
+ * Copyright (C) 2004 Sascha Hauer, Pengutronix <sascha@saschahauer.de>
+ * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
+ *
+ * derived from pxamci.c by Russell King
+ *
+ * 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.
+ *
+ * 2005-04-17 Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ * Changed to conform redesigned i.MX scatter gather DMA interface
+ *
+ * 2005-11-04 Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ * Updated for 2.6.14 kernel
+ *
+ * 2005-12-13 Jay Monkman <jtm@smoothsmoothie.com>
+ * Found and corrected problems in the write path
+ *
+ * 2005-12-30 Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ * The event handling rewritten right way in softirq.
+ * Added many ugly hacks and delays to overcome SDHC
+ * deficiencies
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/sizes.h>
+#include <mach/mmc.h>
+#include <mach/imx-dma.h>
+
+#include "imxmmc.h"
+
+#define DRIVER_NAME "imx-mmc"
+
+#define IMXMCI_INT_MASK_DEFAULT (INT_MASK_BUF_READY | INT_MASK_DATA_TRAN | \
+ INT_MASK_WRITE_OP_DONE | INT_MASK_END_CMD_RES | \
+ INT_MASK_AUTO_CARD_DETECT | INT_MASK_DAT0_EN | INT_MASK_SDIO)
+
+struct imxmci_host {
+ struct mmc_host *mmc;
+ spinlock_t lock;
+ struct resource *res;
+ int irq;
+ imx_dmach_t dma;
+ unsigned int clkrt;
+ unsigned int cmdat;
+ volatile unsigned int imask;
+ unsigned int power_mode;
+ unsigned int present;
+ struct imxmmc_platform_data *pdata;
+
+ struct mmc_request *req;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+
+ struct timer_list timer;
+ struct tasklet_struct tasklet;
+ unsigned int status_reg;
+ unsigned long pending_events;
+ /* Next to fields are there for CPU driven transfers to overcome SDHC deficiencies */
+ u16 *data_ptr;
+ unsigned int data_cnt;
+ atomic_t stuck_timeout;
+
+ unsigned int dma_nents;
+ unsigned int dma_size;
+ unsigned int dma_dir;
+ int dma_allocated;
+
+ unsigned char actual_bus_width;
+
+ int prev_cmd_code;
+
+ struct clk *clk;
+};
+
+#define IMXMCI_PEND_IRQ_b 0
+#define IMXMCI_PEND_DMA_END_b 1
+#define IMXMCI_PEND_DMA_ERR_b 2
+#define IMXMCI_PEND_WAIT_RESP_b 3
+#define IMXMCI_PEND_DMA_DATA_b 4
+#define IMXMCI_PEND_CPU_DATA_b 5
+#define IMXMCI_PEND_CARD_XCHG_b 6
+#define IMXMCI_PEND_SET_INIT_b 7
+#define IMXMCI_PEND_STARTED_b 8
+
+#define IMXMCI_PEND_IRQ_m (1 << IMXMCI_PEND_IRQ_b)
+#define IMXMCI_PEND_DMA_END_m (1 << IMXMCI_PEND_DMA_END_b)
+#define IMXMCI_PEND_DMA_ERR_m (1 << IMXMCI_PEND_DMA_ERR_b)
+#define IMXMCI_PEND_WAIT_RESP_m (1 << IMXMCI_PEND_WAIT_RESP_b)
+#define IMXMCI_PEND_DMA_DATA_m (1 << IMXMCI_PEND_DMA_DATA_b)
+#define IMXMCI_PEND_CPU_DATA_m (1 << IMXMCI_PEND_CPU_DATA_b)
+#define IMXMCI_PEND_CARD_XCHG_m (1 << IMXMCI_PEND_CARD_XCHG_b)
+#define IMXMCI_PEND_SET_INIT_m (1 << IMXMCI_PEND_SET_INIT_b)
+#define IMXMCI_PEND_STARTED_m (1 << IMXMCI_PEND_STARTED_b)
+
+static void imxmci_stop_clock(struct imxmci_host *host)
+{
+ int i = 0;
+ MMC_STR_STP_CLK &= ~STR_STP_CLK_START_CLK;
+ while(i < 0x1000) {
+ if(!(i & 0x7f))
+ MMC_STR_STP_CLK |= STR_STP_CLK_STOP_CLK;
+
+ if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)) {
+ /* Check twice before cut */
+ if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN))
+ return;
+ }
+
+ i++;
+ }
+ dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n");
+}
+
+static int imxmci_start_clock(struct imxmci_host *host)
+{
+ unsigned int trials = 0;
+ unsigned int delay_limit = 128;
+ unsigned long flags;
+
+ MMC_STR_STP_CLK &= ~STR_STP_CLK_STOP_CLK;
+
+ clear_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
+
+ /*
+ * Command start of the clock, this usually succeeds in less
+ * then 6 delay loops, but during card detection (low clockrate)
+ * it takes up to 5000 delay loops and sometimes fails for the first time
+ */
+ MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
+
+ do {
+ unsigned int delay = delay_limit;
+
+ while(delay--){
+ if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
+ /* Check twice before cut */
+ if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)
+ return 0;
+
+ if(test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
+ return 0;
+ }
+
+ local_irq_save(flags);
+ /*
+ * Ensure, that request is not doubled under all possible circumstances.
+ * It is possible, that cock running state is missed, because some other
+ * IRQ or schedule delays this function execution and the clocks has
+ * been already stopped by other means (response processing, SDHC HW)
+ */
+ if(!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events))
+ MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK;
+ local_irq_restore(flags);
+
+ } while(++trials<256);
+
+ dev_err(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n");
+
+ return -1;
+}
+
+static void imxmci_softreset(void)
+{
+ /* reset sequence */
+ MMC_STR_STP_CLK = 0x8;
+ MMC_STR_STP_CLK = 0xD;
+ MMC_STR_STP_CLK = 0x5;
+ MMC_STR_STP_CLK = 0x5;
+ MMC_STR_STP_CLK = 0x5;
+ MMC_STR_STP_CLK = 0x5;
+ MMC_STR_STP_CLK = 0x5;
+ MMC_STR_STP_CLK = 0x5;
+ MMC_STR_STP_CLK = 0x5;
+ MMC_STR_STP_CLK = 0x5;
+
+ MMC_RES_TO = 0xff;
+ MMC_BLK_LEN = 512;
+ MMC_NOB = 1;
+}
+
+static int imxmci_busy_wait_for_status(struct imxmci_host *host,
+ unsigned int *pstat, unsigned int stat_mask,
+ int timeout, const char *where)
+{
+ int loops=0;
+ while(!(*pstat & stat_mask)) {
+ loops+=2;
+ if(loops >= timeout) {
+ dev_dbg(mmc_dev(host->mmc), "busy wait timeout in %s, STATUS = 0x%x (0x%x)\n",
+ where, *pstat, stat_mask);
+ return -1;
+ }
+ udelay(2);
+ *pstat |= MMC_STATUS;
+ }
+ if(!loops)
+ return 0;
+
+ /* The busy-wait is expected there for clock <8MHz due to SDHC hardware flaws */
+ if(!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock>=8000000))
+ dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n",
+ loops, where, *pstat, stat_mask);
+ return loops;
+}
+
+static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data)
+{
+ unsigned int nob = data->blocks;
+ unsigned int blksz = data->blksz;
+ unsigned int datasz = nob * blksz;
+ int i;
+
+ if (data->flags & MMC_DATA_STREAM)
+ nob = 0xffff;
+
+ host->data = data;
+ data->bytes_xfered = 0;
+
+ MMC_NOB = nob;
+ MMC_BLK_LEN = blksz;
+
+ /*
+ * DMA cannot be used for small block sizes, we have to use CPU driven transfers otherwise.
+ * We are in big troubles for non-512 byte transfers according to note in the paragraph
+ * 20.6.7 of User Manual anyway, but we need to be able to transfer SCR at least.
+ * The situation is even more complex in reality. The SDHC in not able to handle wll
+ * partial FIFO fills and reads. The length has to be rounded up to burst size multiple.
+ * This is required for SCR read at least.
+ */
+ if (datasz < 512) {
+ host->dma_size = datasz;
+ if (data->flags & MMC_DATA_READ) {
+ host->dma_dir = DMA_FROM_DEVICE;
+
+ /* Hack to enable read SCR */
+ MMC_NOB = 1;
+ MMC_BLK_LEN = 512;
+ } else {
+ host->dma_dir = DMA_TO_DEVICE;
+ }
+
+ /* Convert back to virtual address */
+ host->data_ptr = (u16*)sg_virt(data->sg);
+ host->data_cnt = 0;
+
+ clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events);
+ set_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events);
+
+ return;
+ }
+
+ if (data->flags & MMC_DATA_READ) {
+ host->dma_dir = DMA_FROM_DEVICE;
+ host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, host->dma_dir);
+
+ imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
+ host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_READ);
+
+ /*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_READ, IMX_DMA_WIDTH_16, CCR_REN);*/
+ CCR(host->dma) = CCR_DMOD_LINEAR | CCR_DSIZ_32 | CCR_SMOD_FIFO | CCR_SSIZ_16 | CCR_REN;
+ } else {
+ host->dma_dir = DMA_TO_DEVICE;
+
+ host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, host->dma_dir);
+
+ imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz,
+ host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_WRITE);
+
+ /*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_WRITE, IMX_DMA_WIDTH_16, CCR_REN);*/
+ CCR(host->dma) = CCR_SMOD_LINEAR | CCR_SSIZ_32 | CCR_DMOD_FIFO | CCR_DSIZ_16 | CCR_REN;
+ }
+
+#if 1 /* This code is there only for consistency checking and can be disabled in future */
+ host->dma_size = 0;
+ for(i=0; i<host->dma_nents; i++)
+ host->dma_size+=data->sg[i].length;
+
+ if (datasz > host->dma_size) {
+ dev_err(mmc_dev(host->mmc), "imxmci_setup_data datasz 0x%x > 0x%x dm_size\n",
+ datasz, host->dma_size);
+ }
+#endif
+
+ host->dma_size = datasz;
+
+ wmb();
+
+ if(host->actual_bus_width == MMC_BUS_WIDTH_4)
+ BLR(host->dma) = 0; /* burst 64 byte read / 64 bytes write */
+ else
+ BLR(host->dma) = 16; /* burst 16 byte read / 16 bytes write */
+
+ RSSR(host->dma) = DMA_REQ_SDHC;
+
+ set_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events);
+ clear_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events);
+
+ /* start DMA engine for read, write is delayed after initial response */
+ if (host->dma_dir == DMA_FROM_DEVICE) {
+ imx_dma_enable(host->dma);
+ }
+}
+
+static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd, unsigned int cmdat)
+{
+ unsigned long flags;
+ u32 imask;
+
+ WARN_ON(host->cmd != NULL);
+ host->cmd = cmd;
+
+ /* Ensure, that clock are stopped else command programming and start fails */
+ imxmci_stop_clock(host);
+
+ if (cmd->flags & MMC_RSP_BUSY)
+ cmdat |= CMD_DAT_CONT_BUSY;
+
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_R1: /* short CRC, OPCODE */
+ case MMC_RSP_R1B:/* short CRC, OPCODE, BUSY */
+ cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R1;
+ break;
+ case MMC_RSP_R2: /* long 136 bit + CRC */
+ cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R2;
+ break;
+ case MMC_RSP_R3: /* short */
+ cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R3;
+ break;
+ default:
+ break;
+ }
+
+ if ( test_and_clear_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events) )
+ cmdat |= CMD_DAT_CONT_INIT; /* This command needs init */
+
+ if ( host->actual_bus_width == MMC_BUS_WIDTH_4 )
+ cmdat |= CMD_DAT_CONT_BUS_WIDTH_4;
+
+ MMC_CMD = cmd->opcode;
+ MMC_ARGH = cmd->arg >> 16;
+ MMC_ARGL = cmd->arg & 0xffff;
+ MMC_CMD_DAT_CONT = cmdat;
+
+ atomic_set(&host->stuck_timeout, 0);
+ set_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events);
+
+
+ imask = IMXMCI_INT_MASK_DEFAULT;
+ imask &= ~INT_MASK_END_CMD_RES;
+ if ( cmdat & CMD_DAT_CONT_DATA_ENABLE ) {
+ /*imask &= ~INT_MASK_BUF_READY;*/
+ imask &= ~INT_MASK_DATA_TRAN;
+ if ( cmdat & CMD_DAT_CONT_WRITE )
+ imask &= ~INT_MASK_WRITE_OP_DONE;
+ if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
+ imask &= ~INT_MASK_BUF_READY;
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->imask = imask;
+ MMC_INT_MASK = host->imask;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ dev_dbg(mmc_dev(host->mmc), "CMD%02d (0x%02x) mask set to 0x%04x\n",
+ cmd->opcode, cmd->opcode, imask);
+
+ imxmci_start_clock(host);
+}
+
+static void imxmci_finish_request(struct imxmci_host *host, struct mmc_request *req)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ host->pending_events &= ~(IMXMCI_PEND_WAIT_RESP_m | IMXMCI_PEND_DMA_END_m |
+ IMXMCI_PEND_DMA_DATA_m | IMXMCI_PEND_CPU_DATA_m);
+
+ host->imask = IMXMCI_INT_MASK_DEFAULT;
+ MMC_INT_MASK = host->imask;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if(req && req->cmd)
+ host->prev_cmd_code = req->cmd->opcode;
+
+ host->req = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+ mmc_request_done(host->mmc, req);
+}
+
+static int imxmci_finish_data(struct imxmci_host *host, unsigned int stat)
+{
+ struct mmc_data *data = host->data;
+ int data_error;
+
+ if(test_and_clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)){
+ imx_dma_disable(host->dma);
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents,
+ host->dma_dir);
+ }
+
+ if ( stat & STATUS_ERR_MASK ) {
+ dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat);
+ if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR))
+ data->error = -EILSEQ;
+ else if(stat & STATUS_TIME_OUT_READ)
+ data->error = -ETIMEDOUT;
+ else
+ data->error = -EIO;
+ } else {
+ data->bytes_xfered = host->dma_size;
+ }
+
+ data_error = data->error;
+
+ host->data = NULL;
+
+ return data_error;
+}
+
+static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
+{
+ struct mmc_command *cmd = host->cmd;
+ int i;
+ u32 a,b,c;
+ struct mmc_data *data = host->data;
+
+ if (!cmd)
+ return 0;
+
+ host->cmd = NULL;
+
+ if (stat & STATUS_TIME_OUT_RESP) {
+ dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n");
+ cmd->error = -ETIMEDOUT;
+ } else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
+ dev_dbg(mmc_dev(host->mmc), "cmd crc error\n");
+ cmd->error = -EILSEQ;
+ }
+
+ if(cmd->flags & MMC_RSP_PRESENT) {
+ if(cmd->flags & MMC_RSP_136) {
+ for (i = 0; i < 4; i++) {
+ u32 a = MMC_RES_FIFO & 0xffff;
+ u32 b = MMC_RES_FIFO & 0xffff;
+ cmd->resp[i] = a<<16 | b;
+ }
+ } else {
+ a = MMC_RES_FIFO & 0xffff;
+ b = MMC_RES_FIFO & 0xffff;
+ c = MMC_RES_FIFO & 0xffff;
+ cmd->resp[0] = a<<24 | b<<8 | c>>8;
+ }
+ }
+
+ dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n",
+ cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error);
+
+ if (data && !cmd->error && !(stat & STATUS_ERR_MASK)) {
+ if (host->req->data->flags & MMC_DATA_WRITE) {
+
+ /* Wait for FIFO to be empty before starting DMA write */
+
+ stat = MMC_STATUS;
+ if(imxmci_busy_wait_for_status(host, &stat,
+ STATUS_APPL_BUFF_FE,
+ 40, "imxmci_cmd_done DMA WR") < 0) {
+ cmd->error = -EIO;
+ imxmci_finish_data(host, stat);
+ if(host->req)
+ imxmci_finish_request(host, host->req);
+ dev_warn(mmc_dev(host->mmc), "STATUS = 0x%04x\n",
+ stat);
+ return 0;
+ }
+
+ if(test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
+ imx_dma_enable(host->dma);
+ }
+ }
+ } else {
+ struct mmc_request *req;
+ imxmci_stop_clock(host);
+ req = host->req;
+
+ if(data)
+ imxmci_finish_data(host, stat);
+
+ if( req ) {
+ imxmci_finish_request(host, req);
+ } else {
+ dev_warn(mmc_dev(host->mmc), "imxmci_cmd_done: no request to finish\n");
+ }
+ }
+
+ return 1;
+}
+
+static int imxmci_data_done(struct imxmci_host *host, unsigned int stat)
+{
+ struct mmc_data *data = host->data;
+ int data_error;
+
+ if (!data)
+ return 0;
+
+ data_error = imxmci_finish_data(host, stat);
+
+ if (host->req->stop) {
+ imxmci_stop_clock(host);
+ imxmci_start_cmd(host, host->req->stop, 0);
+ } else {
+ struct mmc_request *req;
+ req = host->req;
+ if( req ) {
+ imxmci_finish_request(host, req);
+ } else {
+ dev_warn(mmc_dev(host->mmc), "imxmci_data_done: no request to finish\n");
+ }
+ }
+
+ return 1;
+}
+
+static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat)
+{
+ int i;
+ int burst_len;
+ int trans_done = 0;
+ unsigned int stat = *pstat;
+
+ if(host->actual_bus_width != MMC_BUS_WIDTH_4)
+ burst_len = 16;
+ else
+ burst_len = 64;
+
+ /* This is unfortunately required */
+ dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data running STATUS = 0x%x\n",
+ stat);
+
+ udelay(20); /* required for clocks < 8MHz*/
+
+ if(host->dma_dir == DMA_FROM_DEVICE) {
+ imxmci_busy_wait_for_status(host, &stat,
+ STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE |
+ STATUS_TIME_OUT_READ,
+ 50, "imxmci_cpu_driven_data read");
+
+ while((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) &&
+ !(stat & STATUS_TIME_OUT_READ) &&
+ (host->data_cnt < 512)) {
+
+ udelay(20); /* required for clocks < 8MHz*/
+
+ for(i = burst_len; i>=2 ; i-=2) {
+ u16 data;
+ data = MMC_BUFFER_ACCESS;
+ udelay(10); /* required for clocks < 8MHz*/
+ if(host->data_cnt+2 <= host->dma_size) {
+ *(host->data_ptr++) = data;
+ } else {
+ if(host->data_cnt < host->dma_size)
+ *(u8*)(host->data_ptr) = data;
+ }
+ host->data_cnt += 2;
+ }
+
+ stat = MMC_STATUS;
+
+ dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read %d burst %d STATUS = 0x%x\n",
+ host->data_cnt, burst_len, stat);
+ }
+
+ if((stat & STATUS_DATA_TRANS_DONE) && (host->data_cnt >= 512))
+ trans_done = 1;
+
+ if(host->dma_size & 0x1ff)
+ stat &= ~STATUS_CRC_READ_ERR;
+
+ if(stat & STATUS_TIME_OUT_READ) {
+ dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read timeout STATUS = 0x%x\n",
+ stat);
+ trans_done = -1;
+ }
+
+ } else {
+ imxmci_busy_wait_for_status(host, &stat,
+ STATUS_APPL_BUFF_FE,
+ 20, "imxmci_cpu_driven_data write");
+
+ while((stat & STATUS_APPL_BUFF_FE) &&
+ (host->data_cnt < host->dma_size)) {
+ if(burst_len >= host->dma_size - host->data_cnt) {
+ burst_len = host->dma_size - host->data_cnt;
+ host->data_cnt = host->dma_size;
+ trans_done = 1;
+ } else {
+ host->data_cnt += burst_len;
+ }
+
+ for(i = burst_len; i>0 ; i-=2)
+ MMC_BUFFER_ACCESS = *(host->data_ptr++);
+
+ stat = MMC_STATUS;
+
+ dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data write burst %d STATUS = 0x%x\n",
+ burst_len, stat);
+ }
+ }
+
+ *pstat = stat;
+
+ return trans_done;
+}
+
+static void imxmci_dma_irq(int dma, void *devid)
+{
+ struct imxmci_host *host = devid;
+ uint32_t stat = MMC_STATUS;
+
+ atomic_set(&host->stuck_timeout, 0);
+ host->status_reg = stat;
+ set_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
+ tasklet_schedule(&host->tasklet);
+}
+
+static irqreturn_t imxmci_irq(int irq, void *devid)
+{
+ struct imxmci_host *host = devid;
+ uint32_t stat = MMC_STATUS;
+ int handled = 1;
+
+ MMC_INT_MASK = host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT;
+
+ atomic_set(&host->stuck_timeout, 0);
+ host->status_reg = stat;
+ set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
+ set_bit(IMXMCI_PEND_STARTED_b, &host->pending_events);
+ tasklet_schedule(&host->tasklet);
+
+ return IRQ_RETVAL(handled);;
+}
+
+static void imxmci_tasklet_fnc(unsigned long data)
+{
+ struct imxmci_host *host = (struct imxmci_host *)data;
+ u32 stat;
+ unsigned int data_dir_mask = 0; /* STATUS_WR_CRC_ERROR_CODE_MASK */
+ int timeout = 0;
+
+ if(atomic_read(&host->stuck_timeout) > 4) {
+ char *what;
+ timeout = 1;
+ stat = MMC_STATUS;
+ host->status_reg = stat;
+ if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
+ if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
+ what = "RESP+DMA";
+ else
+ what = "RESP";
+ else
+ if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events))
+ if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events))
+ what = "DATA";
+ else
+ what = "DMA";
+ else
+ what = "???";
+
+ dev_err(mmc_dev(host->mmc), "%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n",
+ what, stat, MMC_INT_MASK);
+ dev_err(mmc_dev(host->mmc), "CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n",
+ MMC_CMD_DAT_CONT, MMC_BLK_LEN, MMC_NOB, CCR(host->dma));
+ dev_err(mmc_dev(host->mmc), "CMD%d, prevCMD%d, bus %d-bit, dma_size = 0x%x\n",
+ host->cmd?host->cmd->opcode:0, host->prev_cmd_code, 1<<host->actual_bus_width, host->dma_size);
+ }
+
+ if(!host->present || timeout)
+ host->status_reg = STATUS_TIME_OUT_RESP | STATUS_TIME_OUT_READ |
+ STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR;
+
+ if(test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) {
+ clear_bit(IMXMCI_PEND_IRQ_b, &host->pending_events);
+
+ stat = MMC_STATUS;
+ /*
+ * This is not required in theory, but there is chance to miss some flag
+ * which clears automatically by mask write, FreeScale original code keeps
+ * stat from IRQ time so do I
+ */
+ stat |= host->status_reg;
+
+ if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events))
+ stat &= ~STATUS_CRC_READ_ERR;
+
+ if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
+ imxmci_busy_wait_for_status(host, &stat,
+ STATUS_END_CMD_RESP | STATUS_ERR_MASK,
+ 20, "imxmci_tasklet_fnc resp (ERRATUM #4)");
+ }
+
+ if(stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) {
+ if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
+ imxmci_cmd_done(host, stat);
+ if(host->data && (stat & STATUS_ERR_MASK))
+ imxmci_data_done(host, stat);
+ }
+
+ if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) {
+ stat |= MMC_STATUS;
+ if(imxmci_cpu_driven_data(host, &stat)){
+ if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events))
+ imxmci_cmd_done(host, stat);
+ atomic_clear_mask(IMXMCI_PEND_IRQ_m|IMXMCI_PEND_CPU_DATA_m,
+ &host->pending_events);
+ imxmci_data_done(host, stat);
+ }
+ }
+ }
+
+ if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) &&
+ !test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) {
+
+ stat = MMC_STATUS;
+ /* Same as above */
+ stat |= host->status_reg;
+
+ if(host->dma_dir == DMA_TO_DEVICE) {
+ data_dir_mask = STATUS_WRITE_OP_DONE;
+ } else {
+ data_dir_mask = STATUS_DATA_TRANS_DONE;
+ }
+
+ if(stat & data_dir_mask) {
+ clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events);
+ imxmci_data_done(host, stat);
+ }
+ }
+
+ if(test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) {
+
+ if(host->cmd)
+ imxmci_cmd_done(host, STATUS_TIME_OUT_RESP);
+
+ if(host->data)
+ imxmci_data_done(host, STATUS_TIME_OUT_READ |
+ STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR);
+
+ if(host->req)
+ imxmci_finish_request(host, host->req);
+
+ mmc_detect_change(host->mmc, msecs_to_jiffies(100));
+
+ }
+}
+
+static void imxmci_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+ struct imxmci_host *host = mmc_priv(mmc);
+ unsigned int cmdat;
+
+ WARN_ON(host->req != NULL);
+
+ host->req = req;
+
+ cmdat = 0;
+
+ if (req->data) {
+ imxmci_setup_data(host, req->data);
+
+ cmdat |= CMD_DAT_CONT_DATA_ENABLE;
+
+ if (req->data->flags & MMC_DATA_WRITE)
+ cmdat |= CMD_DAT_CONT_WRITE;
+
+ if (req->data->flags & MMC_DATA_STREAM) {
+ cmdat |= CMD_DAT_CONT_STREAM_BLOCK;
+ }
+ }
+
+ imxmci_start_cmd(host, req->cmd, cmdat);
+}
+
+#define CLK_RATE 19200000
+
+static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct imxmci_host *host = mmc_priv(mmc);
+ int prescaler;
+
+ if( ios->bus_width==MMC_BUS_WIDTH_4 ) {
+ host->actual_bus_width = MMC_BUS_WIDTH_4;
+ imx_gpio_mode(PB11_PF_SD_DAT3);
+ }else{
+ host->actual_bus_width = MMC_BUS_WIDTH_1;
+ imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);
+ }
+
+ if ( host->power_mode != ios->power_mode ) {
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ break;
+ case MMC_POWER_UP:
+ set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
+ break;
+ case MMC_POWER_ON:
+ break;
+ }
+ host->power_mode = ios->power_mode;
+ }
+
+ if ( ios->clock ) {
+ unsigned int clk;
+
+ /* The prescaler is 5 for PERCLK2 equal to 96MHz
+ * then 96MHz / 5 = 19.2 MHz
+ */
+ clk = clk_get_rate(host->clk);
+ prescaler=(clk+(CLK_RATE*7)/8)/CLK_RATE;
+ switch(prescaler) {
+ case 0:
+ case 1: prescaler = 0;
+ break;
+ case 2: prescaler = 1;
+ break;
+ case 3: prescaler = 2;
+ break;
+ case 4: prescaler = 4;
+ break;
+ default:
+ case 5: prescaler = 5;
+ break;
+ }
+
+ dev_dbg(mmc_dev(host->mmc), "PERCLK2 %d MHz -> prescaler %d\n",
+ clk, prescaler);
+
+ for(clk=0; clk<8; clk++) {
+ int x;
+ x = CLK_RATE / (1<<clk);
+ if( x <= ios->clock)
+ break;
+ }
+
+ MMC_STR_STP_CLK |= STR_STP_CLK_ENABLE; /* enable controller */
+
+ imxmci_stop_clock(host);
+ MMC_CLK_RATE = (prescaler<<3) | clk;
+ /*
+ * Under my understanding, clock should not be started there, because it would
+ * initiate SDHC sequencer and send last or random command into card
+ */
+ /*imxmci_start_clock(host);*/
+
+ dev_dbg(mmc_dev(host->mmc), "MMC_CLK_RATE: 0x%08x\n", MMC_CLK_RATE);
+ } else {
+ imxmci_stop_clock(host);
+ }
+}
+
+static int imxmci_get_ro(struct mmc_host *mmc)
+{
+ struct imxmci_host *host = mmc_priv(mmc);
+
+ if (host->pdata && host->pdata->get_ro)
+ return !!host->pdata->get_ro(mmc_dev(mmc));
+ /*
+ * Board doesn't support read only detection; let the mmc core
+ * decide what to do.
+ */
+ return -ENOSYS;
+}
+
+
+static const struct mmc_host_ops imxmci_ops = {
+ .request = imxmci_request,
+ .set_ios = imxmci_set_ios,
+ .get_ro = imxmci_get_ro,
+};
+
+static void imxmci_check_status(unsigned long data)
+{
+ struct imxmci_host *host = (struct imxmci_host *)data;
+
+ if (host->pdata && host->pdata->card_present &&
+ host->pdata->card_present(mmc_dev(host->mmc)) != host->present) {
+ host->present ^= 1;
+ dev_info(mmc_dev(host->mmc), "card %s\n",
+ host->present ? "inserted" : "removed");
+
+ set_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events);
+ tasklet_schedule(&host->tasklet);
+ }
+
+ if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) ||
+ test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) {
+ atomic_inc(&host->stuck_timeout);
+ if(atomic_read(&host->stuck_timeout) > 4)
+ tasklet_schedule(&host->tasklet);
+ } else {
+ atomic_set(&host->stuck_timeout, 0);
+
+ }
+
+ mod_timer(&host->timer, jiffies + (HZ>>1));
+}
+
+static int imxmci_probe(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct imxmci_host *host = NULL;
+ struct resource *r;
+ int ret = 0, irq;
+
+ printk(KERN_INFO "i.MX mmc driver\n");
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!r || irq < 0)
+ return -ENXIO;
+
+ if (!request_mem_region(r->start, 0x100, pdev->name))
+ return -EBUSY;
+
+ mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mmc->ops = &imxmci_ops;
+ mmc->f_min = 150000;
+ mmc->f_max = CLK_RATE/2;
+ mmc->ocr_avail = MMC_VDD_32_33;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+
+ /* MMC core transfer sizes tunable parameters */
+ mmc->max_hw_segs = 64;
+ mmc->max_phys_segs = 64;
+ mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */
+ mmc->max_req_size = 64*512; /* default PAGE_CACHE_SIZE */
+ mmc->max_blk_size = 2048;
+ mmc->max_blk_count = 65535;
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->dma_allocated = 0;
+ host->pdata = pdev->dev.platform_data;
+ if (!host->pdata)
+ dev_warn(&pdev->dev, "No platform data provided!\n");
+
+ spin_lock_init(&host->lock);
+ host->res = r;
+ host->irq = irq;
+
+ host->clk = clk_get(&pdev->dev, "perclk2");
+ if (IS_ERR(host->clk)) {
+ ret = PTR_ERR(host->clk);
+ goto out;
+ }
+ clk_enable(host->clk);
+
+ imx_gpio_mode(PB8_PF_SD_DAT0);
+ imx_gpio_mode(PB9_PF_SD_DAT1);
+ imx_gpio_mode(PB10_PF_SD_DAT2);
+ /* Configured as GPIO with pull-up to ensure right MCC card mode */
+ /* Switched to PB11_PF_SD_DAT3 if 4 bit bus is configured */
+ imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11);
+ /* imx_gpio_mode(PB11_PF_SD_DAT3); */
+ imx_gpio_mode(PB12_PF_SD_CLK);
+ imx_gpio_mode(PB13_PF_SD_CMD);
+
+ imxmci_softreset();
+
+ if ( MMC_REV_NO != 0x390 ) {
+ dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n",
+ MMC_REV_NO);
+ goto out;
+ }
+
+ MMC_READ_TO = 0x2db4; /* recommended in data sheet */
+
+ host->imask = IMXMCI_INT_MASK_DEFAULT;
+ MMC_INT_MASK = host->imask;
+
+ host->dma = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_LOW);
+ if(host->dma < 0) {
+ dev_err(mmc_dev(host->mmc), "imx_dma_request_by_prio failed\n");
+ ret = -EBUSY;
+ goto out;
+ }
+ host->dma_allocated=1;
+ imx_dma_setup_handlers(host->dma, imxmci_dma_irq, NULL, host);
+
+ tasklet_init(&host->tasklet, imxmci_tasklet_fnc, (unsigned long)host);
+ host->status_reg=0;
+ host->pending_events=0;
+
+ ret = request_irq(host->irq, imxmci_irq, 0, DRIVER_NAME, host);
+ if (ret)
+ goto out;
+
+ if (host->pdata && host->pdata->card_present)
+ host->present = host->pdata->card_present(mmc_dev(mmc));
+ else /* if there is no way to detect assume that card is present */
+ host->present = 1;
+
+ init_timer(&host->timer);
+ host->timer.data = (unsigned long)host;
+ host->timer.function = imxmci_check_status;
+ add_timer(&host->timer);
+ mod_timer(&host->timer, jiffies + (HZ>>1));
+
+ platform_set_drvdata(pdev, mmc);
+
+ mmc_add_host(mmc);
+
+ return 0;
+
+out:
+ if (host) {
+ if(host->dma_allocated){
+ imx_dma_free(host->dma);
+ host->dma_allocated=0;
+ }
+ if (host->clk) {
+ clk_disable(host->clk);
+ clk_put(host->clk);
+ }
+ }
+ if (mmc)
+ mmc_free_host(mmc);
+ release_mem_region(r->start, 0x100);
+ return ret;
+}
+
+static int imxmci_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (mmc) {
+ struct imxmci_host *host = mmc_priv(mmc);
+
+ tasklet_disable(&host->tasklet);
+
+ del_timer_sync(&host->timer);
+ mmc_remove_host(mmc);
+
+ free_irq(host->irq, host);
+ if(host->dma_allocated){
+ imx_dma_free(host->dma);
+ host->dma_allocated=0;
+ }
+
+ tasklet_kill(&host->tasklet);
+
+ clk_disable(host->clk);
+ clk_put(host->clk);
+
+ release_mem_region(host->res->start, 0x100);
+
+ mmc_free_host(mmc);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int imxmci_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+ int ret = 0;
+
+ if (mmc)
+ ret = mmc_suspend_host(mmc, state);
+
+ return ret;
+}
+
+static int imxmci_resume(struct platform_device *dev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+ struct imxmci_host *host;
+ int ret = 0;
+
+ if (mmc) {
+ host = mmc_priv(mmc);
+ if(host)
+ set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events);
+ ret = mmc_resume_host(mmc);
+ }
+
+ return ret;
+}
+#else
+#define imxmci_suspend NULL
+#define imxmci_resume NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver imxmci_driver = {
+ .probe = imxmci_probe,
+ .remove = imxmci_remove,
+ .suspend = imxmci_suspend,
+ .resume = imxmci_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init imxmci_init(void)
+{
+ return platform_driver_register(&imxmci_driver);
+}
+
+static void __exit imxmci_exit(void)
+{
+ platform_driver_unregister(&imxmci_driver);
+}
+
+module_init(imxmci_init);
+module_exit(imxmci_exit);
+
+MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx-mmc");
diff --git a/drivers/mmc/host/imxmmc.h b/drivers/mmc/host/imxmmc.h
new file mode 100644
index 0000000..e5339e3
--- /dev/null
+++ b/drivers/mmc/host/imxmmc.h
@@ -0,0 +1,67 @@
+
+# define __REG16(x) (*((volatile u16 *)IO_ADDRESS(x)))
+
+#define MMC_STR_STP_CLK __REG16(IMX_MMC_BASE + 0x00)
+#define MMC_STATUS __REG16(IMX_MMC_BASE + 0x04)
+#define MMC_CLK_RATE __REG16(IMX_MMC_BASE + 0x08)
+#define MMC_CMD_DAT_CONT __REG16(IMX_MMC_BASE + 0x0C)
+#define MMC_RES_TO __REG16(IMX_MMC_BASE + 0x10)
+#define MMC_READ_TO __REG16(IMX_MMC_BASE + 0x14)
+#define MMC_BLK_LEN __REG16(IMX_MMC_BASE + 0x18)
+#define MMC_NOB __REG16(IMX_MMC_BASE + 0x1C)
+#define MMC_REV_NO __REG16(IMX_MMC_BASE + 0x20)
+#define MMC_INT_MASK __REG16(IMX_MMC_BASE + 0x24)
+#define MMC_CMD __REG16(IMX_MMC_BASE + 0x28)
+#define MMC_ARGH __REG16(IMX_MMC_BASE + 0x2C)
+#define MMC_ARGL __REG16(IMX_MMC_BASE + 0x30)
+#define MMC_RES_FIFO __REG16(IMX_MMC_BASE + 0x34)
+#define MMC_BUFFER_ACCESS __REG16(IMX_MMC_BASE + 0x38)
+#define MMC_BUFFER_ACCESS_OFS 0x38
+
+
+#define STR_STP_CLK_ENDIAN (1<<5)
+#define STR_STP_CLK_RESET (1<<3)
+#define STR_STP_CLK_ENABLE (1<<2)
+#define STR_STP_CLK_START_CLK (1<<1)
+#define STR_STP_CLK_STOP_CLK (1<<0)
+#define STATUS_CARD_PRESENCE (1<<15)
+#define STATUS_SDIO_INT_ACTIVE (1<<14)
+#define STATUS_END_CMD_RESP (1<<13)
+#define STATUS_WRITE_OP_DONE (1<<12)
+#define STATUS_DATA_TRANS_DONE (1<<11)
+#define STATUS_WR_CRC_ERROR_CODE_MASK (3<<10)
+#define STATUS_CARD_BUS_CLK_RUN (1<<8)
+#define STATUS_APPL_BUFF_FF (1<<7)
+#define STATUS_APPL_BUFF_FE (1<<6)
+#define STATUS_RESP_CRC_ERR (1<<5)
+#define STATUS_CRC_READ_ERR (1<<3)
+#define STATUS_CRC_WRITE_ERR (1<<2)
+#define STATUS_TIME_OUT_RESP (1<<1)
+#define STATUS_TIME_OUT_READ (1<<0)
+#define STATUS_ERR_MASK 0x2f
+#define CLK_RATE_PRESCALER(x) ((x) & 0x7)
+#define CLK_RATE_CLK_RATE(x) (((x) & 0x7) << 3)
+#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1<<12)
+#define CMD_DAT_CONT_STOP_READWAIT (1<<11)
+#define CMD_DAT_CONT_START_READWAIT (1<<10)
+#define CMD_DAT_CONT_BUS_WIDTH_1 (0<<8)
+#define CMD_DAT_CONT_BUS_WIDTH_4 (2<<8)
+#define CMD_DAT_CONT_INIT (1<<7)
+#define CMD_DAT_CONT_BUSY (1<<6)
+#define CMD_DAT_CONT_STREAM_BLOCK (1<<5)
+#define CMD_DAT_CONT_WRITE (1<<4)
+#define CMD_DAT_CONT_DATA_ENABLE (1<<3)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R1 (1)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R2 (2)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R3 (3)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R4 (4)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R5 (5)
+#define CMD_DAT_CONT_RESPONSE_FORMAT_R6 (6)
+#define INT_MASK_AUTO_CARD_DETECT (1<<6)
+#define INT_MASK_DAT0_EN (1<<5)
+#define INT_MASK_SDIO (1<<4)
+#define INT_MASK_BUF_READY (1<<3)
+#define INT_MASK_END_CMD_RES (1<<2)
+#define INT_MASK_WRITE_OP_DONE (1<<1)
+#define INT_MASK_DATA_TRAN (1<<0)
+#define INT_ALL (0x7f)
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
new file mode 100644
index 0000000..ad00e16
--- /dev/null
+++ b/drivers/mmc/host/mmc_spi.c
@@ -0,0 +1,1439 @@
+/*
+ * mmc_spi.c - Access SD/MMC cards through SPI master controllers
+ *
+ * (C) Copyright 2005, Intec Automation,
+ * Mike Lavender (mike@steroidmicros)
+ * (C) Copyright 2006-2007, David Brownell
+ * (C) Copyright 2007, Axis Communications,
+ * Hans-Peter Nilsson (hp@axis.com)
+ * (C) Copyright 2007, ATRON electronic GmbH,
+ * Jan Nikitenko <jan.nikitenko@gmail.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/bio.h>
+#include <linux/dma-mapping.h>
+#include <linux/crc7.h>
+#include <linux/crc-itu-t.h>
+#include <linux/scatterlist.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h> /* for R1_SPI_* bit values */
+
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
+
+#include <asm/unaligned.h>
+
+
+/* NOTES:
+ *
+ * - For now, we won't try to interoperate with a real mmc/sd/sdio
+ * controller, although some of them do have hardware support for
+ * SPI protocol. The main reason for such configs would be mmc-ish
+ * cards like DataFlash, which don't support that "native" protocol.
+ *
+ * We don't have a "DataFlash/MMC/SD/SDIO card slot" abstraction to
+ * switch between driver stacks, and in any case if "native" mode
+ * is available, it will be faster and hence preferable.
+ *
+ * - MMC depends on a different chipselect management policy than the
+ * SPI interface currently supports for shared bus segments: it needs
+ * to issue multiple spi_message requests with the chipselect active,
+ * using the results of one message to decide the next one to issue.
+ *
+ * Pending updates to the programming interface, this driver expects
+ * that it not share the bus with other drivers (precluding conflicts).
+ *
+ * - We tell the controller to keep the chipselect active from the
+ * beginning of an mmc_host_ops.request until the end. So beware
+ * of SPI controller drivers that mis-handle the cs_change flag!
+ *
+ * However, many cards seem OK with chipselect flapping up/down
+ * during that time ... at least on unshared bus segments.
+ */
+
+
+/*
+ * Local protocol constants, internal to data block protocols.
+ */
+
+/* Response tokens used to ack each block written: */
+#define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f)
+#define SPI_RESPONSE_ACCEPTED ((2 << 1)|1)
+#define SPI_RESPONSE_CRC_ERR ((5 << 1)|1)
+#define SPI_RESPONSE_WRITE_ERR ((6 << 1)|1)
+
+/* Read and write blocks start with these tokens and end with crc;
+ * on error, read tokens act like a subset of R2_SPI_* values.
+ */
+#define SPI_TOKEN_SINGLE 0xfe /* single block r/w, multiblock read */
+#define SPI_TOKEN_MULTI_WRITE 0xfc /* multiblock write */
+#define SPI_TOKEN_STOP_TRAN 0xfd /* terminate multiblock write */
+
+#define MMC_SPI_BLOCKSIZE 512
+
+
+/* These fixed timeouts come from the latest SD specs, which say to ignore
+ * the CSD values. The R1B value is for card erase (e.g. the "I forgot the
+ * card's password" scenario); it's mostly applied to STOP_TRANSMISSION after
+ * reads which takes nowhere near that long. Older cards may be able to use
+ * shorter timeouts ... but why bother?
+ */
+#define r1b_timeout ktime_set(3, 0)
+
+
+/****************************************************************************/
+
+/*
+ * Local Data Structures
+ */
+
+/* "scratch" is per-{command,block} data exchanged with the card */
+struct scratch {
+ u8 status[29];
+ u8 data_token;
+ __be16 crc_val;
+};
+
+struct mmc_spi_host {
+ struct mmc_host *mmc;
+ struct spi_device *spi;
+
+ unsigned char power_mode;
+ u16 powerup_msecs;
+
+ struct mmc_spi_platform_data *pdata;
+
+ /* for bulk data transfers */
+ struct spi_transfer token, t, crc, early_status;
+ struct spi_message m;
+
+ /* for status readback */
+ struct spi_transfer status;
+ struct spi_message readback;
+
+ /* underlying DMA-aware controller, or null */
+ struct device *dma_dev;
+
+ /* buffer used for commands and for message "overhead" */
+ struct scratch *data;
+ dma_addr_t data_dma;
+
+ /* Specs say to write ones most of the time, even when the card
+ * has no need to read its input data; and many cards won't care.
+ * This is our source of those ones.
+ */
+ void *ones;
+ dma_addr_t ones_dma;
+};
+
+
+/****************************************************************************/
+
+/*
+ * MMC-over-SPI protocol glue, used by the MMC stack interface
+ */
+
+static inline int mmc_cs_off(struct mmc_spi_host *host)
+{
+ /* chipselect will always be inactive after setup() */
+ return spi_setup(host->spi);
+}
+
+static int
+mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len)
+{
+ int status;
+
+ if (len > sizeof(*host->data)) {
+ WARN_ON(1);
+ return -EIO;
+ }
+
+ host->status.len = len;
+
+ if (host->dma_dev)
+ dma_sync_single_for_device(host->dma_dev,
+ host->data_dma, sizeof(*host->data),
+ DMA_FROM_DEVICE);
+
+ status = spi_sync(host->spi, &host->readback);
+
+ if (host->dma_dev)
+ dma_sync_single_for_cpu(host->dma_dev,
+ host->data_dma, sizeof(*host->data),
+ DMA_FROM_DEVICE);
+
+ return status;
+}
+
+static int
+mmc_spi_skip(struct mmc_spi_host *host, ktime_t timeout, unsigned n, u8 byte)
+{
+ u8 *cp = host->data->status;
+
+ timeout = ktime_add(timeout, ktime_get());
+
+ while (1) {
+ int status;
+ unsigned i;
+
+ status = mmc_spi_readbytes(host, n);
+ if (status < 0)
+ return status;
+
+ for (i = 0; i < n; i++) {
+ if (cp[i] != byte)
+ return cp[i];
+ }
+
+ /* REVISIT investigate msleep() to avoid busy-wait I/O
+ * in at least some cases.
+ */
+ if (ktime_to_ns(ktime_sub(ktime_get(), timeout)) > 0)
+ break;
+ }
+ return -ETIMEDOUT;
+}
+
+static inline int
+mmc_spi_wait_unbusy(struct mmc_spi_host *host, ktime_t timeout)
+{
+ return mmc_spi_skip(host, timeout, sizeof(host->data->status), 0);
+}
+
+static int mmc_spi_readtoken(struct mmc_spi_host *host, ktime_t timeout)
+{
+ return mmc_spi_skip(host, timeout, 1, 0xff);
+}
+
+
+/*
+ * Note that for SPI, cmd->resp[0] is not the same data as "native" protocol
+ * hosts return! The low byte holds R1_SPI bits. The next byte may hold
+ * R2_SPI bits ... for SEND_STATUS, or after data read errors.
+ *
+ * cmd->resp[1] holds any four-byte response, for R3 (READ_OCR) and on
+ * newer cards R7 (IF_COND).
+ */
+
+static char *maptype(struct mmc_command *cmd)
+{
+ switch (mmc_spi_resp_type(cmd)) {
+ case MMC_RSP_SPI_R1: return "R1";
+ case MMC_RSP_SPI_R1B: return "R1B";
+ case MMC_RSP_SPI_R2: return "R2/R5";
+ case MMC_RSP_SPI_R3: return "R3/R4/R7";
+ default: return "?";
+ }
+}
+
+/* return zero, else negative errno after setting cmd->error */
+static int mmc_spi_response_get(struct mmc_spi_host *host,
+ struct mmc_command *cmd, int cs_on)
+{
+ u8 *cp = host->data->status;
+ u8 *end = cp + host->t.len;
+ int value = 0;
+ char tag[32];
+
+ snprintf(tag, sizeof(tag), " ... CMD%d response SPI_%s",
+ cmd->opcode, maptype(cmd));
+
+ /* Except for data block reads, the whole response will already
+ * be stored in the scratch buffer. It's somewhere after the
+ * command and the first byte we read after it. We ignore that
+ * first byte. After STOP_TRANSMISSION command it may include
+ * two data bits, but otherwise it's all ones.
+ */
+ cp += 8;
+ while (cp < end && *cp == 0xff)
+ cp++;
+
+ /* Data block reads (R1 response types) may need more data... */
+ if (cp == end) {
+ unsigned i;
+
+ cp = host->data->status;
+
+ /* Card sends N(CR) (== 1..8) bytes of all-ones then one
+ * status byte ... and we already scanned 2 bytes.
+ *
+ * REVISIT block read paths use nasty byte-at-a-time I/O
+ * 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...
+ */
+ for (i = 2; i < 9; i++) {
+ value = mmc_spi_readbytes(host, 1);
+ if (value < 0)
+ goto done;
+ if (*cp != 0xff)
+ goto checkstatus;
+ }
+ value = -ETIMEDOUT;
+ goto done;
+ }
+
+checkstatus:
+ if (*cp & 0x80) {
+ dev_dbg(&host->spi->dev, "%s: INVALID RESPONSE, %02x\n",
+ tag, *cp);
+ value = -EBADR;
+ goto done;
+ }
+
+ cmd->resp[0] = *cp++;
+ cmd->error = 0;
+
+ /* Status byte: the entire seven-bit R1 response. */
+ if (cmd->resp[0] != 0) {
+ if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS
+ | R1_SPI_ILLEGAL_COMMAND)
+ & cmd->resp[0])
+ value = -EINVAL;
+ else if (R1_SPI_COM_CRC & cmd->resp[0])
+ value = -EILSEQ;
+ else if ((R1_SPI_ERASE_SEQ | R1_SPI_ERASE_RESET)
+ & cmd->resp[0])
+ value = -EIO;
+ /* else R1_SPI_IDLE, "it's resetting" */
+ }
+
+ switch (mmc_spi_resp_type(cmd)) {
+
+ /* SPI R1B == R1 + busy; STOP_TRANSMISSION (for multiblock reads)
+ * and less-common stuff like various erase operations.
+ */
+ case MMC_RSP_SPI_R1B:
+ /* maybe we read all the busy tokens already */
+ while (cp < end && *cp == 0)
+ cp++;
+ if (cp == end)
+ mmc_spi_wait_unbusy(host, r1b_timeout);
+ break;
+
+ /* SPI R2 == R1 + second status byte; SEND_STATUS
+ * SPI R5 == R1 + data byte; IO_RW_DIRECT
+ */
+ case MMC_RSP_SPI_R2:
+ cmd->resp[0] |= *cp << 8;
+ break;
+
+ /* SPI R3, R4, or R7 == R1 + 4 bytes */
+ case MMC_RSP_SPI_R3:
+ cmd->resp[1] = get_unaligned_be32(cp);
+ break;
+
+ /* SPI R1 == just one status byte */
+ case MMC_RSP_SPI_R1:
+ break;
+
+ default:
+ dev_dbg(&host->spi->dev, "bad response type %04x\n",
+ mmc_spi_resp_type(cmd));
+ if (value >= 0)
+ value = -EINVAL;
+ goto done;
+ }
+
+ if (value < 0)
+ dev_dbg(&host->spi->dev, "%s: resp %04x %08x\n",
+ tag, cmd->resp[0], cmd->resp[1]);
+
+ /* disable chipselect on errors and some success cases */
+ if (value >= 0 && cs_on)
+ return value;
+done:
+ if (value < 0)
+ cmd->error = value;
+ mmc_cs_off(host);
+ return value;
+}
+
+/* Issue command and read its response.
+ * Returns zero on success, negative for error.
+ *
+ * On error, caller must cope with mmc core retry mechanism. That
+ * means immediate low-level resubmit, which affects the bus lock...
+ */
+static int
+mmc_spi_command_send(struct mmc_spi_host *host,
+ struct mmc_request *mrq,
+ struct mmc_command *cmd, int cs_on)
+{
+ struct scratch *data = host->data;
+ u8 *cp = data->status;
+ u32 arg = cmd->arg;
+ int status;
+ struct spi_transfer *t;
+
+ /* We can handle most commands (except block reads) in one full
+ * duplex I/O operation before either starting the next transfer
+ * (data block or command) or else deselecting the card.
+ *
+ * First, write 7 bytes:
+ * - an all-ones byte to ensure the card is ready
+ * - opcode byte (plus start and transmission bits)
+ * - four bytes of big-endian argument
+ * - crc7 (plus end bit) ... always computed, it's cheap
+ *
+ * We init the whole buffer to all-ones, which is what we need
+ * to write while we're reading (later) response data.
+ */
+ memset(cp++, 0xff, sizeof(data->status));
+
+ *cp++ = 0x40 | cmd->opcode;
+ *cp++ = (u8)(arg >> 24);
+ *cp++ = (u8)(arg >> 16);
+ *cp++ = (u8)(arg >> 8);
+ *cp++ = (u8)arg;
+ *cp++ = (crc7(0, &data->status[1], 5) << 1) | 0x01;
+
+ /* Then, read up to 13 bytes (while writing all-ones):
+ * - N(CR) (== 1..8) bytes of all-ones
+ * - status byte (for all response types)
+ * - the rest of the response, either:
+ * + nothing, for R1 or R1B responses
+ * + second status byte, for R2 responses
+ * + four data bytes, for R3 and R7 responses
+ *
+ * Finally, read some more bytes ... in the nice cases we know in
+ * advance how many, and reading 1 more is always OK:
+ * - N(EC) (== 0..N) bytes of all-ones, before deselect/finish
+ * - N(RC) (== 1..N) bytes of all-ones, before next command
+ * - N(WR) (== 1..N) bytes of all-ones, before data write
+ *
+ * So in those cases one full duplex I/O of at most 21 bytes will
+ * handle the whole command, leaving the card ready to receive a
+ * data block or new command. We do that whenever we can, shaving
+ * CPU and IRQ costs (especially when using DMA or FIFOs).
+ *
+ * There are two other cases, where it's not generally practical
+ * to rely on a single I/O:
+ *
+ * - R1B responses need at least N(EC) bytes of all-zeroes.
+ *
+ * In this case we can *try* to fit it into one I/O, then
+ * maybe read more data later.
+ *
+ * - Data block reads are more troublesome, since a variable
+ * number of padding bytes precede the token and data.
+ * + N(CX) (== 0..8) bytes of all-ones, before CSD or CID
+ * + N(AC) (== 1..many) bytes of all-ones
+ *
+ * In this case we currently only have minimal speedups here:
+ * when N(CR) == 1 we can avoid I/O in response_get().
+ */
+ if (cs_on && (mrq->data->flags & MMC_DATA_READ)) {
+ cp += 2; /* min(N(CR)) + status */
+ /* R1 */
+ } else {
+ cp += 10; /* max(N(CR)) + status + min(N(RC),N(WR)) */
+ if (cmd->flags & MMC_RSP_SPI_S2) /* R2/R5 */
+ cp++;
+ else if (cmd->flags & MMC_RSP_SPI_B4) /* R3/R4/R7 */
+ cp += 4;
+ else if (cmd->flags & MMC_RSP_BUSY) /* R1B */
+ cp = data->status + sizeof(data->status);
+ /* else: R1 (most commands) */
+ }
+
+ dev_dbg(&host->spi->dev, " mmc_spi: CMD%d, resp %s\n",
+ cmd->opcode, maptype(cmd));
+
+ /* send command, leaving chipselect active */
+ spi_message_init(&host->m);
+
+ t = &host->t;
+ memset(t, 0, sizeof(*t));
+ t->tx_buf = t->rx_buf = data->status;
+ t->tx_dma = t->rx_dma = host->data_dma;
+ t->len = cp - data->status;
+ t->cs_change = 1;
+ spi_message_add_tail(t, &host->m);
+
+ if (host->dma_dev) {
+ host->m.is_dma_mapped = 1;
+ dma_sync_single_for_device(host->dma_dev,
+ host->data_dma, sizeof(*host->data),
+ DMA_BIDIRECTIONAL);
+ }
+ status = spi_sync(host->spi, &host->m);
+
+ if (host->dma_dev)
+ dma_sync_single_for_cpu(host->dma_dev,
+ host->data_dma, sizeof(*host->data),
+ DMA_BIDIRECTIONAL);
+ if (status < 0) {
+ dev_dbg(&host->spi->dev, " ... write returned %d\n", status);
+ cmd->error = status;
+ return status;
+ }
+
+ /* after no-data commands and STOP_TRANSMISSION, chipselect off */
+ return mmc_spi_response_get(host, cmd, cs_on);
+}
+
+/* Build data message with up to four separate transfers. For TX, we
+ * start by writing the data token. And in most cases, we finish with
+ * a status transfer.
+ *
+ * We always provide TX data for data and CRC. The MMC/SD protocol
+ * requires us to write ones; but Linux defaults to writing zeroes;
+ * so we explicitly initialize it to all ones on RX paths.
+ *
+ * We also handle DMA mapping, so the underlying SPI controller does
+ * not need to (re)do it for each message.
+ */
+static void
+mmc_spi_setup_data_message(
+ struct mmc_spi_host *host,
+ int multiple,
+ enum dma_data_direction direction)
+{
+ struct spi_transfer *t;
+ struct scratch *scratch = host->data;
+ dma_addr_t dma = host->data_dma;
+
+ spi_message_init(&host->m);
+ if (dma)
+ host->m.is_dma_mapped = 1;
+
+ /* for reads, readblock() skips 0xff bytes before finding
+ * the token; for writes, this transfer issues that token.
+ */
+ if (direction == DMA_TO_DEVICE) {
+ t = &host->token;
+ memset(t, 0, sizeof(*t));
+ t->len = 1;
+ if (multiple)
+ scratch->data_token = SPI_TOKEN_MULTI_WRITE;
+ else
+ scratch->data_token = SPI_TOKEN_SINGLE;
+ t->tx_buf = &scratch->data_token;
+ if (dma)
+ t->tx_dma = dma + offsetof(struct scratch, data_token);
+ spi_message_add_tail(t, &host->m);
+ }
+
+ /* Body of transfer is buffer, then CRC ...
+ * either TX-only, or RX with TX-ones.
+ */
+ t = &host->t;
+ memset(t, 0, sizeof(*t));
+ t->tx_buf = host->ones;
+ t->tx_dma = host->ones_dma;
+ /* length and actual buffer info are written later */
+ spi_message_add_tail(t, &host->m);
+
+ t = &host->crc;
+ memset(t, 0, sizeof(*t));
+ t->len = 2;
+ if (direction == DMA_TO_DEVICE) {
+ /* the actual CRC may get written later */
+ t->tx_buf = &scratch->crc_val;
+ if (dma)
+ t->tx_dma = dma + offsetof(struct scratch, crc_val);
+ } else {
+ t->tx_buf = host->ones;
+ t->tx_dma = host->ones_dma;
+ t->rx_buf = &scratch->crc_val;
+ if (dma)
+ t->rx_dma = dma + offsetof(struct scratch, crc_val);
+ }
+ spi_message_add_tail(t, &host->m);
+
+ /*
+ * A single block read is followed by N(EC) [0+] all-ones bytes
+ * before deselect ... don't bother.
+ *
+ * Multiblock reads are followed by N(AC) [1+] all-ones bytes before
+ * the next block is read, or a STOP_TRANSMISSION is issued. We'll
+ * collect that single byte, so readblock() doesn't need to.
+ *
+ * For a write, the one-byte data response follows immediately, then
+ * come zero or more busy bytes, then N(WR) [1+] all-ones bytes.
+ * Then single block reads may deselect, and multiblock ones issue
+ * the next token (next data block, or STOP_TRAN). We can try to
+ * minimize I/O ops by using a single read to collect end-of-busy.
+ */
+ if (multiple || direction == DMA_TO_DEVICE) {
+ t = &host->early_status;
+ memset(t, 0, sizeof(*t));
+ t->len = (direction == DMA_TO_DEVICE)
+ ? sizeof(scratch->status)
+ : 1;
+ t->tx_buf = host->ones;
+ t->tx_dma = host->ones_dma;
+ t->rx_buf = scratch->status;
+ if (dma)
+ t->rx_dma = dma + offsetof(struct scratch, status);
+ t->cs_change = 1;
+ spi_message_add_tail(t, &host->m);
+ }
+}
+
+/*
+ * Write one block:
+ * - caller handled preceding N(WR) [1+] all-ones bytes
+ * - data block
+ * + token
+ * + data bytes
+ * + crc16
+ * - an all-ones byte ... card writes a data-response byte
+ * - followed by N(EC) [0+] all-ones bytes, card writes zero/'busy'
+ *
+ * Return negative errno, else success.
+ */
+static int
+mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t,
+ ktime_t timeout)
+{
+ struct spi_device *spi = host->spi;
+ int status, i;
+ struct scratch *scratch = host->data;
+
+ if (host->mmc->use_spi_crc)
+ scratch->crc_val = cpu_to_be16(
+ crc_itu_t(0, t->tx_buf, t->len));
+ if (host->dma_dev)
+ dma_sync_single_for_device(host->dma_dev,
+ host->data_dma, sizeof(*scratch),
+ DMA_BIDIRECTIONAL);
+
+ status = spi_sync(spi, &host->m);
+
+ if (status != 0) {
+ dev_dbg(&spi->dev, "write error (%d)\n", status);
+ return status;
+ }
+
+ if (host->dma_dev)
+ dma_sync_single_for_cpu(host->dma_dev,
+ host->data_dma, sizeof(*scratch),
+ DMA_BIDIRECTIONAL);
+
+ /*
+ * Get the transmission data-response reply. It must follow
+ * immediately after the data block we transferred. This reply
+ * 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.
+ */
+ switch (SPI_MMC_RESPONSE_CODE(scratch->status[0])) {
+ case SPI_RESPONSE_ACCEPTED:
+ status = 0;
+ break;
+ case SPI_RESPONSE_CRC_ERR:
+ /* host shall then issue MMC_STOP_TRANSMISSION */
+ status = -EILSEQ;
+ break;
+ case SPI_RESPONSE_WRITE_ERR:
+ /* host shall then issue MMC_STOP_TRANSMISSION,
+ * and should MMC_SEND_STATUS to sort it out
+ */
+ status = -EIO;
+ break;
+ default:
+ status = -EPROTO;
+ break;
+ }
+ if (status != 0) {
+ dev_dbg(&spi->dev, "write error %02x (%d)\n",
+ scratch->status[0], status);
+ return status;
+ }
+
+ t->tx_buf += t->len;
+ if (host->dma_dev)
+ t->tx_dma += t->len;
+
+ /* 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)
+ return 0;
+ }
+ return mmc_spi_wait_unbusy(host, timeout);
+}
+
+/*
+ * Read one block:
+ * - skip leading all-ones bytes ... either
+ * + N(AC) [1..f(clock,CSD)] usually, else
+ * + N(CX) [0..8] when reading CSD or CID
+ * - data block
+ * + token ... if error token, no data or crc
+ * + data bytes
+ * + crc16
+ *
+ * After single block reads, we're done; N(EC) [0+] all-ones bytes follow
+ * before dropping chipselect.
+ *
+ * For multiblock reads, caller either reads the next block or issues a
+ * STOP_TRANSMISSION command.
+ */
+static int
+mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t,
+ ktime_t timeout)
+{
+ struct spi_device *spi = host->spi;
+ int status;
+ struct scratch *scratch = host->data;
+
+ /* At least one SD card sends an all-zeroes byte when N(CX)
+ * applies, before the all-ones bytes ... just cope with that.
+ */
+ status = mmc_spi_readbytes(host, 1);
+ if (status < 0)
+ return status;
+ status = scratch->status[0];
+ if (status == 0xff || status == 0)
+ status = mmc_spi_readtoken(host, timeout);
+
+ if (status == SPI_TOKEN_SINGLE) {
+ if (host->dma_dev) {
+ dma_sync_single_for_device(host->dma_dev,
+ host->data_dma, sizeof(*scratch),
+ DMA_BIDIRECTIONAL);
+ dma_sync_single_for_device(host->dma_dev,
+ t->rx_dma, t->len,
+ DMA_FROM_DEVICE);
+ }
+
+ status = spi_sync(spi, &host->m);
+
+ if (host->dma_dev) {
+ dma_sync_single_for_cpu(host->dma_dev,
+ host->data_dma, sizeof(*scratch),
+ DMA_BIDIRECTIONAL);
+ dma_sync_single_for_cpu(host->dma_dev,
+ t->rx_dma, t->len,
+ DMA_FROM_DEVICE);
+ }
+
+ } else {
+ dev_dbg(&spi->dev, "read error %02x (%d)\n", status, status);
+
+ /* we've read extra garbage, timed out, etc */
+ if (status < 0)
+ return status;
+
+ /* low four bits are an R2 subset, fifth seems to be
+ * vendor specific ... map them all to generic error..
+ */
+ return -EIO;
+ }
+
+ if (host->mmc->use_spi_crc) {
+ u16 crc = crc_itu_t(0, t->rx_buf, t->len);
+
+ be16_to_cpus(&scratch->crc_val);
+ if (scratch->crc_val != crc) {
+ dev_dbg(&spi->dev, "read - crc error: crc_val=0x%04x, "
+ "computed=0x%04x len=%d\n",
+ scratch->crc_val, crc, t->len);
+ return -EILSEQ;
+ }
+ }
+
+ t->rx_buf += t->len;
+ if (host->dma_dev)
+ t->rx_dma += t->len;
+
+ return 0;
+}
+
+/*
+ * An MMC/SD data stage includes one or more blocks, optional CRCs,
+ * and inline handshaking. That handhaking makes it unlike most
+ * other SPI protocol stacks.
+ */
+static void
+mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
+ struct mmc_data *data, u32 blk_size)
+{
+ struct spi_device *spi = host->spi;
+ struct device *dma_dev = host->dma_dev;
+ struct spi_transfer *t;
+ enum dma_data_direction direction;
+ struct scatterlist *sg;
+ unsigned n_sg;
+ int multiple = (data->blocks > 1);
+ u32 clock_rate;
+ ktime_t timeout;
+
+ if (data->flags & MMC_DATA_READ)
+ direction = DMA_FROM_DEVICE;
+ else
+ direction = DMA_TO_DEVICE;
+ mmc_spi_setup_data_message(host, multiple, direction);
+ t = &host->t;
+
+ if (t->speed_hz)
+ clock_rate = t->speed_hz;
+ else
+ clock_rate = spi->max_speed_hz;
+
+ timeout = ktime_add_ns(ktime_set(0, 0), data->timeout_ns +
+ data->timeout_clks * 1000000 / clock_rate);
+
+ /* Handle scatterlist segments one at a time, with synch for
+ * each 512-byte block
+ */
+ for (sg = data->sg, n_sg = data->sg_len; n_sg; n_sg--, sg++) {
+ int status = 0;
+ dma_addr_t dma_addr = 0;
+ void *kmap_addr;
+ unsigned length = sg->length;
+ enum dma_data_direction dir = direction;
+
+ /* set up dma mapping for controller drivers that might
+ * use DMA ... though they may fall back to PIO
+ */
+ if (dma_dev) {
+ /* never invalidate whole *shared* pages ... */
+ if ((sg->offset != 0 || length != PAGE_SIZE)
+ && dir == DMA_FROM_DEVICE)
+ dir = DMA_BIDIRECTIONAL;
+
+ dma_addr = dma_map_page(dma_dev, sg_page(sg), 0,
+ PAGE_SIZE, dir);
+ if (direction == DMA_TO_DEVICE)
+ t->tx_dma = dma_addr + sg->offset;
+ else
+ t->rx_dma = dma_addr + sg->offset;
+ }
+
+ /* allow pio too; we don't allow highmem */
+ kmap_addr = kmap(sg_page(sg));
+ if (direction == DMA_TO_DEVICE)
+ t->tx_buf = kmap_addr + sg->offset;
+ else
+ t->rx_buf = kmap_addr + sg->offset;
+
+ /* transfer each block, and update request status */
+ while (length) {
+ t->len = min(length, blk_size);
+
+ dev_dbg(&host->spi->dev,
+ " mmc_spi: %s block, %d bytes\n",
+ (direction == DMA_TO_DEVICE)
+ ? "write"
+ : "read",
+ t->len);
+
+ if (direction == DMA_TO_DEVICE)
+ status = mmc_spi_writeblock(host, t, timeout);
+ else
+ status = mmc_spi_readblock(host, t, timeout);
+ if (status < 0)
+ break;
+
+ data->bytes_xfered += t->len;
+ length -= t->len;
+
+ if (!multiple)
+ break;
+ }
+
+ /* discard mappings */
+ if (direction == DMA_FROM_DEVICE)
+ flush_kernel_dcache_page(sg_page(sg));
+ kunmap(sg_page(sg));
+ if (dma_dev)
+ dma_unmap_page(dma_dev, dma_addr, PAGE_SIZE, dir);
+
+ if (status < 0) {
+ data->error = status;
+ dev_dbg(&spi->dev, "%s status %d\n",
+ (direction == DMA_TO_DEVICE)
+ ? "write" : "read",
+ status);
+ break;
+ }
+ }
+
+ /* NOTE some docs describe an MMC-only SET_BLOCK_COUNT (CMD23) that
+ * can be issued before multiblock writes. Unlike its more widely
+ * documented analogue for SD cards (SET_WR_BLK_ERASE_COUNT, ACMD23),
+ * that can affect the STOP_TRAN logic. Complete (and current)
+ * MMC specs should sort that out before Linux starts using CMD23.
+ */
+ if (direction == DMA_TO_DEVICE && multiple) {
+ struct scratch *scratch = host->data;
+ int tmp;
+ const unsigned statlen = sizeof(scratch->status);
+
+ dev_dbg(&spi->dev, " mmc_spi: STOP_TRAN\n");
+
+ /* Tweak the per-block message we set up earlier by morphing
+ * it to hold single buffer with the token followed by some
+ * all-ones bytes ... skip N(BR) (0..1), scan the rest for
+ * "not busy any longer" status, and leave chip selected.
+ */
+ INIT_LIST_HEAD(&host->m.transfers);
+ list_add(&host->early_status.transfer_list,
+ &host->m.transfers);
+
+ memset(scratch->status, 0xff, statlen);
+ scratch->status[0] = SPI_TOKEN_STOP_TRAN;
+
+ host->early_status.tx_buf = host->early_status.rx_buf;
+ host->early_status.tx_dma = host->early_status.rx_dma;
+ host->early_status.len = statlen;
+
+ if (host->dma_dev)
+ dma_sync_single_for_device(host->dma_dev,
+ host->data_dma, sizeof(*scratch),
+ DMA_BIDIRECTIONAL);
+
+ tmp = spi_sync(spi, &host->m);
+
+ if (host->dma_dev)
+ dma_sync_single_for_cpu(host->dma_dev,
+ host->data_dma, sizeof(*scratch),
+ DMA_BIDIRECTIONAL);
+
+ if (tmp < 0) {
+ if (!data->error)
+ data->error = tmp;
+ return;
+ }
+
+ /* Ideally we collected "not busy" status with one I/O,
+ * avoiding wasteful byte-at-a-time scanning... but more
+ * I/O is often needed.
+ */
+ for (tmp = 2; tmp < statlen; tmp++) {
+ if (scratch->status[tmp] != 0)
+ return;
+ }
+ tmp = mmc_spi_wait_unbusy(host, timeout);
+ if (tmp < 0 && !data->error)
+ data->error = tmp;
+ }
+}
+
+/****************************************************************************/
+
+/*
+ * MMC driver implementation -- the interface to the MMC stack
+ */
+
+static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct mmc_spi_host *host = mmc_priv(mmc);
+ int status = -EINVAL;
+
+#ifdef DEBUG
+ /* MMC core and layered drivers *MUST* issue SPI-aware commands */
+ {
+ struct mmc_command *cmd;
+ int invalid = 0;
+
+ cmd = mrq->cmd;
+ if (!mmc_spi_resp_type(cmd)) {
+ dev_dbg(&host->spi->dev, "bogus command\n");
+ cmd->error = -EINVAL;
+ invalid = 1;
+ }
+
+ cmd = mrq->stop;
+ if (cmd && !mmc_spi_resp_type(cmd)) {
+ dev_dbg(&host->spi->dev, "bogus STOP command\n");
+ cmd->error = -EINVAL;
+ invalid = 1;
+ }
+
+ if (invalid) {
+ dump_stack();
+ mmc_request_done(host->mmc, mrq);
+ return;
+ }
+ }
+#endif
+
+ /* issue command; then optionally data and stop */
+ status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL);
+ if (status == 0 && mrq->data) {
+ mmc_spi_data_do(host, mrq->cmd, mrq->data, mrq->data->blksz);
+ if (mrq->stop)
+ status = mmc_spi_command_send(host, mrq, mrq->stop, 0);
+ else
+ mmc_cs_off(host);
+ }
+
+ mmc_request_done(host->mmc, mrq);
+}
+
+/* See Section 6.4.1, in SD "Simplified Physical Layer Specification 2.0"
+ *
+ * NOTE that here we can't know that the card has just been powered up;
+ * not all MMC/SD sockets support power switching.
+ *
+ * FIXME when the card is still in SPI mode, e.g. from a previous kernel,
+ * this doesn't seem to do the right thing at all...
+ */
+static void mmc_spi_initsequence(struct mmc_spi_host *host)
+{
+ /* Try to be very sure any previous command has completed;
+ * wait till not-busy, skip debris from any old commands.
+ */
+ mmc_spi_wait_unbusy(host, r1b_timeout);
+ mmc_spi_readbytes(host, 10);
+
+ /*
+ * Do a burst with chipselect active-high. We need to do this to
+ * meet the requirement of 74 clock cycles with both chipselect
+ * and CMD (MOSI) high before CMD0 ... after the card has been
+ * powered up to Vdd(min), and so is ready to take commands.
+ *
+ * Some cards are particularly needy of this (e.g. Viking "SD256")
+ * while most others don't seem to care.
+ *
+ * Note that this is one of the places MMC/SD plays games with the
+ * SPI protocol. Another is that when chipselect is released while
+ * the card returns BUSY status, the clock must issue several cycles
+ * with chipselect high before the card will stop driving its output.
+ */
+ host->spi->mode |= SPI_CS_HIGH;
+ if (spi_setup(host->spi) != 0) {
+ /* Just warn; most cards work without it. */
+ dev_warn(&host->spi->dev,
+ "can't change chip-select polarity\n");
+ host->spi->mode &= ~SPI_CS_HIGH;
+ } else {
+ mmc_spi_readbytes(host, 18);
+
+ host->spi->mode &= ~SPI_CS_HIGH;
+ if (spi_setup(host->spi) != 0) {
+ /* Wot, we can't get the same setup we had before? */
+ dev_err(&host->spi->dev,
+ "can't restore chip-select polarity\n");
+ }
+ }
+}
+
+static char *mmc_powerstring(u8 power_mode)
+{
+ switch (power_mode) {
+ case MMC_POWER_OFF: return "off";
+ case MMC_POWER_UP: return "up";
+ case MMC_POWER_ON: return "on";
+ }
+ return "?";
+}
+
+static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct mmc_spi_host *host = mmc_priv(mmc);
+
+ if (host->power_mode != ios->power_mode) {
+ int canpower;
+
+ canpower = host->pdata && host->pdata->setpower;
+
+ dev_dbg(&host->spi->dev, "mmc_spi: power %s (%d)%s\n",
+ mmc_powerstring(ios->power_mode),
+ ios->vdd,
+ canpower ? ", can switch" : "");
+
+ /* switch power on/off if possible, accounting for
+ * max 250msec powerup time if needed.
+ */
+ if (canpower) {
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ case MMC_POWER_UP:
+ host->pdata->setpower(&host->spi->dev,
+ ios->vdd);
+ if (ios->power_mode == MMC_POWER_UP)
+ msleep(host->powerup_msecs);
+ }
+ }
+
+ /* See 6.4.1 in the simplified SD card physical spec 2.0 */
+ if (ios->power_mode == MMC_POWER_ON)
+ mmc_spi_initsequence(host);
+
+ /* If powering down, ground all card inputs to avoid power
+ * delivery from data lines! On a shared SPI bus, this
+ * will probably be temporary; 6.4.2 of the simplified SD
+ * spec says this must last at least 1msec.
+ *
+ * - Clock low means CPOL 0, e.g. mode 0
+ * - MOSI low comes from writing zero
+ * - Chipselect is usually active low...
+ */
+ if (canpower && ios->power_mode == MMC_POWER_OFF) {
+ int mres;
+ u8 nullbyte = 0;
+
+ host->spi->mode &= ~(SPI_CPOL|SPI_CPHA);
+ mres = spi_setup(host->spi);
+ if (mres < 0)
+ dev_dbg(&host->spi->dev,
+ "switch to SPI mode 0 failed\n");
+
+ if (spi_write(host->spi, &nullbyte, 1) < 0)
+ dev_dbg(&host->spi->dev,
+ "put spi signals to low failed\n");
+
+ /*
+ * Now clock should be low due to spi mode 0;
+ * MOSI should be low because of written 0x00;
+ * chipselect should be low (it is active low)
+ * power supply is off, so now MMC is off too!
+ *
+ * FIXME no, chipselect can be high since the
+ * device is inactive and SPI_CS_HIGH is clear...
+ */
+ msleep(10);
+ if (mres == 0) {
+ host->spi->mode |= (SPI_CPOL|SPI_CPHA);
+ mres = spi_setup(host->spi);
+ if (mres < 0)
+ dev_dbg(&host->spi->dev,
+ "switch back to SPI mode 3"
+ " failed\n");
+ }
+ }
+
+ host->power_mode = ios->power_mode;
+ }
+
+ if (host->spi->max_speed_hz != ios->clock && ios->clock != 0) {
+ int status;
+
+ host->spi->max_speed_hz = ios->clock;
+ status = spi_setup(host->spi);
+ dev_dbg(&host->spi->dev,
+ "mmc_spi: clock to %d Hz, %d\n",
+ host->spi->max_speed_hz, status);
+ }
+}
+
+static int mmc_spi_get_ro(struct mmc_host *mmc)
+{
+ struct mmc_spi_host *host = mmc_priv(mmc);
+
+ if (host->pdata && host->pdata->get_ro)
+ return !!host->pdata->get_ro(mmc->parent);
+ /*
+ * Board doesn't support read only detection; let the mmc core
+ * decide what to do.
+ */
+ return -ENOSYS;
+}
+
+static int mmc_spi_get_cd(struct mmc_host *mmc)
+{
+ struct mmc_spi_host *host = mmc_priv(mmc);
+
+ if (host->pdata && host->pdata->get_cd)
+ return !!host->pdata->get_cd(mmc->parent);
+ return -ENOSYS;
+}
+
+static const struct mmc_host_ops mmc_spi_ops = {
+ .request = mmc_spi_request,
+ .set_ios = mmc_spi_set_ios,
+ .get_ro = mmc_spi_get_ro,
+ .get_cd = mmc_spi_get_cd,
+};
+
+
+/****************************************************************************/
+
+/*
+ * SPI driver implementation
+ */
+
+static irqreturn_t
+mmc_spi_detect_irq(int irq, void *mmc)
+{
+ struct mmc_spi_host *host = mmc_priv(mmc);
+ u16 delay_msec = max(host->pdata->detect_delay, (u16)100);
+
+ mmc_detect_change(mmc, msecs_to_jiffies(delay_msec));
+ return IRQ_HANDLED;
+}
+
+struct count_children {
+ unsigned n;
+ struct bus_type *bus;
+};
+
+static int maybe_count_child(struct device *dev, void *c)
+{
+ struct count_children *ccp = c;
+
+ if (dev->bus == ccp->bus) {
+ if (ccp->n)
+ return -EBUSY;
+ ccp->n++;
+ }
+ return 0;
+}
+
+static int mmc_spi_probe(struct spi_device *spi)
+{
+ void *ones;
+ struct mmc_host *mmc;
+ struct mmc_spi_host *host;
+ int status;
+
+ /* 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.
+ */
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+
+ status = spi_setup(spi);
+ if (status < 0) {
+ dev_dbg(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n",
+ spi->mode, spi->max_speed_hz / 1000,
+ status);
+ return status;
+ }
+
+ /* We can use the bus safely iff nobody else will interfere with us.
+ * Most commands consist of one SPI message to issue a command, then
+ * several more to collect its response, then possibly more for data
+ * transfer. Clocking access to other devices during that period will
+ * corrupt the command execution.
+ *
+ * Until we have software primitives which guarantee non-interference,
+ * we'll aim for a hardware-level guarantee.
+ *
+ * REVISIT we can't guarantee another device won't be added later...
+ */
+ if (spi->master->num_chipselect > 1) {
+ struct count_children cc;
+
+ cc.n = 0;
+ cc.bus = spi->dev.bus;
+ status = device_for_each_child(spi->dev.parent, &cc,
+ maybe_count_child);
+ if (status < 0) {
+ dev_err(&spi->dev, "can't share SPI bus\n");
+ return status;
+ }
+
+ dev_warn(&spi->dev, "ASSUMING SPI bus stays unshared!\n");
+ }
+
+ /* We need a supply of ones to transmit. This is the only time
+ * the CPU touches these, so cache coherency isn't a concern.
+ *
+ * NOTE if many systems use more than one MMC-over-SPI connector
+ * it'd save some memory to share this. That's evidently rare.
+ */
+ status = -ENOMEM;
+ ones = kmalloc(MMC_SPI_BLOCKSIZE, GFP_KERNEL);
+ if (!ones)
+ goto nomem;
+ memset(ones, 0xff, MMC_SPI_BLOCKSIZE);
+
+ mmc = mmc_alloc_host(sizeof(*host), &spi->dev);
+ if (!mmc)
+ goto nomem;
+
+ mmc->ops = &mmc_spi_ops;
+ mmc->max_blk_size = MMC_SPI_BLOCKSIZE;
+
+ mmc->caps = MMC_CAP_SPI;
+
+ /* SPI doesn't need the lowspeed device identification thing for
+ * MMC or SD cards, since it never comes up in open drain mode.
+ * That's good; some SPI masters can't handle very low speeds!
+ *
+ * However, low speed SDIO cards need not handle over 400 KHz;
+ * that's the only reason not to use a few MHz for f_min (until
+ * the upper layer reads the target frequency from the CSD).
+ */
+ mmc->f_min = 400000;
+ mmc->f_max = spi->max_speed_hz;
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->spi = spi;
+
+ host->ones = ones;
+
+ /* Platform data is used to hook up things like card sensing
+ * and power switching gpios.
+ */
+ host->pdata = spi->dev.platform_data;
+ if (host->pdata)
+ mmc->ocr_avail = host->pdata->ocr_mask;
+ if (!mmc->ocr_avail) {
+ dev_warn(&spi->dev, "ASSUMING 3.2-3.4 V slot power\n");
+ mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+ }
+ if (host->pdata && host->pdata->setpower) {
+ host->powerup_msecs = host->pdata->powerup_msecs;
+ if (!host->powerup_msecs || host->powerup_msecs > 250)
+ host->powerup_msecs = 250;
+ }
+
+ dev_set_drvdata(&spi->dev, mmc);
+
+ /* preallocate dma buffers */
+ host->data = kmalloc(sizeof(*host->data), GFP_KERNEL);
+ if (!host->data)
+ goto fail_nobuf1;
+
+ if (spi->master->dev.parent->dma_mask) {
+ struct device *dev = spi->master->dev.parent;
+
+ host->dma_dev = dev;
+ host->ones_dma = dma_map_single(dev, ones,
+ MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
+ host->data_dma = dma_map_single(dev, host->data,
+ sizeof(*host->data), DMA_BIDIRECTIONAL);
+
+ /* REVISIT in theory those map operations can fail... */
+
+ dma_sync_single_for_cpu(host->dma_dev,
+ host->data_dma, sizeof(*host->data),
+ DMA_BIDIRECTIONAL);
+ }
+
+ /* setup message for status/busy readback */
+ spi_message_init(&host->readback);
+ host->readback.is_dma_mapped = (host->dma_dev != NULL);
+
+ spi_message_add_tail(&host->status, &host->readback);
+ host->status.tx_buf = host->ones;
+ host->status.tx_dma = host->ones_dma;
+ host->status.rx_buf = &host->data->status;
+ host->status.rx_dma = host->data_dma + offsetof(struct scratch, status);
+ host->status.cs_change = 1;
+
+ /* register card detect irq */
+ if (host->pdata && host->pdata->init) {
+ status = host->pdata->init(&spi->dev, mmc_spi_detect_irq, mmc);
+ if (status != 0)
+ goto fail_glue_init;
+ }
+
+ /* pass platform capabilities, if any */
+ if (host->pdata)
+ mmc->caps |= host->pdata->caps;
+
+ status = mmc_add_host(mmc);
+ if (status != 0)
+ goto fail_add_host;
+
+ dev_info(&spi->dev, "SD/MMC host %s%s%s%s%s\n",
+ dev_name(&mmc->class_dev),
+ host->dma_dev ? "" : ", no DMA",
+ (host->pdata && host->pdata->get_ro)
+ ? "" : ", no WP",
+ (host->pdata && host->pdata->setpower)
+ ? "" : ", no poweroff",
+ (mmc->caps & MMC_CAP_NEEDS_POLL)
+ ? ", cd polling" : "");
+ return 0;
+
+fail_add_host:
+ mmc_remove_host (mmc);
+fail_glue_init:
+ if (host->dma_dev)
+ dma_unmap_single(host->dma_dev, host->data_dma,
+ sizeof(*host->data), DMA_BIDIRECTIONAL);
+ kfree(host->data);
+
+fail_nobuf1:
+ mmc_free_host(mmc);
+ dev_set_drvdata(&spi->dev, NULL);
+
+nomem:
+ kfree(ones);
+ return status;
+}
+
+
+static int __devexit mmc_spi_remove(struct spi_device *spi)
+{
+ struct mmc_host *mmc = dev_get_drvdata(&spi->dev);
+ struct mmc_spi_host *host;
+
+ if (mmc) {
+ host = mmc_priv(mmc);
+
+ /* prevent new mmc_detect_change() calls */
+ if (host->pdata && host->pdata->exit)
+ host->pdata->exit(&spi->dev, mmc);
+
+ mmc_remove_host(mmc);
+
+ if (host->dma_dev) {
+ dma_unmap_single(host->dma_dev, host->ones_dma,
+ MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
+ dma_unmap_single(host->dma_dev, host->data_dma,
+ sizeof(*host->data), DMA_BIDIRECTIONAL);
+ }
+
+ kfree(host->data);
+ kfree(host->ones);
+
+ spi->max_speed_hz = mmc->f_max;
+ mmc_free_host(mmc);
+ dev_set_drvdata(&spi->dev, NULL);
+ }
+ return 0;
+}
+
+
+static struct spi_driver mmc_spi_driver = {
+ .driver = {
+ .name = "mmc_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = mmc_spi_probe,
+ .remove = __devexit_p(mmc_spi_remove),
+};
+
+
+static int __init mmc_spi_init(void)
+{
+ return spi_register_driver(&mmc_spi_driver);
+}
+module_init(mmc_spi_init);
+
+
+static void __exit mmc_spi_exit(void)
+{
+ spi_unregister_driver(&mmc_spi_driver);
+}
+module_exit(mmc_spi_exit);
+
+
+MODULE_AUTHOR("Mike Lavender, David Brownell, "
+ "Hans-Peter Nilsson, Jan Nikitenko");
+MODULE_DESCRIPTION("SPI SD/MMC host driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
new file mode 100644
index 0000000..2fadf32
--- /dev/null
+++ b/drivers/mmc/host/mmci.c
@@ -0,0 +1,725 @@
+/*
+ * linux/drivers/mmc/host/mmci.c - ARM PrimeCell MMCI PL180/1 driver
+ *
+ * Copyright (C) 2003 Deep Blue Solutions, Ltd, 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.
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/highmem.h>
+#include <linux/log2.h>
+#include <linux/mmc/host.h>
+#include <linux/amba/bus.h>
+#include <linux/clk.h>
+#include <linux/scatterlist.h>
+
+#include <asm/cacheflush.h>
+#include <asm/div64.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+#include <asm/mach/mmc.h>
+
+#include "mmci.h"
+
+#define DRIVER_NAME "mmci-pl18x"
+
+#define DBG(host,fmt,args...) \
+ pr_debug("%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args)
+
+static unsigned int fmax = 515633;
+
+static void
+mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
+{
+ writel(0, host->base + MMCICOMMAND);
+
+ BUG_ON(host->data);
+
+ host->mrq = NULL;
+ host->cmd = NULL;
+
+ if (mrq->data)
+ mrq->data->bytes_xfered = host->data_xfered;
+
+ /*
+ * Need to drop the host lock here; mmc_request_done may call
+ * back into the driver...
+ */
+ spin_unlock(&host->lock);
+ mmc_request_done(host->mmc, mrq);
+ spin_lock(&host->lock);
+}
+
+static void mmci_stop_data(struct mmci_host *host)
+{
+ writel(0, host->base + MMCIDATACTRL);
+ writel(0, host->base + MMCIMASK1);
+ host->data = NULL;
+}
+
+static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
+{
+ unsigned int datactrl, timeout, irqmask;
+ unsigned long long clks;
+ void __iomem *base;
+ int blksz_bits;
+
+ DBG(host, "blksz %04x blks %04x flags %08x\n",
+ data->blksz, data->blocks, data->flags);
+
+ host->data = data;
+ host->size = data->blksz;
+ host->data_xfered = 0;
+
+ mmci_init_sg(host, data);
+
+ clks = (unsigned long long)data->timeout_ns * host->cclk;
+ do_div(clks, 1000000000UL);
+
+ timeout = data->timeout_clks + (unsigned int)clks;
+
+ base = host->base;
+ writel(timeout, base + MMCIDATATIMER);
+ writel(host->size, base + MMCIDATALENGTH);
+
+ blksz_bits = ffs(data->blksz) - 1;
+ BUG_ON(1 << blksz_bits != data->blksz);
+
+ datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
+ if (data->flags & MMC_DATA_READ) {
+ datactrl |= MCI_DPSM_DIRECTION;
+ irqmask = MCI_RXFIFOHALFFULLMASK;
+
+ /*
+ * If we have less than a FIFOSIZE of bytes to transfer,
+ * trigger a PIO interrupt as soon as any data is available.
+ */
+ if (host->size < MCI_FIFOSIZE)
+ irqmask |= MCI_RXDATAAVLBLMASK;
+ } else {
+ /*
+ * We don't actually need to include "FIFO empty" here
+ * since its implicit in "FIFO half empty".
+ */
+ irqmask = MCI_TXFIFOHALFEMPTYMASK;
+ }
+
+ writel(datactrl, base + MMCIDATACTRL);
+ writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
+ writel(irqmask, base + MMCIMASK1);
+}
+
+static void
+mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
+{
+ void __iomem *base = host->base;
+
+ DBG(host, "op %02x arg %08x flags %08x\n",
+ cmd->opcode, cmd->arg, cmd->flags);
+
+ if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
+ writel(0, base + MMCICOMMAND);
+ udelay(1);
+ }
+
+ c |= cmd->opcode | MCI_CPSM_ENABLE;
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136)
+ c |= MCI_CPSM_LONGRSP;
+ c |= MCI_CPSM_RESPONSE;
+ }
+ if (/*interrupt*/0)
+ c |= MCI_CPSM_INTERRUPT;
+
+ host->cmd = cmd;
+
+ writel(cmd->arg, base + MMCIARGUMENT);
+ writel(c, base + MMCICOMMAND);
+}
+
+static void
+mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
+ unsigned int status)
+{
+ if (status & MCI_DATABLOCKEND) {
+ host->data_xfered += data->blksz;
+ }
+ if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
+ if (status & MCI_DATACRCFAIL)
+ data->error = -EILSEQ;
+ else if (status & MCI_DATATIMEOUT)
+ data->error = -ETIMEDOUT;
+ else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
+ data->error = -EIO;
+ status |= MCI_DATAEND;
+
+ /*
+ * We hit an error condition. Ensure that any data
+ * partially written to a page is properly coherent.
+ */
+ if (host->sg_len && data->flags & MMC_DATA_READ)
+ flush_dcache_page(sg_page(host->sg_ptr));
+ }
+ if (status & MCI_DATAEND) {
+ mmci_stop_data(host);
+
+ if (!data->stop) {
+ mmci_request_end(host, data->mrq);
+ } else {
+ mmci_start_command(host, data->stop, 0);
+ }
+ }
+}
+
+static void
+mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
+ unsigned int status)
+{
+ void __iomem *base = host->base;
+
+ host->cmd = NULL;
+
+ cmd->resp[0] = readl(base + MMCIRESPONSE0);
+ cmd->resp[1] = readl(base + MMCIRESPONSE1);
+ cmd->resp[2] = readl(base + MMCIRESPONSE2);
+ cmd->resp[3] = readl(base + MMCIRESPONSE3);
+
+ if (status & MCI_CMDTIMEOUT) {
+ cmd->error = -ETIMEDOUT;
+ } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) {
+ cmd->error = -EILSEQ;
+ }
+
+ if (!cmd->data || cmd->error) {
+ if (host->data)
+ mmci_stop_data(host);
+ mmci_request_end(host, cmd->mrq);
+ } else if (!(cmd->data->flags & MMC_DATA_READ)) {
+ mmci_start_data(host, cmd->data);
+ }
+}
+
+static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain)
+{
+ void __iomem *base = host->base;
+ char *ptr = buffer;
+ u32 status;
+ int host_remain = host->size;
+
+ do {
+ int count = host_remain - (readl(base + MMCIFIFOCNT) << 2);
+
+ if (count > remain)
+ count = remain;
+
+ if (count <= 0)
+ break;
+
+ readsl(base + MMCIFIFO, ptr, count >> 2);
+
+ ptr += count;
+ remain -= count;
+ host_remain -= count;
+
+ if (remain == 0)
+ break;
+
+ status = readl(base + MMCISTATUS);
+ } while (status & MCI_RXDATAAVLBL);
+
+ return ptr - buffer;
+}
+
+static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status)
+{
+ void __iomem *base = host->base;
+ char *ptr = buffer;
+
+ do {
+ unsigned int count, maxcnt;
+
+ maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE : MCI_FIFOHALFSIZE;
+ count = min(remain, maxcnt);
+
+ writesl(base + MMCIFIFO, ptr, count >> 2);
+
+ ptr += count;
+ remain -= count;
+
+ if (remain == 0)
+ break;
+
+ status = readl(base + MMCISTATUS);
+ } while (status & MCI_TXFIFOHALFEMPTY);
+
+ return ptr - buffer;
+}
+
+/*
+ * PIO data transfer IRQ handler.
+ */
+static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
+{
+ struct mmci_host *host = dev_id;
+ void __iomem *base = host->base;
+ u32 status;
+
+ status = readl(base + MMCISTATUS);
+
+ DBG(host, "irq1 %08x\n", status);
+
+ do {
+ unsigned long flags;
+ unsigned int remain, len;
+ char *buffer;
+
+ /*
+ * For write, we only need to test the half-empty flag
+ * here - if the FIFO is completely empty, then by
+ * definition it is more than half empty.
+ *
+ * For read, check for data available.
+ */
+ if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL)))
+ break;
+
+ /*
+ * Map the current scatter buffer.
+ */
+ buffer = mmci_kmap_atomic(host, &flags) + host->sg_off;
+ remain = host->sg_ptr->length - host->sg_off;
+
+ len = 0;
+ if (status & MCI_RXACTIVE)
+ len = mmci_pio_read(host, buffer, remain);
+ if (status & MCI_TXACTIVE)
+ len = mmci_pio_write(host, buffer, remain, status);
+
+ /*
+ * Unmap the buffer.
+ */
+ mmci_kunmap_atomic(host, buffer, &flags);
+
+ host->sg_off += len;
+ host->size -= len;
+ remain -= len;
+
+ if (remain)
+ break;
+
+ /*
+ * If we were reading, and we have completed this
+ * page, ensure that the data cache is coherent.
+ */
+ if (status & MCI_RXACTIVE)
+ flush_dcache_page(sg_page(host->sg_ptr));
+
+ if (!mmci_next_sg(host))
+ break;
+
+ status = readl(base + MMCISTATUS);
+ } while (1);
+
+ /*
+ * If we're nearing the end of the read, switch to
+ * "any data available" mode.
+ */
+ if (status & MCI_RXACTIVE && host->size < MCI_FIFOSIZE)
+ writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1);
+
+ /*
+ * If we run out of data, disable the data IRQs; this
+ * prevents a race where the FIFO becomes empty before
+ * the chip itself has disabled the data path, and
+ * stops us racing with our data end IRQ.
+ */
+ if (host->size == 0) {
+ writel(0, base + MMCIMASK1);
+ writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Handle completion of command and data transfers.
+ */
+static irqreturn_t mmci_irq(int irq, void *dev_id)
+{
+ struct mmci_host *host = dev_id;
+ u32 status;
+ int ret = 0;
+
+ spin_lock(&host->lock);
+
+ do {
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+
+ status = readl(host->base + MMCISTATUS);
+ status &= readl(host->base + MMCIMASK0);
+ writel(status, host->base + MMCICLEAR);
+
+ DBG(host, "irq0 %08x\n", status);
+
+ data = host->data;
+ if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|
+ MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data)
+ mmci_data_irq(host, data, status);
+
+ cmd = host->cmd;
+ if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
+ mmci_cmd_irq(host, cmd, status);
+
+ ret = 1;
+ } while (status);
+
+ spin_unlock(&host->lock);
+
+ return IRQ_RETVAL(ret);
+}
+
+static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct mmci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+
+ WARN_ON(host->mrq != NULL);
+
+ if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
+ printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n",
+ mmc_hostname(mmc), mrq->data->blksz);
+ mrq->cmd->error = -EINVAL;
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ host->mrq = mrq;
+
+ if (mrq->data && mrq->data->flags & MMC_DATA_READ)
+ mmci_start_data(host, mrq->data);
+
+ mmci_start_command(host, mrq->cmd, 0);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct mmci_host *host = mmc_priv(mmc);
+ u32 clk = 0, pwr = 0;
+
+ if (ios->clock) {
+ if (ios->clock >= host->mclk) {
+ clk = MCI_CLK_BYPASS;
+ host->cclk = host->mclk;
+ } else {
+ clk = host->mclk / (2 * ios->clock) - 1;
+ if (clk >= 256)
+ clk = 255;
+ host->cclk = host->mclk / (2 * (clk + 1));
+ }
+ clk |= MCI_CLK_ENABLE;
+ }
+
+ if (host->plat->translate_vdd)
+ pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
+
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ break;
+ case MMC_POWER_UP:
+ pwr |= MCI_PWR_UP;
+ break;
+ case MMC_POWER_ON:
+ pwr |= MCI_PWR_ON;
+ break;
+ }
+
+ if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
+ pwr |= MCI_ROD;
+
+ writel(clk, host->base + MMCICLOCK);
+
+ if (host->pwr != pwr) {
+ host->pwr = pwr;
+ writel(pwr, host->base + MMCIPOWER);
+ }
+}
+
+static const struct mmc_host_ops mmci_ops = {
+ .request = mmci_request,
+ .set_ios = mmci_set_ios,
+};
+
+static void mmci_check_status(unsigned long data)
+{
+ struct mmci_host *host = (struct mmci_host *)data;
+ unsigned int status;
+
+ status = host->plat->status(mmc_dev(host->mmc));
+ if (status ^ host->oldstat)
+ mmc_detect_change(host->mmc, 0);
+
+ host->oldstat = status;
+ mod_timer(&host->timer, jiffies + HZ);
+}
+
+static int mmci_probe(struct amba_device *dev, void *id)
+{
+ struct mmc_platform_data *plat = dev->dev.platform_data;
+ struct mmci_host *host;
+ struct mmc_host *mmc;
+ int ret;
+
+ /* must have platform data */
+ if (!plat) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = amba_request_regions(dev, DRIVER_NAME);
+ if (ret)
+ goto out;
+
+ mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ goto rel_regions;
+ }
+
+ host = mmc_priv(mmc);
+ host->clk = clk_get(&dev->dev, "MCLK");
+ if (IS_ERR(host->clk)) {
+ ret = PTR_ERR(host->clk);
+ host->clk = NULL;
+ goto host_free;
+ }
+
+ ret = clk_enable(host->clk);
+ if (ret)
+ goto clk_free;
+
+ host->plat = plat;
+ host->mclk = clk_get_rate(host->clk);
+ /*
+ * According to the spec, mclk is max 100 MHz,
+ * so we try to adjust the clock down to this,
+ * (if possible).
+ */
+ if (host->mclk > 100000000) {
+ ret = clk_set_rate(host->clk, 100000000);
+ if (ret < 0)
+ goto clk_disable;
+ host->mclk = clk_get_rate(host->clk);
+ DBG(host, "eventual mclk rate: %u Hz\n", host->mclk);
+ }
+ host->mmc = mmc;
+ host->base = ioremap(dev->res.start, SZ_4K);
+ if (!host->base) {
+ ret = -ENOMEM;
+ goto clk_disable;
+ }
+
+ mmc->ops = &mmci_ops;
+ mmc->f_min = (host->mclk + 511) / 512;
+ mmc->f_max = min(host->mclk, fmax);
+ mmc->ocr_avail = plat->ocr_mask;
+
+ /*
+ * We can do SGIO
+ */
+ mmc->max_hw_segs = 16;
+ mmc->max_phys_segs = NR_SG;
+
+ /*
+ * Since we only have a 16-bit data length register, we must
+ * ensure that we don't exceed 2^16-1 bytes in a single request.
+ */
+ mmc->max_req_size = 65535;
+
+ /*
+ * Set the maximum segment size. Since we aren't doing DMA
+ * (yet) we are only limited by the data length register.
+ */
+ mmc->max_seg_size = mmc->max_req_size;
+
+ /*
+ * Block size can be up to 2048 bytes, but must be a power of two.
+ */
+ mmc->max_blk_size = 2048;
+
+ /*
+ * No limit on the number of blocks transferred.
+ */
+ mmc->max_blk_count = mmc->max_req_size;
+
+ spin_lock_init(&host->lock);
+
+ writel(0, host->base + MMCIMASK0);
+ writel(0, host->base + MMCIMASK1);
+ writel(0xfff, host->base + MMCICLEAR);
+
+ ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);
+ if (ret)
+ goto unmap;
+
+ ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host);
+ if (ret)
+ goto irq0_free;
+
+ writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+
+ amba_set_drvdata(dev, mmc);
+
+ mmc_add_host(mmc);
+
+ printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n",
+ mmc_hostname(mmc), amba_rev(dev), amba_config(dev),
+ (unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]);
+
+ init_timer(&host->timer);
+ host->timer.data = (unsigned long)host;
+ host->timer.function = mmci_check_status;
+ host->timer.expires = jiffies + HZ;
+ add_timer(&host->timer);
+
+ return 0;
+
+ irq0_free:
+ free_irq(dev->irq[0], host);
+ unmap:
+ iounmap(host->base);
+ clk_disable:
+ clk_disable(host->clk);
+ clk_free:
+ clk_put(host->clk);
+ host_free:
+ mmc_free_host(mmc);
+ rel_regions:
+ amba_release_regions(dev);
+ out:
+ return ret;
+}
+
+static int mmci_remove(struct amba_device *dev)
+{
+ struct mmc_host *mmc = amba_get_drvdata(dev);
+
+ amba_set_drvdata(dev, NULL);
+
+ if (mmc) {
+ struct mmci_host *host = mmc_priv(mmc);
+
+ del_timer_sync(&host->timer);
+
+ mmc_remove_host(mmc);
+
+ writel(0, host->base + MMCIMASK0);
+ writel(0, host->base + MMCIMASK1);
+
+ writel(0, host->base + MMCICOMMAND);
+ writel(0, host->base + MMCIDATACTRL);
+
+ free_irq(dev->irq[0], host);
+ free_irq(dev->irq[1], host);
+
+ iounmap(host->base);
+ clk_disable(host->clk);
+ clk_put(host->clk);
+
+ mmc_free_host(mmc);
+
+ amba_release_regions(dev);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mmci_suspend(struct amba_device *dev, pm_message_t state)
+{
+ struct mmc_host *mmc = amba_get_drvdata(dev);
+ int ret = 0;
+
+ if (mmc) {
+ struct mmci_host *host = mmc_priv(mmc);
+
+ ret = mmc_suspend_host(mmc, state);
+ if (ret == 0)
+ writel(0, host->base + MMCIMASK0);
+ }
+
+ return ret;
+}
+
+static int mmci_resume(struct amba_device *dev)
+{
+ struct mmc_host *mmc = amba_get_drvdata(dev);
+ int ret = 0;
+
+ if (mmc) {
+ struct mmci_host *host = mmc_priv(mmc);
+
+ writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+
+ ret = mmc_resume_host(mmc);
+ }
+
+ return ret;
+}
+#else
+#define mmci_suspend NULL
+#define mmci_resume NULL
+#endif
+
+static struct amba_id mmci_ids[] = {
+ {
+ .id = 0x00041180,
+ .mask = 0x000fffff,
+ },
+ {
+ .id = 0x00041181,
+ .mask = 0x000fffff,
+ },
+ { 0, 0 },
+};
+
+static struct amba_driver mmci_driver = {
+ .drv = {
+ .name = DRIVER_NAME,
+ },
+ .probe = mmci_probe,
+ .remove = mmci_remove,
+ .suspend = mmci_suspend,
+ .resume = mmci_resume,
+ .id_table = mmci_ids,
+};
+
+static int __init mmci_init(void)
+{
+ return amba_driver_register(&mmci_driver);
+}
+
+static void __exit mmci_exit(void)
+{
+ amba_driver_unregister(&mmci_driver);
+}
+
+module_init(mmci_init);
+module_exit(mmci_exit);
+module_param(fmax, uint, 0444);
+
+MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
new file mode 100644
index 0000000..0f39c49
--- /dev/null
+++ b/drivers/mmc/host/mmci.h
@@ -0,0 +1,179 @@
+/*
+ * linux/drivers/mmc/host/mmci.h - ARM PrimeCell MMCI PL180/1 driver
+ *
+ * Copyright (C) 2003 Deep Blue Solutions, Ltd, 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.
+ */
+#define MMCIPOWER 0x000
+#define MCI_PWR_OFF 0x00
+#define MCI_PWR_UP 0x02
+#define MCI_PWR_ON 0x03
+#define MCI_OD (1 << 6)
+#define MCI_ROD (1 << 7)
+
+#define MMCICLOCK 0x004
+#define MCI_CLK_ENABLE (1 << 8)
+#define MCI_CLK_PWRSAVE (1 << 9)
+#define MCI_CLK_BYPASS (1 << 10)
+
+#define MMCIARGUMENT 0x008
+#define MMCICOMMAND 0x00c
+#define MCI_CPSM_RESPONSE (1 << 6)
+#define MCI_CPSM_LONGRSP (1 << 7)
+#define MCI_CPSM_INTERRUPT (1 << 8)
+#define MCI_CPSM_PENDING (1 << 9)
+#define MCI_CPSM_ENABLE (1 << 10)
+
+#define MMCIRESPCMD 0x010
+#define MMCIRESPONSE0 0x014
+#define MMCIRESPONSE1 0x018
+#define MMCIRESPONSE2 0x01c
+#define MMCIRESPONSE3 0x020
+#define MMCIDATATIMER 0x024
+#define MMCIDATALENGTH 0x028
+#define MMCIDATACTRL 0x02c
+#define MCI_DPSM_ENABLE (1 << 0)
+#define MCI_DPSM_DIRECTION (1 << 1)
+#define MCI_DPSM_MODE (1 << 2)
+#define MCI_DPSM_DMAENABLE (1 << 3)
+
+#define MMCIDATACNT 0x030
+#define MMCISTATUS 0x034
+#define MCI_CMDCRCFAIL (1 << 0)
+#define MCI_DATACRCFAIL (1 << 1)
+#define MCI_CMDTIMEOUT (1 << 2)
+#define MCI_DATATIMEOUT (1 << 3)
+#define MCI_TXUNDERRUN (1 << 4)
+#define MCI_RXOVERRUN (1 << 5)
+#define MCI_CMDRESPEND (1 << 6)
+#define MCI_CMDSENT (1 << 7)
+#define MCI_DATAEND (1 << 8)
+#define MCI_DATABLOCKEND (1 << 10)
+#define MCI_CMDACTIVE (1 << 11)
+#define MCI_TXACTIVE (1 << 12)
+#define MCI_RXACTIVE (1 << 13)
+#define MCI_TXFIFOHALFEMPTY (1 << 14)
+#define MCI_RXFIFOHALFFULL (1 << 15)
+#define MCI_TXFIFOFULL (1 << 16)
+#define MCI_RXFIFOFULL (1 << 17)
+#define MCI_TXFIFOEMPTY (1 << 18)
+#define MCI_RXFIFOEMPTY (1 << 19)
+#define MCI_TXDATAAVLBL (1 << 20)
+#define MCI_RXDATAAVLBL (1 << 21)
+
+#define MMCICLEAR 0x038
+#define MCI_CMDCRCFAILCLR (1 << 0)
+#define MCI_DATACRCFAILCLR (1 << 1)
+#define MCI_CMDTIMEOUTCLR (1 << 2)
+#define MCI_DATATIMEOUTCLR (1 << 3)
+#define MCI_TXUNDERRUNCLR (1 << 4)
+#define MCI_RXOVERRUNCLR (1 << 5)
+#define MCI_CMDRESPENDCLR (1 << 6)
+#define MCI_CMDSENTCLR (1 << 7)
+#define MCI_DATAENDCLR (1 << 8)
+#define MCI_DATABLOCKENDCLR (1 << 10)
+
+#define MMCIMASK0 0x03c
+#define MCI_CMDCRCFAILMASK (1 << 0)
+#define MCI_DATACRCFAILMASK (1 << 1)
+#define MCI_CMDTIMEOUTMASK (1 << 2)
+#define MCI_DATATIMEOUTMASK (1 << 3)
+#define MCI_TXUNDERRUNMASK (1 << 4)
+#define MCI_RXOVERRUNMASK (1 << 5)
+#define MCI_CMDRESPENDMASK (1 << 6)
+#define MCI_CMDSENTMASK (1 << 7)
+#define MCI_DATAENDMASK (1 << 8)
+#define MCI_DATABLOCKENDMASK (1 << 10)
+#define MCI_CMDACTIVEMASK (1 << 11)
+#define MCI_TXACTIVEMASK (1 << 12)
+#define MCI_RXACTIVEMASK (1 << 13)
+#define MCI_TXFIFOHALFEMPTYMASK (1 << 14)
+#define MCI_RXFIFOHALFFULLMASK (1 << 15)
+#define MCI_TXFIFOFULLMASK (1 << 16)
+#define MCI_RXFIFOFULLMASK (1 << 17)
+#define MCI_TXFIFOEMPTYMASK (1 << 18)
+#define MCI_RXFIFOEMPTYMASK (1 << 19)
+#define MCI_TXDATAAVLBLMASK (1 << 20)
+#define MCI_RXDATAAVLBLMASK (1 << 21)
+
+#define MMCIMASK1 0x040
+#define MMCIFIFOCNT 0x048
+#define MMCIFIFO 0x080 /* to 0x0bc */
+
+#define MCI_IRQENABLE \
+ (MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \
+ MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \
+ MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATABLOCKENDMASK)
+
+/*
+ * The size of the FIFO in bytes.
+ */
+#define MCI_FIFOSIZE (16*4)
+
+#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
+
+#define NR_SG 16
+
+struct clk;
+
+struct mmci_host {
+ void __iomem *base;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ struct mmc_host *mmc;
+ struct clk *clk;
+
+ unsigned int data_xfered;
+
+ spinlock_t lock;
+
+ unsigned int mclk;
+ unsigned int cclk;
+ u32 pwr;
+ struct mmc_platform_data *plat;
+
+ struct timer_list timer;
+ unsigned int oldstat;
+
+ unsigned int sg_len;
+
+ /* pio stuff */
+ struct scatterlist *sg_ptr;
+ unsigned int sg_off;
+ unsigned int size;
+};
+
+static inline void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
+{
+ /*
+ * Ideally, we want the higher levels to pass us a scatter list.
+ */
+ host->sg_len = data->sg_len;
+ host->sg_ptr = data->sg;
+ host->sg_off = 0;
+}
+
+static inline int mmci_next_sg(struct mmci_host *host)
+{
+ host->sg_ptr++;
+ host->sg_off = 0;
+ return --host->sg_len;
+}
+
+static inline char *mmci_kmap_atomic(struct mmci_host *host, unsigned long *flags)
+{
+ struct scatterlist *sg = host->sg_ptr;
+
+ local_irq_save(*flags);
+ return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+}
+
+static inline void mmci_kunmap_atomic(struct mmci_host *host, void *buffer, unsigned long *flags)
+{
+ kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+ local_irq_restore(*flags);
+}
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
new file mode 100644
index 0000000..1b9fc3c
--- /dev/null
+++ b/drivers/mmc/host/omap.c
@@ -0,0 +1,1629 @@
+/*
+ * linux/drivers/mmc/host/omap.c
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Written by Tuukka Tikkanen and Juha Yrjölä<juha.yrjola@nokia.com>
+ * Misc hacks here and there by Tony Lindgren <tony@atomide.com>
+ * Other hacks (DMA, SD, etc) by David Brownell
+ *
+ * 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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/clk.h>
+#include <linux/scatterlist.h>
+#include <linux/i2c/tps65010.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <mach/board.h>
+#include <mach/mmc.h>
+#include <mach/gpio.h>
+#include <mach/dma.h>
+#include <mach/mux.h>
+#include <mach/fpga.h>
+
+#define OMAP_MMC_REG_CMD 0x00
+#define OMAP_MMC_REG_ARGL 0x04
+#define OMAP_MMC_REG_ARGH 0x08
+#define OMAP_MMC_REG_CON 0x0c
+#define OMAP_MMC_REG_STAT 0x10
+#define OMAP_MMC_REG_IE 0x14
+#define OMAP_MMC_REG_CTO 0x18
+#define OMAP_MMC_REG_DTO 0x1c
+#define OMAP_MMC_REG_DATA 0x20
+#define OMAP_MMC_REG_BLEN 0x24
+#define OMAP_MMC_REG_NBLK 0x28
+#define OMAP_MMC_REG_BUF 0x2c
+#define OMAP_MMC_REG_SDIO 0x34
+#define OMAP_MMC_REG_REV 0x3c
+#define OMAP_MMC_REG_RSP0 0x40
+#define OMAP_MMC_REG_RSP1 0x44
+#define OMAP_MMC_REG_RSP2 0x48
+#define OMAP_MMC_REG_RSP3 0x4c
+#define OMAP_MMC_REG_RSP4 0x50
+#define OMAP_MMC_REG_RSP5 0x54
+#define OMAP_MMC_REG_RSP6 0x58
+#define OMAP_MMC_REG_RSP7 0x5c
+#define OMAP_MMC_REG_IOSR 0x60
+#define OMAP_MMC_REG_SYSC 0x64
+#define OMAP_MMC_REG_SYSS 0x68
+
+#define OMAP_MMC_STAT_CARD_ERR (1 << 14)
+#define OMAP_MMC_STAT_CARD_IRQ (1 << 13)
+#define OMAP_MMC_STAT_OCR_BUSY (1 << 12)
+#define OMAP_MMC_STAT_A_EMPTY (1 << 11)
+#define OMAP_MMC_STAT_A_FULL (1 << 10)
+#define OMAP_MMC_STAT_CMD_CRC (1 << 8)
+#define OMAP_MMC_STAT_CMD_TOUT (1 << 7)
+#define OMAP_MMC_STAT_DATA_CRC (1 << 6)
+#define OMAP_MMC_STAT_DATA_TOUT (1 << 5)
+#define OMAP_MMC_STAT_END_BUSY (1 << 4)
+#define OMAP_MMC_STAT_END_OF_DATA (1 << 3)
+#define OMAP_MMC_STAT_CARD_BUSY (1 << 2)
+#define OMAP_MMC_STAT_END_OF_CMD (1 << 0)
+
+#define OMAP_MMC_READ(host, reg) __raw_readw((host)->virt_base + OMAP_MMC_REG_##reg)
+#define OMAP_MMC_WRITE(host, reg, val) __raw_writew((val), (host)->virt_base + OMAP_MMC_REG_##reg)
+
+/*
+ * Command types
+ */
+#define OMAP_MMC_CMDTYPE_BC 0
+#define OMAP_MMC_CMDTYPE_BCR 1
+#define OMAP_MMC_CMDTYPE_AC 2
+#define OMAP_MMC_CMDTYPE_ADTC 3
+
+
+#define DRIVER_NAME "mmci-omap"
+
+/* Specifies how often in millisecs to poll for card status changes
+ * when the cover switch is open */
+#define OMAP_MMC_COVER_POLL_DELAY 500
+
+struct mmc_omap_host;
+
+struct mmc_omap_slot {
+ int id;
+ unsigned int vdd;
+ u16 saved_con;
+ u16 bus_mode;
+ unsigned int fclk_freq;
+ unsigned powered:1;
+
+ struct tasklet_struct cover_tasklet;
+ struct timer_list cover_timer;
+ unsigned cover_open;
+
+ struct mmc_request *mrq;
+ struct mmc_omap_host *host;
+ struct mmc_host *mmc;
+ struct omap_mmc_slot_data *pdata;
+};
+
+struct mmc_omap_host {
+ int initialized;
+ int suspended;
+ struct mmc_request * mrq;
+ struct mmc_command * cmd;
+ struct mmc_data * data;
+ struct mmc_host * mmc;
+ struct device * dev;
+ unsigned char id; /* 16xx chips have 2 MMC blocks */
+ struct clk * iclk;
+ struct clk * fclk;
+ struct resource *mem_res;
+ void __iomem *virt_base;
+ unsigned int phys_base;
+ int irq;
+ unsigned char bus_mode;
+ unsigned char hw_bus_mode;
+
+ struct work_struct cmd_abort_work;
+ unsigned abort:1;
+ struct timer_list cmd_abort_timer;
+
+ struct work_struct slot_release_work;
+ struct mmc_omap_slot *next_slot;
+ struct work_struct send_stop_work;
+ struct mmc_data *stop_data;
+
+ unsigned int sg_len;
+ int sg_idx;
+ u16 * buffer;
+ u32 buffer_bytes_left;
+ u32 total_bytes_left;
+
+ unsigned use_dma:1;
+ unsigned brs_received:1, dma_done:1;
+ unsigned dma_is_read:1;
+ unsigned dma_in_use:1;
+ int dma_ch;
+ spinlock_t dma_lock;
+ struct timer_list dma_timer;
+ unsigned dma_len;
+
+ short power_pin;
+
+ struct mmc_omap_slot *slots[OMAP_MMC_MAX_SLOTS];
+ struct mmc_omap_slot *current_slot;
+ spinlock_t slot_lock;
+ wait_queue_head_t slot_wq;
+ int nr_slots;
+
+ struct timer_list clk_timer;
+ spinlock_t clk_lock; /* for changing enabled state */
+ unsigned int fclk_enabled:1;
+
+ struct omap_mmc_platform_data *pdata;
+};
+
+static void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot)
+{
+ unsigned long tick_ns;
+
+ if (slot != NULL && slot->host->fclk_enabled && slot->fclk_freq > 0) {
+ tick_ns = (1000000000 + slot->fclk_freq - 1) / slot->fclk_freq;
+ ndelay(8 * tick_ns);
+ }
+}
+
+static void mmc_omap_fclk_enable(struct mmc_omap_host *host, unsigned int enable)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->clk_lock, flags);
+ if (host->fclk_enabled != enable) {
+ host->fclk_enabled = enable;
+ if (enable)
+ clk_enable(host->fclk);
+ else
+ clk_disable(host->fclk);
+ }
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+}
+
+static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed)
+{
+ struct mmc_omap_host *host = slot->host;
+ unsigned long flags;
+
+ if (claimed)
+ goto no_claim;
+ spin_lock_irqsave(&host->slot_lock, flags);
+ while (host->mmc != NULL) {
+ spin_unlock_irqrestore(&host->slot_lock, flags);
+ wait_event(host->slot_wq, host->mmc == NULL);
+ spin_lock_irqsave(&host->slot_lock, flags);
+ }
+ host->mmc = slot->mmc;
+ spin_unlock_irqrestore(&host->slot_lock, flags);
+no_claim:
+ del_timer(&host->clk_timer);
+ if (host->current_slot != slot || !claimed)
+ mmc_omap_fclk_offdelay(host->current_slot);
+
+ if (host->current_slot != slot) {
+ OMAP_MMC_WRITE(host, CON, slot->saved_con & 0xFC00);
+ if (host->pdata->switch_slot != NULL)
+ host->pdata->switch_slot(mmc_dev(slot->mmc), slot->id);
+ host->current_slot = slot;
+ }
+
+ if (claimed) {
+ mmc_omap_fclk_enable(host, 1);
+
+ /* Doing the dummy read here seems to work around some bug
+ * at least in OMAP24xx silicon where the command would not
+ * start after writing the CMD register. Sigh. */
+ OMAP_MMC_READ(host, CON);
+
+ OMAP_MMC_WRITE(host, CON, slot->saved_con);
+ } else
+ mmc_omap_fclk_enable(host, 0);
+}
+
+static void mmc_omap_start_request(struct mmc_omap_host *host,
+ struct mmc_request *req);
+
+static void mmc_omap_slot_release_work(struct work_struct *work)
+{
+ struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
+ slot_release_work);
+ struct mmc_omap_slot *next_slot = host->next_slot;
+ struct mmc_request *rq;
+
+ host->next_slot = NULL;
+ mmc_omap_select_slot(next_slot, 1);
+
+ rq = next_slot->mrq;
+ next_slot->mrq = NULL;
+ mmc_omap_start_request(host, rq);
+}
+
+static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled)
+{
+ struct mmc_omap_host *host = slot->host;
+ unsigned long flags;
+ int i;
+
+ BUG_ON(slot == NULL || host->mmc == NULL);
+
+ if (clk_enabled)
+ /* Keeps clock running for at least 8 cycles on valid freq */
+ mod_timer(&host->clk_timer, jiffies + HZ/10);
+ else {
+ del_timer(&host->clk_timer);
+ mmc_omap_fclk_offdelay(slot);
+ mmc_omap_fclk_enable(host, 0);
+ }
+
+ spin_lock_irqsave(&host->slot_lock, flags);
+ /* Check for any pending requests */
+ for (i = 0; i < host->nr_slots; i++) {
+ struct mmc_omap_slot *new_slot;
+
+ if (host->slots[i] == NULL || host->slots[i]->mrq == NULL)
+ continue;
+
+ BUG_ON(host->next_slot != NULL);
+ new_slot = host->slots[i];
+ /* The current slot should not have a request in queue */
+ BUG_ON(new_slot == host->current_slot);
+
+ host->next_slot = new_slot;
+ host->mmc = new_slot->mmc;
+ spin_unlock_irqrestore(&host->slot_lock, flags);
+ schedule_work(&host->slot_release_work);
+ return;
+ }
+
+ host->mmc = NULL;
+ wake_up(&host->slot_wq);
+ spin_unlock_irqrestore(&host->slot_lock, flags);
+}
+
+static inline
+int mmc_omap_cover_is_open(struct mmc_omap_slot *slot)
+{
+ if (slot->pdata->get_cover_state)
+ return slot->pdata->get_cover_state(mmc_dev(slot->mmc),
+ slot->id);
+ return 0;
+}
+
+static ssize_t
+mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
+ struct mmc_omap_slot *slot = mmc_priv(mmc);
+
+ return sprintf(buf, "%s\n", mmc_omap_cover_is_open(slot) ? "open" :
+ "closed");
+}
+
+static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL);
+
+static ssize_t
+mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
+ struct mmc_omap_slot *slot = mmc_priv(mmc);
+
+ return sprintf(buf, "%s\n", slot->pdata->name);
+}
+
+static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL);
+
+static void
+mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
+{
+ u32 cmdreg;
+ u32 resptype;
+ u32 cmdtype;
+
+ host->cmd = cmd;
+
+ resptype = 0;
+ cmdtype = 0;
+
+ /* Our hardware needs to know exact type */
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ break;
+ case MMC_RSP_R1:
+ case MMC_RSP_R1B:
+ /* resp 1, 1b, 6, 7 */
+ resptype = 1;
+ break;
+ case MMC_RSP_R2:
+ resptype = 2;
+ break;
+ case MMC_RSP_R3:
+ resptype = 3;
+ break;
+ default:
+ dev_err(mmc_dev(host->mmc), "Invalid response type: %04x\n", mmc_resp_type(cmd));
+ break;
+ }
+
+ if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) {
+ cmdtype = OMAP_MMC_CMDTYPE_ADTC;
+ } else if (mmc_cmd_type(cmd) == MMC_CMD_BC) {
+ cmdtype = OMAP_MMC_CMDTYPE_BC;
+ } else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) {
+ cmdtype = OMAP_MMC_CMDTYPE_BCR;
+ } else {
+ cmdtype = OMAP_MMC_CMDTYPE_AC;
+ }
+
+ cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12);
+
+ if (host->current_slot->bus_mode == MMC_BUSMODE_OPENDRAIN)
+ cmdreg |= 1 << 6;
+
+ if (cmd->flags & MMC_RSP_BUSY)
+ cmdreg |= 1 << 11;
+
+ if (host->data && !(host->data->flags & MMC_DATA_WRITE))
+ cmdreg |= 1 << 15;
+
+ mod_timer(&host->cmd_abort_timer, jiffies + HZ/2);
+
+ OMAP_MMC_WRITE(host, CTO, 200);
+ OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
+ OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16);
+ OMAP_MMC_WRITE(host, IE,
+ OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL |
+ OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT |
+ OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT |
+ OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR |
+ OMAP_MMC_STAT_END_OF_DATA);
+ OMAP_MMC_WRITE(host, CMD, cmdreg);
+}
+
+static void
+mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data,
+ int abort)
+{
+ enum dma_data_direction dma_data_dir;
+
+ BUG_ON(host->dma_ch < 0);
+ if (data->error)
+ omap_stop_dma(host->dma_ch);
+ /* Release DMA channel lazily */
+ mod_timer(&host->dma_timer, jiffies + HZ);
+ if (data->flags & MMC_DATA_WRITE)
+ dma_data_dir = DMA_TO_DEVICE;
+ else
+ dma_data_dir = DMA_FROM_DEVICE;
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
+ dma_data_dir);
+}
+
+static void mmc_omap_send_stop_work(struct work_struct *work)
+{
+ struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
+ send_stop_work);
+ struct mmc_omap_slot *slot = host->current_slot;
+ struct mmc_data *data = host->stop_data;
+ unsigned long tick_ns;
+
+ tick_ns = (1000000000 + slot->fclk_freq - 1)/slot->fclk_freq;
+ ndelay(8*tick_ns);
+
+ mmc_omap_start_command(host, data->stop);
+}
+
+static void
+mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ if (host->dma_in_use)
+ mmc_omap_release_dma(host, data, data->error);
+
+ host->data = NULL;
+ host->sg_len = 0;
+
+ /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing
+ * dozens of requests until the card finishes writing data.
+ * It'd be cheaper to just wait till an EOFB interrupt arrives...
+ */
+
+ if (!data->stop) {
+ struct mmc_host *mmc;
+
+ host->mrq = NULL;
+ mmc = host->mmc;
+ mmc_omap_release_slot(host->current_slot, 1);
+ mmc_request_done(mmc, data->mrq);
+ return;
+ }
+
+ host->stop_data = data;
+ schedule_work(&host->send_stop_work);
+}
+
+static void
+mmc_omap_send_abort(struct mmc_omap_host *host, int maxloops)
+{
+ struct mmc_omap_slot *slot = host->current_slot;
+ unsigned int restarts, passes, timeout;
+ u16 stat = 0;
+
+ /* Sending abort takes 80 clocks. Have some extra and round up */
+ timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq;
+ restarts = 0;
+ while (restarts < maxloops) {
+ OMAP_MMC_WRITE(host, STAT, 0xFFFF);
+ OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7));
+
+ passes = 0;
+ while (passes < timeout) {
+ stat = OMAP_MMC_READ(host, STAT);
+ if (stat & OMAP_MMC_STAT_END_OF_CMD)
+ goto out;
+ udelay(1);
+ passes++;
+ }
+
+ restarts++;
+ }
+out:
+ OMAP_MMC_WRITE(host, STAT, stat);
+}
+
+static void
+mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ if (host->dma_in_use)
+ mmc_omap_release_dma(host, data, 1);
+
+ host->data = NULL;
+ host->sg_len = 0;
+
+ mmc_omap_send_abort(host, 10000);
+}
+
+static void
+mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ unsigned long flags;
+ int done;
+
+ if (!host->dma_in_use) {
+ mmc_omap_xfer_done(host, data);
+ return;
+ }
+ done = 0;
+ spin_lock_irqsave(&host->dma_lock, flags);
+ if (host->dma_done)
+ done = 1;
+ else
+ host->brs_received = 1;
+ spin_unlock_irqrestore(&host->dma_lock, flags);
+ if (done)
+ mmc_omap_xfer_done(host, data);
+}
+
+static void
+mmc_omap_dma_timer(unsigned long data)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+
+ BUG_ON(host->dma_ch < 0);
+ omap_free_dma(host->dma_ch);
+ host->dma_ch = -1;
+}
+
+static void
+mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ unsigned long flags;
+ int done;
+
+ done = 0;
+ spin_lock_irqsave(&host->dma_lock, flags);
+ if (host->brs_received)
+ done = 1;
+ else
+ host->dma_done = 1;
+ spin_unlock_irqrestore(&host->dma_lock, flags);
+ if (done)
+ mmc_omap_xfer_done(host, data);
+}
+
+static void
+mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
+{
+ host->cmd = NULL;
+
+ del_timer(&host->cmd_abort_timer);
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
+ /* response type 2 */
+ cmd->resp[3] =
+ OMAP_MMC_READ(host, RSP0) |
+ (OMAP_MMC_READ(host, RSP1) << 16);
+ cmd->resp[2] =
+ OMAP_MMC_READ(host, RSP2) |
+ (OMAP_MMC_READ(host, RSP3) << 16);
+ cmd->resp[1] =
+ OMAP_MMC_READ(host, RSP4) |
+ (OMAP_MMC_READ(host, RSP5) << 16);
+ cmd->resp[0] =
+ OMAP_MMC_READ(host, RSP6) |
+ (OMAP_MMC_READ(host, RSP7) << 16);
+ } else {
+ /* response types 1, 1b, 3, 4, 5, 6 */
+ cmd->resp[0] =
+ OMAP_MMC_READ(host, RSP6) |
+ (OMAP_MMC_READ(host, RSP7) << 16);
+ }
+ }
+
+ if (host->data == NULL || cmd->error) {
+ struct mmc_host *mmc;
+
+ if (host->data != NULL)
+ mmc_omap_abort_xfer(host, host->data);
+ host->mrq = NULL;
+ mmc = host->mmc;
+ mmc_omap_release_slot(host->current_slot, 1);
+ mmc_request_done(mmc, cmd->mrq);
+ }
+}
+
+/*
+ * Abort stuck command. Can occur when card is removed while it is being
+ * read.
+ */
+static void mmc_omap_abort_command(struct work_struct *work)
+{
+ struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
+ cmd_abort_work);
+ BUG_ON(!host->cmd);
+
+ dev_dbg(mmc_dev(host->mmc), "Aborting stuck command CMD%d\n",
+ host->cmd->opcode);
+
+ if (host->cmd->error == 0)
+ host->cmd->error = -ETIMEDOUT;
+
+ if (host->data == NULL) {
+ struct mmc_command *cmd;
+ struct mmc_host *mmc;
+
+ cmd = host->cmd;
+ host->cmd = NULL;
+ mmc_omap_send_abort(host, 10000);
+
+ host->mrq = NULL;
+ mmc = host->mmc;
+ mmc_omap_release_slot(host->current_slot, 1);
+ mmc_request_done(mmc, cmd->mrq);
+ } else
+ mmc_omap_cmd_done(host, host->cmd);
+
+ host->abort = 0;
+ enable_irq(host->irq);
+}
+
+static void
+mmc_omap_cmd_timer(unsigned long data)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->slot_lock, flags);
+ if (host->cmd != NULL && !host->abort) {
+ OMAP_MMC_WRITE(host, IE, 0);
+ disable_irq(host->irq);
+ host->abort = 1;
+ schedule_work(&host->cmd_abort_work);
+ }
+ spin_unlock_irqrestore(&host->slot_lock, flags);
+}
+
+/* PIO only */
+static void
+mmc_omap_sg_to_buf(struct mmc_omap_host *host)
+{
+ struct scatterlist *sg;
+
+ sg = host->data->sg + host->sg_idx;
+ host->buffer_bytes_left = sg->length;
+ host->buffer = sg_virt(sg);
+ if (host->buffer_bytes_left > host->total_bytes_left)
+ host->buffer_bytes_left = host->total_bytes_left;
+}
+
+static void
+mmc_omap_clk_timer(unsigned long data)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+
+ mmc_omap_fclk_enable(host, 0);
+}
+
+/* PIO only */
+static void
+mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
+{
+ int n;
+
+ if (host->buffer_bytes_left == 0) {
+ host->sg_idx++;
+ BUG_ON(host->sg_idx == host->sg_len);
+ mmc_omap_sg_to_buf(host);
+ }
+ n = 64;
+ if (n > host->buffer_bytes_left)
+ n = host->buffer_bytes_left;
+ host->buffer_bytes_left -= n;
+ host->total_bytes_left -= n;
+ host->data->bytes_xfered += n;
+
+ if (write) {
+ __raw_writesw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n);
+ } else {
+ __raw_readsw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n);
+ }
+}
+
+static inline void mmc_omap_report_irq(u16 status)
+{
+ static const char *mmc_omap_status_bits[] = {
+ "EOC", "CD", "CB", "BRS", "EOFB", "DTO", "DCRC", "CTO",
+ "CCRC", "CRW", "AF", "AE", "OCRB", "CIRQ", "CERR"
+ };
+ int i, c = 0;
+
+ for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++)
+ if (status & (1 << i)) {
+ if (c)
+ printk(" ");
+ printk("%s", mmc_omap_status_bits[i]);
+ c++;
+ }
+}
+
+static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
+{
+ struct mmc_omap_host * host = (struct mmc_omap_host *)dev_id;
+ u16 status;
+ int end_command;
+ int end_transfer;
+ int transfer_error, cmd_error;
+
+ if (host->cmd == NULL && host->data == NULL) {
+ status = OMAP_MMC_READ(host, STAT);
+ dev_info(mmc_dev(host->slots[0]->mmc),
+ "Spurious IRQ 0x%04x\n", status);
+ if (status != 0) {
+ OMAP_MMC_WRITE(host, STAT, status);
+ OMAP_MMC_WRITE(host, IE, 0);
+ }
+ return IRQ_HANDLED;
+ }
+
+ end_command = 0;
+ end_transfer = 0;
+ transfer_error = 0;
+ cmd_error = 0;
+
+ while ((status = OMAP_MMC_READ(host, STAT)) != 0) {
+ int cmd;
+
+ OMAP_MMC_WRITE(host, STAT, status);
+ if (host->cmd != NULL)
+ cmd = host->cmd->opcode;
+ else
+ cmd = -1;
+#ifdef CONFIG_MMC_DEBUG
+ dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ",
+ status, cmd);
+ mmc_omap_report_irq(status);
+ printk("\n");
+#endif
+ if (host->total_bytes_left) {
+ if ((status & OMAP_MMC_STAT_A_FULL) ||
+ (status & OMAP_MMC_STAT_END_OF_DATA))
+ mmc_omap_xfer_data(host, 0);
+ if (status & OMAP_MMC_STAT_A_EMPTY)
+ mmc_omap_xfer_data(host, 1);
+ }
+
+ if (status & OMAP_MMC_STAT_END_OF_DATA)
+ end_transfer = 1;
+
+ if (status & OMAP_MMC_STAT_DATA_TOUT) {
+ dev_dbg(mmc_dev(host->mmc), "data timeout (CMD%d)\n",
+ cmd);
+ if (host->data) {
+ host->data->error = -ETIMEDOUT;
+ transfer_error = 1;
+ }
+ }
+
+ if (status & OMAP_MMC_STAT_DATA_CRC) {
+ if (host->data) {
+ host->data->error = -EILSEQ;
+ dev_dbg(mmc_dev(host->mmc),
+ "data CRC error, bytes left %d\n",
+ host->total_bytes_left);
+ transfer_error = 1;
+ } else {
+ dev_dbg(mmc_dev(host->mmc), "data CRC error\n");
+ }
+ }
+
+ if (status & OMAP_MMC_STAT_CMD_TOUT) {
+ /* Timeouts are routine with some commands */
+ if (host->cmd) {
+ struct mmc_omap_slot *slot =
+ host->current_slot;
+ if (slot == NULL ||
+ !mmc_omap_cover_is_open(slot))
+ dev_err(mmc_dev(host->mmc),
+ "command timeout (CMD%d)\n",
+ cmd);
+ host->cmd->error = -ETIMEDOUT;
+ end_command = 1;
+ cmd_error = 1;
+ }
+ }
+
+ if (status & OMAP_MMC_STAT_CMD_CRC) {
+ if (host->cmd) {
+ dev_err(mmc_dev(host->mmc),
+ "command CRC error (CMD%d, arg 0x%08x)\n",
+ cmd, host->cmd->arg);
+ host->cmd->error = -EILSEQ;
+ end_command = 1;
+ cmd_error = 1;
+ } else
+ dev_err(mmc_dev(host->mmc),
+ "command CRC error without cmd?\n");
+ }
+
+ if (status & OMAP_MMC_STAT_CARD_ERR) {
+ dev_dbg(mmc_dev(host->mmc),
+ "ignoring card status error (CMD%d)\n",
+ cmd);
+ end_command = 1;
+ }
+
+ /*
+ * NOTE: On 1610 the END_OF_CMD may come too early when
+ * starting a write
+ */
+ if ((status & OMAP_MMC_STAT_END_OF_CMD) &&
+ (!(status & OMAP_MMC_STAT_A_EMPTY))) {
+ end_command = 1;
+ }
+ }
+
+ if (cmd_error && host->data) {
+ del_timer(&host->cmd_abort_timer);
+ host->abort = 1;
+ OMAP_MMC_WRITE(host, IE, 0);
+ disable_irq(host->irq);
+ schedule_work(&host->cmd_abort_work);
+ return IRQ_HANDLED;
+ }
+
+ if (end_command)
+ mmc_omap_cmd_done(host, host->cmd);
+ if (host->data != NULL) {
+ if (transfer_error)
+ mmc_omap_xfer_done(host, host->data);
+ else if (end_transfer)
+ mmc_omap_end_of_data(host, host->data);
+ }
+
+ return IRQ_HANDLED;
+}
+
+void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed)
+{
+ int cover_open;
+ struct mmc_omap_host *host = dev_get_drvdata(dev);
+ struct mmc_omap_slot *slot = host->slots[num];
+
+ BUG_ON(num >= host->nr_slots);
+
+ /* Other subsystems can call in here before we're initialised. */
+ if (host->nr_slots == 0 || !host->slots[num])
+ return;
+
+ cover_open = mmc_omap_cover_is_open(slot);
+ if (cover_open != slot->cover_open) {
+ slot->cover_open = cover_open;
+ sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch");
+ }
+
+ tasklet_hi_schedule(&slot->cover_tasklet);
+}
+
+static void mmc_omap_cover_timer(unsigned long arg)
+{
+ struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg;
+ tasklet_schedule(&slot->cover_tasklet);
+}
+
+static void mmc_omap_cover_handler(unsigned long param)
+{
+ struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param;
+ int cover_open = mmc_omap_cover_is_open(slot);
+
+ mmc_detect_change(slot->mmc, 0);
+ if (!cover_open)
+ return;
+
+ /*
+ * If no card is inserted, we postpone polling until
+ * the cover has been closed.
+ */
+ if (slot->mmc->card == NULL || !mmc_card_present(slot->mmc->card))
+ return;
+
+ mod_timer(&slot->cover_timer,
+ jiffies + msecs_to_jiffies(OMAP_MMC_COVER_POLL_DELAY));
+}
+
+/* Prepare to transfer the next segment of a scatterlist */
+static void
+mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ int dma_ch = host->dma_ch;
+ unsigned long data_addr;
+ u16 buf, frame;
+ u32 count;
+ struct scatterlist *sg = &data->sg[host->sg_idx];
+ int src_port = 0;
+ int dst_port = 0;
+ int sync_dev = 0;
+
+ data_addr = host->phys_base + OMAP_MMC_REG_DATA;
+ frame = data->blksz;
+ count = sg_dma_len(sg);
+
+ if ((data->blocks == 1) && (count > data->blksz))
+ count = frame;
+
+ host->dma_len = count;
+
+ /* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx.
+ * Use 16 or 32 word frames when the blocksize is at least that large.
+ * Blocksize is usually 512 bytes; but not for some SD reads.
+ */
+ if (cpu_is_omap15xx() && frame > 32)
+ frame = 32;
+ else if (frame > 64)
+ frame = 64;
+ count /= frame;
+ frame >>= 1;
+
+ if (!(data->flags & MMC_DATA_WRITE)) {
+ buf = 0x800f | ((frame - 1) << 8);
+
+ if (cpu_class_is_omap1()) {
+ src_port = OMAP_DMA_PORT_TIPB;
+ dst_port = OMAP_DMA_PORT_EMIFF;
+ }
+ if (cpu_is_omap24xx())
+ sync_dev = OMAP24XX_DMA_MMC1_RX;
+
+ omap_set_dma_src_params(dma_ch, src_port,
+ OMAP_DMA_AMODE_CONSTANT,
+ data_addr, 0, 0);
+ omap_set_dma_dest_params(dma_ch, dst_port,
+ OMAP_DMA_AMODE_POST_INC,
+ sg_dma_address(sg), 0, 0);
+ omap_set_dma_dest_data_pack(dma_ch, 1);
+ omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
+ } else {
+ buf = 0x0f80 | ((frame - 1) << 0);
+
+ if (cpu_class_is_omap1()) {
+ src_port = OMAP_DMA_PORT_EMIFF;
+ dst_port = OMAP_DMA_PORT_TIPB;
+ }
+ if (cpu_is_omap24xx())
+ sync_dev = OMAP24XX_DMA_MMC1_TX;
+
+ omap_set_dma_dest_params(dma_ch, dst_port,
+ OMAP_DMA_AMODE_CONSTANT,
+ data_addr, 0, 0);
+ omap_set_dma_src_params(dma_ch, src_port,
+ OMAP_DMA_AMODE_POST_INC,
+ sg_dma_address(sg), 0, 0);
+ omap_set_dma_src_data_pack(dma_ch, 1);
+ omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
+ }
+
+ /* Max limit for DMA frame count is 0xffff */
+ BUG_ON(count > 0xffff);
+
+ OMAP_MMC_WRITE(host, BUF, buf);
+ omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16,
+ frame, count, OMAP_DMA_SYNC_FRAME,
+ sync_dev, 0);
+}
+
+/* A scatterlist segment completed */
+static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+ struct mmc_data *mmcdat = host->data;
+
+ if (unlikely(host->dma_ch < 0)) {
+ dev_err(mmc_dev(host->mmc),
+ "DMA callback while DMA not enabled\n");
+ return;
+ }
+ /* FIXME: We really should do something to _handle_ the errors */
+ if (ch_status & OMAP1_DMA_TOUT_IRQ) {
+ dev_err(mmc_dev(host->mmc),"DMA timeout\n");
+ return;
+ }
+ if (ch_status & OMAP_DMA_DROP_IRQ) {
+ dev_err(mmc_dev(host->mmc), "DMA sync error\n");
+ return;
+ }
+ if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
+ return;
+ }
+ mmcdat->bytes_xfered += host->dma_len;
+ host->sg_idx++;
+ if (host->sg_idx < host->sg_len) {
+ mmc_omap_prepare_dma(host, host->data);
+ omap_start_dma(host->dma_ch);
+ } else
+ mmc_omap_dma_done(host, host->data);
+}
+
+static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ const char *dma_dev_name;
+ int sync_dev, dma_ch, is_read, r;
+
+ is_read = !(data->flags & MMC_DATA_WRITE);
+ del_timer_sync(&host->dma_timer);
+ if (host->dma_ch >= 0) {
+ if (is_read == host->dma_is_read)
+ return 0;
+ omap_free_dma(host->dma_ch);
+ host->dma_ch = -1;
+ }
+
+ if (is_read) {
+ if (host->id == 1) {
+ sync_dev = OMAP_DMA_MMC_RX;
+ dma_dev_name = "MMC1 read";
+ } else {
+ sync_dev = OMAP_DMA_MMC2_RX;
+ dma_dev_name = "MMC2 read";
+ }
+ } else {
+ if (host->id == 1) {
+ sync_dev = OMAP_DMA_MMC_TX;
+ dma_dev_name = "MMC1 write";
+ } else {
+ sync_dev = OMAP_DMA_MMC2_TX;
+ dma_dev_name = "MMC2 write";
+ }
+ }
+ r = omap_request_dma(sync_dev, dma_dev_name, mmc_omap_dma_cb,
+ host, &dma_ch);
+ if (r != 0) {
+ dev_dbg(mmc_dev(host->mmc), "omap_request_dma() failed with %d\n", r);
+ return r;
+ }
+ host->dma_ch = dma_ch;
+ host->dma_is_read = is_read;
+
+ return 0;
+}
+
+static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req)
+{
+ u16 reg;
+
+ reg = OMAP_MMC_READ(host, SDIO);
+ reg &= ~(1 << 5);
+ OMAP_MMC_WRITE(host, SDIO, reg);
+ /* Set maximum timeout */
+ OMAP_MMC_WRITE(host, CTO, 0xff);
+}
+
+static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req)
+{
+ unsigned int timeout, cycle_ns;
+ u16 reg;
+
+ cycle_ns = 1000000000 / host->current_slot->fclk_freq;
+ timeout = req->data->timeout_ns / cycle_ns;
+ timeout += req->data->timeout_clks;
+
+ /* Check if we need to use timeout multiplier register */
+ reg = OMAP_MMC_READ(host, SDIO);
+ if (timeout > 0xffff) {
+ reg |= (1 << 5);
+ timeout /= 1024;
+ } else
+ reg &= ~(1 << 5);
+ OMAP_MMC_WRITE(host, SDIO, reg);
+ OMAP_MMC_WRITE(host, DTO, timeout);
+}
+
+static void
+mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
+{
+ struct mmc_data *data = req->data;
+ int i, use_dma, block_size;
+ unsigned sg_len;
+
+ host->data = data;
+ if (data == NULL) {
+ OMAP_MMC_WRITE(host, BLEN, 0);
+ OMAP_MMC_WRITE(host, NBLK, 0);
+ OMAP_MMC_WRITE(host, BUF, 0);
+ host->dma_in_use = 0;
+ set_cmd_timeout(host, req);
+ return;
+ }
+
+ block_size = data->blksz;
+
+ OMAP_MMC_WRITE(host, NBLK, data->blocks - 1);
+ OMAP_MMC_WRITE(host, BLEN, block_size - 1);
+ set_data_timeout(host, req);
+
+ /* cope with calling layer confusion; it issues "single
+ * block" writes using multi-block scatterlists.
+ */
+ sg_len = (data->blocks == 1) ? 1 : data->sg_len;
+
+ /* Only do DMA for entire blocks */
+ use_dma = host->use_dma;
+ if (use_dma) {
+ for (i = 0; i < sg_len; i++) {
+ if ((data->sg[i].length % block_size) != 0) {
+ use_dma = 0;
+ break;
+ }
+ }
+ }
+
+ host->sg_idx = 0;
+ if (use_dma) {
+ if (mmc_omap_get_dma_channel(host, data) == 0) {
+ enum dma_data_direction dma_data_dir;
+
+ if (data->flags & MMC_DATA_WRITE)
+ dma_data_dir = DMA_TO_DEVICE;
+ else
+ dma_data_dir = DMA_FROM_DEVICE;
+
+ host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ sg_len, dma_data_dir);
+ host->total_bytes_left = 0;
+ mmc_omap_prepare_dma(host, req->data);
+ host->brs_received = 0;
+ host->dma_done = 0;
+ host->dma_in_use = 1;
+ } else
+ use_dma = 0;
+ }
+
+ /* Revert to PIO? */
+ if (!use_dma) {
+ OMAP_MMC_WRITE(host, BUF, 0x1f1f);
+ host->total_bytes_left = data->blocks * block_size;
+ host->sg_len = sg_len;
+ mmc_omap_sg_to_buf(host);
+ host->dma_in_use = 0;
+ }
+}
+
+static void mmc_omap_start_request(struct mmc_omap_host *host,
+ struct mmc_request *req)
+{
+ BUG_ON(host->mrq != NULL);
+
+ host->mrq = req;
+
+ /* only touch fifo AFTER the controller readies it */
+ mmc_omap_prepare_data(host, req);
+ mmc_omap_start_command(host, req->cmd);
+ if (host->dma_in_use)
+ omap_start_dma(host->dma_ch);
+ BUG_ON(irqs_disabled());
+}
+
+static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+ struct mmc_omap_slot *slot = mmc_priv(mmc);
+ struct mmc_omap_host *host = slot->host;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->slot_lock, flags);
+ if (host->mmc != NULL) {
+ BUG_ON(slot->mrq != NULL);
+ slot->mrq = req;
+ spin_unlock_irqrestore(&host->slot_lock, flags);
+ return;
+ } else
+ host->mmc = mmc;
+ spin_unlock_irqrestore(&host->slot_lock, flags);
+ mmc_omap_select_slot(slot, 1);
+ mmc_omap_start_request(host, req);
+}
+
+static void mmc_omap_set_power(struct mmc_omap_slot *slot, int power_on,
+ int vdd)
+{
+ struct mmc_omap_host *host;
+
+ host = slot->host;
+
+ if (slot->pdata->set_power != NULL)
+ slot->pdata->set_power(mmc_dev(slot->mmc), slot->id, power_on,
+ vdd);
+
+ if (cpu_is_omap24xx()) {
+ u16 w;
+
+ if (power_on) {
+ w = OMAP_MMC_READ(host, CON);
+ OMAP_MMC_WRITE(host, CON, w | (1 << 11));
+ } else {
+ w = OMAP_MMC_READ(host, CON);
+ OMAP_MMC_WRITE(host, CON, w & ~(1 << 11));
+ }
+ }
+}
+
+static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct mmc_omap_slot *slot = mmc_priv(mmc);
+ struct mmc_omap_host *host = slot->host;
+ int func_clk_rate = clk_get_rate(host->fclk);
+ int dsor;
+
+ if (ios->clock == 0)
+ return 0;
+
+ dsor = func_clk_rate / ios->clock;
+ if (dsor < 1)
+ dsor = 1;
+
+ if (func_clk_rate / dsor > ios->clock)
+ dsor++;
+
+ if (dsor > 250)
+ dsor = 250;
+
+ slot->fclk_freq = func_clk_rate / dsor;
+
+ if (ios->bus_width == MMC_BUS_WIDTH_4)
+ dsor |= 1 << 15;
+
+ return dsor;
+}
+
+static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct mmc_omap_slot *slot = mmc_priv(mmc);
+ struct mmc_omap_host *host = slot->host;
+ int i, dsor;
+ int clk_enabled;
+
+ mmc_omap_select_slot(slot, 0);
+
+ dsor = mmc_omap_calc_divisor(mmc, ios);
+
+ if (ios->vdd != slot->vdd)
+ slot->vdd = ios->vdd;
+
+ clk_enabled = 0;
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ mmc_omap_set_power(slot, 0, ios->vdd);
+ break;
+ case MMC_POWER_UP:
+ /* Cannot touch dsor yet, just power up MMC */
+ mmc_omap_set_power(slot, 1, ios->vdd);
+ goto exit;
+ case MMC_POWER_ON:
+ mmc_omap_fclk_enable(host, 1);
+ clk_enabled = 1;
+ dsor |= 1 << 11;
+ break;
+ }
+
+ if (slot->bus_mode != ios->bus_mode) {
+ if (slot->pdata->set_bus_mode != NULL)
+ slot->pdata->set_bus_mode(mmc_dev(mmc), slot->id,
+ ios->bus_mode);
+ slot->bus_mode = ios->bus_mode;
+ }
+
+ /* On insanely high arm_per frequencies something sometimes
+ * goes somehow out of sync, and the POW bit is not being set,
+ * which results in the while loop below getting stuck.
+ * Writing to the CON register twice seems to do the trick. */
+ for (i = 0; i < 2; i++)
+ OMAP_MMC_WRITE(host, CON, dsor);
+ slot->saved_con = dsor;
+ if (ios->power_mode == MMC_POWER_ON) {
+ /* worst case at 400kHz, 80 cycles makes 200 microsecs */
+ int usecs = 250;
+
+ /* Send clock cycles, poll completion */
+ OMAP_MMC_WRITE(host, IE, 0);
+ OMAP_MMC_WRITE(host, STAT, 0xffff);
+ OMAP_MMC_WRITE(host, CMD, 1 << 7);
+ while (usecs > 0 && (OMAP_MMC_READ(host, STAT) & 1) == 0) {
+ udelay(1);
+ usecs--;
+ }
+ OMAP_MMC_WRITE(host, STAT, 1);
+ }
+
+exit:
+ mmc_omap_release_slot(slot, clk_enabled);
+}
+
+static const struct mmc_host_ops mmc_omap_ops = {
+ .request = mmc_omap_request,
+ .set_ios = mmc_omap_set_ios,
+};
+
+static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)
+{
+ struct mmc_omap_slot *slot = NULL;
+ struct mmc_host *mmc;
+ int r;
+
+ mmc = mmc_alloc_host(sizeof(struct mmc_omap_slot), host->dev);
+ if (mmc == NULL)
+ return -ENOMEM;
+
+ slot = mmc_priv(mmc);
+ slot->host = host;
+ slot->mmc = mmc;
+ slot->id = id;
+ slot->pdata = &host->pdata->slots[id];
+
+ host->slots[id] = slot;
+
+ mmc->caps = 0;
+ if (host->pdata->conf.wire4)
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+ mmc->ops = &mmc_omap_ops;
+ mmc->f_min = 400000;
+
+ if (cpu_class_is_omap2())
+ mmc->f_max = 48000000;
+ else
+ mmc->f_max = 24000000;
+ if (host->pdata->max_freq)
+ mmc->f_max = min(host->pdata->max_freq, mmc->f_max);
+ mmc->ocr_avail = slot->pdata->ocr_mask;
+
+ /* Use scatterlist DMA to reduce per-transfer costs.
+ * NOTE max_seg_size assumption that small blocks aren't
+ * normally used (except e.g. for reading SD registers).
+ */
+ mmc->max_phys_segs = 32;
+ mmc->max_hw_segs = 32;
+ mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */
+ mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */
+ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
+ mmc->max_seg_size = mmc->max_req_size;
+
+ r = mmc_add_host(mmc);
+ if (r < 0)
+ goto err_remove_host;
+
+ if (slot->pdata->name != NULL) {
+ r = device_create_file(&mmc->class_dev,
+ &dev_attr_slot_name);
+ if (r < 0)
+ goto err_remove_host;
+ }
+
+ if (slot->pdata->get_cover_state != NULL) {
+ r = device_create_file(&mmc->class_dev,
+ &dev_attr_cover_switch);
+ if (r < 0)
+ goto err_remove_slot_name;
+
+ setup_timer(&slot->cover_timer, mmc_omap_cover_timer,
+ (unsigned long)slot);
+ tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
+ (unsigned long)slot);
+ tasklet_schedule(&slot->cover_tasklet);
+ }
+
+ return 0;
+
+err_remove_slot_name:
+ if (slot->pdata->name != NULL)
+ device_remove_file(&mmc->class_dev, &dev_attr_slot_name);
+err_remove_host:
+ mmc_remove_host(mmc);
+ mmc_free_host(mmc);
+ return r;
+}
+
+static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
+{
+ struct mmc_host *mmc = slot->mmc;
+
+ if (slot->pdata->name != NULL)
+ device_remove_file(&mmc->class_dev, &dev_attr_slot_name);
+ if (slot->pdata->get_cover_state != NULL)
+ device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
+
+ tasklet_kill(&slot->cover_tasklet);
+ del_timer_sync(&slot->cover_timer);
+ flush_scheduled_work();
+
+ mmc_remove_host(mmc);
+ mmc_free_host(mmc);
+}
+
+static int __init mmc_omap_probe(struct platform_device *pdev)
+{
+ struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
+ struct mmc_omap_host *host = NULL;
+ struct resource *res;
+ int i, ret = 0;
+ int irq;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "platform data missing\n");
+ return -ENXIO;
+ }
+ if (pdata->nr_slots == 0) {
+ dev_err(&pdev->dev, "no slots\n");
+ return -ENXIO;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (res == NULL || irq < 0)
+ return -ENXIO;
+
+ res = request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name);
+ if (res == NULL)
+ return -EBUSY;
+
+ host = kzalloc(sizeof(struct mmc_omap_host), GFP_KERNEL);
+ if (host == NULL) {
+ ret = -ENOMEM;
+ goto err_free_mem_region;
+ }
+
+ INIT_WORK(&host->slot_release_work, mmc_omap_slot_release_work);
+ INIT_WORK(&host->send_stop_work, mmc_omap_send_stop_work);
+
+ INIT_WORK(&host->cmd_abort_work, mmc_omap_abort_command);
+ setup_timer(&host->cmd_abort_timer, mmc_omap_cmd_timer,
+ (unsigned long) host);
+
+ spin_lock_init(&host->clk_lock);
+ setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host);
+
+ spin_lock_init(&host->dma_lock);
+ setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host);
+ spin_lock_init(&host->slot_lock);
+ init_waitqueue_head(&host->slot_wq);
+
+ host->pdata = pdata;
+ host->dev = &pdev->dev;
+ platform_set_drvdata(pdev, host);
+
+ host->id = pdev->id;
+ host->mem_res = res;
+ host->irq = irq;
+
+ host->use_dma = 1;
+ host->dma_ch = -1;
+
+ host->irq = irq;
+ host->phys_base = host->mem_res->start;
+ host->virt_base = ioremap(res->start, res->end - res->start + 1);
+ if (!host->virt_base)
+ goto err_ioremap;
+
+ if (cpu_is_omap24xx()) {
+ host->iclk = clk_get(&pdev->dev, "mmc_ick");
+ if (IS_ERR(host->iclk))
+ goto err_free_mmc_host;
+ clk_enable(host->iclk);
+ }
+
+ if (!cpu_is_omap24xx())
+ host->fclk = clk_get(&pdev->dev, "mmc_ck");
+ else
+ host->fclk = clk_get(&pdev->dev, "mmc_fck");
+
+ if (IS_ERR(host->fclk)) {
+ ret = PTR_ERR(host->fclk);
+ goto err_free_iclk;
+ }
+
+ ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
+ if (ret)
+ goto err_free_fclk;
+
+ if (pdata->init != NULL) {
+ ret = pdata->init(&pdev->dev);
+ if (ret < 0)
+ goto err_free_irq;
+ }
+
+ host->nr_slots = pdata->nr_slots;
+ for (i = 0; i < pdata->nr_slots; i++) {
+ ret = mmc_omap_new_slot(host, i);
+ if (ret < 0) {
+ while (--i >= 0)
+ mmc_omap_remove_slot(host->slots[i]);
+
+ goto err_plat_cleanup;
+ }
+ }
+
+ return 0;
+
+err_plat_cleanup:
+ if (pdata->cleanup)
+ pdata->cleanup(&pdev->dev);
+err_free_irq:
+ free_irq(host->irq, host);
+err_free_fclk:
+ clk_put(host->fclk);
+err_free_iclk:
+ if (host->iclk != NULL) {
+ clk_disable(host->iclk);
+ clk_put(host->iclk);
+ }
+err_free_mmc_host:
+ iounmap(host->virt_base);
+err_ioremap:
+ kfree(host);
+err_free_mem_region:
+ release_mem_region(res->start, res->end - res->start + 1);
+ return ret;
+}
+
+static int mmc_omap_remove(struct platform_device *pdev)
+{
+ struct mmc_omap_host *host = platform_get_drvdata(pdev);
+ int i;
+
+ platform_set_drvdata(pdev, NULL);
+
+ BUG_ON(host == NULL);
+
+ for (i = 0; i < host->nr_slots; i++)
+ mmc_omap_remove_slot(host->slots[i]);
+
+ if (host->pdata->cleanup)
+ host->pdata->cleanup(&pdev->dev);
+
+ if (host->iclk && !IS_ERR(host->iclk))
+ clk_put(host->iclk);
+ if (host->fclk && !IS_ERR(host->fclk))
+ clk_put(host->fclk);
+
+ iounmap(host->virt_base);
+ release_mem_region(pdev->resource[0].start,
+ pdev->resource[0].end - pdev->resource[0].start + 1);
+
+ kfree(host);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ int i, ret = 0;
+ struct mmc_omap_host *host = platform_get_drvdata(pdev);
+
+ if (host == NULL || host->suspended)
+ return 0;
+
+ for (i = 0; i < host->nr_slots; i++) {
+ struct mmc_omap_slot *slot;
+
+ slot = host->slots[i];
+ ret = mmc_suspend_host(slot->mmc, mesg);
+ if (ret < 0) {
+ while (--i >= 0) {
+ slot = host->slots[i];
+ mmc_resume_host(slot->mmc);
+ }
+ return ret;
+ }
+ }
+ host->suspended = 1;
+ return 0;
+}
+
+static int mmc_omap_resume(struct platform_device *pdev)
+{
+ int i, ret = 0;
+ struct mmc_omap_host *host = platform_get_drvdata(pdev);
+
+ if (host == NULL || !host->suspended)
+ return 0;
+
+ for (i = 0; i < host->nr_slots; i++) {
+ struct mmc_omap_slot *slot;
+ slot = host->slots[i];
+ ret = mmc_resume_host(slot->mmc);
+ if (ret < 0)
+ return ret;
+
+ host->suspended = 0;
+ }
+ return 0;
+}
+#else
+#define mmc_omap_suspend NULL
+#define mmc_omap_resume NULL
+#endif
+
+static struct platform_driver mmc_omap_driver = {
+ .probe = mmc_omap_probe,
+ .remove = mmc_omap_remove,
+ .suspend = mmc_omap_suspend,
+ .resume = mmc_omap_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mmc_omap_init(void)
+{
+ return platform_driver_register(&mmc_omap_driver);
+}
+
+static void __exit mmc_omap_exit(void)
+{
+ platform_driver_unregister(&mmc_omap_driver);
+}
+
+module_init(mmc_omap_init);
+module_exit(mmc_omap_exit);
+
+MODULE_DESCRIPTION("OMAP Multimedia Card driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Juha Yrjölä");
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
new file mode 100644
index 0000000..ebfaa99
--- /dev/null
+++ b/drivers/mmc/host/pxamci.c
@@ -0,0 +1,731 @@
+/*
+ * linux/drivers/mmc/host/pxa.c - PXA MMCI driver
+ *
+ * Copyright (C) 2003 Russell King, 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.
+ *
+ * This hardware is really sick:
+ * - No way to clear interrupts.
+ * - Have to turn off the clock whenever we touch the device.
+ * - Doesn't tell you how many data blocks were transferred.
+ * Yuck!
+ *
+ * 1 and 3 byte data transfers not supported
+ * max block length up to 1023
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mmc/host.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+
+#include <mach/pxa-regs.h>
+#include <mach/mmc.h>
+
+#include "pxamci.h"
+
+#define DRIVER_NAME "pxa2xx-mci"
+
+#define NR_SG 1
+#define CLKRT_OFF (~0)
+
+struct pxamci_host {
+ struct mmc_host *mmc;
+ spinlock_t lock;
+ struct resource *res;
+ void __iomem *base;
+ struct clk *clk;
+ unsigned long clkrate;
+ int irq;
+ int dma;
+ unsigned int clkrt;
+ unsigned int cmdat;
+ unsigned int imask;
+ unsigned int power_mode;
+ struct pxamci_platform_data *pdata;
+
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+
+ dma_addr_t sg_dma;
+ struct pxa_dma_desc *sg_cpu;
+ unsigned int dma_len;
+
+ unsigned int dma_dir;
+ unsigned int dma_drcmrrx;
+ unsigned int dma_drcmrtx;
+};
+
+static void pxamci_stop_clock(struct pxamci_host *host)
+{
+ if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
+ unsigned long timeout = 10000;
+ unsigned int v;
+
+ writel(STOP_CLOCK, host->base + MMC_STRPCL);
+
+ do {
+ v = readl(host->base + MMC_STAT);
+ if (!(v & STAT_CLK_EN))
+ break;
+ udelay(1);
+ } while (timeout--);
+
+ if (v & STAT_CLK_EN)
+ dev_err(mmc_dev(host->mmc), "unable to stop clock\n");
+ }
+}
+
+static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->imask &= ~mask;
+ writel(host->imask, host->base + MMC_I_MASK);
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->imask |= mask;
+ writel(host->imask, host->base + MMC_I_MASK);
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
+{
+ unsigned int nob = data->blocks;
+ unsigned long long clks;
+ unsigned int timeout;
+ bool dalgn = 0;
+ u32 dcmd;
+ int i;
+
+ host->data = data;
+
+ if (data->flags & MMC_DATA_STREAM)
+ nob = 0xffff;
+
+ writel(nob, host->base + MMC_NOB);
+ writel(data->blksz, host->base + MMC_BLKLEN);
+
+ clks = (unsigned long long)data->timeout_ns * host->clkrate;
+ do_div(clks, 1000000000UL);
+ timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt);
+ writel((timeout + 255) / 256, host->base + MMC_RDTO);
+
+ if (data->flags & MMC_DATA_READ) {
+ host->dma_dir = DMA_FROM_DEVICE;
+ dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG;
+ DRCMR(host->dma_drcmrtx) = 0;
+ DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD;
+ } else {
+ host->dma_dir = DMA_TO_DEVICE;
+ dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC;
+ DRCMR(host->dma_drcmrrx) = 0;
+ DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD;
+ }
+
+ dcmd |= DCMD_BURST32 | DCMD_WIDTH1;
+
+ host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ host->dma_dir);
+
+ for (i = 0; i < host->dma_len; i++) {
+ unsigned int length = sg_dma_len(&data->sg[i]);
+ host->sg_cpu[i].dcmd = dcmd | length;
+ if (length & 31 && !(data->flags & MMC_DATA_READ))
+ host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN;
+ /* Not aligned to 8-byte boundary? */
+ if (sg_dma_address(&data->sg[i]) & 0x7)
+ dalgn = 1;
+ if (data->flags & MMC_DATA_READ) {
+ host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
+ host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
+ } else {
+ host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]);
+ host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO;
+ }
+ host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
+ sizeof(struct pxa_dma_desc);
+ }
+ host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP;
+ wmb();
+
+ /*
+ * The PXA27x DMA controller encounters overhead when working with
+ * unaligned (to 8-byte boundaries) data, so switch on byte alignment
+ * mode only if we have unaligned data.
+ */
+ if (dalgn)
+ DALGN |= (1 << host->dma);
+ else
+ DALGN &= ~(1 << host->dma);
+ DDADR(host->dma) = host->sg_dma;
+ DCSR(host->dma) = DCSR_RUN;
+}
+
+static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat)
+{
+ WARN_ON(host->cmd != NULL);
+ host->cmd = cmd;
+
+ if (cmd->flags & MMC_RSP_BUSY)
+ cmdat |= CMDAT_BUSY;
+
+#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
+ switch (RSP_TYPE(mmc_resp_type(cmd))) {
+ case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */
+ cmdat |= CMDAT_RESP_SHORT;
+ break;
+ case RSP_TYPE(MMC_RSP_R3):
+ cmdat |= CMDAT_RESP_R3;
+ break;
+ case RSP_TYPE(MMC_RSP_R2):
+ cmdat |= CMDAT_RESP_R2;
+ break;
+ default:
+ break;
+ }
+
+ writel(cmd->opcode, host->base + MMC_CMD);
+ writel(cmd->arg >> 16, host->base + MMC_ARGH);
+ writel(cmd->arg & 0xffff, host->base + MMC_ARGL);
+ writel(cmdat, host->base + MMC_CMDAT);
+ writel(host->clkrt, host->base + MMC_CLKRT);
+
+ writel(START_CLOCK, host->base + MMC_STRPCL);
+
+ pxamci_enable_irq(host, END_CMD_RES);
+}
+
+static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq)
+{
+ host->mrq = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+ mmc_request_done(host->mmc, mrq);
+}
+
+static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
+{
+ struct mmc_command *cmd = host->cmd;
+ int i;
+ u32 v;
+
+ if (!cmd)
+ return 0;
+
+ host->cmd = NULL;
+
+ /*
+ * Did I mention this is Sick. We always need to
+ * discard the upper 8 bits of the first 16-bit word.
+ */
+ v = readl(host->base + MMC_RES) & 0xffff;
+ for (i = 0; i < 4; i++) {
+ u32 w1 = readl(host->base + MMC_RES) & 0xffff;
+ u32 w2 = readl(host->base + MMC_RES) & 0xffff;
+ cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8;
+ v = w2;
+ }
+
+ if (stat & STAT_TIME_OUT_RESPONSE) {
+ cmd->error = -ETIMEDOUT;
+ } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
+#ifdef CONFIG_PXA27x
+ /*
+ * workaround for erratum #42:
+ * Intel PXA27x Family Processor Specification Update Rev 001
+ * A bogus CRC error can appear if the msb of a 136 bit
+ * response is a one.
+ */
+ if (cmd->flags & MMC_RSP_136 && cmd->resp[0] & 0x80000000) {
+ pr_debug("ignoring CRC from command %d - *risky*\n", cmd->opcode);
+ } else
+#endif
+ cmd->error = -EILSEQ;
+ }
+
+ pxamci_disable_irq(host, END_CMD_RES);
+ if (host->data && !cmd->error) {
+ pxamci_enable_irq(host, DATA_TRAN_DONE);
+ } else {
+ pxamci_finish_request(host, host->mrq);
+ }
+
+ return 1;
+}
+
+static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
+{
+ struct mmc_data *data = host->data;
+
+ if (!data)
+ return 0;
+
+ DCSR(host->dma) = 0;
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
+ host->dma_dir);
+
+ if (stat & STAT_READ_TIME_OUT)
+ data->error = -ETIMEDOUT;
+ else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR))
+ data->error = -EILSEQ;
+
+ /*
+ * There appears to be a hardware design bug here. There seems to
+ * be no way to find out how much data was transferred to the card.
+ * This means that if there was an error on any block, we mark all
+ * data blocks as being in error.
+ */
+ if (!data->error)
+ data->bytes_xfered = data->blocks * data->blksz;
+ else
+ data->bytes_xfered = 0;
+
+ pxamci_disable_irq(host, DATA_TRAN_DONE);
+
+ host->data = NULL;
+ if (host->mrq->stop) {
+ pxamci_stop_clock(host);
+ pxamci_start_cmd(host, host->mrq->stop, host->cmdat);
+ } else {
+ pxamci_finish_request(host, host->mrq);
+ }
+
+ return 1;
+}
+
+static irqreturn_t pxamci_irq(int irq, void *devid)
+{
+ struct pxamci_host *host = devid;
+ unsigned int ireg;
+ int handled = 0;
+
+ ireg = readl(host->base + MMC_I_REG) & ~readl(host->base + MMC_I_MASK);
+
+ if (ireg) {
+ unsigned stat = readl(host->base + MMC_STAT);
+
+ pr_debug("PXAMCI: irq %08x stat %08x\n", ireg, stat);
+
+ if (ireg & END_CMD_RES)
+ handled |= pxamci_cmd_done(host, stat);
+ if (ireg & DATA_TRAN_DONE)
+ handled |= pxamci_data_done(host, stat);
+ if (ireg & SDIO_INT) {
+ mmc_signal_sdio_irq(host->mmc);
+ handled = 1;
+ }
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct pxamci_host *host = mmc_priv(mmc);
+ unsigned int cmdat;
+
+ WARN_ON(host->mrq != NULL);
+
+ host->mrq = mrq;
+
+ pxamci_stop_clock(host);
+
+ cmdat = host->cmdat;
+ host->cmdat &= ~CMDAT_INIT;
+
+ if (mrq->data) {
+ pxamci_setup_data(host, mrq->data);
+
+ cmdat &= ~CMDAT_BUSY;
+ cmdat |= CMDAT_DATAEN | CMDAT_DMAEN;
+ if (mrq->data->flags & MMC_DATA_WRITE)
+ cmdat |= CMDAT_WRITE;
+
+ if (mrq->data->flags & MMC_DATA_STREAM)
+ cmdat |= CMDAT_STREAM;
+ }
+
+ pxamci_start_cmd(host, mrq->cmd, cmdat);
+}
+
+static int pxamci_get_ro(struct mmc_host *mmc)
+{
+ struct pxamci_host *host = mmc_priv(mmc);
+
+ if (host->pdata && host->pdata->get_ro)
+ return !!host->pdata->get_ro(mmc_dev(mmc));
+ /*
+ * Board doesn't support read only detection; let the mmc core
+ * decide what to do.
+ */
+ return -ENOSYS;
+}
+
+static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct pxamci_host *host = mmc_priv(mmc);
+
+ if (ios->clock) {
+ unsigned long rate = host->clkrate;
+ unsigned int clk = rate / ios->clock;
+
+ if (host->clkrt == CLKRT_OFF)
+ clk_enable(host->clk);
+
+ if (ios->clock == 26000000) {
+ /* to support 26MHz on pxa300/pxa310 */
+ host->clkrt = 7;
+ } else {
+ /* to handle (19.5MHz, 26MHz) */
+ if (!clk)
+ clk = 1;
+
+ /*
+ * clk might result in a lower divisor than we
+ * desire. check for that condition and adjust
+ * as appropriate.
+ */
+ if (rate / clk > ios->clock)
+ clk <<= 1;
+ host->clkrt = fls(clk) - 1;
+ }
+
+ /*
+ * we write clkrt on the next command
+ */
+ } else {
+ pxamci_stop_clock(host);
+ if (host->clkrt != CLKRT_OFF) {
+ host->clkrt = CLKRT_OFF;
+ clk_disable(host->clk);
+ }
+ }
+
+ if (host->power_mode != ios->power_mode) {
+ host->power_mode = ios->power_mode;
+
+ if (host->pdata && host->pdata->setpower)
+ host->pdata->setpower(mmc_dev(mmc), ios->vdd);
+
+ if (ios->power_mode == MMC_POWER_ON)
+ host->cmdat |= CMDAT_INIT;
+ }
+
+ if (ios->bus_width == MMC_BUS_WIDTH_4)
+ host->cmdat |= CMDAT_SD_4DAT;
+ else
+ host->cmdat &= ~CMDAT_SD_4DAT;
+
+ pr_debug("PXAMCI: clkrt = %x cmdat = %x\n",
+ host->clkrt, host->cmdat);
+}
+
+static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable)
+{
+ struct pxamci_host *pxa_host = mmc_priv(host);
+
+ if (enable)
+ pxamci_enable_irq(pxa_host, SDIO_INT);
+ else
+ pxamci_disable_irq(pxa_host, SDIO_INT);
+}
+
+static const struct mmc_host_ops pxamci_ops = {
+ .request = pxamci_request,
+ .get_ro = pxamci_get_ro,
+ .set_ios = pxamci_set_ios,
+ .enable_sdio_irq = pxamci_enable_sdio_irq,
+};
+
+static void pxamci_dma_irq(int dma, void *devid)
+{
+ struct pxamci_host *host = devid;
+ int dcsr = DCSR(dma);
+ DCSR(dma) = dcsr & ~DCSR_STOPIRQEN;
+
+ if (dcsr & DCSR_ENDINTR) {
+ writel(BUF_PART_FULL, host->base + MMC_PRTBUF);
+ } else {
+ printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
+ mmc_hostname(host->mmc), dma, dcsr);
+ host->data->error = -EIO;
+ pxamci_data_done(host, 0);
+ }
+}
+
+static irqreturn_t pxamci_detect_irq(int irq, void *devid)
+{
+ struct pxamci_host *host = mmc_priv(devid);
+
+ mmc_detect_change(devid, host->pdata->detect_delay);
+ return IRQ_HANDLED;
+}
+
+static int pxamci_probe(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct pxamci_host *host = NULL;
+ struct resource *r, *dmarx, *dmatx;
+ int ret, irq;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!r || irq < 0)
+ return -ENXIO;
+
+ r = request_mem_region(r->start, SZ_4K, DRIVER_NAME);
+ if (!r)
+ return -EBUSY;
+
+ mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mmc->ops = &pxamci_ops;
+
+ /*
+ * We can do SG-DMA, but we don't because we never know how much
+ * data we successfully wrote to the card.
+ */
+ mmc->max_phys_segs = NR_SG;
+
+ /*
+ * Our hardware DMA can handle a maximum of one page per SG entry.
+ */
+ mmc->max_seg_size = PAGE_SIZE;
+
+ /*
+ * Block length register is only 10 bits before PXA27x.
+ */
+ mmc->max_blk_size = cpu_is_pxa25x() ? 1023 : 2048;
+
+ /*
+ * Block count register is 16 bits.
+ */
+ mmc->max_blk_count = 65535;
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->dma = -1;
+ host->pdata = pdev->dev.platform_data;
+ host->clkrt = CLKRT_OFF;
+
+ host->clk = clk_get(&pdev->dev, "MMCCLK");
+ if (IS_ERR(host->clk)) {
+ ret = PTR_ERR(host->clk);
+ host->clk = NULL;
+ goto out;
+ }
+
+ host->clkrate = clk_get_rate(host->clk);
+
+ /*
+ * Calculate minimum clock rate, rounding up.
+ */
+ mmc->f_min = (host->clkrate + 63) / 64;
+ mmc->f_max = (cpu_is_pxa300() || cpu_is_pxa310()) ? 26000000
+ : host->clkrate;
+
+ mmc->ocr_avail = host->pdata ?
+ host->pdata->ocr_mask :
+ MMC_VDD_32_33|MMC_VDD_33_34;
+ mmc->caps = 0;
+ host->cmdat = 0;
+ if (!cpu_is_pxa25x()) {
+ mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
+ host->cmdat |= CMDAT_SDIO_INT_EN;
+ if (cpu_is_pxa300() || cpu_is_pxa310())
+ mmc->caps |= MMC_CAP_MMC_HIGHSPEED |
+ MMC_CAP_SD_HIGHSPEED;
+ }
+
+ host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
+ if (!host->sg_cpu) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ spin_lock_init(&host->lock);
+ host->res = r;
+ host->irq = irq;
+ host->imask = MMC_I_MASK_ALL;
+
+ host->base = ioremap(r->start, SZ_4K);
+ if (!host->base) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Ensure that the host controller is shut down, and setup
+ * with our defaults.
+ */
+ pxamci_stop_clock(host);
+ writel(0, host->base + MMC_SPI);
+ writel(64, host->base + MMC_RESTO);
+ writel(host->imask, host->base + MMC_I_MASK);
+
+ host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW,
+ pxamci_dma_irq, host);
+ if (host->dma < 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host);
+ if (ret)
+ goto out;
+
+ platform_set_drvdata(pdev, mmc);
+
+ dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dmarx) {
+ ret = -ENXIO;
+ goto out;
+ }
+ host->dma_drcmrrx = dmarx->start;
+
+ dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!dmatx) {
+ ret = -ENXIO;
+ goto out;
+ }
+ host->dma_drcmrtx = dmatx->start;
+
+ if (host->pdata && host->pdata->init)
+ host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc);
+
+ mmc_add_host(mmc);
+
+ return 0;
+
+ out:
+ if (host) {
+ if (host->dma >= 0)
+ pxa_free_dma(host->dma);
+ if (host->base)
+ iounmap(host->base);
+ if (host->sg_cpu)
+ dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+ if (host->clk)
+ clk_put(host->clk);
+ }
+ if (mmc)
+ mmc_free_host(mmc);
+ release_resource(r);
+ return ret;
+}
+
+static int pxamci_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (mmc) {
+ struct pxamci_host *host = mmc_priv(mmc);
+
+ if (host->pdata && host->pdata->exit)
+ host->pdata->exit(&pdev->dev, mmc);
+
+ mmc_remove_host(mmc);
+
+ pxamci_stop_clock(host);
+ writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD|
+ END_CMD_RES|PRG_DONE|DATA_TRAN_DONE,
+ host->base + MMC_I_MASK);
+
+ DRCMR(host->dma_drcmrrx) = 0;
+ DRCMR(host->dma_drcmrtx) = 0;
+
+ free_irq(host->irq, host);
+ pxa_free_dma(host->dma);
+ iounmap(host->base);
+ dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+
+ clk_put(host->clk);
+
+ release_resource(host->res);
+
+ mmc_free_host(mmc);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pxamci_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+ int ret = 0;
+
+ if (mmc)
+ ret = mmc_suspend_host(mmc, state);
+
+ return ret;
+}
+
+static int pxamci_resume(struct platform_device *dev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+ int ret = 0;
+
+ if (mmc)
+ ret = mmc_resume_host(mmc);
+
+ return ret;
+}
+#else
+#define pxamci_suspend NULL
+#define pxamci_resume NULL
+#endif
+
+static struct platform_driver pxamci_driver = {
+ .probe = pxamci_probe,
+ .remove = pxamci_remove,
+ .suspend = pxamci_suspend,
+ .resume = pxamci_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pxamci_init(void)
+{
+ return platform_driver_register(&pxamci_driver);
+}
+
+static void __exit pxamci_exit(void)
+{
+ platform_driver_unregister(&pxamci_driver);
+}
+
+module_init(pxamci_init);
+module_exit(pxamci_exit);
+
+MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-mci");
diff --git a/drivers/mmc/host/pxamci.h b/drivers/mmc/host/pxamci.h
new file mode 100644
index 0000000..f6c2e2f
--- /dev/null
+++ b/drivers/mmc/host/pxamci.h
@@ -0,0 +1,90 @@
+#define MMC_STRPCL 0x0000
+#define STOP_CLOCK (1 << 0)
+#define START_CLOCK (2 << 0)
+
+#define MMC_STAT 0x0004
+#define STAT_END_CMD_RES (1 << 13)
+#define STAT_PRG_DONE (1 << 12)
+#define STAT_DATA_TRAN_DONE (1 << 11)
+#define STAT_CLK_EN (1 << 8)
+#define STAT_RECV_FIFO_FULL (1 << 7)
+#define STAT_XMIT_FIFO_EMPTY (1 << 6)
+#define STAT_RES_CRC_ERR (1 << 5)
+#define STAT_SPI_READ_ERROR_TOKEN (1 << 4)
+#define STAT_CRC_READ_ERROR (1 << 3)
+#define STAT_CRC_WRITE_ERROR (1 << 2)
+#define STAT_TIME_OUT_RESPONSE (1 << 1)
+#define STAT_READ_TIME_OUT (1 << 0)
+
+#define MMC_CLKRT 0x0008 /* 3 bit */
+
+#define MMC_SPI 0x000c
+#define SPI_CS_ADDRESS (1 << 3)
+#define SPI_CS_EN (1 << 2)
+#define CRC_ON (1 << 1)
+#define SPI_EN (1 << 0)
+
+#define MMC_CMDAT 0x0010
+#define CMDAT_SDIO_INT_EN (1 << 11)
+#define CMDAT_SD_4DAT (1 << 8)
+#define CMDAT_DMAEN (1 << 7)
+#define CMDAT_INIT (1 << 6)
+#define CMDAT_BUSY (1 << 5)
+#define CMDAT_STREAM (1 << 4) /* 1 = stream */
+#define CMDAT_WRITE (1 << 3) /* 1 = write */
+#define CMDAT_DATAEN (1 << 2)
+#define CMDAT_RESP_NONE (0 << 0)
+#define CMDAT_RESP_SHORT (1 << 0)
+#define CMDAT_RESP_R2 (2 << 0)
+#define CMDAT_RESP_R3 (3 << 0)
+
+#define MMC_RESTO 0x0014 /* 7 bit */
+
+#define MMC_RDTO 0x0018 /* 16 bit */
+
+#define MMC_BLKLEN 0x001c /* 10 bit */
+
+#define MMC_NOB 0x0020 /* 16 bit */
+
+#define MMC_PRTBUF 0x0024
+#define BUF_PART_FULL (1 << 0)
+
+#define MMC_I_MASK 0x0028
+
+/*PXA27x MMC interrupts*/
+#define SDIO_SUSPEND_ACK (1 << 12)
+#define SDIO_INT (1 << 11)
+#define RD_STALLED (1 << 10)
+#define RES_ERR (1 << 9)
+#define DAT_ERR (1 << 8)
+#define TINT (1 << 7)
+
+/*PXA2xx MMC interrupts*/
+#define TXFIFO_WR_REQ (1 << 6)
+#define RXFIFO_RD_REQ (1 << 5)
+#define CLK_IS_OFF (1 << 4)
+#define STOP_CMD (1 << 3)
+#define END_CMD_RES (1 << 2)
+#define PRG_DONE (1 << 1)
+#define DATA_TRAN_DONE (1 << 0)
+
+#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
+#define MMC_I_MASK_ALL 0x00001fff
+#else
+#define MMC_I_MASK_ALL 0x0000007f
+#endif
+
+#define MMC_I_REG 0x002c
+/* same as MMC_I_MASK */
+
+#define MMC_CMD 0x0030
+
+#define MMC_ARGH 0x0034 /* 16 bit */
+
+#define MMC_ARGL 0x0038 /* 16 bit */
+
+#define MMC_RES 0x003c /* 16 bit */
+
+#define MMC_RXFIFO 0x0040 /* 8 bit */
+
+#define MMC_TXFIFO 0x0044 /* 8 bit */
diff --git a/drivers/mmc/host/ricoh_mmc.c b/drivers/mmc/host/ricoh_mmc.c
new file mode 100644
index 0000000..a16d760
--- /dev/null
+++ b/drivers/mmc/host/ricoh_mmc.c
@@ -0,0 +1,257 @@
+/*
+ * ricoh_mmc.c - Dummy driver to disable the Rioch MMC controller.
+ *
+ * Copyright (C) 2007 Philip Langdale, 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+/*
+ * This is a conceptually ridiculous driver, but it is required by the way
+ * the Ricoh multi-function R5C832 works. This chip implements firewire
+ * and four different memory card controllers. Two of those controllers are
+ * an SDHCI controller and a proprietary MMC controller. The linux SDHCI
+ * driver supports MMC cards but the chip detects MMC cards in hardware
+ * and directs them to the MMC controller - so the SDHCI driver never sees
+ * them. To get around this, we must disable the useless MMC controller.
+ * At that point, the SDHCI controller will start seeing them. As a bonus,
+ * a detection event occurs immediately, even if the MMC card is already
+ * in the reader.
+ *
+ * The relevant registers live on the firewire function, so this is unavoidably
+ * ugly. Such is life.
+ */
+
+#include <linux/pci.h>
+
+#define DRIVER_NAME "ricoh-mmc"
+
+static const struct pci_device_id pci_ids[] __devinitdata = {
+ {
+ .vendor = PCI_VENDOR_ID_RICOH,
+ .device = PCI_DEVICE_ID_RICOH_R5C843,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ },
+ { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static int ricoh_mmc_disable(struct pci_dev *fw_dev)
+{
+ u8 write_enable;
+ u8 write_target;
+ u8 disable;
+
+ if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) {
+ /* via RL5C476 */
+
+ pci_read_config_byte(fw_dev, 0xB7, &disable);
+ if (disable & 0x02) {
+ printk(KERN_INFO DRIVER_NAME
+ ": Controller already disabled. " \
+ "Nothing to do.\n");
+ return -ENODEV;
+ }
+
+ pci_read_config_byte(fw_dev, 0x8E, &write_enable);
+ pci_write_config_byte(fw_dev, 0x8E, 0xAA);
+ pci_read_config_byte(fw_dev, 0x8D, &write_target);
+ pci_write_config_byte(fw_dev, 0x8D, 0xB7);
+ pci_write_config_byte(fw_dev, 0xB7, disable | 0x02);
+ pci_write_config_byte(fw_dev, 0x8E, write_enable);
+ pci_write_config_byte(fw_dev, 0x8D, write_target);
+ } else {
+ /* via R5C832 */
+
+ pci_read_config_byte(fw_dev, 0xCB, &disable);
+ if (disable & 0x02) {
+ printk(KERN_INFO DRIVER_NAME
+ ": Controller already disabled. " \
+ "Nothing to do.\n");
+ return -ENODEV;
+ }
+
+ pci_read_config_byte(fw_dev, 0xCA, &write_enable);
+ pci_write_config_byte(fw_dev, 0xCA, 0x57);
+ pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
+ pci_write_config_byte(fw_dev, 0xCA, write_enable);
+ }
+
+ printk(KERN_INFO DRIVER_NAME
+ ": Controller is now disabled.\n");
+
+ return 0;
+}
+
+static int ricoh_mmc_enable(struct pci_dev *fw_dev)
+{
+ u8 write_enable;
+ u8 write_target;
+ u8 disable;
+
+ if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) {
+ /* via RL5C476 */
+
+ pci_read_config_byte(fw_dev, 0x8E, &write_enable);
+ pci_write_config_byte(fw_dev, 0x8E, 0xAA);
+ pci_read_config_byte(fw_dev, 0x8D, &write_target);
+ pci_write_config_byte(fw_dev, 0x8D, 0xB7);
+ pci_read_config_byte(fw_dev, 0xB7, &disable);
+ pci_write_config_byte(fw_dev, 0xB7, disable & ~0x02);
+ pci_write_config_byte(fw_dev, 0x8E, write_enable);
+ pci_write_config_byte(fw_dev, 0x8D, write_target);
+ } else {
+ /* via R5C832 */
+
+ pci_read_config_byte(fw_dev, 0xCA, &write_enable);
+ pci_read_config_byte(fw_dev, 0xCB, &disable);
+ pci_write_config_byte(fw_dev, 0xCA, 0x57);
+ pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
+ pci_write_config_byte(fw_dev, 0xCA, write_enable);
+ }
+
+ printk(KERN_INFO DRIVER_NAME
+ ": Controller is now re-enabled.\n");
+
+ return 0;
+}
+
+static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ u8 rev;
+ u8 ctrlfound = 0;
+
+ struct pci_dev *fw_dev = NULL;
+
+ BUG_ON(pdev == NULL);
+ BUG_ON(ent == NULL);
+
+ pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
+
+ printk(KERN_INFO DRIVER_NAME
+ ": Ricoh MMC controller found at %s [%04x:%04x] (rev %x)\n",
+ pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
+ (int)rev);
+
+ while ((fw_dev =
+ pci_get_device(PCI_VENDOR_ID_RICOH,
+ PCI_DEVICE_ID_RICOH_RL5C476, fw_dev))) {
+ if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
+ pdev->bus == fw_dev->bus) {
+ if (ricoh_mmc_disable(fw_dev) != 0)
+ return -ENODEV;
+
+ pci_set_drvdata(pdev, fw_dev);
+
+ ++ctrlfound;
+ break;
+ }
+ }
+
+ fw_dev = NULL;
+
+ while (!ctrlfound &&
+ (fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH,
+ PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
+ if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
+ pdev->bus == fw_dev->bus) {
+ if (ricoh_mmc_disable(fw_dev) != 0)
+ return -ENODEV;
+
+ pci_set_drvdata(pdev, fw_dev);
+
+ ++ctrlfound;
+ }
+ }
+
+ if (!ctrlfound) {
+ printk(KERN_WARNING DRIVER_NAME
+ ": Main firewire function not found. Cannot disable controller.\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __devexit ricoh_mmc_remove(struct pci_dev *pdev)
+{
+ struct pci_dev *fw_dev = NULL;
+
+ fw_dev = pci_get_drvdata(pdev);
+ BUG_ON(fw_dev == NULL);
+
+ ricoh_mmc_enable(fw_dev);
+
+ pci_set_drvdata(pdev, NULL);
+}
+
+static int ricoh_mmc_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct pci_dev *fw_dev = NULL;
+
+ fw_dev = pci_get_drvdata(pdev);
+ BUG_ON(fw_dev == NULL);
+
+ printk(KERN_INFO DRIVER_NAME ": Suspending.\n");
+
+ ricoh_mmc_enable(fw_dev);
+
+ return 0;
+}
+
+static int ricoh_mmc_resume(struct pci_dev *pdev)
+{
+ struct pci_dev *fw_dev = NULL;
+
+ fw_dev = pci_get_drvdata(pdev);
+ BUG_ON(fw_dev == NULL);
+
+ printk(KERN_INFO DRIVER_NAME ": Resuming.\n");
+
+ ricoh_mmc_disable(fw_dev);
+
+ return 0;
+}
+
+static struct pci_driver ricoh_mmc_driver = {
+ .name = DRIVER_NAME,
+ .id_table = pci_ids,
+ .probe = ricoh_mmc_probe,
+ .remove = __devexit_p(ricoh_mmc_remove),
+ .suspend = ricoh_mmc_suspend,
+ .resume = ricoh_mmc_resume,
+};
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int __init ricoh_mmc_drv_init(void)
+{
+ printk(KERN_INFO DRIVER_NAME
+ ": Ricoh MMC Controller disabling driver\n");
+ printk(KERN_INFO DRIVER_NAME ": Copyright(c) Philip Langdale\n");
+
+ return pci_register_driver(&ricoh_mmc_driver);
+}
+
+static void __exit ricoh_mmc_drv_exit(void)
+{
+ pci_unregister_driver(&ricoh_mmc_driver);
+}
+
+module_init(ricoh_mmc_drv_init);
+module_exit(ricoh_mmc_drv_exit);
+
+MODULE_AUTHOR("Philip Langdale <philipl@alumni.utexas.net>");
+MODULE_DESCRIPTION("Ricoh MMC Controller disabling driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
new file mode 100644
index 0000000..c6e5539
--- /dev/null
+++ b/drivers/mmc/host/s3cmci.c
@@ -0,0 +1,1568 @@
+/*
+ * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
+ *
+ * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de>
+ *
+ * Current driver maintained by Ben Dooks and Simtec Electronics
+ * Copyright (C) 2008 Simtec Electronics <ben-linux@fluff.org>
+ *
+ * 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/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/mmc/host.h>
+#include <linux/platform_device.h>
+#include <linux/cpufreq.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <asm/dma.h>
+
+#include <mach/regs-sdi.h>
+#include <mach/regs-gpio.h>
+
+#include <asm/plat-s3c24xx/mci.h>
+
+#include "s3cmci.h"
+
+#define DRIVER_NAME "s3c-mci"
+
+enum dbg_channels {
+ dbg_err = (1 << 0),
+ dbg_debug = (1 << 1),
+ dbg_info = (1 << 2),
+ dbg_irq = (1 << 3),
+ dbg_sg = (1 << 4),
+ dbg_dma = (1 << 5),
+ dbg_pio = (1 << 6),
+ dbg_fail = (1 << 7),
+ dbg_conf = (1 << 8),
+};
+
+static const int dbgmap_err = dbg_fail;
+static const int dbgmap_info = dbg_info | dbg_conf;
+static const int dbgmap_debug = dbg_err | dbg_debug;
+
+#define dbg(host, channels, args...) \
+ do { \
+ if (dbgmap_err & channels) \
+ dev_err(&host->pdev->dev, args); \
+ else if (dbgmap_info & channels) \
+ dev_info(&host->pdev->dev, args); \
+ else if (dbgmap_debug & channels) \
+ dev_dbg(&host->pdev->dev, args); \
+ } while (0)
+
+#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
+
+static struct s3c2410_dma_client s3cmci_dma_client = {
+ .name = "s3c-mci",
+};
+
+static void finalize_request(struct s3cmci_host *host);
+static void s3cmci_send_request(struct mmc_host *mmc);
+static void s3cmci_reset(struct s3cmci_host *host);
+
+#ifdef CONFIG_MMC_DEBUG
+
+static void dbg_dumpregs(struct s3cmci_host *host, char *prefix)
+{
+ u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize;
+ u32 datcon, datcnt, datsta, fsta, imask;
+
+ con = readl(host->base + S3C2410_SDICON);
+ pre = readl(host->base + S3C2410_SDIPRE);
+ cmdarg = readl(host->base + S3C2410_SDICMDARG);
+ cmdcon = readl(host->base + S3C2410_SDICMDCON);
+ cmdsta = readl(host->base + S3C2410_SDICMDSTAT);
+ r0 = readl(host->base + S3C2410_SDIRSP0);
+ r1 = readl(host->base + S3C2410_SDIRSP1);
+ r2 = readl(host->base + S3C2410_SDIRSP2);
+ r3 = readl(host->base + S3C2410_SDIRSP3);
+ timer = readl(host->base + S3C2410_SDITIMER);
+ bsize = readl(host->base + S3C2410_SDIBSIZE);
+ datcon = readl(host->base + S3C2410_SDIDCON);
+ datcnt = readl(host->base + S3C2410_SDIDCNT);
+ datsta = readl(host->base + S3C2410_SDIDSTA);
+ fsta = readl(host->base + S3C2410_SDIFSTA);
+ imask = readl(host->base + host->sdiimsk);
+
+ dbg(host, dbg_debug, "%s CON:[%08x] PRE:[%08x] TMR:[%08x]\n",
+ prefix, con, pre, timer);
+
+ dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n",
+ prefix, cmdcon, cmdarg, cmdsta);
+
+ dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]"
+ " DSTA:[%08x] DCNT:[%08x]\n",
+ prefix, datcon, fsta, datsta, datcnt);
+
+ dbg(host, dbg_debug, "%s R0:[%08x] R1:[%08x]"
+ " R2:[%08x] R3:[%08x]\n",
+ prefix, r0, r1, r2, r3);
+}
+
+static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd,
+ int stop)
+{
+ snprintf(host->dbgmsg_cmd, 300,
+ "#%u%s op:%i arg:0x%08x flags:0x08%x retries:%u",
+ host->ccnt, (stop ? " (STOP)" : ""),
+ cmd->opcode, cmd->arg, cmd->flags, cmd->retries);
+
+ if (cmd->data) {
+ snprintf(host->dbgmsg_dat, 300,
+ "#%u bsize:%u blocks:%u bytes:%u",
+ host->dcnt, cmd->data->blksz,
+ cmd->data->blocks,
+ cmd->data->blocks * cmd->data->blksz);
+ } else {
+ host->dbgmsg_dat[0] = '\0';
+ }
+}
+
+static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd,
+ int fail)
+{
+ unsigned int dbglvl = fail ? dbg_fail : dbg_debug;
+
+ if (!cmd)
+ return;
+
+ if (cmd->error == 0) {
+ dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n",
+ host->dbgmsg_cmd, cmd->resp[0]);
+ } else {
+ dbg(host, dbglvl, "CMD[ERR %i] %s Status:%s\n",
+ cmd->error, host->dbgmsg_cmd, host->status);
+ }
+
+ if (!cmd->data)
+ return;
+
+ if (cmd->data->error == 0) {
+ dbg(host, dbglvl, "DAT[OK] %s\n", host->dbgmsg_dat);
+ } else {
+ dbg(host, dbglvl, "DAT[ERR %i] %s DCNT:0x%08x\n",
+ cmd->data->error, host->dbgmsg_dat,
+ readl(host->base + S3C2410_SDIDCNT));
+ }
+}
+#else
+static void dbg_dumpcmd(struct s3cmci_host *host,
+ struct mmc_command *cmd, int fail) { }
+
+static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd,
+ int stop) { }
+
+static void dbg_dumpregs(struct s3cmci_host *host, char *prefix) { }
+
+#endif /* CONFIG_MMC_DEBUG */
+
+static inline u32 enable_imask(struct s3cmci_host *host, u32 imask)
+{
+ u32 newmask;
+
+ newmask = readl(host->base + host->sdiimsk);
+ newmask |= imask;
+
+ writel(newmask, host->base + host->sdiimsk);
+
+ return newmask;
+}
+
+static inline u32 disable_imask(struct s3cmci_host *host, u32 imask)
+{
+ u32 newmask;
+
+ newmask = readl(host->base + host->sdiimsk);
+ newmask &= ~imask;
+
+ writel(newmask, host->base + host->sdiimsk);
+
+ return newmask;
+}
+
+static inline void clear_imask(struct s3cmci_host *host)
+{
+ writel(0, host->base + host->sdiimsk);
+}
+
+static inline int get_data_buffer(struct s3cmci_host *host,
+ u32 *bytes, u32 **pointer)
+{
+ struct scatterlist *sg;
+
+ if (host->pio_active == XFER_NONE)
+ return -EINVAL;
+
+ if ((!host->mrq) || (!host->mrq->data))
+ return -EINVAL;
+
+ if (host->pio_sgptr >= host->mrq->data->sg_len) {
+ dbg(host, dbg_debug, "no more buffers (%i/%i)\n",
+ host->pio_sgptr, host->mrq->data->sg_len);
+ return -EBUSY;
+ }
+ sg = &host->mrq->data->sg[host->pio_sgptr];
+
+ *bytes = sg->length;
+ *pointer = sg_virt(sg);
+
+ host->pio_sgptr++;
+
+ dbg(host, dbg_sg, "new buffer (%i/%i)\n",
+ host->pio_sgptr, host->mrq->data->sg_len);
+
+ return 0;
+}
+
+static inline u32 fifo_count(struct s3cmci_host *host)
+{
+ u32 fifostat = readl(host->base + S3C2410_SDIFSTA);
+
+ fifostat &= S3C2410_SDIFSTA_COUNTMASK;
+ return fifostat;
+}
+
+static inline u32 fifo_free(struct s3cmci_host *host)
+{
+ u32 fifostat = readl(host->base + S3C2410_SDIFSTA);
+
+ fifostat &= S3C2410_SDIFSTA_COUNTMASK;
+ return 63 - fifostat;
+}
+
+static void do_pio_read(struct s3cmci_host *host)
+{
+ int res;
+ u32 fifo;
+ u32 *ptr;
+ u32 fifo_words;
+ void __iomem *from_ptr;
+
+ /* write real prescaler to host, it might be set slow to fix */
+ writel(host->prescaler, host->base + S3C2410_SDIPRE);
+
+ from_ptr = host->base + host->sdidata;
+
+ while ((fifo = fifo_count(host))) {
+ if (!host->pio_bytes) {
+ res = get_data_buffer(host, &host->pio_bytes,
+ &host->pio_ptr);
+ if (res) {
+ host->pio_active = XFER_NONE;
+ host->complete_what = COMPLETION_FINALIZE;
+
+ dbg(host, dbg_pio, "pio_read(): "
+ "complete (no more data).\n");
+ return;
+ }
+
+ dbg(host, dbg_pio,
+ "pio_read(): new target: [%i]@[%p]\n",
+ host->pio_bytes, host->pio_ptr);
+ }
+
+ dbg(host, dbg_pio,
+ "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n",
+ fifo, host->pio_bytes,
+ readl(host->base + S3C2410_SDIDCNT));
+
+ /* If we have reached the end of the block, we can
+ * read a word and get 1 to 3 bytes. If we in the
+ * middle of the block, we have to read full words,
+ * otherwise we will write garbage, so round down to
+ * an even multiple of 4. */
+ if (fifo >= host->pio_bytes)
+ fifo = host->pio_bytes;
+ else
+ fifo -= fifo & 3;
+
+ host->pio_bytes -= fifo;
+ host->pio_count += fifo;
+
+ fifo_words = fifo >> 2;
+ ptr = host->pio_ptr;
+ while (fifo_words--)
+ *ptr++ = readl(from_ptr);
+ host->pio_ptr = ptr;
+
+ if (fifo & 3) {
+ u32 n = fifo & 3;
+ u32 data = readl(from_ptr);
+ u8 *p = (u8 *)host->pio_ptr;
+
+ while (n--) {
+ *p++ = data;
+ data >>= 8;
+ }
+ }
+ }
+
+ if (!host->pio_bytes) {
+ res = get_data_buffer(host, &host->pio_bytes, &host->pio_ptr);
+ if (res) {
+ dbg(host, dbg_pio,
+ "pio_read(): complete (no more buffers).\n");
+ host->pio_active = XFER_NONE;
+ host->complete_what = COMPLETION_FINALIZE;
+
+ return;
+ }
+ }
+
+ enable_imask(host,
+ S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
+}
+
+static void do_pio_write(struct s3cmci_host *host)
+{
+ void __iomem *to_ptr;
+ int res;
+ u32 fifo;
+ u32 *ptr;
+
+ to_ptr = host->base + host->sdidata;
+
+ while ((fifo = fifo_free(host)) > 3) {
+ if (!host->pio_bytes) {
+ res = get_data_buffer(host, &host->pio_bytes,
+ &host->pio_ptr);
+ if (res) {
+ dbg(host, dbg_pio,
+ "pio_write(): complete (no more data).\n");
+ host->pio_active = XFER_NONE;
+
+ return;
+ }
+
+ dbg(host, dbg_pio,
+ "pio_write(): new source: [%i]@[%p]\n",
+ host->pio_bytes, host->pio_ptr);
+
+ }
+
+ /* If we have reached the end of the block, we have to
+ * write exactly the remaining number of bytes. If we
+ * in the middle of the block, we have to write full
+ * words, so round down to an even multiple of 4. */
+ if (fifo >= host->pio_bytes)
+ fifo = host->pio_bytes;
+ else
+ fifo -= fifo & 3;
+
+ host->pio_bytes -= fifo;
+ host->pio_count += fifo;
+
+ fifo = (fifo + 3) >> 2;
+ ptr = host->pio_ptr;
+ while (fifo--)
+ writel(*ptr++, to_ptr);
+ host->pio_ptr = ptr;
+ }
+
+ enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
+}
+
+static void pio_tasklet(unsigned long data)
+{
+ struct s3cmci_host *host = (struct s3cmci_host *) data;
+
+
+ disable_irq(host->irq);
+
+ if (host->pio_active == XFER_WRITE)
+ do_pio_write(host);
+
+ if (host->pio_active == XFER_READ)
+ do_pio_read(host);
+
+ if (host->complete_what == COMPLETION_FINALIZE) {
+ clear_imask(host);
+ if (host->pio_active != XFER_NONE) {
+ dbg(host, dbg_err, "unfinished %s "
+ "- pio_count:[%u] pio_bytes:[%u]\n",
+ (host->pio_active == XFER_READ) ? "read" : "write",
+ host->pio_count, host->pio_bytes);
+
+ if (host->mrq->data)
+ host->mrq->data->error = -EINVAL;
+ }
+
+ finalize_request(host);
+ } else
+ enable_irq(host->irq);
+}
+
+/*
+ * ISR for SDI Interface IRQ
+ * Communication between driver and ISR works as follows:
+ * host->mrq points to current request
+ * host->complete_what Indicates when the request is considered done
+ * COMPLETION_CMDSENT when the command was sent
+ * COMPLETION_RSPFIN when a response was received
+ * COMPLETION_XFERFINISH when the data transfer is finished
+ * COMPLETION_XFERFINISH_RSPFIN both of the above.
+ * host->complete_request is the completion-object the driver waits for
+ *
+ * 1) Driver sets up host->mrq and host->complete_what
+ * 2) Driver prepares the transfer
+ * 3) Driver enables interrupts
+ * 4) Driver starts transfer
+ * 5) Driver waits for host->complete_rquest
+ * 6) ISR checks for request status (errors and success)
+ * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
+ * 7) ISR completes host->complete_request
+ * 8) ISR disables interrupts
+ * 9) Driver wakes up and takes care of the request
+ *
+ * Note: "->error"-fields are expected to be set to 0 before the request
+ * was issued by mmc.c - therefore they are only set, when an error
+ * contition comes up
+ */
+
+static irqreturn_t s3cmci_irq(int irq, void *dev_id)
+{
+ struct s3cmci_host *host = dev_id;
+ struct mmc_command *cmd;
+ u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
+ u32 mci_cclear, mci_dclear;
+ unsigned long iflags;
+
+ spin_lock_irqsave(&host->complete_lock, iflags);
+
+ mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
+ mci_dsta = readl(host->base + S3C2410_SDIDSTA);
+ mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
+ mci_fsta = readl(host->base + S3C2410_SDIFSTA);
+ mci_imsk = readl(host->base + host->sdiimsk);
+ mci_cclear = 0;
+ mci_dclear = 0;
+
+ if ((host->complete_what == COMPLETION_NONE) ||
+ (host->complete_what == COMPLETION_FINALIZE)) {
+ host->status = "nothing to complete";
+ clear_imask(host);
+ goto irq_out;
+ }
+
+ if (!host->mrq) {
+ host->status = "no active mrq";
+ clear_imask(host);
+ goto irq_out;
+ }
+
+ cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;
+
+ if (!cmd) {
+ host->status = "no active cmd";
+ clear_imask(host);
+ goto irq_out;
+ }
+
+ if (!host->dodma) {
+ if ((host->pio_active == XFER_WRITE) &&
+ (mci_fsta & S3C2410_SDIFSTA_TFDET)) {
+
+ disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
+ tasklet_schedule(&host->pio_tasklet);
+ host->status = "pio tx";
+ }
+
+ if ((host->pio_active == XFER_READ) &&
+ (mci_fsta & S3C2410_SDIFSTA_RFDET)) {
+
+ disable_imask(host,
+ S3C2410_SDIIMSK_RXFIFOHALF |
+ S3C2410_SDIIMSK_RXFIFOLAST);
+
+ tasklet_schedule(&host->pio_tasklet);
+ host->status = "pio rx";
+ }
+ }
+
+ if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
+ dbg(host, dbg_err, "CMDSTAT: error CMDTIMEOUT\n");
+ cmd->error = -ETIMEDOUT;
+ host->status = "error: command timeout";
+ goto fail_transfer;
+ }
+
+ if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) {
+ if (host->complete_what == COMPLETION_CMDSENT) {
+ host->status = "ok: command sent";
+ goto close_transfer;
+ }
+
+ mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
+ }
+
+ if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
+ if (cmd->flags & MMC_RSP_CRC) {
+ if (host->mrq->cmd->flags & MMC_RSP_136) {
+ dbg(host, dbg_irq,
+ "fixup: ignore CRC fail with long rsp\n");
+ } else {
+ /* note, we used to fail the transfer
+ * here, but it seems that this is just
+ * the hardware getting it wrong.
+ *
+ * cmd->error = -EILSEQ;
+ * host->status = "error: bad command crc";
+ * goto fail_transfer;
+ */
+ }
+ }
+
+ mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
+ }
+
+ if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) {
+ if (host->complete_what == COMPLETION_RSPFIN) {
+ host->status = "ok: command response received";
+ goto close_transfer;
+ }
+
+ if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
+ host->complete_what = COMPLETION_XFERFINISH;
+
+ mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
+ }
+
+ /* errors handled after this point are only relevant
+ when a data transfer is in progress */
+
+ if (!cmd->data)
+ goto clear_status_bits;
+
+ /* Check for FIFO failure */
+ if (host->is2440) {
+ if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) {
+ dbg(host, dbg_err, "FIFO failure\n");
+ host->mrq->data->error = -EILSEQ;
+ host->status = "error: 2440 fifo failure";
+ goto fail_transfer;
+ }
+ } else {
+ if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
+ dbg(host, dbg_err, "FIFO failure\n");
+ cmd->data->error = -EILSEQ;
+ host->status = "error: fifo failure";
+ goto fail_transfer;
+ }
+ }
+
+ if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
+ dbg(host, dbg_err, "bad data crc (outgoing)\n");
+ cmd->data->error = -EILSEQ;
+ host->status = "error: bad data crc (outgoing)";
+ goto fail_transfer;
+ }
+
+ if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {
+ dbg(host, dbg_err, "bad data crc (incoming)\n");
+ cmd->data->error = -EILSEQ;
+ host->status = "error: bad data crc (incoming)";
+ goto fail_transfer;
+ }
+
+ if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
+ dbg(host, dbg_err, "data timeout\n");
+ cmd->data->error = -ETIMEDOUT;
+ host->status = "error: data timeout";
+ goto fail_transfer;
+ }
+
+ if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) {
+ if (host->complete_what == COMPLETION_XFERFINISH) {
+ host->status = "ok: data transfer completed";
+ goto close_transfer;
+ }
+
+ if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
+ host->complete_what = COMPLETION_RSPFIN;
+
+ mci_dclear |= S3C2410_SDIDSTA_XFERFINISH;
+ }
+
+clear_status_bits:
+ writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);
+ writel(mci_dclear, host->base + S3C2410_SDIDSTA);
+
+ goto irq_out;
+
+fail_transfer:
+ host->pio_active = XFER_NONE;
+
+close_transfer:
+ host->complete_what = COMPLETION_FINALIZE;
+
+ clear_imask(host);
+ tasklet_schedule(&host->pio_tasklet);
+
+ goto irq_out;
+
+irq_out:
+ dbg(host, dbg_irq,
+ "csta:0x%08x dsta:0x%08x fsta:0x%08x dcnt:0x%08x status:%s.\n",
+ mci_csta, mci_dsta, mci_fsta, mci_dcnt, host->status);
+
+ spin_unlock_irqrestore(&host->complete_lock, iflags);
+ return IRQ_HANDLED;
+
+}
+
+/*
+ * ISR for the CardDetect Pin
+*/
+
+static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
+{
+ struct s3cmci_host *host = (struct s3cmci_host *)dev_id;
+
+ dbg(host, dbg_irq, "card detect\n");
+
+ mmc_detect_change(host->mmc, msecs_to_jiffies(500));
+
+ return IRQ_HANDLED;
+}
+
+static void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch,
+ void *buf_id, int size,
+ enum s3c2410_dma_buffresult result)
+{
+ struct s3cmci_host *host = buf_id;
+ unsigned long iflags;
+ u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt;
+
+ mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
+ mci_dsta = readl(host->base + S3C2410_SDIDSTA);
+ mci_fsta = readl(host->base + S3C2410_SDIFSTA);
+ mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
+
+ BUG_ON(!host->mrq);
+ BUG_ON(!host->mrq->data);
+ BUG_ON(!host->dmatogo);
+
+ spin_lock_irqsave(&host->complete_lock, iflags);
+
+ if (result != S3C2410_RES_OK) {
+ dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "
+ "fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",
+ mci_csta, mci_dsta, mci_fsta,
+ mci_dcnt, result, host->dmatogo);
+
+ goto fail_request;
+ }
+
+ host->dmatogo--;
+ if (host->dmatogo) {
+ dbg(host, dbg_dma, "DMA DONE Size:%i DSTA:[%08x] "
+ "DCNT:[%08x] toGo:%u\n",
+ size, mci_dsta, mci_dcnt, host->dmatogo);
+
+ goto out;
+ }
+
+ dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
+ size, mci_dsta, mci_dcnt);
+
+ host->complete_what = COMPLETION_FINALIZE;
+
+out:
+ tasklet_schedule(&host->pio_tasklet);
+ spin_unlock_irqrestore(&host->complete_lock, iflags);
+ return;
+
+fail_request:
+ host->mrq->data->error = -EINVAL;
+ host->complete_what = COMPLETION_FINALIZE;
+ writel(0, host->base + host->sdiimsk);
+ goto out;
+
+}
+
+static void finalize_request(struct s3cmci_host *host)
+{
+ struct mmc_request *mrq = host->mrq;
+ struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
+ int debug_as_failure = 0;
+
+ if (host->complete_what != COMPLETION_FINALIZE)
+ return;
+
+ if (!mrq)
+ return;
+
+ if (cmd->data && (cmd->error == 0) &&
+ (cmd->data->error == 0)) {
+ if (host->dodma && (!host->dma_complete)) {
+ dbg(host, dbg_dma, "DMA Missing!\n");
+ return;
+ }
+ }
+
+ /* Read response from controller. */
+ cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0);
+ cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1);
+ cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2);
+ cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);
+
+ writel(host->prescaler, host->base + S3C2410_SDIPRE);
+
+ if (cmd->error)
+ debug_as_failure = 1;
+
+ if (cmd->data && cmd->data->error)
+ debug_as_failure = 1;
+
+ dbg_dumpcmd(host, cmd, debug_as_failure);
+
+ /* Cleanup controller */
+ writel(0, host->base + S3C2410_SDICMDARG);
+ writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
+ writel(0, host->base + S3C2410_SDICMDCON);
+ writel(0, host->base + host->sdiimsk);
+
+ if (cmd->data && cmd->error)
+ cmd->data->error = cmd->error;
+
+ if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) {
+ host->cmd_is_stop = 1;
+ s3cmci_send_request(host->mmc);
+ return;
+ }
+
+ /* If we have no data transfer we are finished here */
+ if (!mrq->data)
+ goto request_done;
+
+ /* Calulate the amout of bytes transfer if there was no error */
+ if (mrq->data->error == 0) {
+ mrq->data->bytes_xfered =
+ (mrq->data->blocks * mrq->data->blksz);
+ } else {
+ mrq->data->bytes_xfered = 0;
+ }
+
+ /* If we had an error while transfering data we flush the
+ * DMA channel and the fifo to clear out any garbage. */
+ if (mrq->data->error != 0) {
+ if (host->dodma)
+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+
+ if (host->is2440) {
+ /* Clear failure register and reset fifo. */
+ writel(S3C2440_SDIFSTA_FIFORESET |
+ S3C2440_SDIFSTA_FIFOFAIL,
+ host->base + S3C2410_SDIFSTA);
+ } else {
+ u32 mci_con;
+
+ /* reset fifo */
+ mci_con = readl(host->base + S3C2410_SDICON);
+ mci_con |= S3C2410_SDICON_FIFORESET;
+
+ writel(mci_con, host->base + S3C2410_SDICON);
+ }
+ }
+
+request_done:
+ host->complete_what = COMPLETION_NONE;
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
+}
+
+static void s3cmci_dma_setup(struct s3cmci_host *host,
+ enum s3c2410_dmasrc source)
+{
+ static enum s3c2410_dmasrc last_source = -1;
+ static int setup_ok;
+
+ if (last_source == source)
+ return;
+
+ last_source = source;
+
+ s3c2410_dma_devconfig(host->dma, source, 3,
+ host->mem->start + host->sdidata);
+
+ if (!setup_ok) {
+ s3c2410_dma_config(host->dma, 4, 0);
+ s3c2410_dma_set_buffdone_fn(host->dma,
+ s3cmci_dma_done_callback);
+ s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
+ setup_ok = 1;
+ }
+}
+
+static void s3cmci_send_command(struct s3cmci_host *host,
+ struct mmc_command *cmd)
+{
+ u32 ccon, imsk;
+
+ imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
+ S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
+ S3C2410_SDIIMSK_RESPONSECRC;
+
+ enable_imask(host, imsk);
+
+ if (cmd->data)
+ host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
+ else if (cmd->flags & MMC_RSP_PRESENT)
+ host->complete_what = COMPLETION_RSPFIN;
+ else
+ host->complete_what = COMPLETION_CMDSENT;
+
+ writel(cmd->arg, host->base + S3C2410_SDICMDARG);
+
+ ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;
+ ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;
+
+ if (cmd->flags & MMC_RSP_PRESENT)
+ ccon |= S3C2410_SDICMDCON_WAITRSP;
+
+ if (cmd->flags & MMC_RSP_136)
+ ccon |= S3C2410_SDICMDCON_LONGRSP;
+
+ writel(ccon, host->base + S3C2410_SDICMDCON);
+}
+
+static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
+{
+ u32 dcon, imsk, stoptries = 3;
+
+ /* write DCON register */
+
+ if (!data) {
+ writel(0, host->base + S3C2410_SDIDCON);
+ return 0;
+ }
+
+ if ((data->blksz & 3) != 0) {
+ /* We cannot deal with unaligned blocks with more than
+ * one block being transfered. */
+
+ if (data->blocks > 1) {
+ pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz);
+ return -EINVAL;
+ }
+ }
+
+ while (readl(host->base + S3C2410_SDIDSTA) &
+ (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
+
+ dbg(host, dbg_err,
+ "mci_setup_data() transfer stillin progress.\n");
+
+ writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
+ s3cmci_reset(host);
+
+ if ((stoptries--) == 0) {
+ dbg_dumpregs(host, "DRF");
+ return -EINVAL;
+ }
+ }
+
+ dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;
+
+ if (host->dodma)
+ dcon |= S3C2410_SDIDCON_DMAEN;
+
+ if (host->bus_width == MMC_BUS_WIDTH_4)
+ dcon |= S3C2410_SDIDCON_WIDEBUS;
+
+ if (!(data->flags & MMC_DATA_STREAM))
+ dcon |= S3C2410_SDIDCON_BLOCKMODE;
+
+ if (data->flags & MMC_DATA_WRITE) {
+ dcon |= S3C2410_SDIDCON_TXAFTERRESP;
+ dcon |= S3C2410_SDIDCON_XFER_TXSTART;
+ }
+
+ if (data->flags & MMC_DATA_READ) {
+ dcon |= S3C2410_SDIDCON_RXAFTERCMD;
+ dcon |= S3C2410_SDIDCON_XFER_RXSTART;
+ }
+
+ if (host->is2440) {
+ dcon |= S3C2440_SDIDCON_DS_WORD;
+ dcon |= S3C2440_SDIDCON_DATSTART;
+ }
+
+ writel(dcon, host->base + S3C2410_SDIDCON);
+
+ /* write BSIZE register */
+
+ writel(data->blksz, host->base + S3C2410_SDIBSIZE);
+
+ /* add to IMASK register */
+ imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
+ S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
+
+ enable_imask(host, imsk);
+
+ /* write TIMER register */
+
+ if (host->is2440) {
+ writel(0x007FFFFF, host->base + S3C2410_SDITIMER);
+ } else {
+ writel(0x0000FFFF, host->base + S3C2410_SDITIMER);
+
+ /* FIX: set slow clock to prevent timeouts on read */
+ if (data->flags & MMC_DATA_READ)
+ writel(0xFF, host->base + S3C2410_SDIPRE);
+ }
+
+ return 0;
+}
+
+#define BOTH_DIR (MMC_DATA_WRITE | MMC_DATA_READ)
+
+static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data)
+{
+ int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
+
+ BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
+
+ host->pio_sgptr = 0;
+ host->pio_bytes = 0;
+ host->pio_count = 0;
+ host->pio_active = rw ? XFER_WRITE : XFER_READ;
+
+ if (rw) {
+ do_pio_write(host);
+ enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
+ } else {
+ enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF
+ | S3C2410_SDIIMSK_RXFIFOLAST);
+ }
+
+ return 0;
+}
+
+static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
+{
+ int dma_len, i;
+ int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
+
+ BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
+
+ s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW);
+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+
+ dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ (rw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ if (dma_len == 0)
+ return -ENOMEM;
+
+ host->dma_complete = 0;
+ host->dmatogo = dma_len;
+
+ for (i = 0; i < dma_len; i++) {
+ int res;
+
+ dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,
+ sg_dma_address(&data->sg[i]),
+ sg_dma_len(&data->sg[i]));
+
+ res = s3c2410_dma_enqueue(host->dma, (void *) host,
+ sg_dma_address(&data->sg[i]),
+ sg_dma_len(&data->sg[i]));
+
+ if (res) {
+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+ return -EBUSY;
+ }
+ }
+
+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);
+
+ return 0;
+}
+
+static void s3cmci_send_request(struct mmc_host *mmc)
+{
+ struct s3cmci_host *host = mmc_priv(mmc);
+ struct mmc_request *mrq = host->mrq;
+ struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
+
+ host->ccnt++;
+ prepare_dbgmsg(host, cmd, host->cmd_is_stop);
+
+ /* Clear command, data and fifo status registers
+ Fifo clear only necessary on 2440, but doesn't hurt on 2410
+ */
+ writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
+ writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
+ writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);
+
+ if (cmd->data) {
+ int res = s3cmci_setup_data(host, cmd->data);
+
+ host->dcnt++;
+
+ if (res) {
+ dbg(host, dbg_err, "setup data error %d\n", res);
+ cmd->error = res;
+ cmd->data->error = res;
+
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
+ if (host->dodma)
+ res = s3cmci_prepare_dma(host, cmd->data);
+ else
+ res = s3cmci_prepare_pio(host, cmd->data);
+
+ if (res) {
+ dbg(host, dbg_err, "data prepare error %d\n", res);
+ cmd->error = res;
+ cmd->data->error = res;
+
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+ }
+
+ /* Send command */
+ s3cmci_send_command(host, cmd);
+
+ /* Enable Interrupt */
+ enable_irq(host->irq);
+}
+
+static int s3cmci_card_present(struct mmc_host *mmc)
+{
+ struct s3cmci_host *host = mmc_priv(mmc);
+ struct s3c24xx_mci_pdata *pdata = host->pdata;
+ int ret;
+
+ if (pdata->gpio_detect == 0)
+ return -ENOSYS;
+
+ ret = s3c2410_gpio_getpin(pdata->gpio_detect) ? 0 : 1;
+ return ret ^ pdata->detect_invert;
+}
+
+static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct s3cmci_host *host = mmc_priv(mmc);
+
+ host->status = "mmc request";
+ host->cmd_is_stop = 0;
+ host->mrq = mrq;
+
+ if (s3cmci_card_present(mmc) == 0) {
+ dbg(host, dbg_err, "%s: no medium present\n", __func__);
+ host->mrq->cmd->error = -ENOMEDIUM;
+ mmc_request_done(mmc, mrq);
+ } else
+ s3cmci_send_request(mmc);
+}
+
+static void s3cmci_set_clk(struct s3cmci_host *host, struct mmc_ios *ios)
+{
+ u32 mci_psc;
+
+ /* Set clock */
+ for (mci_psc = 0; mci_psc < 255; mci_psc++) {
+ host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
+
+ if (host->real_rate <= ios->clock)
+ break;
+ }
+
+ if (mci_psc > 255)
+ mci_psc = 255;
+
+ host->prescaler = mci_psc;
+ writel(host->prescaler, host->base + S3C2410_SDIPRE);
+
+ /* If requested clock is 0, real_rate will be 0, too */
+ if (ios->clock == 0)
+ host->real_rate = 0;
+}
+
+static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct s3cmci_host *host = mmc_priv(mmc);
+ u32 mci_con;
+
+ /* Set the power state */
+
+ mci_con = readl(host->base + S3C2410_SDICON);
+
+ switch (ios->power_mode) {
+ case MMC_POWER_ON:
+ case MMC_POWER_UP:
+ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);
+ s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);
+ s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);
+ s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
+ s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
+ s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
+
+ if (host->pdata->set_power)
+ host->pdata->set_power(ios->power_mode, ios->vdd);
+
+ if (!host->is2440)
+ mci_con |= S3C2410_SDICON_FIFORESET;
+
+ break;
+
+ case MMC_POWER_OFF:
+ default:
+ s3c2410_gpio_setpin(S3C2410_GPE5, 0);
+ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
+
+ if (host->is2440)
+ mci_con |= S3C2440_SDICON_SDRESET;
+
+ if (host->pdata->set_power)
+ host->pdata->set_power(ios->power_mode, ios->vdd);
+
+ break;
+ }
+
+ s3cmci_set_clk(host, ios);
+
+ /* Set CLOCK_ENABLE */
+ if (ios->clock)
+ mci_con |= S3C2410_SDICON_CLOCKTYPE;
+ else
+ mci_con &= ~S3C2410_SDICON_CLOCKTYPE;
+
+ writel(mci_con, host->base + S3C2410_SDICON);
+
+ if ((ios->power_mode == MMC_POWER_ON) ||
+ (ios->power_mode == MMC_POWER_UP)) {
+ dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n",
+ host->real_rate/1000, ios->clock/1000);
+ } else {
+ dbg(host, dbg_conf, "powered down.\n");
+ }
+
+ host->bus_width = ios->bus_width;
+}
+
+static void s3cmci_reset(struct s3cmci_host *host)
+{
+ u32 con = readl(host->base + S3C2410_SDICON);
+
+ con |= S3C2440_SDICON_SDRESET;
+ writel(con, host->base + S3C2410_SDICON);
+}
+
+static int s3cmci_get_ro(struct mmc_host *mmc)
+{
+ struct s3cmci_host *host = mmc_priv(mmc);
+ struct s3c24xx_mci_pdata *pdata = host->pdata;
+ int ret;
+
+ if (pdata->gpio_wprotect == 0)
+ return 0;
+
+ ret = s3c2410_gpio_getpin(pdata->gpio_wprotect);
+
+ if (pdata->wprotect_invert)
+ ret = !ret;
+
+ return ret;
+}
+
+static struct mmc_host_ops s3cmci_ops = {
+ .request = s3cmci_request,
+ .set_ios = s3cmci_set_ios,
+ .get_ro = s3cmci_get_ro,
+ .get_cd = s3cmci_card_present,
+};
+
+static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
+ /* This is currently here to avoid a number of if (host->pdata)
+ * checks. Any zero fields to ensure reaonable defaults are picked. */
+};
+
+#ifdef CONFIG_CPU_FREQ
+
+static int s3cmci_cpufreq_transition(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ struct s3cmci_host *host;
+ struct mmc_host *mmc;
+ unsigned long newclk;
+ unsigned long flags;
+
+ host = container_of(nb, struct s3cmci_host, freq_transition);
+ newclk = clk_get_rate(host->clk);
+ mmc = host->mmc;
+
+ if ((val == CPUFREQ_PRECHANGE && newclk > host->clk_rate) ||
+ (val == CPUFREQ_POSTCHANGE && newclk < host->clk_rate)) {
+ spin_lock_irqsave(&mmc->lock, flags);
+
+ host->clk_rate = newclk;
+
+ if (mmc->ios.power_mode != MMC_POWER_OFF &&
+ mmc->ios.clock != 0)
+ s3cmci_set_clk(host, &mmc->ios);
+
+ spin_unlock_irqrestore(&mmc->lock, flags);
+ }
+
+ return 0;
+}
+
+static inline int s3cmci_cpufreq_register(struct s3cmci_host *host)
+{
+ host->freq_transition.notifier_call = s3cmci_cpufreq_transition;
+
+ return cpufreq_register_notifier(&host->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host)
+{
+ cpufreq_unregister_notifier(&host->freq_transition,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int s3cmci_cpufreq_register(struct s3cmci_host *host)
+{
+ return 0;
+}
+
+static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host)
+{
+}
+#endif
+
+static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
+{
+ struct s3cmci_host *host;
+ struct mmc_host *mmc;
+ int ret;
+
+ mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ goto probe_out;
+ }
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->pdev = pdev;
+ host->is2440 = is2440;
+
+ host->pdata = pdev->dev.platform_data;
+ if (!host->pdata) {
+ pdev->dev.platform_data = &s3cmci_def_pdata;
+ host->pdata = &s3cmci_def_pdata;
+ }
+
+ spin_lock_init(&host->complete_lock);
+ tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
+
+ if (is2440) {
+ host->sdiimsk = S3C2440_SDIIMSK;
+ host->sdidata = S3C2440_SDIDATA;
+ host->clk_div = 1;
+ } else {
+ host->sdiimsk = S3C2410_SDIIMSK;
+ host->sdidata = S3C2410_SDIDATA;
+ host->clk_div = 2;
+ }
+
+ host->dodma = 0;
+ host->complete_what = COMPLETION_NONE;
+ host->pio_active = XFER_NONE;
+
+ host->dma = S3CMCI_DMA;
+
+ host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!host->mem) {
+ dev_err(&pdev->dev,
+ "failed to get io memory region resouce.\n");
+
+ ret = -ENOENT;
+ goto probe_free_host;
+ }
+
+ host->mem = request_mem_region(host->mem->start,
+ RESSIZE(host->mem), pdev->name);
+
+ if (!host->mem) {
+ dev_err(&pdev->dev, "failed to request io memory region.\n");
+ ret = -ENOENT;
+ goto probe_free_host;
+ }
+
+ host->base = ioremap(host->mem->start, RESSIZE(host->mem));
+ if (!host->base) {
+ dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
+ ret = -EINVAL;
+ goto probe_free_mem_region;
+ }
+
+ host->irq = platform_get_irq(pdev, 0);
+ if (host->irq == 0) {
+ dev_err(&pdev->dev, "failed to get interrupt resouce.\n");
+ ret = -EINVAL;
+ goto probe_iounmap;
+ }
+
+ if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
+ dev_err(&pdev->dev, "failed to request mci interrupt.\n");
+ ret = -ENOENT;
+ goto probe_iounmap;
+ }
+
+ /* We get spurious interrupts even when we have set the IMSK
+ * register to ignore everything, so use disable_irq() to make
+ * ensure we don't lock the system with un-serviceable requests. */
+
+ disable_irq(host->irq);
+
+ host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
+
+ if (host->irq_cd >= 0) {
+ if (request_irq(host->irq_cd, s3cmci_irq_cd,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ DRIVER_NAME, host)) {
+ dev_err(&pdev->dev, "can't get card detect irq.\n");
+ ret = -ENOENT;
+ goto probe_free_irq;
+ }
+ } else {
+ dev_warn(&pdev->dev, "host detect has no irq available\n");
+ s3c2410_gpio_cfgpin(host->pdata->gpio_detect,
+ S3C2410_GPIO_INPUT);
+ }
+
+ if (host->pdata->gpio_wprotect)
+ s3c2410_gpio_cfgpin(host->pdata->gpio_wprotect,
+ S3C2410_GPIO_INPUT);
+
+ if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL) < 0) {
+ dev_err(&pdev->dev, "unable to get DMA channel.\n");
+ ret = -EBUSY;
+ goto probe_free_irq_cd;
+ }
+
+ host->clk = clk_get(&pdev->dev, "sdi");
+ if (IS_ERR(host->clk)) {
+ dev_err(&pdev->dev, "failed to find clock source.\n");
+ ret = PTR_ERR(host->clk);
+ host->clk = NULL;
+ goto probe_free_host;
+ }
+
+ ret = clk_enable(host->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable clock source.\n");
+ goto clk_free;
+ }
+
+ host->clk_rate = clk_get_rate(host->clk);
+
+ mmc->ops = &s3cmci_ops;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->f_min = host->clk_rate / (host->clk_div * 256);
+ mmc->f_max = host->clk_rate / host->clk_div;
+
+ if (host->pdata->ocr_avail)
+ mmc->ocr_avail = host->pdata->ocr_avail;
+
+ mmc->max_blk_count = 4095;
+ mmc->max_blk_size = 4095;
+ mmc->max_req_size = 4095 * 512;
+ mmc->max_seg_size = mmc->max_req_size;
+
+ mmc->max_phys_segs = 128;
+ mmc->max_hw_segs = 128;
+
+ dbg(host, dbg_debug,
+ "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
+ (host->is2440?"2440":""),
+ host->base, host->irq, host->irq_cd, host->dma);
+
+ ret = s3cmci_cpufreq_register(host);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register cpufreq\n");
+ goto free_dmabuf;
+ }
+
+ ret = mmc_add_host(mmc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add mmc host.\n");
+ goto free_cpufreq;
+ }
+
+ platform_set_drvdata(pdev, mmc);
+ dev_info(&pdev->dev, "initialisation done.\n");
+
+ return 0;
+
+ free_cpufreq:
+ s3cmci_cpufreq_deregister(host);
+
+ free_dmabuf:
+ clk_disable(host->clk);
+
+ clk_free:
+ clk_put(host->clk);
+
+ probe_free_irq_cd:
+ if (host->irq_cd >= 0)
+ free_irq(host->irq_cd, host);
+
+ probe_free_irq:
+ free_irq(host->irq, host);
+
+ probe_iounmap:
+ iounmap(host->base);
+
+ probe_free_mem_region:
+ release_mem_region(host->mem->start, RESSIZE(host->mem));
+
+ probe_free_host:
+ mmc_free_host(mmc);
+ probe_out:
+ return ret;
+}
+
+static void s3cmci_shutdown(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct s3cmci_host *host = mmc_priv(mmc);
+
+ if (host->irq_cd >= 0)
+ free_irq(host->irq_cd, host);
+
+ s3cmci_cpufreq_deregister(host);
+ mmc_remove_host(mmc);
+ clk_disable(host->clk);
+}
+
+static int __devexit s3cmci_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct s3cmci_host *host = mmc_priv(mmc);
+
+ s3cmci_shutdown(pdev);
+
+ clk_put(host->clk);
+
+ tasklet_disable(&host->pio_tasklet);
+ s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client);
+
+ free_irq(host->irq, host);
+
+ iounmap(host->base);
+ release_mem_region(host->mem->start, RESSIZE(host->mem));
+
+ mmc_free_host(mmc);
+ return 0;
+}
+
+static int __devinit s3cmci_2410_probe(struct platform_device *dev)
+{
+ return s3cmci_probe(dev, 0);
+}
+
+static int __devinit s3cmci_2412_probe(struct platform_device *dev)
+{
+ return s3cmci_probe(dev, 1);
+}
+
+static int __devinit s3cmci_2440_probe(struct platform_device *dev)
+{
+ return s3cmci_probe(dev, 1);
+}
+
+#ifdef CONFIG_PM
+
+static int s3cmci_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+
+ return mmc_suspend_host(mmc, state);
+}
+
+static int s3cmci_resume(struct platform_device *dev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+
+ return mmc_resume_host(mmc);
+}
+
+#else /* CONFIG_PM */
+#define s3cmci_suspend NULL
+#define s3cmci_resume NULL
+#endif /* CONFIG_PM */
+
+
+static struct platform_driver s3cmci_2410_driver = {
+ .driver.name = "s3c2410-sdi",
+ .driver.owner = THIS_MODULE,
+ .probe = s3cmci_2410_probe,
+ .remove = __devexit_p(s3cmci_remove),
+ .shutdown = s3cmci_shutdown,
+ .suspend = s3cmci_suspend,
+ .resume = s3cmci_resume,
+};
+
+static struct platform_driver s3cmci_2412_driver = {
+ .driver.name = "s3c2412-sdi",
+ .driver.owner = THIS_MODULE,
+ .probe = s3cmci_2412_probe,
+ .remove = __devexit_p(s3cmci_remove),
+ .shutdown = s3cmci_shutdown,
+ .suspend = s3cmci_suspend,
+ .resume = s3cmci_resume,
+};
+
+static struct platform_driver s3cmci_2440_driver = {
+ .driver.name = "s3c2440-sdi",
+ .driver.owner = THIS_MODULE,
+ .probe = s3cmci_2440_probe,
+ .remove = __devexit_p(s3cmci_remove),
+ .shutdown = s3cmci_shutdown,
+ .suspend = s3cmci_suspend,
+ .resume = s3cmci_resume,
+};
+
+
+static int __init s3cmci_init(void)
+{
+ platform_driver_register(&s3cmci_2410_driver);
+ platform_driver_register(&s3cmci_2412_driver);
+ platform_driver_register(&s3cmci_2440_driver);
+ return 0;
+}
+
+static void __exit s3cmci_exit(void)
+{
+ platform_driver_unregister(&s3cmci_2410_driver);
+ platform_driver_unregister(&s3cmci_2412_driver);
+ platform_driver_unregister(&s3cmci_2440_driver);
+}
+
+module_init(s3cmci_init);
+module_exit(s3cmci_exit);
+
+MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>, Ben Dooks <ben-linux@fluff.org>");
+MODULE_ALIAS("platform:s3c2410-sdi");
+MODULE_ALIAS("platform:s3c2412-sdi");
+MODULE_ALIAS("platform:s3c2440-sdi");
diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h
new file mode 100644
index 0000000..ca1ba3d
--- /dev/null
+++ b/drivers/mmc/host/s3cmci.h
@@ -0,0 +1,74 @@
+/*
+ * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
+ *
+ * Copyright (C) 2004-2006 Thomas Kleffel, 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.
+ */
+
+/* FIXME: DMA Resource management ?! */
+#define S3CMCI_DMA 0
+
+enum s3cmci_waitfor {
+ COMPLETION_NONE,
+ COMPLETION_FINALIZE,
+ COMPLETION_CMDSENT,
+ COMPLETION_RSPFIN,
+ COMPLETION_XFERFINISH,
+ COMPLETION_XFERFINISH_RSPFIN,
+};
+
+struct s3cmci_host {
+ struct platform_device *pdev;
+ struct s3c24xx_mci_pdata *pdata;
+ struct mmc_host *mmc;
+ struct resource *mem;
+ struct clk *clk;
+ void __iomem *base;
+ int irq;
+ int irq_cd;
+ int dma;
+
+ unsigned long clk_rate;
+ unsigned long clk_div;
+ unsigned long real_rate;
+ u8 prescaler;
+
+ int is2440;
+ unsigned sdiimsk;
+ unsigned sdidata;
+ int dodma;
+ int dmatogo;
+
+ struct mmc_request *mrq;
+ int cmd_is_stop;
+
+ spinlock_t complete_lock;
+ enum s3cmci_waitfor complete_what;
+
+ int dma_complete;
+
+ u32 pio_sgptr;
+ u32 pio_bytes;
+ u32 pio_count;
+ u32 *pio_ptr;
+#define XFER_NONE 0
+#define XFER_READ 1
+#define XFER_WRITE 2
+ u32 pio_active;
+
+ int bus_width;
+
+ char dbgmsg_cmd[301];
+ char dbgmsg_dat[301];
+ char *status;
+
+ unsigned int ccnt, dcnt;
+ struct tasklet_struct pio_tasklet;
+
+#ifdef CONFIG_CPU_FREQ
+ struct notifier_block freq_transition;
+#endif
+};
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
new file mode 100644
index 0000000..51e89e0
--- /dev/null
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -0,0 +1,735 @@
+/* linux/drivers/mmc/host/sdhci-pci.c - SDHCI on PCI bus interface
+ *
+ * Copyright (C) 2005-2008 Pierre Ossman, 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * Thanks to the following companies for their support:
+ *
+ * - JMicron (hardware and technical support)
+ */
+
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/mmc/host.h>
+
+#include <asm/scatterlist.h>
+#include <asm/io.h>
+
+#include "sdhci.h"
+
+/*
+ * PCI registers
+ */
+
+#define PCI_SDHCI_IFPIO 0x00
+#define PCI_SDHCI_IFDMA 0x01
+#define PCI_SDHCI_IFVENDOR 0x02
+
+#define PCI_SLOT_INFO 0x40 /* 8 bits */
+#define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7)
+#define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07
+
+#define MAX_SLOTS 8
+
+struct sdhci_pci_chip;
+struct sdhci_pci_slot;
+
+struct sdhci_pci_fixes {
+ unsigned int quirks;
+
+ int (*probe)(struct sdhci_pci_chip*);
+
+ int (*probe_slot)(struct sdhci_pci_slot*);
+ void (*remove_slot)(struct sdhci_pci_slot*, int);
+
+ int (*suspend)(struct sdhci_pci_chip*,
+ pm_message_t);
+ int (*resume)(struct sdhci_pci_chip*);
+};
+
+struct sdhci_pci_slot {
+ struct sdhci_pci_chip *chip;
+ struct sdhci_host *host;
+
+ int pci_bar;
+};
+
+struct sdhci_pci_chip {
+ struct pci_dev *pdev;
+
+ unsigned int quirks;
+ const struct sdhci_pci_fixes *fixes;
+
+ int num_slots; /* Slots on controller */
+ struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */
+};
+
+
+/*****************************************************************************\
+ * *
+ * Hardware specific quirk handling *
+ * *
+\*****************************************************************************/
+
+static int ricoh_probe(struct sdhci_pci_chip *chip)
+{
+ if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM)
+ chip->quirks |= SDHCI_QUIRK_CLOCK_BEFORE_RESET;
+
+ if (chip->pdev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG)
+ chip->quirks |= SDHCI_QUIRK_NO_CARD_NO_RESET;
+
+ return 0;
+}
+
+static const struct sdhci_pci_fixes sdhci_ricoh = {
+ .probe = ricoh_probe,
+ .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR,
+};
+
+static const struct sdhci_pci_fixes sdhci_ene_712 = {
+ .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE |
+ SDHCI_QUIRK_BROKEN_DMA,
+};
+
+static const struct sdhci_pci_fixes sdhci_ene_714 = {
+ .quirks = SDHCI_QUIRK_SINGLE_POWER_WRITE |
+ SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
+ SDHCI_QUIRK_BROKEN_DMA,
+};
+
+static const struct sdhci_pci_fixes sdhci_cafe = {
+ .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
+ SDHCI_QUIRK_NO_BUSY_IRQ |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+};
+
+static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
+{
+ u8 scratch;
+ int ret;
+
+ ret = pci_read_config_byte(chip->pdev, 0xAE, &scratch);
+ if (ret)
+ return ret;
+
+ /*
+ * Turn PMOS on [bit 0], set over current detection to 2.4 V
+ * [bit 1:2] and enable over current debouncing [bit 6].
+ */
+ if (on)
+ scratch |= 0x47;
+ else
+ scratch &= ~0x47;
+
+ ret = pci_write_config_byte(chip->pdev, 0xAE, scratch);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int jmicron_probe(struct sdhci_pci_chip *chip)
+{
+ int ret;
+
+ if (chip->pdev->revision == 0) {
+ chip->quirks |= SDHCI_QUIRK_32BIT_DMA_ADDR |
+ SDHCI_QUIRK_32BIT_DMA_SIZE |
+ SDHCI_QUIRK_32BIT_ADMA_SIZE |
+ SDHCI_QUIRK_RESET_AFTER_REQUEST |
+ SDHCI_QUIRK_BROKEN_SMALL_PIO |
+ SDHCI_QUIRK_FORCE_HIGHSPEED;
+ }
+
+ /*
+ * JMicron chips can have two interfaces to the same hardware
+ * in order to work around limitations in Microsoft's driver.
+ * We need to make sure we only bind to one of them.
+ *
+ * This code assumes two things:
+ *
+ * 1. The PCI code adds subfunctions in order.
+ *
+ * 2. The MMC interface has a lower subfunction number
+ * than the SD interface.
+ */
+ if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_SD) {
+ struct pci_dev *sd_dev;
+
+ sd_dev = NULL;
+ while ((sd_dev = pci_get_device(PCI_VENDOR_ID_JMICRON,
+ PCI_DEVICE_ID_JMICRON_JMB38X_MMC, sd_dev)) != NULL) {
+ if ((PCI_SLOT(chip->pdev->devfn) ==
+ PCI_SLOT(sd_dev->devfn)) &&
+ (chip->pdev->bus == sd_dev->bus))
+ break;
+ }
+
+ if (sd_dev) {
+ pci_dev_put(sd_dev);
+ dev_info(&chip->pdev->dev, "Refusing to bind to "
+ "secondary interface.\n");
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * JMicron chips need a bit of a nudge to enable the power
+ * output pins.
+ */
+ ret = jmicron_pmos(chip, 1);
+ if (ret) {
+ dev_err(&chip->pdev->dev, "Failure enabling card power\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void jmicron_enable_mmc(struct sdhci_host *host, int on)
+{
+ u8 scratch;
+
+ scratch = readb(host->ioaddr + 0xC0);
+
+ if (on)
+ scratch |= 0x01;
+ else
+ scratch &= ~0x01;
+
+ writeb(scratch, host->ioaddr + 0xC0);
+}
+
+static int jmicron_probe_slot(struct sdhci_pci_slot *slot)
+{
+ if (slot->chip->pdev->revision == 0) {
+ u16 version;
+
+ version = readl(slot->host->ioaddr + SDHCI_HOST_VERSION);
+ version = (version & SDHCI_VENDOR_VER_MASK) >>
+ SDHCI_VENDOR_VER_SHIFT;
+
+ /*
+ * Older versions of the chip have lots of nasty glitches
+ * in the ADMA engine. It's best just to avoid it
+ * completely.
+ */
+ if (version < 0xAC)
+ slot->host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
+ }
+
+ /*
+ * The secondary interface requires a bit set to get the
+ * interrupts.
+ */
+ if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC)
+ jmicron_enable_mmc(slot->host, 1);
+
+ return 0;
+}
+
+static void jmicron_remove_slot(struct sdhci_pci_slot *slot, int dead)
+{
+ if (dead)
+ return;
+
+ if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC)
+ jmicron_enable_mmc(slot->host, 0);
+}
+
+static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state)
+{
+ int i;
+
+ if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) {
+ for (i = 0;i < chip->num_slots;i++)
+ jmicron_enable_mmc(chip->slots[i]->host, 0);
+ }
+
+ return 0;
+}
+
+static int jmicron_resume(struct sdhci_pci_chip *chip)
+{
+ int ret, i;
+
+ if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC) {
+ for (i = 0;i < chip->num_slots;i++)
+ jmicron_enable_mmc(chip->slots[i]->host, 1);
+ }
+
+ ret = jmicron_pmos(chip, 1);
+ if (ret) {
+ dev_err(&chip->pdev->dev, "Failure enabling card power\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct sdhci_pci_fixes sdhci_jmicron = {
+ .probe = jmicron_probe,
+
+ .probe_slot = jmicron_probe_slot,
+ .remove_slot = jmicron_remove_slot,
+
+ .suspend = jmicron_suspend,
+ .resume = jmicron_resume,
+};
+
+static const struct pci_device_id pci_ids[] __devinitdata = {
+ {
+ .vendor = PCI_VENDOR_ID_RICOH,
+ .device = PCI_DEVICE_ID_RICOH_R5C822,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_ricoh,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_ENE,
+ .device = PCI_DEVICE_ID_ENE_CB712_SD,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_ene_712,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_ENE,
+ .device = PCI_DEVICE_ID_ENE_CB712_SD_2,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_ene_712,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_ENE,
+ .device = PCI_DEVICE_ID_ENE_CB714_SD,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_ene_714,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_ENE,
+ .device = PCI_DEVICE_ID_ENE_CB714_SD_2,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_ene_714,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_MARVELL,
+ .device = PCI_DEVICE_ID_MARVELL_88ALP01_SD,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_cafe,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_JMICRON,
+ .device = PCI_DEVICE_ID_JMICRON_JMB38X_SD,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_jmicron,
+ },
+
+ {
+ .vendor = PCI_VENDOR_ID_JMICRON,
+ .device = PCI_DEVICE_ID_JMICRON_JMB38X_MMC,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_jmicron,
+ },
+
+ { /* Generic SD host controller */
+ PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
+ },
+
+ { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+/*****************************************************************************\
+ * *
+ * SDHCI core callbacks *
+ * *
+\*****************************************************************************/
+
+static int sdhci_pci_enable_dma(struct sdhci_host *host)
+{
+ struct sdhci_pci_slot *slot;
+ struct pci_dev *pdev;
+ int ret;
+
+ slot = sdhci_priv(host);
+ pdev = slot->chip->pdev;
+
+ if (((pdev->class & 0xFFFF00) == (PCI_CLASS_SYSTEM_SDHCI << 8)) &&
+ ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) &&
+ (host->flags & SDHCI_USE_DMA)) {
+ dev_warn(&pdev->dev, "Will use DMA mode even though HW "
+ "doesn't fully claim to support it.\n");
+ }
+
+ ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+ if (ret)
+ return ret;
+
+ pci_set_master(pdev);
+
+ return 0;
+}
+
+static struct sdhci_ops sdhci_pci_ops = {
+ .enable_dma = sdhci_pci_enable_dma,
+};
+
+/*****************************************************************************\
+ * *
+ * Suspend/resume *
+ * *
+\*****************************************************************************/
+
+#ifdef CONFIG_PM
+
+static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+ struct sdhci_pci_chip *chip;
+ struct sdhci_pci_slot *slot;
+ int i, ret;
+
+ chip = pci_get_drvdata(pdev);
+ if (!chip)
+ return 0;
+
+ for (i = 0;i < chip->num_slots;i++) {
+ slot = chip->slots[i];
+ if (!slot)
+ continue;
+
+ ret = sdhci_suspend_host(slot->host, state);
+
+ if (ret) {
+ for (i--;i >= 0;i--)
+ sdhci_resume_host(chip->slots[i]->host);
+ return ret;
+ }
+ }
+
+ if (chip->fixes && chip->fixes->suspend) {
+ ret = chip->fixes->suspend(chip, state);
+ if (ret) {
+ for (i = chip->num_slots - 1;i >= 0;i--)
+ sdhci_resume_host(chip->slots[i]->host);
+ return ret;
+ }
+ }
+
+ pci_save_state(pdev);
+ pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+static int sdhci_pci_resume (struct pci_dev *pdev)
+{
+ struct sdhci_pci_chip *chip;
+ struct sdhci_pci_slot *slot;
+ int i, ret;
+
+ chip = pci_get_drvdata(pdev);
+ if (!chip)
+ return 0;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ if (chip->fixes && chip->fixes->resume) {
+ ret = chip->fixes->resume(chip);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0;i < chip->num_slots;i++) {
+ slot = chip->slots[i];
+ if (!slot)
+ continue;
+
+ ret = sdhci_resume_host(slot->host);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+#else /* CONFIG_PM */
+
+#define sdhci_pci_suspend NULL
+#define sdhci_pci_resume NULL
+
+#endif /* CONFIG_PM */
+
+/*****************************************************************************\
+ * *
+ * Device probing/removal *
+ * *
+\*****************************************************************************/
+
+static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
+ struct pci_dev *pdev, struct sdhci_pci_chip *chip, int bar)
+{
+ struct sdhci_pci_slot *slot;
+ struct sdhci_host *host;
+
+ resource_size_t addr;
+
+ int ret;
+
+ if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
+ dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (pci_resource_len(pdev, bar) != 0x100) {
+ dev_err(&pdev->dev, "Invalid iomem size. You may "
+ "experience problems.\n");
+ }
+
+ if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) {
+ dev_err(&pdev->dev, "Vendor specific interface. Aborting.\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) {
+ dev_err(&pdev->dev, "Unknown interface. Aborting.\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot));
+ if (IS_ERR(host)) {
+ ret = PTR_ERR(host);
+ goto unmap;
+ }
+
+ slot = sdhci_priv(host);
+
+ slot->chip = chip;
+ slot->host = host;
+ slot->pci_bar = bar;
+
+ host->hw_name = "PCI";
+ host->ops = &sdhci_pci_ops;
+ host->quirks = chip->quirks;
+
+ host->irq = pdev->irq;
+
+ ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc));
+ if (ret) {
+ dev_err(&pdev->dev, "cannot request region\n");
+ return ERR_PTR(ret);
+ }
+
+ addr = pci_resource_start(pdev, bar);
+ host->ioaddr = ioremap_nocache(addr, pci_resource_len(pdev, bar));
+ if (!host->ioaddr) {
+ dev_err(&pdev->dev, "failed to remap registers\n");
+ goto release;
+ }
+
+ if (chip->fixes && chip->fixes->probe_slot) {
+ ret = chip->fixes->probe_slot(slot);
+ if (ret)
+ goto unmap;
+ }
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto remove;
+
+ return slot;
+
+remove:
+ if (chip->fixes && chip->fixes->remove_slot)
+ chip->fixes->remove_slot(slot, 0);
+
+unmap:
+ iounmap(host->ioaddr);
+
+release:
+ pci_release_region(pdev, bar);
+ sdhci_free_host(host);
+
+ return ERR_PTR(ret);
+}
+
+static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
+{
+ int dead;
+ u32 scratch;
+
+ dead = 0;
+ scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS);
+ if (scratch == (u32)-1)
+ dead = 1;
+
+ sdhci_remove_host(slot->host, dead);
+
+ if (slot->chip->fixes && slot->chip->fixes->remove_slot)
+ slot->chip->fixes->remove_slot(slot, dead);
+
+ pci_release_region(slot->chip->pdev, slot->pci_bar);
+
+ sdhci_free_host(slot->host);
+}
+
+static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct sdhci_pci_chip *chip;
+ struct sdhci_pci_slot *slot;
+
+ u8 slots, rev, first_bar;
+ int ret, i;
+
+ BUG_ON(pdev == NULL);
+ BUG_ON(ent == NULL);
+
+ pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
+
+ dev_info(&pdev->dev, "SDHCI controller found [%04x:%04x] (rev %x)\n",
+ (int)pdev->vendor, (int)pdev->device, (int)rev);
+
+ ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots);
+ if (ret)
+ return ret;
+
+ slots = PCI_SLOT_INFO_SLOTS(slots) + 1;
+ dev_dbg(&pdev->dev, "found %d slot(s)\n", slots);
+ if (slots == 0)
+ return -ENODEV;
+
+ BUG_ON(slots > MAX_SLOTS);
+
+ ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar);
+ if (ret)
+ return ret;
+
+ first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK;
+
+ if (first_bar > 5) {
+ dev_err(&pdev->dev, "Invalid first BAR. Aborting.\n");
+ return -ENODEV;
+ }
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL);
+ if (!chip) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ chip->pdev = pdev;
+ chip->fixes = (const struct sdhci_pci_fixes*)ent->driver_data;
+ if (chip->fixes)
+ chip->quirks = chip->fixes->quirks;
+ chip->num_slots = slots;
+
+ pci_set_drvdata(pdev, chip);
+
+ if (chip->fixes && chip->fixes->probe) {
+ ret = chip->fixes->probe(chip);
+ if (ret)
+ goto free;
+ }
+
+ for (i = 0;i < slots;i++) {
+ slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i);
+ if (IS_ERR(slot)) {
+ for (i--;i >= 0;i--)
+ sdhci_pci_remove_slot(chip->slots[i]);
+ ret = PTR_ERR(slot);
+ goto free;
+ }
+
+ chip->slots[i] = slot;
+ }
+
+ return 0;
+
+free:
+ pci_set_drvdata(pdev, NULL);
+ kfree(chip);
+
+err:
+ pci_disable_device(pdev);
+ return ret;
+}
+
+static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
+{
+ int i;
+ struct sdhci_pci_chip *chip;
+
+ chip = pci_get_drvdata(pdev);
+
+ if (chip) {
+ for (i = 0;i < chip->num_slots; i++)
+ sdhci_pci_remove_slot(chip->slots[i]);
+
+ pci_set_drvdata(pdev, NULL);
+ kfree(chip);
+ }
+
+ pci_disable_device(pdev);
+}
+
+static struct pci_driver sdhci_driver = {
+ .name = "sdhci-pci",
+ .id_table = pci_ids,
+ .probe = sdhci_pci_probe,
+ .remove = __devexit_p(sdhci_pci_remove),
+ .suspend = sdhci_pci_suspend,
+ .resume = sdhci_pci_resume,
+};
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int __init sdhci_drv_init(void)
+{
+ return pci_register_driver(&sdhci_driver);
+}
+
+static void __exit sdhci_drv_exit(void)
+{
+ pci_unregister_driver(&sdhci_driver);
+}
+
+module_init(sdhci_drv_init);
+module_exit(sdhci_drv_exit);
+
+MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
new file mode 100644
index 0000000..b4f6b3c
--- /dev/null
+++ b/drivers/mmc/host/sdhci.c
@@ -0,0 +1,1841 @@
+/*
+ * linux/drivers/mmc/host/sdhci.c - Secure Digital Host Controller Interface driver
+ *
+ * Copyright (C) 2005-2008 Pierre Ossman, 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * Thanks to the following companies for their support:
+ *
+ * - JMicron (hardware and technical support)
+ */
+
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+
+#include <linux/leds.h>
+
+#include <linux/mmc/host.h>
+
+#include "sdhci.h"
+
+#define DRIVER_NAME "sdhci"
+
+#define DBG(f, x...) \
+ pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x)
+
+static unsigned int debug_quirks = 0;
+
+static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
+static void sdhci_finish_data(struct sdhci_host *);
+
+static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
+static void sdhci_finish_command(struct sdhci_host *);
+
+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));
+ 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));
+ printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_ARGUMENT),
+ readw(host->ioaddr + 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));
+ 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));
+ 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));
+ 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));
+ 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));
+ 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));
+ printk(KERN_DEBUG DRIVER_NAME ": Caps: 0x%08x | Max curr: 0x%08x\n",
+ readl(host->ioaddr + SDHCI_CAPABILITIES),
+ readl(host->ioaddr + SDHCI_MAX_CURRENT));
+
+ printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n");
+}
+
+/*****************************************************************************\
+ * *
+ * Low level functions *
+ * *
+\*****************************************************************************/
+
+static void sdhci_reset(struct sdhci_host *host, u8 mask)
+{
+ unsigned long timeout;
+
+ if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
+ if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
+ SDHCI_CARD_PRESENT))
+ return;
+ }
+
+ writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET);
+
+ if (mask & SDHCI_RESET_ALL)
+ host->clock = 0;
+
+ /* Wait max 100 ms */
+ timeout = 100;
+
+ /* hw clears the bit when it's done */
+ while (readb(host->ioaddr + SDHCI_SOFTWARE_RESET) & mask) {
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
+ mmc_hostname(host->mmc), (int)mask);
+ sdhci_dumpregs(host);
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+}
+
+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_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;
+
+ writel(intmask, host->ioaddr + SDHCI_INT_ENABLE);
+ writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+}
+
+static void sdhci_activate_led(struct sdhci_host *host)
+{
+ u8 ctrl;
+
+ ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+ ctrl |= SDHCI_CTRL_LED;
+ writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+}
+
+static void sdhci_deactivate_led(struct sdhci_host *host)
+{
+ u8 ctrl;
+
+ ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+ ctrl &= ~SDHCI_CTRL_LED;
+ writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+}
+
+#ifdef CONFIG_LEDS_CLASS
+static void sdhci_led_control(struct led_classdev *led,
+ enum led_brightness brightness)
+{
+ struct sdhci_host *host = container_of(led, struct sdhci_host, led);
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (brightness == LED_OFF)
+ sdhci_deactivate_led(host);
+ else
+ sdhci_activate_led(host);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+#endif
+
+/*****************************************************************************\
+ * *
+ * Core functions *
+ * *
+\*****************************************************************************/
+
+static void sdhci_read_block_pio(struct sdhci_host *host)
+{
+ unsigned long flags;
+ size_t blksize, len, chunk;
+ u32 uninitialized_var(scratch);
+ u8 *buf;
+
+ DBG("PIO reading\n");
+
+ blksize = host->data->blksz;
+ chunk = 0;
+
+ local_irq_save(flags);
+
+ while (blksize) {
+ if (!sg_miter_next(&host->sg_miter))
+ BUG();
+
+ len = min(host->sg_miter.length, blksize);
+
+ blksize -= len;
+ host->sg_miter.consumed = len;
+
+ buf = host->sg_miter.addr;
+
+ while (len) {
+ if (chunk == 0) {
+ scratch = readl(host->ioaddr + SDHCI_BUFFER);
+ chunk = 4;
+ }
+
+ *buf = scratch & 0xFF;
+
+ buf++;
+ scratch >>= 8;
+ chunk--;
+ len--;
+ }
+ }
+
+ sg_miter_stop(&host->sg_miter);
+
+ local_irq_restore(flags);
+}
+
+static void sdhci_write_block_pio(struct sdhci_host *host)
+{
+ unsigned long flags;
+ size_t blksize, len, chunk;
+ u32 scratch;
+ u8 *buf;
+
+ DBG("PIO writing\n");
+
+ blksize = host->data->blksz;
+ chunk = 0;
+ scratch = 0;
+
+ local_irq_save(flags);
+
+ while (blksize) {
+ if (!sg_miter_next(&host->sg_miter))
+ BUG();
+
+ len = min(host->sg_miter.length, blksize);
+
+ blksize -= len;
+ host->sg_miter.consumed = len;
+
+ buf = host->sg_miter.addr;
+
+ while (len) {
+ scratch |= (u32)*buf << (chunk * 8);
+
+ buf++;
+ chunk++;
+ len--;
+
+ if ((chunk == 4) || ((len == 0) && (blksize == 0))) {
+ writel(scratch, host->ioaddr + SDHCI_BUFFER);
+ chunk = 0;
+ scratch = 0;
+ }
+ }
+ }
+
+ sg_miter_stop(&host->sg_miter);
+
+ local_irq_restore(flags);
+}
+
+static void sdhci_transfer_pio(struct sdhci_host *host)
+{
+ u32 mask;
+
+ BUG_ON(!host->data);
+
+ if (host->blocks == 0)
+ return;
+
+ if (host->data->flags & MMC_DATA_READ)
+ mask = SDHCI_DATA_AVAILABLE;
+ else
+ mask = SDHCI_SPACE_AVAILABLE;
+
+ /*
+ * Some controllers (JMicron JMB38x) mess up the buffer bits
+ * for transfers < 4 bytes. As long as it is just one block,
+ * we can ignore the bits.
+ */
+ if ((host->quirks & SDHCI_QUIRK_BROKEN_SMALL_PIO) &&
+ (host->data->blocks == 1))
+ mask = ~0;
+
+ while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+ if (host->data->flags & MMC_DATA_READ)
+ sdhci_read_block_pio(host);
+ else
+ sdhci_write_block_pio(host);
+
+ host->blocks--;
+ if (host->blocks == 0)
+ break;
+ }
+
+ DBG("PIO transfer complete.\n");
+}
+
+static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags)
+{
+ local_irq_save(*flags);
+ return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+}
+
+static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
+{
+ kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+ local_irq_restore(*flags);
+}
+
+static int sdhci_adma_table_pre(struct sdhci_host *host,
+ struct mmc_data *data)
+{
+ int direction;
+
+ u8 *desc;
+ u8 *align;
+ dma_addr_t addr;
+ dma_addr_t align_addr;
+ int len, offset;
+
+ struct scatterlist *sg;
+ int i;
+ char *buffer;
+ unsigned long flags;
+
+ /*
+ * The spec does not specify endianness of descriptor table.
+ * We currently guess that it is LE.
+ */
+
+ if (data->flags & MMC_DATA_READ)
+ direction = DMA_FROM_DEVICE;
+ else
+ direction = DMA_TO_DEVICE;
+
+ /*
+ * The ADMA descriptor table is mapped further down as we
+ * need to fill it with data first.
+ */
+
+ host->align_addr = dma_map_single(mmc_dev(host->mmc),
+ host->align_buffer, 128 * 4, direction);
+ if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
+ goto fail;
+ BUG_ON(host->align_addr & 0x3);
+
+ host->sg_count = dma_map_sg(mmc_dev(host->mmc),
+ data->sg, data->sg_len, direction);
+ if (host->sg_count == 0)
+ goto unmap_align;
+
+ desc = host->adma_desc;
+ align = host->align_buffer;
+
+ align_addr = host->align_addr;
+
+ for_each_sg(data->sg, sg, host->sg_count, i) {
+ addr = sg_dma_address(sg);
+ len = sg_dma_len(sg);
+
+ /*
+ * The SDHCI specification states that ADMA
+ * addresses must be 32-bit aligned. If they
+ * aren't, then we use a bounce buffer for
+ * the (up to three) bytes that screw up the
+ * alignment.
+ */
+ offset = (4 - (addr & 0x3)) & 0x3;
+ if (offset) {
+ if (data->flags & MMC_DATA_WRITE) {
+ buffer = sdhci_kmap_atomic(sg, &flags);
+ WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
+ memcpy(align, buffer, offset);
+ sdhci_kunmap_atomic(buffer, &flags);
+ }
+
+ desc[7] = (align_addr >> 24) & 0xff;
+ desc[6] = (align_addr >> 16) & 0xff;
+ desc[5] = (align_addr >> 8) & 0xff;
+ desc[4] = (align_addr >> 0) & 0xff;
+
+ BUG_ON(offset > 65536);
+
+ desc[3] = (offset >> 8) & 0xff;
+ desc[2] = (offset >> 0) & 0xff;
+
+ desc[1] = 0x00;
+ desc[0] = 0x21; /* tran, valid */
+
+ align += 4;
+ align_addr += 4;
+
+ desc += 8;
+
+ addr += offset;
+ len -= offset;
+ }
+
+ desc[7] = (addr >> 24) & 0xff;
+ desc[6] = (addr >> 16) & 0xff;
+ desc[5] = (addr >> 8) & 0xff;
+ desc[4] = (addr >> 0) & 0xff;
+
+ BUG_ON(len > 65536);
+
+ desc[3] = (len >> 8) & 0xff;
+ desc[2] = (len >> 0) & 0xff;
+
+ desc[1] = 0x00;
+ desc[0] = 0x21; /* tran, valid */
+
+ desc += 8;
+
+ /*
+ * If this triggers then we have a calculation bug
+ * somewhere. :/
+ */
+ WARN_ON((desc - host->adma_desc) > (128 * 2 + 1) * 4);
+ }
+
+ /*
+ * Add a terminating entry.
+ */
+ desc[7] = 0;
+ desc[6] = 0;
+ desc[5] = 0;
+ desc[4] = 0;
+
+ desc[3] = 0;
+ desc[2] = 0;
+
+ desc[1] = 0x00;
+ desc[0] = 0x03; /* nop, end, valid */
+
+ /*
+ * Resync align buffer as we might have changed it.
+ */
+ if (data->flags & MMC_DATA_WRITE) {
+ dma_sync_single_for_device(mmc_dev(host->mmc),
+ host->align_addr, 128 * 4, direction);
+ }
+
+ host->adma_addr = dma_map_single(mmc_dev(host->mmc),
+ host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE);
+ if (dma_mapping_error(mmc_dev(host->mmc), host->adma_addr))
+ goto unmap_entries;
+ BUG_ON(host->adma_addr & 0x3);
+
+ return 0;
+
+unmap_entries:
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, direction);
+unmap_align:
+ dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
+ 128 * 4, direction);
+fail:
+ return -EINVAL;
+}
+
+static void sdhci_adma_table_post(struct sdhci_host *host,
+ struct mmc_data *data)
+{
+ int direction;
+
+ struct scatterlist *sg;
+ int i, size;
+ u8 *align;
+ char *buffer;
+ unsigned long flags;
+
+ if (data->flags & MMC_DATA_READ)
+ direction = DMA_FROM_DEVICE;
+ else
+ direction = DMA_TO_DEVICE;
+
+ dma_unmap_single(mmc_dev(host->mmc), host->adma_addr,
+ (128 * 2 + 1) * 4, DMA_TO_DEVICE);
+
+ dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
+ 128 * 4, direction);
+
+ if (data->flags & MMC_DATA_READ) {
+ dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg,
+ data->sg_len, direction);
+
+ align = host->align_buffer;
+
+ for_each_sg(data->sg, sg, host->sg_count, i) {
+ if (sg_dma_address(sg) & 0x3) {
+ size = 4 - (sg_dma_address(sg) & 0x3);
+
+ buffer = sdhci_kmap_atomic(sg, &flags);
+ WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
+ memcpy(buffer, align, size);
+ sdhci_kunmap_atomic(buffer, &flags);
+
+ align += 4;
+ }
+ }
+ }
+
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, direction);
+}
+
+static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data)
+{
+ u8 count;
+ unsigned target_timeout, current_timeout;
+
+ /*
+ * If the host controller provides us with an incorrect timeout
+ * value, just skip the check and use 0xE. The hardware may take
+ * longer to time out, but that's much better than having a too-short
+ * timeout value.
+ */
+ if ((host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL))
+ return 0xE;
+
+ /* timeout in us */
+ target_timeout = data->timeout_ns / 1000 +
+ data->timeout_clks / host->clock;
+
+ /*
+ * Figure out needed cycles.
+ * We do this in steps in order to fit inside a 32 bit int.
+ * The first step is the minimum timeout, which will have a
+ * minimum resolution of 6 bits:
+ * (1) 2^13*1000 > 2^22,
+ * (2) host->timeout_clk < 2^16
+ * =>
+ * (1) / (2) > 2^6
+ */
+ count = 0;
+ current_timeout = (1 << 13) * 1000 / host->timeout_clk;
+ while (current_timeout < target_timeout) {
+ count++;
+ current_timeout <<= 1;
+ if (count >= 0xF)
+ break;
+ }
+
+ if (count >= 0xF) {
+ printk(KERN_WARNING "%s: Too large timeout requested!\n",
+ mmc_hostname(host->mmc));
+ count = 0xE;
+ }
+
+ return count;
+}
+
+static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
+{
+ u8 count;
+ u8 ctrl;
+ int ret;
+
+ WARN_ON(host->data);
+
+ if (data == NULL)
+ return;
+
+ /* Sanity checks */
+ BUG_ON(data->blksz * data->blocks > 524288);
+ BUG_ON(data->blksz > host->mmc->max_blk_size);
+ BUG_ON(data->blocks > 65535);
+
+ host->data = data;
+ host->data_early = 0;
+
+ count = sdhci_calc_timeout(host, data);
+ writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
+
+ if (host->flags & SDHCI_USE_DMA)
+ host->flags |= SDHCI_REQ_USE_DMA;
+
+ /*
+ * FIXME: This doesn't account for merging when mapping the
+ * scatterlist.
+ */
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ int broken, i;
+ struct scatterlist *sg;
+
+ broken = 0;
+ if (host->flags & SDHCI_USE_ADMA) {
+ if (host->quirks & SDHCI_QUIRK_32BIT_ADMA_SIZE)
+ broken = 1;
+ } else {
+ if (host->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE)
+ broken = 1;
+ }
+
+ if (unlikely(broken)) {
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ if (sg->length & 0x3) {
+ DBG("Reverting to PIO because of "
+ "transfer size (%d)\n",
+ sg->length);
+ host->flags &= ~SDHCI_REQ_USE_DMA;
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * The assumption here being that alignment is the same after
+ * translation to device address space.
+ */
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ int broken, i;
+ struct scatterlist *sg;
+
+ broken = 0;
+ if (host->flags & SDHCI_USE_ADMA) {
+ /*
+ * As we use 3 byte chunks to work around
+ * alignment problems, we need to check this
+ * quirk.
+ */
+ if (host->quirks & SDHCI_QUIRK_32BIT_ADMA_SIZE)
+ broken = 1;
+ } else {
+ if (host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR)
+ broken = 1;
+ }
+
+ if (unlikely(broken)) {
+ for_each_sg(data->sg, sg, data->sg_len, i) {
+ if (sg->offset & 0x3) {
+ DBG("Reverting to PIO because of "
+ "bad alignment\n");
+ host->flags &= ~SDHCI_REQ_USE_DMA;
+ break;
+ }
+ }
+ }
+ }
+
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ if (host->flags & SDHCI_USE_ADMA) {
+ ret = sdhci_adma_table_pre(host, data);
+ if (ret) {
+ /*
+ * This only happens when someone fed
+ * us an invalid request.
+ */
+ WARN_ON(1);
+ host->flags &= ~SDHCI_REQ_USE_DMA;
+ } else {
+ writel(host->adma_addr,
+ host->ioaddr + SDHCI_ADMA_ADDRESS);
+ }
+ } else {
+ int sg_cnt;
+
+ sg_cnt = dma_map_sg(mmc_dev(host->mmc),
+ data->sg, data->sg_len,
+ (data->flags & MMC_DATA_READ) ?
+ DMA_FROM_DEVICE :
+ DMA_TO_DEVICE);
+ if (sg_cnt == 0) {
+ /*
+ * This only happens when someone fed
+ * us an invalid request.
+ */
+ WARN_ON(1);
+ host->flags &= ~SDHCI_REQ_USE_DMA;
+ } else {
+ WARN_ON(sg_cnt != 1);
+ writel(sg_dma_address(data->sg),
+ host->ioaddr + SDHCI_DMA_ADDRESS);
+ }
+ }
+ }
+
+ /*
+ * Always adjust the DMA selection as some controllers
+ * (e.g. JMicron) can't do PIO properly when the selection
+ * is ADMA.
+ */
+ if (host->version >= SDHCI_SPEC_200) {
+ ctrl = readb(host->ioaddr + 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);
+ }
+
+ if (!(host->flags & SDHCI_REQ_USE_DMA)) {
+ sg_miter_start(&host->sg_miter,
+ data->sg, data->sg_len, SG_MITER_ATOMIC);
+ host->blocks = data->blocks;
+ }
+
+ /* 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);
+}
+
+static void sdhci_set_transfer_mode(struct sdhci_host *host,
+ struct mmc_data *data)
+{
+ u16 mode;
+
+ if (data == NULL)
+ return;
+
+ WARN_ON(!host->data);
+
+ mode = SDHCI_TRNS_BLK_CNT_EN;
+ if (data->blocks > 1)
+ mode |= SDHCI_TRNS_MULTI;
+ if (data->flags & MMC_DATA_READ)
+ mode |= SDHCI_TRNS_READ;
+ if (host->flags & SDHCI_REQ_USE_DMA)
+ mode |= SDHCI_TRNS_DMA;
+
+ writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE);
+}
+
+static void sdhci_finish_data(struct sdhci_host *host)
+{
+ struct mmc_data *data;
+
+ BUG_ON(!host->data);
+
+ data = host->data;
+ host->data = NULL;
+
+ if (host->flags & SDHCI_REQ_USE_DMA) {
+ if (host->flags & SDHCI_USE_ADMA)
+ sdhci_adma_table_post(host, data);
+ else {
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+ data->sg_len, (data->flags & MMC_DATA_READ) ?
+ DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ }
+ }
+
+ /*
+ * The specification states that the block count register must
+ * be updated, but it does not specify at what point in the
+ * data flow. That makes the register entirely useless to read
+ * back so we have to assume that nothing made it to the card
+ * in the event of an error.
+ */
+ if (data->error)
+ data->bytes_xfered = 0;
+ else
+ data->bytes_xfered = data->blksz * data->blocks;
+
+ if (data->stop) {
+ /*
+ * The controller needs a reset of internal state machines
+ * upon error conditions.
+ */
+ if (data->error) {
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+ }
+
+ sdhci_send_command(host, data->stop);
+ } else
+ tasklet_schedule(&host->finish_tasklet);
+}
+
+static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
+{
+ int flags;
+ u32 mask;
+ unsigned long timeout;
+
+ WARN_ON(host->cmd);
+
+ /* Wait max 10 ms */
+ timeout = 10;
+
+ mask = SDHCI_CMD_INHIBIT;
+ if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
+ mask |= SDHCI_DATA_INHIBIT;
+
+ /* We shouldn't wait for data inihibit for stop commands, even
+ though they might use busy signaling */
+ if (host->mrq->data && (cmd == host->mrq->data->stop))
+ mask &= ~SDHCI_DATA_INHIBIT;
+
+ while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: Controller never released "
+ "inhibit bit(s).\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ cmd->error = -EIO;
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ mod_timer(&host->timer, jiffies + 10 * HZ);
+
+ host->cmd = cmd;
+
+ sdhci_prepare_data(host, cmd->data);
+
+ writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);
+
+ sdhci_set_transfer_mode(host, cmd->data);
+
+ if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
+ printk(KERN_ERR "%s: Unsupported response type!\n",
+ mmc_hostname(host->mmc));
+ cmd->error = -EINVAL;
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ }
+
+ if (!(cmd->flags & MMC_RSP_PRESENT))
+ flags = SDHCI_CMD_RESP_NONE;
+ else if (cmd->flags & MMC_RSP_136)
+ flags = SDHCI_CMD_RESP_LONG;
+ else if (cmd->flags & MMC_RSP_BUSY)
+ flags = SDHCI_CMD_RESP_SHORT_BUSY;
+ else
+ flags = SDHCI_CMD_RESP_SHORT;
+
+ if (cmd->flags & MMC_RSP_CRC)
+ flags |= SDHCI_CMD_CRC;
+ if (cmd->flags & MMC_RSP_OPCODE)
+ flags |= SDHCI_CMD_INDEX;
+ if (cmd->data)
+ flags |= SDHCI_CMD_DATA;
+
+ writew(SDHCI_MAKE_CMD(cmd->opcode, flags),
+ host->ioaddr + SDHCI_COMMAND);
+}
+
+static void sdhci_finish_command(struct sdhci_host *host)
+{
+ int i;
+
+ BUG_ON(host->cmd == NULL);
+
+ if (host->cmd->flags & MMC_RSP_PRESENT) {
+ 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 +
+ SDHCI_RESPONSE + (3-i)*4) << 8;
+ if (i != 3)
+ host->cmd->resp[i] |=
+ readb(host->ioaddr +
+ SDHCI_RESPONSE + (3-i)*4-1);
+ }
+ } else {
+ host->cmd->resp[0] = readl(host->ioaddr + SDHCI_RESPONSE);
+ }
+ }
+
+ host->cmd->error = 0;
+
+ if (host->data && host->data_early)
+ sdhci_finish_data(host);
+
+ if (!host->cmd->data)
+ tasklet_schedule(&host->finish_tasklet);
+
+ host->cmd = NULL;
+}
+
+static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ int div;
+ u16 clk;
+ unsigned long timeout;
+
+ if (clock == host->clock)
+ return;
+
+ writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ if (clock == 0)
+ goto out;
+
+ for (div = 1;div < 256;div *= 2) {
+ if ((host->max_clk / div) <= clock)
+ break;
+ }
+ div >>= 1;
+
+ clk = div << SDHCI_DIVIDER_SHIFT;
+ clk |= SDHCI_CLOCK_INT_EN;
+ writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ /* Wait max 10 ms */
+ timeout = 10;
+ while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL))
+ & SDHCI_CLOCK_INT_STABLE)) {
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: Internal clock never "
+ "stabilised.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+ return;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+out:
+ host->clock = clock;
+}
+
+static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
+{
+ u8 pwr;
+
+ if (host->power == power)
+ return;
+
+ if (power == (unsigned short)-1) {
+ writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
+ goto out;
+ }
+
+ /*
+ * Spec says that we should clear the power reg before setting
+ * 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);
+
+ pwr = SDHCI_POWER_ON;
+
+ switch (1 << power) {
+ case MMC_VDD_165_195:
+ pwr |= SDHCI_POWER_180;
+ break;
+ case MMC_VDD_29_30:
+ case MMC_VDD_30_31:
+ pwr |= SDHCI_POWER_300;
+ break;
+ case MMC_VDD_32_33:
+ case MMC_VDD_33_34:
+ pwr |= SDHCI_POWER_330;
+ break;
+ default:
+ BUG();
+ }
+
+ /*
+ * At least the Marvell CaFe chip gets confused if we set the voltage
+ * 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);
+
+ writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL);
+
+out:
+ host->power = power;
+}
+
+/*****************************************************************************\
+ * *
+ * MMC callbacks *
+ * *
+\*****************************************************************************/
+
+static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+
+ host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ WARN_ON(host->mrq != NULL);
+
+#ifndef CONFIG_LEDS_CLASS
+ sdhci_activate_led(host);
+#endif
+
+ host->mrq = mrq;
+
+ if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
+ || (host->flags & SDHCI_DEVICE_DEAD)) {
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ } else
+ sdhci_send_command(host, mrq->cmd);
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+ u8 ctrl;
+
+ host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->flags & SDHCI_DEVICE_DEAD)
+ goto out;
+
+ /*
+ * Reset the chip on each power off.
+ * Should clear out any weird states.
+ */
+ if (ios->power_mode == MMC_POWER_OFF) {
+ writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ sdhci_init(host);
+ }
+
+ sdhci_set_clock(host, ios->clock);
+
+ if (ios->power_mode == MMC_POWER_OFF)
+ sdhci_set_power(host, -1);
+ else
+ sdhci_set_power(host, ios->vdd);
+
+ ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+
+ if (ios->bus_width == MMC_BUS_WIDTH_4)
+ ctrl |= SDHCI_CTRL_4BITBUS;
+ else
+ ctrl &= ~SDHCI_CTRL_4BITBUS;
+
+ if (ios->timing == MMC_TIMING_SD_HS)
+ ctrl |= SDHCI_CTRL_HISPD;
+ else
+ ctrl &= ~SDHCI_CTRL_HISPD;
+
+ writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+
+ /*
+ * Some (ENE) controllers go apeshit on some ios operation,
+ * signalling timeout and CRC errors even on CMD0. Resetting
+ * it on each ios seems to solve the problem.
+ */
+ if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
+ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+out:
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int sdhci_get_ro(struct mmc_host *mmc)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+ int present;
+
+ host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->flags & SDHCI_DEVICE_DEAD)
+ present = 0;
+ else
+ present = readl(host->ioaddr + SDHCI_PRESENT_STATE);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return !(present & SDHCI_WRITE_PROTECT);
+}
+
+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);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ 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);
+
+out:
+ mmiowb();
+
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static const struct mmc_host_ops sdhci_ops = {
+ .request = sdhci_request,
+ .set_ios = sdhci_set_ios,
+ .get_ro = sdhci_get_ro,
+ .enable_sdio_irq = sdhci_enable_sdio_irq,
+};
+
+/*****************************************************************************\
+ * *
+ * Tasklets *
+ * *
+\*****************************************************************************/
+
+static void sdhci_tasklet_card(unsigned long param)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+
+ host = (struct sdhci_host*)param;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+ if (host->mrq) {
+ printk(KERN_ERR "%s: Card removed during transfer!\n",
+ mmc_hostname(host->mmc));
+ printk(KERN_ERR "%s: Resetting controller.\n",
+ mmc_hostname(host->mmc));
+
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ }
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ mmc_detect_change(host->mmc, msecs_to_jiffies(200));
+}
+
+static void sdhci_tasklet_finish(unsigned long param)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+ struct mmc_request *mrq;
+
+ host = (struct sdhci_host*)param;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ del_timer(&host->timer);
+
+ mrq = host->mrq;
+
+ /*
+ * The controller needs a reset of internal state machines
+ * upon error conditions.
+ */
+ if (!(host->flags & SDHCI_DEVICE_DEAD) &&
+ (mrq->cmd->error ||
+ (mrq->data && (mrq->data->error ||
+ (mrq->data->stop && mrq->data->stop->error))) ||
+ (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
+
+ /* Some controllers need this kick or reset won't work here */
+ if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
+ unsigned int clock;
+
+ /* This is to force an update */
+ clock = host->clock;
+ host->clock = 0;
+ sdhci_set_clock(host, clock);
+ }
+
+ /* Spec says we should do both at the same time, but Ricoh
+ controllers do not like that. */
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+ }
+
+ host->mrq = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+
+#ifndef CONFIG_LEDS_CLASS
+ sdhci_deactivate_led(host);
+#endif
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ mmc_request_done(host->mmc, mrq);
+}
+
+static void sdhci_timeout_timer(unsigned long data)
+{
+ struct sdhci_host *host;
+ unsigned long flags;
+
+ host = (struct sdhci_host*)data;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->mrq) {
+ printk(KERN_ERR "%s: Timeout waiting for hardware "
+ "interrupt.\n", mmc_hostname(host->mmc));
+ sdhci_dumpregs(host);
+
+ if (host->data) {
+ host->data->error = -ETIMEDOUT;
+ sdhci_finish_data(host);
+ } else {
+ if (host->cmd)
+ host->cmd->error = -ETIMEDOUT;
+ else
+ host->mrq->cmd->error = -ETIMEDOUT;
+
+ tasklet_schedule(&host->finish_tasklet);
+ }
+ }
+
+ mmiowb();
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+/*****************************************************************************\
+ * *
+ * Interrupt handling *
+ * *
+\*****************************************************************************/
+
+static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
+{
+ BUG_ON(intmask == 0);
+
+ if (!host->cmd) {
+ printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
+ "though no command operation was in progress.\n",
+ mmc_hostname(host->mmc), (unsigned)intmask);
+ sdhci_dumpregs(host);
+ return;
+ }
+
+ if (intmask & SDHCI_INT_TIMEOUT)
+ host->cmd->error = -ETIMEDOUT;
+ else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
+ SDHCI_INT_INDEX))
+ host->cmd->error = -EILSEQ;
+
+ if (host->cmd->error) {
+ tasklet_schedule(&host->finish_tasklet);
+ return;
+ }
+
+ /*
+ * The host can send and interrupt when the busy state has
+ * ended, allowing us to wait without wasting CPU cycles.
+ * Unfortunately this is overloaded on the "data complete"
+ * interrupt, so we need to take some care when handling
+ * it.
+ *
+ * Note: The 1.0 specification is a bit ambiguous about this
+ * feature so there might be some problems with older
+ * controllers.
+ */
+ if (host->cmd->flags & MMC_RSP_BUSY) {
+ if (host->cmd->data)
+ DBG("Cannot wait for busy signal when also "
+ "doing a data transfer");
+ else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ))
+ return;
+
+ /* The controller does not support the end-of-busy IRQ,
+ * fall through and take the SDHCI_INT_RESPONSE */
+ }
+
+ if (intmask & SDHCI_INT_RESPONSE)
+ sdhci_finish_command(host);
+}
+
+static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
+{
+ BUG_ON(intmask == 0);
+
+ if (!host->data) {
+ /*
+ * The "data complete" interrupt is also used to
+ * indicate that a busy state has ended. See comment
+ * above in sdhci_cmd_irq().
+ */
+ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) {
+ if (intmask & SDHCI_INT_DATA_END) {
+ sdhci_finish_command(host);
+ return;
+ }
+ }
+
+ printk(KERN_ERR "%s: Got data interrupt 0x%08x even "
+ "though no data operation was in progress.\n",
+ mmc_hostname(host->mmc), (unsigned)intmask);
+ sdhci_dumpregs(host);
+
+ return;
+ }
+
+ if (intmask & SDHCI_INT_DATA_TIMEOUT)
+ host->data->error = -ETIMEDOUT;
+ else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT))
+ host->data->error = -EILSEQ;
+ else if (intmask & SDHCI_INT_ADMA_ERROR)
+ host->data->error = -EIO;
+
+ if (host->data->error)
+ sdhci_finish_data(host);
+ else {
+ if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
+ sdhci_transfer_pio(host);
+
+ /*
+ * We currently don't do anything fancy with DMA
+ * boundaries, but as we can't disable the feature
+ * 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);
+
+ if (intmask & SDHCI_INT_DATA_END) {
+ if (host->cmd) {
+ /*
+ * Data managed to finish before the
+ * command completed. Make sure we do
+ * things in the proper order.
+ */
+ host->data_early = 1;
+ } else {
+ sdhci_finish_data(host);
+ }
+ }
+ }
+}
+
+static irqreturn_t sdhci_irq(int irq, void *dev_id)
+{
+ irqreturn_t result;
+ struct sdhci_host* host = dev_id;
+ u32 intmask;
+ int cardint = 0;
+
+ spin_lock(&host->lock);
+
+ intmask = readl(host->ioaddr + SDHCI_INT_STATUS);
+
+ if (!intmask || intmask == 0xffffffff) {
+ result = IRQ_NONE;
+ goto out;
+ }
+
+ DBG("*** %s got interrupt: 0x%08x\n",
+ 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);
+ 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_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_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
+ }
+
+ intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
+
+ intmask &= ~SDHCI_INT_ERROR;
+
+ 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);
+ }
+
+ intmask &= ~SDHCI_INT_BUS_POWER;
+
+ if (intmask & SDHCI_INT_CARD_INT)
+ cardint = 1;
+
+ intmask &= ~SDHCI_INT_CARD_INT;
+
+ if (intmask) {
+ printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
+ mmc_hostname(host->mmc), intmask);
+ sdhci_dumpregs(host);
+
+ writel(intmask, host->ioaddr + SDHCI_INT_STATUS);
+ }
+
+ result = IRQ_HANDLED;
+
+ mmiowb();
+out:
+ spin_unlock(&host->lock);
+
+ /*
+ * We have to delay this as it calls back into the driver.
+ */
+ if (cardint)
+ mmc_signal_sdio_irq(host->mmc);
+
+ return result;
+}
+
+/*****************************************************************************\
+ * *
+ * Suspend/resume *
+ * *
+\*****************************************************************************/
+
+#ifdef CONFIG_PM
+
+int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
+{
+ int ret;
+
+ ret = mmc_suspend_host(host->mmc, state);
+ if (ret)
+ return ret;
+
+ free_irq(host->irq, host);
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(sdhci_suspend_host);
+
+int sdhci_resume_host(struct sdhci_host *host)
+{
+ int ret;
+
+ if (host->flags & SDHCI_USE_DMA) {
+ if (host->ops->enable_dma)
+ host->ops->enable_dma(host);
+ }
+
+ ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
+ mmc_hostname(host->mmc), host);
+ if (ret)
+ return ret;
+
+ sdhci_init(host);
+ mmiowb();
+
+ ret = mmc_resume_host(host->mmc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(sdhci_resume_host);
+
+#endif /* CONFIG_PM */
+
+/*****************************************************************************\
+ * *
+ * Device allocation/registration *
+ * *
+\*****************************************************************************/
+
+struct sdhci_host *sdhci_alloc_host(struct device *dev,
+ size_t priv_size)
+{
+ struct mmc_host *mmc;
+ struct sdhci_host *host;
+
+ WARN_ON(dev == NULL);
+
+ mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);
+ if (!mmc)
+ return ERR_PTR(-ENOMEM);
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+
+ return host;
+}
+
+EXPORT_SYMBOL_GPL(sdhci_alloc_host);
+
+int sdhci_add_host(struct sdhci_host *host)
+{
+ struct mmc_host *mmc;
+ unsigned int caps;
+ int ret;
+
+ WARN_ON(host == NULL);
+ if (host == NULL)
+ return -EINVAL;
+
+ mmc = host->mmc;
+
+ if (debug_quirks)
+ host->quirks = debug_quirks;
+
+ sdhci_reset(host, SDHCI_RESET_ALL);
+
+ host->version = readw(host->ioaddr + SDHCI_HOST_VERSION);
+ host->version = (host->version & SDHCI_SPEC_VER_MASK)
+ >> SDHCI_SPEC_VER_SHIFT;
+ if (host->version > SDHCI_SPEC_200) {
+ printk(KERN_ERR "%s: Unknown controller version (%d). "
+ "You may experience problems.\n", mmc_hostname(mmc),
+ host->version);
+ }
+
+ caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
+
+ if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
+ host->flags |= SDHCI_USE_DMA;
+ else if (!(caps & SDHCI_CAN_DO_DMA))
+ DBG("Controller doesn't have DMA capability\n");
+ else
+ host->flags |= SDHCI_USE_DMA;
+
+ if ((host->quirks & SDHCI_QUIRK_BROKEN_DMA) &&
+ (host->flags & SDHCI_USE_DMA)) {
+ DBG("Disabling DMA as it is marked broken\n");
+ host->flags &= ~SDHCI_USE_DMA;
+ }
+
+ if (host->flags & SDHCI_USE_DMA) {
+ if ((host->version >= SDHCI_SPEC_200) &&
+ (caps & SDHCI_CAN_DO_ADMA2))
+ host->flags |= SDHCI_USE_ADMA;
+ }
+
+ if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
+ (host->flags & SDHCI_USE_ADMA)) {
+ DBG("Disabling ADMA as it is marked broken\n");
+ host->flags &= ~SDHCI_USE_ADMA;
+ }
+
+ if (host->flags & SDHCI_USE_DMA) {
+ if (host->ops->enable_dma) {
+ if (host->ops->enable_dma(host)) {
+ printk(KERN_WARNING "%s: No suitable DMA "
+ "available. Falling back to PIO.\n",
+ mmc_hostname(mmc));
+ host->flags &= ~(SDHCI_USE_DMA | SDHCI_USE_ADMA);
+ }
+ }
+ }
+
+ if (host->flags & SDHCI_USE_ADMA) {
+ /*
+ * We need to allocate descriptors for all sg entries
+ * (128) and potentially one alignment transfer for
+ * each of those entries.
+ */
+ host->adma_desc = kmalloc((128 * 2 + 1) * 4, GFP_KERNEL);
+ host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);
+ if (!host->adma_desc || !host->align_buffer) {
+ kfree(host->adma_desc);
+ kfree(host->align_buffer);
+ printk(KERN_WARNING "%s: Unable to allocate ADMA "
+ "buffers. Falling back to standard DMA.\n",
+ mmc_hostname(mmc));
+ host->flags &= ~SDHCI_USE_ADMA;
+ }
+ }
+
+ /*
+ * If we use DMA, then it's up to the caller to set the DMA
+ * mask, but PIO does not need the hw shim so we set a new
+ * mask here in that case.
+ */
+ if (!(host->flags & SDHCI_USE_DMA)) {
+ host->dma_mask = DMA_BIT_MASK(64);
+ mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
+ }
+
+ host->max_clk =
+ (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
+ if (host->max_clk == 0) {
+ printk(KERN_ERR "%s: Hardware doesn't specify base clock "
+ "frequency.\n", mmc_hostname(mmc));
+ return -ENODEV;
+ }
+ 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 (caps & SDHCI_TIMEOUT_CLK_UNIT)
+ host->timeout_clk *= 1000;
+
+ /*
+ * Set host parameters.
+ */
+ mmc->ops = &sdhci_ops;
+ mmc->f_min = host->max_clk / 256;
+ mmc->f_max = host->max_clk;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
+
+ if ((caps & SDHCI_CAN_DO_HISPD) ||
+ (host->quirks & SDHCI_QUIRK_FORCE_HIGHSPEED))
+ mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+
+ mmc->ocr_avail = 0;
+ if (caps & SDHCI_CAN_VDD_330)
+ mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
+ if (caps & SDHCI_CAN_VDD_300)
+ mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31;
+ if (caps & SDHCI_CAN_VDD_180)
+ mmc->ocr_avail |= MMC_VDD_165_195;
+
+ if (mmc->ocr_avail == 0) {
+ printk(KERN_ERR "%s: Hardware doesn't report any "
+ "support voltages.\n", mmc_hostname(mmc));
+ return -ENODEV;
+ }
+
+ spin_lock_init(&host->lock);
+
+ /*
+ * Maximum number of segments. Depends on if the hardware
+ * can do scatter/gather or not.
+ */
+ if (host->flags & SDHCI_USE_ADMA)
+ mmc->max_hw_segs = 128;
+ else if (host->flags & SDHCI_USE_DMA)
+ mmc->max_hw_segs = 1;
+ else /* PIO */
+ mmc->max_hw_segs = 128;
+ mmc->max_phys_segs = 128;
+
+ /*
+ * Maximum number of sectors in one transfer. Limited by DMA boundary
+ * size (512KiB).
+ */
+ mmc->max_req_size = 524288;
+
+ /*
+ * Maximum segment size. Could be one segment with the maximum number
+ * of bytes. When doing hardware scatter/gather, each entry cannot
+ * be larger than 64 KiB though.
+ */
+ if (host->flags & SDHCI_USE_ADMA)
+ mmc->max_seg_size = 65536;
+ else
+ mmc->max_seg_size = mmc->max_req_size;
+
+ /*
+ * 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;
+
+ /*
+ * Maximum block count.
+ */
+ mmc->max_blk_count = 65535;
+
+ /*
+ * Init tasklets.
+ */
+ tasklet_init(&host->card_tasklet,
+ sdhci_tasklet_card, (unsigned long)host);
+ tasklet_init(&host->finish_tasklet,
+ sdhci_tasklet_finish, (unsigned long)host);
+
+ setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
+
+ ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
+ mmc_hostname(mmc), host);
+ if (ret)
+ goto untasklet;
+
+ sdhci_init(host);
+
+#ifdef CONFIG_MMC_DEBUG
+ sdhci_dumpregs(host);
+#endif
+
+#ifdef CONFIG_LEDS_CLASS
+ snprintf(host->led_name, sizeof(host->led_name),
+ "%s::", mmc_hostname(mmc));
+ host->led.name = host->led_name;
+ host->led.brightness = LED_OFF;
+ host->led.default_trigger = mmc_hostname(mmc);
+ host->led.brightness_set = sdhci_led_control;
+
+ ret = led_classdev_register(mmc_dev(mmc), &host->led);
+ if (ret)
+ goto reset;
+#endif
+
+ mmiowb();
+
+ mmc_add_host(mmc);
+
+ printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s%s\n",
+ mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
+ (host->flags & SDHCI_USE_ADMA)?"A":"",
+ (host->flags & SDHCI_USE_DMA)?"DMA":"PIO");
+
+ return 0;
+
+#ifdef CONFIG_LEDS_CLASS
+reset:
+ sdhci_reset(host, SDHCI_RESET_ALL);
+ free_irq(host->irq, host);
+#endif
+untasklet:
+ tasklet_kill(&host->card_tasklet);
+ tasklet_kill(&host->finish_tasklet);
+
+ return ret;
+}
+
+EXPORT_SYMBOL_GPL(sdhci_add_host);
+
+void sdhci_remove_host(struct sdhci_host *host, int dead)
+{
+ unsigned long flags;
+
+ if (dead) {
+ spin_lock_irqsave(&host->lock, flags);
+
+ host->flags |= SDHCI_DEVICE_DEAD;
+
+ if (host->mrq) {
+ printk(KERN_ERR "%s: Controller removed during "
+ " transfer!\n", mmc_hostname(host->mmc));
+
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+
+ mmc_remove_host(host->mmc);
+
+#ifdef CONFIG_LEDS_CLASS
+ led_classdev_unregister(&host->led);
+#endif
+
+ if (!dead)
+ sdhci_reset(host, SDHCI_RESET_ALL);
+
+ free_irq(host->irq, host);
+
+ del_timer_sync(&host->timer);
+
+ tasklet_kill(&host->card_tasklet);
+ tasklet_kill(&host->finish_tasklet);
+
+ kfree(host->adma_desc);
+ kfree(host->align_buffer);
+
+ host->adma_desc = NULL;
+ host->align_buffer = NULL;
+}
+
+EXPORT_SYMBOL_GPL(sdhci_remove_host);
+
+void sdhci_free_host(struct sdhci_host *host)
+{
+ mmc_free_host(host->mmc);
+}
+
+EXPORT_SYMBOL_GPL(sdhci_free_host);
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int __init sdhci_drv_init(void)
+{
+ printk(KERN_INFO DRIVER_NAME
+ ": Secure Digital Host Controller Interface driver\n");
+ printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
+
+ return 0;
+}
+
+static void __exit sdhci_drv_exit(void)
+{
+}
+
+module_init(sdhci_drv_init);
+module_exit(sdhci_drv_exit);
+
+module_param(debug_quirks, uint, 0444);
+
+MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface core driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
new file mode 100644
index 0000000..04c02ac
--- /dev/null
+++ b/drivers/mmc/host/sdhci.h
@@ -0,0 +1,291 @@
+/*
+ * linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver
+ *
+ * Copyright (C) 2005-2008 Pierre Ossman, 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/scatterlist.h>
+
+/*
+ * Controller registers
+ */
+
+#define SDHCI_DMA_ADDRESS 0x00
+
+#define SDHCI_BLOCK_SIZE 0x04
+#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
+
+#define SDHCI_BLOCK_COUNT 0x06
+
+#define SDHCI_ARGUMENT 0x08
+
+#define SDHCI_TRANSFER_MODE 0x0C
+#define SDHCI_TRNS_DMA 0x01
+#define SDHCI_TRNS_BLK_CNT_EN 0x02
+#define SDHCI_TRNS_ACMD12 0x04
+#define SDHCI_TRNS_READ 0x10
+#define SDHCI_TRNS_MULTI 0x20
+
+#define SDHCI_COMMAND 0x0E
+#define SDHCI_CMD_RESP_MASK 0x03
+#define SDHCI_CMD_CRC 0x08
+#define SDHCI_CMD_INDEX 0x10
+#define SDHCI_CMD_DATA 0x20
+
+#define SDHCI_CMD_RESP_NONE 0x00
+#define SDHCI_CMD_RESP_LONG 0x01
+#define SDHCI_CMD_RESP_SHORT 0x02
+#define SDHCI_CMD_RESP_SHORT_BUSY 0x03
+
+#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
+
+#define SDHCI_RESPONSE 0x10
+
+#define SDHCI_BUFFER 0x20
+
+#define SDHCI_PRESENT_STATE 0x24
+#define SDHCI_CMD_INHIBIT 0x00000001
+#define SDHCI_DATA_INHIBIT 0x00000002
+#define SDHCI_DOING_WRITE 0x00000100
+#define SDHCI_DOING_READ 0x00000200
+#define SDHCI_SPACE_AVAILABLE 0x00000400
+#define SDHCI_DATA_AVAILABLE 0x00000800
+#define SDHCI_CARD_PRESENT 0x00010000
+#define SDHCI_WRITE_PROTECT 0x00080000
+
+#define SDHCI_HOST_CONTROL 0x28
+#define SDHCI_CTRL_LED 0x01
+#define SDHCI_CTRL_4BITBUS 0x02
+#define SDHCI_CTRL_HISPD 0x04
+#define SDHCI_CTRL_DMA_MASK 0x18
+#define SDHCI_CTRL_SDMA 0x00
+#define SDHCI_CTRL_ADMA1 0x08
+#define SDHCI_CTRL_ADMA32 0x10
+#define SDHCI_CTRL_ADMA64 0x18
+
+#define SDHCI_POWER_CONTROL 0x29
+#define SDHCI_POWER_ON 0x01
+#define SDHCI_POWER_180 0x0A
+#define SDHCI_POWER_300 0x0C
+#define SDHCI_POWER_330 0x0E
+
+#define SDHCI_BLOCK_GAP_CONTROL 0x2A
+
+#define SDHCI_WAKE_UP_CONTROL 0x2B
+
+#define SDHCI_CLOCK_CONTROL 0x2C
+#define SDHCI_DIVIDER_SHIFT 8
+#define SDHCI_CLOCK_CARD_EN 0x0004
+#define SDHCI_CLOCK_INT_STABLE 0x0002
+#define SDHCI_CLOCK_INT_EN 0x0001
+
+#define SDHCI_TIMEOUT_CONTROL 0x2E
+
+#define SDHCI_SOFTWARE_RESET 0x2F
+#define SDHCI_RESET_ALL 0x01
+#define SDHCI_RESET_CMD 0x02
+#define SDHCI_RESET_DATA 0x04
+
+#define SDHCI_INT_STATUS 0x30
+#define SDHCI_INT_ENABLE 0x34
+#define SDHCI_SIGNAL_ENABLE 0x38
+#define SDHCI_INT_RESPONSE 0x00000001
+#define SDHCI_INT_DATA_END 0x00000002
+#define SDHCI_INT_DMA_END 0x00000008
+#define SDHCI_INT_SPACE_AVAIL 0x00000010
+#define SDHCI_INT_DATA_AVAIL 0x00000020
+#define SDHCI_INT_CARD_INSERT 0x00000040
+#define SDHCI_INT_CARD_REMOVE 0x00000080
+#define SDHCI_INT_CARD_INT 0x00000100
+#define SDHCI_INT_ERROR 0x00008000
+#define SDHCI_INT_TIMEOUT 0x00010000
+#define SDHCI_INT_CRC 0x00020000
+#define SDHCI_INT_END_BIT 0x00040000
+#define SDHCI_INT_INDEX 0x00080000
+#define SDHCI_INT_DATA_TIMEOUT 0x00100000
+#define SDHCI_INT_DATA_CRC 0x00200000
+#define SDHCI_INT_DATA_END_BIT 0x00400000
+#define SDHCI_INT_BUS_POWER 0x00800000
+#define SDHCI_INT_ACMD12ERR 0x01000000
+#define SDHCI_INT_ADMA_ERROR 0x02000000
+
+#define SDHCI_INT_NORMAL_MASK 0x00007FFF
+#define SDHCI_INT_ERROR_MASK 0xFFFF8000
+
+#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
+ SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
+#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
+ SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
+ SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
+ SDHCI_INT_DATA_END_BIT)
+
+#define SDHCI_ACMD12_ERR 0x3C
+
+/* 3E-3F reserved */
+
+#define SDHCI_CAPABILITIES 0x40
+#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
+#define SDHCI_TIMEOUT_CLK_SHIFT 0
+#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
+#define SDHCI_CLOCK_BASE_MASK 0x00003F00
+#define SDHCI_CLOCK_BASE_SHIFT 8
+#define SDHCI_MAX_BLOCK_MASK 0x00030000
+#define SDHCI_MAX_BLOCK_SHIFT 16
+#define SDHCI_CAN_DO_ADMA2 0x00080000
+#define SDHCI_CAN_DO_ADMA1 0x00100000
+#define SDHCI_CAN_DO_HISPD 0x00200000
+#define SDHCI_CAN_DO_DMA 0x00400000
+#define SDHCI_CAN_VDD_330 0x01000000
+#define SDHCI_CAN_VDD_300 0x02000000
+#define SDHCI_CAN_VDD_180 0x04000000
+#define SDHCI_CAN_64BIT 0x10000000
+
+/* 44-47 reserved for more caps */
+
+#define SDHCI_MAX_CURRENT 0x48
+
+/* 4C-4F reserved for more max current */
+
+#define SDHCI_SET_ACMD12_ERROR 0x50
+#define SDHCI_SET_INT_ERROR 0x52
+
+#define SDHCI_ADMA_ERROR 0x54
+
+/* 55-57 reserved */
+
+#define SDHCI_ADMA_ADDRESS 0x58
+
+/* 60-FB reserved */
+
+#define SDHCI_SLOT_INT_STATUS 0xFC
+
+#define SDHCI_HOST_VERSION 0xFE
+#define SDHCI_VENDOR_VER_MASK 0xFF00
+#define SDHCI_VENDOR_VER_SHIFT 8
+#define SDHCI_SPEC_VER_MASK 0x00FF
+#define SDHCI_SPEC_VER_SHIFT 0
+#define SDHCI_SPEC_100 0
+#define SDHCI_SPEC_200 1
+
+struct sdhci_ops;
+
+struct sdhci_host {
+ /* Data set by hardware interface driver */
+ const char *hw_name; /* Hardware bus name */
+
+ unsigned int quirks; /* Deviations from spec. */
+
+/* Controller doesn't honor resets unless we touch the clock register */
+#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
+/* Controller has bad caps bits, but really supports DMA */
+#define SDHCI_QUIRK_FORCE_DMA (1<<1)
+/* Controller doesn't like to be reset when there is no card inserted. */
+#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
+/* Controller doesn't like clearing the power reg before a change */
+#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
+/* Controller has flaky internal state so reset it on each ios change */
+#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4)
+/* Controller has an unusable DMA engine */
+#define SDHCI_QUIRK_BROKEN_DMA (1<<5)
+/* Controller has an unusable ADMA engine */
+#define SDHCI_QUIRK_BROKEN_ADMA (1<<6)
+/* Controller can only DMA from 32-bit aligned addresses */
+#define SDHCI_QUIRK_32BIT_DMA_ADDR (1<<7)
+/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
+#define SDHCI_QUIRK_32BIT_DMA_SIZE (1<<8)
+/* Controller can only ADMA chunks that are a multiple of 32 bits */
+#define SDHCI_QUIRK_32BIT_ADMA_SIZE (1<<9)
+/* Controller needs to be reset after each request to stay stable */
+#define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<10)
+/* Controller needs voltage and power writes to happen separately */
+#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<11)
+/* Controller provides an incorrect timeout value for transfers */
+#define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12)
+/* Controller has an issue with buffer bits for small transfers */
+#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
+/* Controller supports high speed but doesn't have the caps bit set */
+#define SDHCI_QUIRK_FORCE_HIGHSPEED (1<<14)
+/* Controller does not provide transfer-complete interrupt when not busy */
+#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<15)
+
+ int irq; /* Device IRQ */
+ void __iomem * ioaddr; /* Mapped address */
+
+ const struct sdhci_ops *ops; /* Low level hw interface */
+
+ /* Internal data */
+ struct mmc_host *mmc; /* MMC structure */
+ u64 dma_mask; /* custom DMA mask */
+
+#ifdef CONFIG_LEDS_CLASS
+ struct led_classdev led; /* LED control */
+ char led_name[32];
+#endif
+
+ spinlock_t lock; /* Mutex */
+
+ int flags; /* Host attributes */
+#define SDHCI_USE_DMA (1<<0) /* Host is DMA capable */
+#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
+#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
+#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
+
+ unsigned int version; /* SDHCI spec. version */
+
+ unsigned int max_clk; /* Max possible freq (MHz) */
+ unsigned int timeout_clk; /* Timeout freq (KHz) */
+
+ unsigned int clock; /* Current clock (MHz) */
+ unsigned short power; /* Current voltage */
+
+ struct mmc_request *mrq; /* Current request */
+ struct mmc_command *cmd; /* Current command */
+ struct mmc_data *data; /* Current data request */
+ unsigned int data_early:1; /* Data finished before cmd */
+
+ struct sg_mapping_iter sg_miter; /* SG state for PIO */
+ unsigned int blocks; /* remaining PIO blocks */
+
+ int sg_count; /* Mapped sg entries */
+
+ u8 *adma_desc; /* ADMA descriptor table */
+ u8 *align_buffer; /* Bounce buffer */
+
+ dma_addr_t adma_addr; /* Mapped ADMA descr. table */
+ dma_addr_t align_addr; /* Mapped bounce buffer */
+
+ struct tasklet_struct card_tasklet; /* Tasklet structures */
+ struct tasklet_struct finish_tasklet;
+
+ struct timer_list timer; /* Timer for timeouts */
+
+ unsigned long private[0] ____cacheline_aligned;
+};
+
+
+struct sdhci_ops {
+ int (*enable_dma)(struct sdhci_host *host);
+};
+
+
+extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
+ size_t priv_size);
+extern void sdhci_free_host(struct sdhci_host *host);
+
+static inline void *sdhci_priv(struct sdhci_host *host)
+{
+ return (void *)host->private;
+}
+
+extern int sdhci_add_host(struct sdhci_host *host);
+extern void sdhci_remove_host(struct sdhci_host *host, int dead);
+
+#ifdef CONFIG_PM
+extern int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state);
+extern int sdhci_resume_host(struct sdhci_host *host);
+#endif
diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c
new file mode 100644
index 0000000..1df44d9
--- /dev/null
+++ b/drivers/mmc/host/sdricoh_cs.c
@@ -0,0 +1,574 @@
+/*
+ * sdricoh_cs.c - driver for Ricoh Secure Digital Card Readers that can be
+ * found on some Ricoh RL5c476 II cardbus bridge
+ *
+ * Copyright (C) 2006 - 2008 Sascha Sommer <saschasommer@freenet.de>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+#define DEBUG
+#define VERBOSE_DEBUG
+*/
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/scatterlist.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <linux/io.h>
+
+#include <linux/mmc/host.h>
+
+#define DRIVER_NAME "sdricoh_cs"
+
+static unsigned int switchlocked;
+
+/* i/o region */
+#define SDRICOH_PCI_REGION 0
+#define SDRICOH_PCI_REGION_SIZE 0x1000
+
+/* registers */
+#define R104_VERSION 0x104
+#define R200_CMD 0x200
+#define R204_CMD_ARG 0x204
+#define R208_DATAIO 0x208
+#define R20C_RESP 0x20c
+#define R21C_STATUS 0x21c
+#define R2E0_INIT 0x2e0
+#define R2E4_STATUS_RESP 0x2e4
+#define R2F0_RESET 0x2f0
+#define R224_MODE 0x224
+#define R226_BLOCKSIZE 0x226
+#define R228_POWER 0x228
+#define R230_DATA 0x230
+
+/* flags for the R21C_STATUS register */
+#define STATUS_CMD_FINISHED 0x00000001
+#define STATUS_TRANSFER_FINISHED 0x00000004
+#define STATUS_CARD_INSERTED 0x00000020
+#define STATUS_CARD_LOCKED 0x00000080
+#define STATUS_CMD_TIMEOUT 0x00400000
+#define STATUS_READY_TO_READ 0x01000000
+#define STATUS_READY_TO_WRITE 0x02000000
+#define STATUS_BUSY 0x40000000
+
+/* timeouts */
+#define INIT_TIMEOUT 100
+#define CMD_TIMEOUT 100000
+#define TRANSFER_TIMEOUT 100000
+#define BUSY_TIMEOUT 32767
+
+/* list of supported pcmcia devices */
+static struct pcmcia_device_id pcmcia_ids[] = {
+ /* vendor and device strings followed by their crc32 hashes */
+ PCMCIA_DEVICE_PROD_ID12("RICOH", "Bay1Controller", 0xd9f522ed,
+ 0xc3901202),
+ PCMCIA_DEVICE_NULL,
+};
+
+MODULE_DEVICE_TABLE(pcmcia, pcmcia_ids);
+
+/* mmc privdata */
+struct sdricoh_host {
+ struct device *dev;
+ struct mmc_host *mmc; /* MMC structure */
+ unsigned char __iomem *iobase;
+ struct pci_dev *pci_dev;
+ int app_cmd;
+};
+
+/***************** register i/o helper functions *****************************/
+
+static inline unsigned int sdricoh_readl(struct sdricoh_host *host,
+ unsigned int reg)
+{
+ unsigned int value = readl(host->iobase + reg);
+ dev_vdbg(host->dev, "rl %x 0x%x\n", reg, value);
+ return value;
+}
+
+static inline void sdricoh_writel(struct sdricoh_host *host, unsigned int reg,
+ unsigned int value)
+{
+ writel(value, host->iobase + reg);
+ dev_vdbg(host->dev, "wl %x 0x%x\n", reg, value);
+
+}
+
+static inline unsigned int sdricoh_readw(struct sdricoh_host *host,
+ unsigned int reg)
+{
+ unsigned int value = readw(host->iobase + reg);
+ dev_vdbg(host->dev, "rb %x 0x%x\n", reg, value);
+ return value;
+}
+
+static inline void sdricoh_writew(struct sdricoh_host *host, unsigned int reg,
+ unsigned short value)
+{
+ writew(value, host->iobase + reg);
+ dev_vdbg(host->dev, "ww %x 0x%x\n", reg, value);
+}
+
+static inline unsigned int sdricoh_readb(struct sdricoh_host *host,
+ unsigned int reg)
+{
+ unsigned int value = readb(host->iobase + reg);
+ dev_vdbg(host->dev, "rb %x 0x%x\n", reg, value);
+ return value;
+}
+
+static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted,
+ unsigned int timeout){
+ unsigned int loop;
+ unsigned int status = 0;
+ struct device *dev = host->dev;
+ for (loop = 0; loop < timeout; loop++) {
+ status = sdricoh_readl(host, R21C_STATUS);
+ sdricoh_writel(host, R2E4_STATUS_RESP, status);
+ if (status & wanted)
+ break;
+ }
+
+ if (loop == timeout) {
+ dev_err(dev, "query_status: timeout waiting for %x\n", wanted);
+ return -ETIMEDOUT;
+ }
+
+ /* do not do this check in the loop as some commands fail otherwise */
+ if (status & 0x7F0000) {
+ dev_err(dev, "waiting for status bit %x failed\n", wanted);
+ return -EINVAL;
+ }
+ return 0;
+
+}
+
+static int sdricoh_mmc_cmd(struct sdricoh_host *host, unsigned char opcode,
+ unsigned int arg)
+{
+ unsigned int status;
+ int result = 0;
+ unsigned int loop = 0;
+ /* reset status reg? */
+ sdricoh_writel(host, R21C_STATUS, 0x18);
+ /* fill parameters */
+ sdricoh_writel(host, R204_CMD_ARG, arg);
+ sdricoh_writel(host, R200_CMD, (0x10000 << 8) | opcode);
+ /* wait for command completion */
+ if (opcode) {
+ for (loop = 0; loop < CMD_TIMEOUT; loop++) {
+ status = sdricoh_readl(host, R21C_STATUS);
+ sdricoh_writel(host, R2E4_STATUS_RESP, status);
+ if (status & STATUS_CMD_FINISHED)
+ break;
+ }
+ /* don't check for timeout in the loop it is not always
+ reset correctly
+ */
+ if (loop == CMD_TIMEOUT || status & STATUS_CMD_TIMEOUT)
+ result = -ETIMEDOUT;
+
+ }
+
+ return result;
+
+}
+
+static int sdricoh_reset(struct sdricoh_host *host)
+{
+ dev_dbg(host->dev, "reset\n");
+ sdricoh_writel(host, R2F0_RESET, 0x10001);
+ sdricoh_writel(host, R2E0_INIT, 0x10000);
+ if (sdricoh_readl(host, R2E0_INIT) != 0x10000)
+ return -EIO;
+ sdricoh_writel(host, R2E0_INIT, 0x10007);
+
+ sdricoh_writel(host, R224_MODE, 0x2000000);
+ sdricoh_writel(host, R228_POWER, 0xe0);
+
+
+ /* status register ? */
+ sdricoh_writel(host, R21C_STATUS, 0x18);
+
+ return 0;
+}
+
+static int sdricoh_blockio(struct sdricoh_host *host, int read,
+ u8 *buf, int len)
+{
+ int size;
+ u32 data = 0;
+ /* wait until the data is available */
+ if (read) {
+ if (sdricoh_query_status(host, STATUS_READY_TO_READ,
+ TRANSFER_TIMEOUT))
+ return -ETIMEDOUT;
+ sdricoh_writel(host, R21C_STATUS, 0x18);
+ /* read data */
+ while (len) {
+ data = sdricoh_readl(host, R230_DATA);
+ size = min(len, 4);
+ len -= size;
+ while (size) {
+ *buf = data & 0xFF;
+ buf++;
+ data >>= 8;
+ size--;
+ }
+ }
+ } else {
+ if (sdricoh_query_status(host, STATUS_READY_TO_WRITE,
+ TRANSFER_TIMEOUT))
+ return -ETIMEDOUT;
+ sdricoh_writel(host, R21C_STATUS, 0x18);
+ /* write data */
+ while (len) {
+ size = min(len, 4);
+ len -= size;
+ while (size) {
+ data >>= 8;
+ data |= (u32)*buf << 24;
+ buf++;
+ size--;
+ }
+ sdricoh_writel(host, R230_DATA, data);
+ }
+ }
+
+ if (len)
+ return -EIO;
+
+ return 0;
+}
+
+static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct sdricoh_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd = mrq->cmd;
+ struct mmc_data *data = cmd->data;
+ struct device *dev = host->dev;
+ unsigned char opcode = cmd->opcode;
+ int i;
+
+ dev_dbg(dev, "=============================\n");
+ dev_dbg(dev, "sdricoh_request opcode=%i\n", opcode);
+
+ sdricoh_writel(host, R21C_STATUS, 0x18);
+
+ /* MMC_APP_CMDs need some special handling */
+ if (host->app_cmd) {
+ opcode |= 64;
+ host->app_cmd = 0;
+ } else if (opcode == 55)
+ host->app_cmd = 1;
+
+ /* read/write commands seem to require this */
+ if (data) {
+ sdricoh_writew(host, R226_BLOCKSIZE, data->blksz);
+ sdricoh_writel(host, R208_DATAIO, 0);
+ }
+
+ cmd->error = sdricoh_mmc_cmd(host, opcode, cmd->arg);
+
+ /* read response buffer */
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
+ /* CRC is stripped so we need to do some shifting. */
+ for (i = 0; i < 4; i++) {
+ cmd->resp[i] =
+ sdricoh_readl(host,
+ R20C_RESP + (3 - i) * 4) << 8;
+ if (i != 3)
+ cmd->resp[i] |=
+ sdricoh_readb(host, R20C_RESP +
+ (3 - i) * 4 - 1);
+ }
+ } else
+ cmd->resp[0] = sdricoh_readl(host, R20C_RESP);
+ }
+
+ /* transfer data */
+ if (data && cmd->error == 0) {
+ dev_dbg(dev, "transfer: blksz %i blocks %i sg_len %i "
+ "sg length %i\n", data->blksz, data->blocks,
+ data->sg_len, data->sg->length);
+
+ /* enter data reading mode */
+ sdricoh_writel(host, R21C_STATUS, 0x837f031e);
+ for (i = 0; i < data->blocks; i++) {
+ size_t len = data->blksz;
+ u8 *buf;
+ struct page *page;
+ int result;
+ page = sg_page(data->sg);
+
+ buf = kmap(page) + data->sg->offset + (len * i);
+ result =
+ sdricoh_blockio(host,
+ data->flags & MMC_DATA_READ, buf, len);
+ kunmap(page);
+ flush_dcache_page(page);
+ if (result) {
+ dev_err(dev, "sdricoh_request: cmd %i "
+ "block transfer failed\n", cmd->opcode);
+ cmd->error = result;
+ break;
+ } else
+ data->bytes_xfered += len;
+ }
+
+ sdricoh_writel(host, R208_DATAIO, 1);
+
+ if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED,
+ TRANSFER_TIMEOUT)) {
+ dev_err(dev, "sdricoh_request: transfer end error\n");
+ cmd->error = -EINVAL;
+ }
+ }
+ /* FIXME check busy flag */
+
+ mmc_request_done(mmc, mrq);
+ dev_dbg(dev, "=============================\n");
+}
+
+static void sdricoh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdricoh_host *host = mmc_priv(mmc);
+ dev_dbg(host->dev, "set_ios\n");
+
+ if (ios->power_mode == MMC_POWER_ON) {
+ sdricoh_writel(host, R228_POWER, 0xc0e0);
+
+ if (ios->bus_width == MMC_BUS_WIDTH_4) {
+ sdricoh_writel(host, R224_MODE, 0x2000300);
+ sdricoh_writel(host, R228_POWER, 0x40e0);
+ } else {
+ sdricoh_writel(host, R224_MODE, 0x2000340);
+ }
+
+ } else if (ios->power_mode == MMC_POWER_UP) {
+ sdricoh_writel(host, R224_MODE, 0x2000320);
+ sdricoh_writel(host, R228_POWER, 0xe0);
+ }
+}
+
+static int sdricoh_get_ro(struct mmc_host *mmc)
+{
+ struct sdricoh_host *host = mmc_priv(mmc);
+ unsigned int status;
+
+ status = sdricoh_readl(host, R21C_STATUS);
+ sdricoh_writel(host, R2E4_STATUS_RESP, status);
+
+ /* some notebooks seem to have the locked flag switched */
+ if (switchlocked)
+ return !(status & STATUS_CARD_LOCKED);
+
+ return (status & STATUS_CARD_LOCKED);
+}
+
+static struct mmc_host_ops sdricoh_ops = {
+ .request = sdricoh_request,
+ .set_ios = sdricoh_set_ios,
+ .get_ro = sdricoh_get_ro,
+};
+
+/* initialize the control and register it to the mmc framework */
+static int sdricoh_init_mmc(struct pci_dev *pci_dev,
+ struct pcmcia_device *pcmcia_dev)
+{
+ int result = 0;
+ void __iomem *iobase = NULL;
+ struct mmc_host *mmc = NULL;
+ struct sdricoh_host *host = NULL;
+ struct device *dev = &pcmcia_dev->dev;
+ /* map iomem */
+ if (pci_resource_len(pci_dev, SDRICOH_PCI_REGION) !=
+ SDRICOH_PCI_REGION_SIZE) {
+ dev_dbg(dev, "unexpected pci resource len\n");
+ return -ENODEV;
+ }
+ iobase =
+ pci_iomap(pci_dev, SDRICOH_PCI_REGION, SDRICOH_PCI_REGION_SIZE);
+ if (!iobase) {
+ dev_err(dev, "unable to map iobase\n");
+ return -ENODEV;
+ }
+ /* check version? */
+ if (readl(iobase + R104_VERSION) != 0x4000) {
+ dev_dbg(dev, "no supported mmc controller found\n");
+ result = -ENODEV;
+ goto err;
+ }
+ /* allocate privdata */
+ mmc = pcmcia_dev->priv =
+ mmc_alloc_host(sizeof(struct sdricoh_host), &pcmcia_dev->dev);
+ if (!mmc) {
+ dev_err(dev, "mmc_alloc_host failed\n");
+ result = -ENOMEM;
+ goto err;
+ }
+ host = mmc_priv(mmc);
+
+ host->iobase = iobase;
+ host->dev = dev;
+ host->pci_dev = pci_dev;
+
+ mmc->ops = &sdricoh_ops;
+
+ /* FIXME: frequency and voltage handling is done by the controller
+ */
+ mmc->f_min = 450000;
+ mmc->f_max = 24000000;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+ mmc->max_seg_size = 1024 * 512;
+ mmc->max_blk_size = 512;
+
+ /* reset the controler */
+ if (sdricoh_reset(host)) {
+ dev_dbg(dev, "could not reset\n");
+ result = -EIO;
+ goto err;
+
+ }
+
+ result = mmc_add_host(mmc);
+
+ if (!result) {
+ dev_dbg(dev, "mmc host registered\n");
+ return 0;
+ }
+
+err:
+ if (iobase)
+ iounmap(iobase);
+ if (mmc)
+ mmc_free_host(mmc);
+
+ return result;
+}
+
+/* search for supported mmc controllers */
+static int sdricoh_pcmcia_probe(struct pcmcia_device *pcmcia_dev)
+{
+ struct pci_dev *pci_dev = NULL;
+
+ dev_info(&pcmcia_dev->dev, "Searching MMC controller for pcmcia device"
+ " %s %s ...\n", pcmcia_dev->prod_id[0], pcmcia_dev->prod_id[1]);
+
+ /* search pci cardbus bridge that contains the mmc controler */
+ /* the io region is already claimed by yenta_socket... */
+ while ((pci_dev =
+ pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_RL5C476,
+ pci_dev))) {
+ /* try to init the device */
+ if (!sdricoh_init_mmc(pci_dev, pcmcia_dev)) {
+ dev_info(&pcmcia_dev->dev, "MMC controller found\n");
+ return 0;
+ }
+
+ }
+ dev_err(&pcmcia_dev->dev, "No MMC controller was found.\n");
+ return -ENODEV;
+}
+
+static void sdricoh_pcmcia_detach(struct pcmcia_device *link)
+{
+ struct mmc_host *mmc = link->priv;
+
+ dev_dbg(&link->dev, "detach\n");
+
+ /* remove mmc host */
+ if (mmc) {
+ struct sdricoh_host *host = mmc_priv(mmc);
+ mmc_remove_host(mmc);
+ pci_iounmap(host->pci_dev, host->iobase);
+ pci_dev_put(host->pci_dev);
+ mmc_free_host(mmc);
+ }
+ pcmcia_disable_device(link);
+
+}
+
+#ifdef CONFIG_PM
+static int sdricoh_pcmcia_suspend(struct pcmcia_device *link)
+{
+ struct mmc_host *mmc = link->priv;
+ dev_dbg(&link->dev, "suspend\n");
+ mmc_suspend_host(mmc, PMSG_SUSPEND);
+ return 0;
+}
+
+static int sdricoh_pcmcia_resume(struct pcmcia_device *link)
+{
+ struct mmc_host *mmc = link->priv;
+ dev_dbg(&link->dev, "resume\n");
+ sdricoh_reset(mmc_priv(mmc));
+ mmc_resume_host(mmc);
+ return 0;
+}
+#else
+#define sdricoh_pcmcia_suspend NULL
+#define sdricoh_pcmcia_resume NULL
+#endif
+
+static struct pcmcia_driver sdricoh_driver = {
+ .drv = {
+ .name = DRIVER_NAME,
+ },
+ .probe = sdricoh_pcmcia_probe,
+ .remove = sdricoh_pcmcia_detach,
+ .id_table = pcmcia_ids,
+ .suspend = sdricoh_pcmcia_suspend,
+ .resume = sdricoh_pcmcia_resume,
+};
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int __init sdricoh_drv_init(void)
+{
+ return pcmcia_register_driver(&sdricoh_driver);
+}
+
+static void __exit sdricoh_drv_exit(void)
+{
+ pcmcia_unregister_driver(&sdricoh_driver);
+}
+
+module_init(sdricoh_drv_init);
+module_exit(sdricoh_drv_exit);
+
+module_param(switchlocked, uint, 0444);
+
+MODULE_AUTHOR("Sascha Sommer <saschasommer@freenet.de>");
+MODULE_DESCRIPTION("Ricoh PCMCIA Secure Digital Interface driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM_DESC(switchlocked, "Switch the cards locked status."
+ "Use this when unlocked cards are shown readonly (default 0)");
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
new file mode 100644
index 0000000..82554dd
--- /dev/null
+++ b/drivers/mmc/host/tifm_sd.c
@@ -0,0 +1,1095 @@
+/*
+ * tifm_sd.c - TI FlashMedia driver
+ *
+ * Copyright (C) 2006 Alex Dubov <oakad@yahoo.com>
+ *
+ * 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.
+ *
+ * Special thanks to Brad Campbell for extensive testing of this driver.
+ *
+ */
+
+
+#include <linux/tifm.h>
+#include <linux/mmc/host.h>
+#include <linux/highmem.h>
+#include <linux/scatterlist.h>
+#include <asm/io.h>
+
+#define DRIVER_NAME "tifm_sd"
+#define DRIVER_VERSION "0.8"
+
+static int no_dma = 0;
+static int fixed_timeout = 0;
+module_param(no_dma, bool, 0644);
+module_param(fixed_timeout, bool, 0644);
+
+/* Constants here are mostly from OMAP5912 datasheet */
+#define TIFM_MMCSD_RESET 0x0002
+#define TIFM_MMCSD_CLKMASK 0x03ff
+#define TIFM_MMCSD_POWER 0x0800
+#define TIFM_MMCSD_4BBUS 0x8000
+#define TIFM_MMCSD_RXDE 0x8000 /* rx dma enable */
+#define TIFM_MMCSD_TXDE 0x0080 /* tx dma enable */
+#define TIFM_MMCSD_BUFINT 0x0c00 /* set bits: AE, AF */
+#define TIFM_MMCSD_DPE 0x0020 /* data timeout counted in kilocycles */
+#define TIFM_MMCSD_INAB 0x0080 /* abort / initialize command */
+#define TIFM_MMCSD_READ 0x8000
+
+#define TIFM_MMCSD_ERRMASK 0x01e0 /* set bits: CCRC, CTO, DCRC, DTO */
+#define TIFM_MMCSD_EOC 0x0001 /* end of command phase */
+#define TIFM_MMCSD_CD 0x0002 /* card detect */
+#define TIFM_MMCSD_CB 0x0004 /* card enter busy state */
+#define TIFM_MMCSD_BRS 0x0008 /* block received/sent */
+#define TIFM_MMCSD_EOFB 0x0010 /* card exit busy state */
+#define TIFM_MMCSD_DTO 0x0020 /* data time-out */
+#define TIFM_MMCSD_DCRC 0x0040 /* data crc error */
+#define TIFM_MMCSD_CTO 0x0080 /* command time-out */
+#define TIFM_MMCSD_CCRC 0x0100 /* command crc error */
+#define TIFM_MMCSD_AF 0x0400 /* fifo almost full */
+#define TIFM_MMCSD_AE 0x0800 /* fifo almost empty */
+#define TIFM_MMCSD_OCRB 0x1000 /* OCR busy */
+#define TIFM_MMCSD_CIRQ 0x2000 /* card irq (cmd40/sdio) */
+#define TIFM_MMCSD_CERR 0x4000 /* card status error */
+
+#define TIFM_MMCSD_ODTO 0x0040 /* open drain / extended timeout */
+#define TIFM_MMCSD_CARD_RO 0x0200 /* card is read-only */
+
+#define TIFM_MMCSD_FIFO_SIZE 0x0020
+
+#define TIFM_MMCSD_RSP_R0 0x0000
+#define TIFM_MMCSD_RSP_R1 0x0100
+#define TIFM_MMCSD_RSP_R2 0x0200
+#define TIFM_MMCSD_RSP_R3 0x0300
+#define TIFM_MMCSD_RSP_R4 0x0400
+#define TIFM_MMCSD_RSP_R5 0x0500
+#define TIFM_MMCSD_RSP_R6 0x0600
+
+#define TIFM_MMCSD_RSP_BUSY 0x0800
+
+#define TIFM_MMCSD_CMD_BC 0x0000
+#define TIFM_MMCSD_CMD_BCR 0x1000
+#define TIFM_MMCSD_CMD_AC 0x2000
+#define TIFM_MMCSD_CMD_ADTC 0x3000
+
+#define TIFM_MMCSD_MAX_BLOCK_SIZE 0x0800UL
+
+enum {
+ CMD_READY = 0x0001,
+ FIFO_READY = 0x0002,
+ BRS_READY = 0x0004,
+ SCMD_ACTIVE = 0x0008,
+ SCMD_READY = 0x0010,
+ CARD_BUSY = 0x0020,
+ DATA_CARRY = 0x0040
+};
+
+struct tifm_sd {
+ struct tifm_dev *dev;
+
+ unsigned short eject:1,
+ open_drain:1,
+ no_dma:1;
+ unsigned short cmd_flags;
+
+ unsigned int clk_freq;
+ unsigned int clk_div;
+ unsigned long timeout_jiffies;
+
+ struct tasklet_struct finish_tasklet;
+ struct timer_list timer;
+ struct mmc_request *req;
+
+ int sg_len;
+ int sg_pos;
+ unsigned int block_pos;
+ struct scatterlist bounce_buf;
+ unsigned char bounce_buf_data[TIFM_MMCSD_MAX_BLOCK_SIZE];
+};
+
+/* for some reason, host won't respond correctly to readw/writew */
+static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg,
+ unsigned int off, unsigned int cnt)
+{
+ struct tifm_dev *sock = host->dev;
+ unsigned char *buf;
+ unsigned int pos = 0, val;
+
+ buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + off;
+ if (host->cmd_flags & DATA_CARRY) {
+ buf[pos++] = host->bounce_buf_data[0];
+ host->cmd_flags &= ~DATA_CARRY;
+ }
+
+ while (pos < cnt) {
+ val = readl(sock->addr + SOCK_MMCSD_DATA);
+ buf[pos++] = val & 0xff;
+ if (pos == cnt) {
+ host->bounce_buf_data[0] = (val >> 8) & 0xff;
+ host->cmd_flags |= DATA_CARRY;
+ break;
+ }
+ buf[pos++] = (val >> 8) & 0xff;
+ }
+ kunmap_atomic(buf - off, KM_BIO_DST_IRQ);
+}
+
+static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
+ unsigned int off, unsigned int cnt)
+{
+ struct tifm_dev *sock = host->dev;
+ unsigned char *buf;
+ unsigned int pos = 0, val;
+
+ buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + off;
+ if (host->cmd_flags & DATA_CARRY) {
+ val = host->bounce_buf_data[0] | ((buf[pos++] << 8) & 0xff00);
+ writel(val, sock->addr + SOCK_MMCSD_DATA);
+ host->cmd_flags &= ~DATA_CARRY;
+ }
+
+ while (pos < cnt) {
+ val = buf[pos++];
+ if (pos == cnt) {
+ host->bounce_buf_data[0] = val & 0xff;
+ host->cmd_flags |= DATA_CARRY;
+ break;
+ }
+ val |= (buf[pos++] << 8) & 0xff00;
+ writel(val, sock->addr + SOCK_MMCSD_DATA);
+ }
+ kunmap_atomic(buf - off, KM_BIO_SRC_IRQ);
+}
+
+static void tifm_sd_transfer_data(struct tifm_sd *host)
+{
+ struct mmc_data *r_data = host->req->cmd->data;
+ struct scatterlist *sg = r_data->sg;
+ unsigned int off, cnt, t_size = TIFM_MMCSD_FIFO_SIZE * 2;
+ unsigned int p_off, p_cnt;
+ struct page *pg;
+
+ if (host->sg_pos == host->sg_len)
+ return;
+ while (t_size) {
+ cnt = sg[host->sg_pos].length - host->block_pos;
+ if (!cnt) {
+ host->block_pos = 0;
+ host->sg_pos++;
+ if (host->sg_pos == host->sg_len) {
+ if ((r_data->flags & MMC_DATA_WRITE)
+ && (host->cmd_flags & DATA_CARRY))
+ writel(host->bounce_buf_data[0],
+ host->dev->addr
+ + SOCK_MMCSD_DATA);
+
+ return;
+ }
+ cnt = sg[host->sg_pos].length;
+ }
+ off = sg[host->sg_pos].offset + host->block_pos;
+
+ pg = nth_page(sg_page(&sg[host->sg_pos]), off >> PAGE_SHIFT);
+ p_off = offset_in_page(off);
+ p_cnt = PAGE_SIZE - p_off;
+ p_cnt = min(p_cnt, cnt);
+ p_cnt = min(p_cnt, t_size);
+
+ if (r_data->flags & MMC_DATA_READ)
+ tifm_sd_read_fifo(host, pg, p_off, p_cnt);
+ else if (r_data->flags & MMC_DATA_WRITE)
+ tifm_sd_write_fifo(host, pg, p_off, p_cnt);
+
+ t_size -= p_cnt;
+ host->block_pos += p_cnt;
+ }
+}
+
+static void tifm_sd_copy_page(struct page *dst, unsigned int dst_off,
+ struct page *src, unsigned int src_off,
+ unsigned int count)
+{
+ unsigned char *src_buf = kmap_atomic(src, KM_BIO_SRC_IRQ) + src_off;
+ unsigned char *dst_buf = kmap_atomic(dst, KM_BIO_DST_IRQ) + dst_off;
+
+ memcpy(dst_buf, src_buf, count);
+
+ kunmap_atomic(dst_buf - dst_off, KM_BIO_DST_IRQ);
+ kunmap_atomic(src_buf - src_off, KM_BIO_SRC_IRQ);
+}
+
+static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data)
+{
+ struct scatterlist *sg = r_data->sg;
+ unsigned int t_size = r_data->blksz;
+ unsigned int off, cnt;
+ unsigned int p_off, p_cnt;
+ struct page *pg;
+
+ dev_dbg(&host->dev->dev, "bouncing block\n");
+ while (t_size) {
+ cnt = sg[host->sg_pos].length - host->block_pos;
+ if (!cnt) {
+ host->block_pos = 0;
+ host->sg_pos++;
+ if (host->sg_pos == host->sg_len)
+ return;
+ cnt = sg[host->sg_pos].length;
+ }
+ off = sg[host->sg_pos].offset + host->block_pos;
+
+ pg = nth_page(sg_page(&sg[host->sg_pos]), off >> PAGE_SHIFT);
+ p_off = offset_in_page(off);
+ p_cnt = PAGE_SIZE - p_off;
+ p_cnt = min(p_cnt, cnt);
+ p_cnt = min(p_cnt, t_size);
+
+ if (r_data->flags & MMC_DATA_WRITE)
+ tifm_sd_copy_page(sg_page(&host->bounce_buf),
+ r_data->blksz - t_size,
+ pg, p_off, p_cnt);
+ else if (r_data->flags & MMC_DATA_READ)
+ tifm_sd_copy_page(pg, p_off, sg_page(&host->bounce_buf),
+ r_data->blksz - t_size, p_cnt);
+
+ t_size -= p_cnt;
+ host->block_pos += p_cnt;
+ }
+}
+
+static int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data)
+{
+ struct tifm_dev *sock = host->dev;
+ unsigned int t_size = TIFM_DMA_TSIZE * r_data->blksz;
+ unsigned int dma_len, dma_blk_cnt, dma_off;
+ struct scatterlist *sg = NULL;
+ unsigned long flags;
+
+ if (host->sg_pos == host->sg_len)
+ return 1;
+
+ if (host->cmd_flags & DATA_CARRY) {
+ host->cmd_flags &= ~DATA_CARRY;
+ local_irq_save(flags);
+ tifm_sd_bounce_block(host, r_data);
+ local_irq_restore(flags);
+ if (host->sg_pos == host->sg_len)
+ return 1;
+ }
+
+ dma_len = sg_dma_len(&r_data->sg[host->sg_pos]) - host->block_pos;
+ if (!dma_len) {
+ host->block_pos = 0;
+ host->sg_pos++;
+ if (host->sg_pos == host->sg_len)
+ return 1;
+ dma_len = sg_dma_len(&r_data->sg[host->sg_pos]);
+ }
+
+ if (dma_len < t_size) {
+ dma_blk_cnt = dma_len / r_data->blksz;
+ dma_off = host->block_pos;
+ host->block_pos += dma_blk_cnt * r_data->blksz;
+ } else {
+ dma_blk_cnt = TIFM_DMA_TSIZE;
+ dma_off = host->block_pos;
+ host->block_pos += t_size;
+ }
+
+ if (dma_blk_cnt)
+ sg = &r_data->sg[host->sg_pos];
+ else if (dma_len) {
+ if (r_data->flags & MMC_DATA_WRITE) {
+ local_irq_save(flags);
+ tifm_sd_bounce_block(host, r_data);
+ local_irq_restore(flags);
+ } else
+ host->cmd_flags |= DATA_CARRY;
+
+ sg = &host->bounce_buf;
+ dma_off = 0;
+ dma_blk_cnt = 1;
+ } else
+ return 1;
+
+ dev_dbg(&sock->dev, "setting dma for %d blocks\n", dma_blk_cnt);
+ writel(sg_dma_address(sg) + dma_off, sock->addr + SOCK_DMA_ADDRESS);
+ if (r_data->flags & MMC_DATA_WRITE)
+ writel((dma_blk_cnt << 8) | TIFM_DMA_TX | TIFM_DMA_EN,
+ sock->addr + SOCK_DMA_CONTROL);
+ else
+ writel((dma_blk_cnt << 8) | TIFM_DMA_EN,
+ sock->addr + SOCK_DMA_CONTROL);
+
+ return 0;
+}
+
+static unsigned int tifm_sd_op_flags(struct mmc_command *cmd)
+{
+ unsigned int rc = 0;
+
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ rc |= TIFM_MMCSD_RSP_R0;
+ break;
+ case MMC_RSP_R1B:
+ rc |= TIFM_MMCSD_RSP_BUSY; // deliberate fall-through
+ case MMC_RSP_R1:
+ rc |= TIFM_MMCSD_RSP_R1;
+ break;
+ case MMC_RSP_R2:
+ rc |= TIFM_MMCSD_RSP_R2;
+ break;
+ case MMC_RSP_R3:
+ rc |= TIFM_MMCSD_RSP_R3;
+ break;
+ default:
+ BUG();
+ }
+
+ switch (mmc_cmd_type(cmd)) {
+ case MMC_CMD_BC:
+ rc |= TIFM_MMCSD_CMD_BC;
+ break;
+ case MMC_CMD_BCR:
+ rc |= TIFM_MMCSD_CMD_BCR;
+ break;
+ case MMC_CMD_AC:
+ rc |= TIFM_MMCSD_CMD_AC;
+ break;
+ case MMC_CMD_ADTC:
+ rc |= TIFM_MMCSD_CMD_ADTC;
+ break;
+ default:
+ BUG();
+ }
+ return rc;
+}
+
+static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd)
+{
+ struct tifm_dev *sock = host->dev;
+ unsigned int cmd_mask = tifm_sd_op_flags(cmd);
+
+ if (host->open_drain)
+ cmd_mask |= TIFM_MMCSD_ODTO;
+
+ if (cmd->data && (cmd->data->flags & MMC_DATA_READ))
+ cmd_mask |= TIFM_MMCSD_READ;
+
+ dev_dbg(&sock->dev, "executing opcode 0x%x, arg: 0x%x, mask: 0x%x\n",
+ cmd->opcode, cmd->arg, cmd_mask);
+
+ writel((cmd->arg >> 16) & 0xffff, sock->addr + SOCK_MMCSD_ARG_HIGH);
+ writel(cmd->arg & 0xffff, sock->addr + SOCK_MMCSD_ARG_LOW);
+ writel(cmd->opcode | cmd_mask, sock->addr + SOCK_MMCSD_COMMAND);
+}
+
+static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock)
+{
+ cmd->resp[0] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x1c) << 16)
+ | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x18);
+ cmd->resp[1] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x14) << 16)
+ | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x10);
+ cmd->resp[2] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x0c) << 16)
+ | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x08);
+ cmd->resp[3] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x04) << 16)
+ | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00);
+}
+
+static void tifm_sd_check_status(struct tifm_sd *host)
+{
+ struct tifm_dev *sock = host->dev;
+ struct mmc_command *cmd = host->req->cmd;
+
+ if (cmd->error)
+ goto finish_request;
+
+ if (!(host->cmd_flags & CMD_READY))
+ return;
+
+ if (cmd->data) {
+ if (cmd->data->error) {
+ if ((host->cmd_flags & SCMD_ACTIVE)
+ && !(host->cmd_flags & SCMD_READY))
+ return;
+
+ goto finish_request;
+ }
+
+ if (!(host->cmd_flags & BRS_READY))
+ return;
+
+ if (!(host->no_dma || (host->cmd_flags & FIFO_READY)))
+ return;
+
+ if (cmd->data->flags & MMC_DATA_WRITE) {
+ if (host->req->stop) {
+ if (!(host->cmd_flags & SCMD_ACTIVE)) {
+ host->cmd_flags |= SCMD_ACTIVE;
+ writel(TIFM_MMCSD_EOFB
+ | readl(sock->addr
+ + SOCK_MMCSD_INT_ENABLE),
+ sock->addr
+ + SOCK_MMCSD_INT_ENABLE);
+ tifm_sd_exec(host, host->req->stop);
+ return;
+ } else {
+ if (!(host->cmd_flags & SCMD_READY)
+ || (host->cmd_flags & CARD_BUSY))
+ return;
+ writel((~TIFM_MMCSD_EOFB)
+ & readl(sock->addr
+ + SOCK_MMCSD_INT_ENABLE),
+ sock->addr
+ + SOCK_MMCSD_INT_ENABLE);
+ }
+ } else {
+ if (host->cmd_flags & CARD_BUSY)
+ return;
+ writel((~TIFM_MMCSD_EOFB)
+ & readl(sock->addr
+ + SOCK_MMCSD_INT_ENABLE),
+ sock->addr + SOCK_MMCSD_INT_ENABLE);
+ }
+ } else {
+ if (host->req->stop) {
+ if (!(host->cmd_flags & SCMD_ACTIVE)) {
+ host->cmd_flags |= SCMD_ACTIVE;
+ tifm_sd_exec(host, host->req->stop);
+ return;
+ } else {
+ if (!(host->cmd_flags & SCMD_READY))
+ return;
+ }
+ }
+ }
+ }
+finish_request:
+ tasklet_schedule(&host->finish_tasklet);
+}
+
+/* Called from interrupt handler */
+static void tifm_sd_data_event(struct tifm_dev *sock)
+{
+ struct tifm_sd *host;
+ unsigned int fifo_status = 0;
+ struct mmc_data *r_data = NULL;
+
+ spin_lock(&sock->lock);
+ host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock));
+ fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);
+ dev_dbg(&sock->dev, "data event: fifo_status %x, flags %x\n",
+ fifo_status, host->cmd_flags);
+
+ if (host->req) {
+ r_data = host->req->cmd->data;
+
+ if (r_data && (fifo_status & TIFM_FIFO_READY)) {
+ if (tifm_sd_set_dma_data(host, r_data)) {
+ host->cmd_flags |= FIFO_READY;
+ tifm_sd_check_status(host);
+ }
+ }
+ }
+
+ writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS);
+ spin_unlock(&sock->lock);
+}
+
+/* Called from interrupt handler */
+static void tifm_sd_card_event(struct tifm_dev *sock)
+{
+ struct tifm_sd *host;
+ unsigned int host_status = 0;
+ int cmd_error = 0;
+ struct mmc_command *cmd = NULL;
+ unsigned long flags;
+
+ spin_lock(&sock->lock);
+ host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock));
+ host_status = readl(sock->addr + SOCK_MMCSD_STATUS);
+ dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n",
+ host_status, host->cmd_flags);
+
+ if (host->req) {
+ cmd = host->req->cmd;
+
+ if (host_status & TIFM_MMCSD_ERRMASK) {
+ writel(host_status & TIFM_MMCSD_ERRMASK,
+ sock->addr + SOCK_MMCSD_STATUS);
+ if (host_status & TIFM_MMCSD_CTO)
+ cmd_error = -ETIMEDOUT;
+ else if (host_status & TIFM_MMCSD_CCRC)
+ cmd_error = -EILSEQ;
+
+ if (cmd->data) {
+ if (host_status & TIFM_MMCSD_DTO)
+ cmd->data->error = -ETIMEDOUT;
+ else if (host_status & TIFM_MMCSD_DCRC)
+ cmd->data->error = -EILSEQ;
+ }
+
+ writel(TIFM_FIFO_INT_SETALL,
+ sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+ writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
+
+ if (host->req->stop) {
+ if (host->cmd_flags & SCMD_ACTIVE) {
+ host->req->stop->error = cmd_error;
+ host->cmd_flags |= SCMD_READY;
+ } else {
+ cmd->error = cmd_error;
+ host->cmd_flags |= SCMD_ACTIVE;
+ tifm_sd_exec(host, host->req->stop);
+ goto done;
+ }
+ } else
+ cmd->error = cmd_error;
+ } else {
+ if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) {
+ if (!(host->cmd_flags & CMD_READY)) {
+ host->cmd_flags |= CMD_READY;
+ tifm_sd_fetch_resp(cmd, sock);
+ } else if (host->cmd_flags & SCMD_ACTIVE) {
+ host->cmd_flags |= SCMD_READY;
+ tifm_sd_fetch_resp(host->req->stop,
+ sock);
+ }
+ }
+ if (host_status & TIFM_MMCSD_BRS)
+ host->cmd_flags |= BRS_READY;
+ }
+
+ if (host->no_dma && cmd->data) {
+ if (host_status & TIFM_MMCSD_AE)
+ writel(host_status & TIFM_MMCSD_AE,
+ sock->addr + SOCK_MMCSD_STATUS);
+
+ if (host_status & (TIFM_MMCSD_AE | TIFM_MMCSD_AF
+ | TIFM_MMCSD_BRS)) {
+ local_irq_save(flags);
+ tifm_sd_transfer_data(host);
+ local_irq_restore(flags);
+ host_status &= ~TIFM_MMCSD_AE;
+ }
+ }
+
+ if (host_status & TIFM_MMCSD_EOFB)
+ host->cmd_flags &= ~CARD_BUSY;
+ else if (host_status & TIFM_MMCSD_CB)
+ host->cmd_flags |= CARD_BUSY;
+
+ tifm_sd_check_status(host);
+ }
+done:
+ writel(host_status, sock->addr + SOCK_MMCSD_STATUS);
+ spin_unlock(&sock->lock);
+}
+
+static void tifm_sd_set_data_timeout(struct tifm_sd *host,
+ struct mmc_data *data)
+{
+ struct tifm_dev *sock = host->dev;
+ unsigned int data_timeout = data->timeout_clks;
+
+ if (fixed_timeout)
+ return;
+
+ data_timeout += data->timeout_ns /
+ ((1000000000UL / host->clk_freq) * host->clk_div);
+
+ if (data_timeout < 0xffff) {
+ writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO);
+ writel((~TIFM_MMCSD_DPE)
+ & readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG),
+ sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG);
+ } else {
+ data_timeout = (data_timeout >> 10) + 1;
+ if (data_timeout > 0xffff)
+ data_timeout = 0; /* set to unlimited */
+ writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO);
+ writel(TIFM_MMCSD_DPE
+ | readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG),
+ sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG);
+ }
+}
+
+static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct tifm_sd *host = mmc_priv(mmc);
+ struct tifm_dev *sock = host->dev;
+ unsigned long flags;
+ struct mmc_data *r_data = mrq->cmd->data;
+
+ spin_lock_irqsave(&sock->lock, flags);
+ if (host->eject) {
+ mrq->cmd->error = -ENOMEDIUM;
+ goto err_out;
+ }
+
+ if (host->req) {
+ printk(KERN_ERR "%s : unfinished request detected\n",
+ dev_name(&sock->dev));
+ mrq->cmd->error = -ETIMEDOUT;
+ goto err_out;
+ }
+
+ host->cmd_flags = 0;
+ host->block_pos = 0;
+ host->sg_pos = 0;
+
+ if (mrq->data && !is_power_of_2(mrq->data->blksz))
+ host->no_dma = 1;
+ else
+ host->no_dma = no_dma ? 1 : 0;
+
+ if (r_data) {
+ tifm_sd_set_data_timeout(host, r_data);
+
+ if ((r_data->flags & MMC_DATA_WRITE) && !mrq->stop)
+ writel(TIFM_MMCSD_EOFB
+ | readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
+ sock->addr + SOCK_MMCSD_INT_ENABLE);
+
+ if (host->no_dma) {
+ writel(TIFM_MMCSD_BUFINT
+ | readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
+ sock->addr + SOCK_MMCSD_INT_ENABLE);
+ writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8)
+ | (TIFM_MMCSD_FIFO_SIZE - 1),
+ sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
+
+ host->sg_len = r_data->sg_len;
+ } else {
+ sg_init_one(&host->bounce_buf, host->bounce_buf_data,
+ r_data->blksz);
+
+ if(1 != tifm_map_sg(sock, &host->bounce_buf, 1,
+ r_data->flags & MMC_DATA_WRITE
+ ? PCI_DMA_TODEVICE
+ : PCI_DMA_FROMDEVICE)) {
+ printk(KERN_ERR "%s : scatterlist map failed\n",
+ dev_name(&sock->dev));
+ mrq->cmd->error = -ENOMEM;
+ goto err_out;
+ }
+ host->sg_len = tifm_map_sg(sock, r_data->sg,
+ r_data->sg_len,
+ r_data->flags
+ & MMC_DATA_WRITE
+ ? PCI_DMA_TODEVICE
+ : PCI_DMA_FROMDEVICE);
+ if (host->sg_len < 1) {
+ printk(KERN_ERR "%s : scatterlist map failed\n",
+ dev_name(&sock->dev));
+ tifm_unmap_sg(sock, &host->bounce_buf, 1,
+ r_data->flags & MMC_DATA_WRITE
+ ? PCI_DMA_TODEVICE
+ : PCI_DMA_FROMDEVICE);
+ mrq->cmd->error = -ENOMEM;
+ goto err_out;
+ }
+
+ writel(TIFM_FIFO_INT_SETALL,
+ sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+ writel(ilog2(r_data->blksz) - 2,
+ sock->addr + SOCK_FIFO_PAGE_SIZE);
+ writel(TIFM_FIFO_ENABLE,
+ sock->addr + SOCK_FIFO_CONTROL);
+ writel(TIFM_FIFO_INTMASK,
+ sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
+
+ if (r_data->flags & MMC_DATA_WRITE)
+ writel(TIFM_MMCSD_TXDE,
+ sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
+ else
+ writel(TIFM_MMCSD_RXDE,
+ sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
+
+ tifm_sd_set_dma_data(host, r_data);
+ }
+
+ writel(r_data->blocks - 1,
+ sock->addr + SOCK_MMCSD_NUM_BLOCKS);
+ writel(r_data->blksz - 1,
+ sock->addr + SOCK_MMCSD_BLOCK_LEN);
+ }
+
+ host->req = mrq;
+ mod_timer(&host->timer, jiffies + host->timeout_jiffies);
+ writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL),
+ sock->addr + SOCK_CONTROL);
+ tifm_sd_exec(host, mrq->cmd);
+ spin_unlock_irqrestore(&sock->lock, flags);
+ return;
+
+err_out:
+ spin_unlock_irqrestore(&sock->lock, flags);
+ mmc_request_done(mmc, mrq);
+}
+
+static void tifm_sd_end_cmd(unsigned long data)
+{
+ struct tifm_sd *host = (struct tifm_sd*)data;
+ struct tifm_dev *sock = host->dev;
+ struct mmc_host *mmc = tifm_get_drvdata(sock);
+ struct mmc_request *mrq;
+ struct mmc_data *r_data = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sock->lock, flags);
+
+ del_timer(&host->timer);
+ mrq = host->req;
+ host->req = NULL;
+
+ if (!mrq) {
+ printk(KERN_ERR " %s : no request to complete?\n",
+ dev_name(&sock->dev));
+ spin_unlock_irqrestore(&sock->lock, flags);
+ return;
+ }
+
+ r_data = mrq->cmd->data;
+ if (r_data) {
+ if (host->no_dma) {
+ writel((~TIFM_MMCSD_BUFINT)
+ & readl(sock->addr + SOCK_MMCSD_INT_ENABLE),
+ sock->addr + SOCK_MMCSD_INT_ENABLE);
+ } else {
+ tifm_unmap_sg(sock, &host->bounce_buf, 1,
+ (r_data->flags & MMC_DATA_WRITE)
+ ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+ tifm_unmap_sg(sock, r_data->sg, r_data->sg_len,
+ (r_data->flags & MMC_DATA_WRITE)
+ ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+ }
+
+ r_data->bytes_xfered = r_data->blocks
+ - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1;
+ r_data->bytes_xfered *= r_data->blksz;
+ r_data->bytes_xfered += r_data->blksz
+ - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1;
+ }
+
+ writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL),
+ sock->addr + SOCK_CONTROL);
+
+ spin_unlock_irqrestore(&sock->lock, flags);
+ mmc_request_done(mmc, mrq);
+}
+
+static void tifm_sd_abort(unsigned long data)
+{
+ struct tifm_sd *host = (struct tifm_sd*)data;
+
+ printk(KERN_ERR
+ "%s : card failed to respond for a long period of time "
+ "(%x, %x)\n",
+ dev_name(&host->dev->dev), host->req->cmd->opcode, host->cmd_flags);
+
+ tifm_eject(host->dev);
+}
+
+static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct tifm_sd *host = mmc_priv(mmc);
+ struct tifm_dev *sock = host->dev;
+ unsigned int clk_div1, clk_div2;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sock->lock, flags);
+
+ dev_dbg(&sock->dev, "ios: clock = %u, vdd = %x, bus_mode = %x, "
+ "chip_select = %x, power_mode = %x, bus_width = %x\n",
+ ios->clock, ios->vdd, ios->bus_mode, ios->chip_select,
+ ios->power_mode, ios->bus_width);
+
+ if (ios->bus_width == MMC_BUS_WIDTH_4) {
+ writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG),
+ sock->addr + SOCK_MMCSD_CONFIG);
+ } else {
+ writel((~TIFM_MMCSD_4BBUS)
+ & readl(sock->addr + SOCK_MMCSD_CONFIG),
+ sock->addr + SOCK_MMCSD_CONFIG);
+ }
+
+ if (ios->clock) {
+ clk_div1 = 20000000 / ios->clock;
+ if (!clk_div1)
+ clk_div1 = 1;
+
+ clk_div2 = 24000000 / ios->clock;
+ if (!clk_div2)
+ clk_div2 = 1;
+
+ if ((20000000 / clk_div1) > ios->clock)
+ clk_div1++;
+ if ((24000000 / clk_div2) > ios->clock)
+ clk_div2++;
+ if ((20000000 / clk_div1) > (24000000 / clk_div2)) {
+ host->clk_freq = 20000000;
+ host->clk_div = clk_div1;
+ writel((~TIFM_CTRL_FAST_CLK)
+ & readl(sock->addr + SOCK_CONTROL),
+ sock->addr + SOCK_CONTROL);
+ } else {
+ host->clk_freq = 24000000;
+ host->clk_div = clk_div2;
+ writel(TIFM_CTRL_FAST_CLK
+ | readl(sock->addr + SOCK_CONTROL),
+ sock->addr + SOCK_CONTROL);
+ }
+ } else {
+ host->clk_div = 0;
+ }
+ host->clk_div &= TIFM_MMCSD_CLKMASK;
+ writel(host->clk_div
+ | ((~TIFM_MMCSD_CLKMASK)
+ & readl(sock->addr + SOCK_MMCSD_CONFIG)),
+ sock->addr + SOCK_MMCSD_CONFIG);
+
+ host->open_drain = (ios->bus_mode == MMC_BUSMODE_OPENDRAIN);
+
+ /* chip_select : maybe later */
+ //vdd
+ //power is set before probe / after remove
+
+ spin_unlock_irqrestore(&sock->lock, flags);
+}
+
+static int tifm_sd_ro(struct mmc_host *mmc)
+{
+ int rc = 0;
+ struct tifm_sd *host = mmc_priv(mmc);
+ struct tifm_dev *sock = host->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sock->lock, flags);
+ if (TIFM_MMCSD_CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE))
+ rc = 1;
+ spin_unlock_irqrestore(&sock->lock, flags);
+ return rc;
+}
+
+static const struct mmc_host_ops tifm_sd_ops = {
+ .request = tifm_sd_request,
+ .set_ios = tifm_sd_ios,
+ .get_ro = tifm_sd_ro
+};
+
+static int tifm_sd_initialize_host(struct tifm_sd *host)
+{
+ int rc;
+ unsigned int host_status = 0;
+ struct tifm_dev *sock = host->dev;
+
+ writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
+ mmiowb();
+ host->clk_div = 61;
+ host->clk_freq = 20000000;
+ writel(TIFM_MMCSD_RESET, sock->addr + SOCK_MMCSD_SYSTEM_CONTROL);
+ writel(host->clk_div | TIFM_MMCSD_POWER,
+ sock->addr + SOCK_MMCSD_CONFIG);
+
+ /* wait up to 0.51 sec for reset */
+ for (rc = 32; rc <= 256; rc <<= 1) {
+ if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) {
+ rc = 0;
+ break;
+ }
+ msleep(rc);
+ }
+
+ if (rc) {
+ printk(KERN_ERR "%s : controller failed to reset\n",
+ dev_name(&sock->dev));
+ return -ENODEV;
+ }
+
+ writel(0, sock->addr + SOCK_MMCSD_NUM_BLOCKS);
+ writel(host->clk_div | TIFM_MMCSD_POWER,
+ sock->addr + SOCK_MMCSD_CONFIG);
+ writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG);
+
+ // command timeout fixed to 64 clocks for now
+ writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO);
+ writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND);
+
+ for (rc = 16; rc <= 64; rc <<= 1) {
+ host_status = readl(sock->addr + SOCK_MMCSD_STATUS);
+ writel(host_status, sock->addr + SOCK_MMCSD_STATUS);
+ if (!(host_status & TIFM_MMCSD_ERRMASK)
+ && (host_status & TIFM_MMCSD_EOC)) {
+ rc = 0;
+ break;
+ }
+ msleep(rc);
+ }
+
+ if (rc) {
+ printk(KERN_ERR
+ "%s : card not ready - probe failed on initialization\n",
+ dev_name(&sock->dev));
+ return -ENODEV;
+ }
+
+ writel(TIFM_MMCSD_CERR | TIFM_MMCSD_BRS | TIFM_MMCSD_EOC
+ | TIFM_MMCSD_ERRMASK,
+ sock->addr + SOCK_MMCSD_INT_ENABLE);
+ mmiowb();
+
+ return 0;
+}
+
+static int tifm_sd_probe(struct tifm_dev *sock)
+{
+ struct mmc_host *mmc;
+ struct tifm_sd *host;
+ int rc = -EIO;
+
+ if (!(TIFM_SOCK_STATE_OCCUPIED
+ & readl(sock->addr + SOCK_PRESENT_STATE))) {
+ printk(KERN_WARNING "%s : card gone, unexpectedly\n",
+ dev_name(&sock->dev));
+ return rc;
+ }
+
+ mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev);
+ if (!mmc)
+ return -ENOMEM;
+
+ host = mmc_priv(mmc);
+ tifm_set_drvdata(sock, mmc);
+ host->dev = sock;
+ host->timeout_jiffies = msecs_to_jiffies(1000);
+
+ tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd,
+ (unsigned long)host);
+ setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host);
+
+ mmc->ops = &tifm_sd_ops;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->f_min = 20000000 / 60;
+ mmc->f_max = 24000000;
+
+ mmc->max_blk_count = 2048;
+ mmc->max_hw_segs = mmc->max_blk_count;
+ mmc->max_blk_size = min(TIFM_MMCSD_MAX_BLOCK_SIZE, PAGE_SIZE);
+ mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size;
+ mmc->max_req_size = mmc->max_seg_size;
+ mmc->max_phys_segs = mmc->max_hw_segs;
+
+ sock->card_event = tifm_sd_card_event;
+ sock->data_event = tifm_sd_data_event;
+ rc = tifm_sd_initialize_host(host);
+
+ if (!rc)
+ rc = mmc_add_host(mmc);
+ if (!rc)
+ return 0;
+
+ mmc_free_host(mmc);
+ return rc;
+}
+
+static void tifm_sd_remove(struct tifm_dev *sock)
+{
+ struct mmc_host *mmc = tifm_get_drvdata(sock);
+ struct tifm_sd *host = mmc_priv(mmc);
+ unsigned long flags;
+
+ spin_lock_irqsave(&sock->lock, flags);
+ host->eject = 1;
+ writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE);
+ mmiowb();
+ spin_unlock_irqrestore(&sock->lock, flags);
+
+ tasklet_kill(&host->finish_tasklet);
+
+ spin_lock_irqsave(&sock->lock, flags);
+ if (host->req) {
+ writel(TIFM_FIFO_INT_SETALL,
+ sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
+ writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
+ host->req->cmd->error = -ENOMEDIUM;
+ if (host->req->stop)
+ host->req->stop->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ }
+ spin_unlock_irqrestore(&sock->lock, flags);
+ mmc_remove_host(mmc);
+ dev_dbg(&sock->dev, "after remove\n");
+
+ mmc_free_host(mmc);
+}
+
+#ifdef CONFIG_PM
+
+static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state)
+{
+ return mmc_suspend_host(tifm_get_drvdata(sock), state);
+}
+
+static int tifm_sd_resume(struct tifm_dev *sock)
+{
+ struct mmc_host *mmc = tifm_get_drvdata(sock);
+ struct tifm_sd *host = mmc_priv(mmc);
+ int rc;
+
+ rc = tifm_sd_initialize_host(host);
+ dev_dbg(&sock->dev, "resume initialize %d\n", rc);
+
+ if (rc)
+ host->eject = 1;
+ else
+ rc = mmc_resume_host(mmc);
+
+ return rc;
+}
+
+#else
+
+#define tifm_sd_suspend NULL
+#define tifm_sd_resume NULL
+
+#endif /* CONFIG_PM */
+
+static struct tifm_device_id tifm_sd_id_tbl[] = {
+ { TIFM_TYPE_SD }, { }
+};
+
+static struct tifm_driver tifm_sd_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE
+ },
+ .id_table = tifm_sd_id_tbl,
+ .probe = tifm_sd_probe,
+ .remove = tifm_sd_remove,
+ .suspend = tifm_sd_suspend,
+ .resume = tifm_sd_resume
+};
+
+static int __init tifm_sd_init(void)
+{
+ return tifm_register_driver(&tifm_sd_driver);
+}
+
+static void __exit tifm_sd_exit(void)
+{
+ tifm_unregister_driver(&tifm_sd_driver);
+}
+
+MODULE_AUTHOR("Alex Dubov");
+MODULE_DESCRIPTION("TI FlashMedia SD driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl);
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(tifm_sd_init);
+module_exit(tifm_sd_exit);
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
new file mode 100644
index 0000000..95430b8
--- /dev/null
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -0,0 +1,691 @@
+/*
+ * linux/drivers/mmc/tmio_mmc.c
+ *
+ * Copyright (C) 2004 Ian Molton
+ * Copyright (C) 2007 Ian Molton
+ *
+ * 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.
+ *
+ * Driver for the MMC / SD / SDIO cell found in:
+ *
+ * TC6393XB TC6391XB TC6387XB T7L66XB
+ *
+ * This driver draws mainly on scattered spec sheets, Reverse engineering
+ * of the toshiba e800 SD driver and some parts of the 2.4 ASIC3 driver (4 bit
+ * support). (Further 4 bit support from a later datasheet).
+ *
+ * TODO:
+ * Investigate using a workqueue for PIO transfers
+ * Eliminate FIXMEs
+ * SDIO support
+ * Better Power management
+ * Handle MMC errors better
+ * double buffer support
+ *
+ */
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tmio.h>
+
+#include "tmio_mmc.h"
+
+/*
+ * Fixme - documentation conflicts on what the clock values are for the
+ * various dividers.
+ * One document I have says that its a divisor of a 24MHz clock, another 33.
+ * This probably depends on HCLK for a given platform, so we may need to
+ * require HCLK be passed to us from the MFD core.
+ *
+ */
+
+static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
+{
+ void __iomem *cnf = host->cnf;
+ void __iomem *ctl = host->ctl;
+ u32 clk = 0, clock;
+
+ if (new_clock) {
+ for (clock = 46875, clk = 0x100; new_clock >= (clock<<1); ) {
+ clock <<= 1;
+ clk >>= 1;
+ }
+ if (clk & 0x1)
+ clk = 0x20000;
+
+ clk >>= 2;
+ tmio_iowrite8((clk & 0x8000) ? 0 : 1, cnf + CNF_SD_CLK_MODE);
+ clk |= 0x100;
+ }
+
+ tmio_iowrite16(clk, ctl + CTL_SD_CARD_CLK_CTL);
+}
+
+static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
+{
+ void __iomem *ctl = host->ctl;
+
+ tmio_iowrite16(0x0000, ctl + CTL_CLK_AND_WAIT_CTL);
+ msleep(10);
+ tmio_iowrite16(tmio_ioread16(ctl + CTL_SD_CARD_CLK_CTL) & ~0x0100,
+ ctl + CTL_SD_CARD_CLK_CTL);
+ msleep(10);
+}
+
+static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
+{
+ void __iomem *ctl = host->ctl;
+
+ tmio_iowrite16(tmio_ioread16(ctl + CTL_SD_CARD_CLK_CTL) | 0x0100,
+ ctl + CTL_SD_CARD_CLK_CTL);
+ msleep(10);
+ tmio_iowrite16(0x0100, ctl + CTL_CLK_AND_WAIT_CTL);
+ msleep(10);
+}
+
+static void reset(struct tmio_mmc_host *host)
+{
+ void __iomem *ctl = host->ctl;
+
+ /* FIXME - should we set stop clock reg here */
+ tmio_iowrite16(0x0000, ctl + CTL_RESET_SD);
+ tmio_iowrite16(0x0000, ctl + CTL_RESET_SDIO);
+ msleep(10);
+ tmio_iowrite16(0x0001, ctl + CTL_RESET_SD);
+ tmio_iowrite16(0x0001, ctl + CTL_RESET_SDIO);
+ msleep(10);
+}
+
+static void
+tmio_mmc_finish_request(struct tmio_mmc_host *host)
+{
+ struct mmc_request *mrq = host->mrq;
+
+ host->mrq = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+
+ mmc_request_done(host->mmc, mrq);
+}
+
+/* These are the bitmasks the tmio chip requires to implement the MMC response
+ * types. Note that R1 and R6 are the same in this scheme. */
+#define APP_CMD 0x0040
+#define RESP_NONE 0x0300
+#define RESP_R1 0x0400
+#define RESP_R1B 0x0500
+#define RESP_R2 0x0600
+#define RESP_R3 0x0700
+#define DATA_PRESENT 0x0800
+#define TRANSFER_READ 0x1000
+#define TRANSFER_MULTI 0x2000
+#define SECURITY_CMD 0x4000
+
+static int
+tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd)
+{
+ void __iomem *ctl = host->ctl;
+ struct mmc_data *data = host->data;
+ int c = cmd->opcode;
+
+ /* Command 12 is handled by hardware */
+ if (cmd->opcode == 12 && !cmd->arg) {
+ tmio_iowrite16(0x001, ctl + CTL_STOP_INTERNAL_ACTION);
+ return 0;
+ }
+
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE: c |= RESP_NONE; break;
+ case MMC_RSP_R1: c |= RESP_R1; break;
+ case MMC_RSP_R1B: c |= RESP_R1B; break;
+ case MMC_RSP_R2: c |= RESP_R2; break;
+ case MMC_RSP_R3: c |= RESP_R3; break;
+ default:
+ pr_debug("Unknown response type %d\n", mmc_resp_type(cmd));
+ return -EINVAL;
+ }
+
+ host->cmd = cmd;
+
+/* FIXME - this seems to be ok comented out but the spec suggest this bit should
+ * be set when issuing app commands.
+ * if(cmd->flags & MMC_FLAG_ACMD)
+ * c |= APP_CMD;
+ */
+ if (data) {
+ c |= DATA_PRESENT;
+ if (data->blocks > 1) {
+ tmio_iowrite16(0x100, ctl + CTL_STOP_INTERNAL_ACTION);
+ c |= TRANSFER_MULTI;
+ }
+ if (data->flags & MMC_DATA_READ)
+ c |= TRANSFER_READ;
+ }
+
+ enable_mmc_irqs(ctl, TMIO_MASK_CMD);
+
+ /* Fire off the command */
+ tmio_iowrite32(cmd->arg, ctl + CTL_ARG_REG);
+ tmio_iowrite16(c, ctl + CTL_SD_CMD);
+
+ return 0;
+}
+
+/* This chip always returns (at least?) as much data as you ask for.
+ * I'm unsure what happens if you ask for less than a block. This should be
+ * looked into to ensure that a funny length read doesnt hose the controller.
+ *
+ */
+static inline void tmio_mmc_pio_irq(struct tmio_mmc_host *host)
+{
+ void __iomem *ctl = host->ctl;
+ struct mmc_data *data = host->data;
+ unsigned short *buf;
+ unsigned int count;
+ unsigned long flags;
+
+ if (!data) {
+ pr_debug("Spurious PIO IRQ\n");
+ return;
+ }
+
+ buf = (unsigned short *)(tmio_mmc_kmap_atomic(host, &flags) +
+ host->sg_off);
+
+ count = host->sg_ptr->length - host->sg_off;
+ if (count > data->blksz)
+ count = data->blksz;
+
+ pr_debug("count: %08x offset: %08x flags %08x\n",
+ count, host->sg_off, data->flags);
+
+ /* Transfer the data */
+ if (data->flags & MMC_DATA_READ)
+ tmio_ioread16_rep(ctl + CTL_SD_DATA_PORT, buf, count >> 1);
+ else
+ tmio_iowrite16_rep(ctl + CTL_SD_DATA_PORT, buf, count >> 1);
+
+ host->sg_off += count;
+
+ tmio_mmc_kunmap_atomic(host, &flags);
+
+ if (host->sg_off == host->sg_ptr->length)
+ tmio_mmc_next_sg(host);
+
+ return;
+}
+
+static inline void tmio_mmc_data_irq(struct tmio_mmc_host *host)
+{
+ void __iomem *ctl = host->ctl;
+ struct mmc_data *data = host->data;
+ struct mmc_command *stop = data->stop;
+
+ host->data = NULL;
+
+ if (!data) {
+ pr_debug("Spurious data end IRQ\n");
+ return;
+ }
+
+ /* FIXME - return correct transfer count on errors */
+ if (!data->error)
+ data->bytes_xfered = data->blocks * data->blksz;
+ else
+ data->bytes_xfered = 0;
+
+ pr_debug("Completed data request\n");
+
+ /*FIXME - other drivers allow an optional stop command of any given type
+ * which we dont do, as the chip can auto generate them.
+ * Perhaps we can be smarter about when to use auto CMD12 and
+ * only issue the auto request when we know this is the desired
+ * stop command, allowing fallback to the stop command the
+ * upper layers expect. For now, we do what works.
+ */
+
+ if (data->flags & MMC_DATA_READ)
+ disable_mmc_irqs(ctl, TMIO_MASK_READOP);
+ else
+ disable_mmc_irqs(ctl, TMIO_MASK_WRITEOP);
+
+ if (stop) {
+ if (stop->opcode == 12 && !stop->arg)
+ tmio_iowrite16(0x000, ctl + CTL_STOP_INTERNAL_ACTION);
+ else
+ BUG();
+ }
+
+ tmio_mmc_finish_request(host);
+}
+
+static inline void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
+ unsigned int stat)
+{
+ void __iomem *ctl = host->ctl, *addr;
+ struct mmc_command *cmd = host->cmd;
+ int i;
+
+ if (!host->cmd) {
+ pr_debug("Spurious CMD irq\n");
+ return;
+ }
+
+ host->cmd = NULL;
+
+ /* This controller is sicker than the PXA one. Not only do we need to
+ * drop the top 8 bits of the first response word, we also need to
+ * modify the order of the response for short response command types.
+ */
+
+ for (i = 3, addr = ctl + CTL_RESPONSE ; i >= 0 ; i--, addr += 4)
+ cmd->resp[i] = tmio_ioread32(addr);
+
+ if (cmd->flags & MMC_RSP_136) {
+ cmd->resp[0] = (cmd->resp[0] << 8) | (cmd->resp[1] >> 24);
+ cmd->resp[1] = (cmd->resp[1] << 8) | (cmd->resp[2] >> 24);
+ cmd->resp[2] = (cmd->resp[2] << 8) | (cmd->resp[3] >> 24);
+ cmd->resp[3] <<= 8;
+ } else if (cmd->flags & MMC_RSP_R3) {
+ cmd->resp[0] = cmd->resp[3];
+ }
+
+ if (stat & TMIO_STAT_CMDTIMEOUT)
+ cmd->error = -ETIMEDOUT;
+ else if (stat & TMIO_STAT_CRCFAIL && cmd->flags & MMC_RSP_CRC)
+ cmd->error = -EILSEQ;
+
+ /* If there is data to handle we enable data IRQs here, and
+ * we will ultimatley finish the request in the data_end handler.
+ * If theres no data or we encountered an error, finish now.
+ */
+ if (host->data && !cmd->error) {
+ if (host->data->flags & MMC_DATA_READ)
+ enable_mmc_irqs(ctl, TMIO_MASK_READOP);
+ else
+ enable_mmc_irqs(ctl, TMIO_MASK_WRITEOP);
+ } else {
+ tmio_mmc_finish_request(host);
+ }
+
+ return;
+}
+
+
+static irqreturn_t tmio_mmc_irq(int irq, void *devid)
+{
+ struct tmio_mmc_host *host = devid;
+ void __iomem *ctl = host->ctl;
+ unsigned int ireg, irq_mask, status;
+
+ pr_debug("MMC IRQ begin\n");
+
+ status = tmio_ioread32(ctl + CTL_STATUS);
+ irq_mask = tmio_ioread32(ctl + CTL_IRQ_MASK);
+ ireg = status & TMIO_MASK_IRQ & ~irq_mask;
+
+ pr_debug_status(status);
+ pr_debug_status(ireg);
+
+ if (!ireg) {
+ disable_mmc_irqs(ctl, status & ~irq_mask);
+
+ pr_debug("tmio_mmc: Spurious irq, disabling! "
+ "0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg);
+ pr_debug_status(status);
+
+ goto out;
+ }
+
+ while (ireg) {
+ /* Card insert / remove attempts */
+ if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)) {
+ ack_mmc_irqs(ctl, TMIO_STAT_CARD_INSERT |
+ TMIO_STAT_CARD_REMOVE);
+ mmc_detect_change(host->mmc, 0);
+ }
+
+ /* CRC and other errors */
+/* if (ireg & TMIO_STAT_ERR_IRQ)
+ * handled |= tmio_error_irq(host, irq, stat);
+ */
+
+ /* Command completion */
+ if (ireg & TMIO_MASK_CMD) {
+ ack_mmc_irqs(ctl, TMIO_MASK_CMD);
+ tmio_mmc_cmd_irq(host, status);
+ }
+
+ /* Data transfer */
+ if (ireg & (TMIO_STAT_RXRDY | TMIO_STAT_TXRQ)) {
+ ack_mmc_irqs(ctl, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ);
+ tmio_mmc_pio_irq(host);
+ }
+
+ /* Data transfer completion */
+ if (ireg & TMIO_STAT_DATAEND) {
+ ack_mmc_irqs(ctl, TMIO_STAT_DATAEND);
+ tmio_mmc_data_irq(host);
+ }
+
+ /* Check status - keep going until we've handled it all */
+ status = tmio_ioread32(ctl + CTL_STATUS);
+ irq_mask = tmio_ioread32(ctl + CTL_IRQ_MASK);
+ ireg = status & TMIO_MASK_IRQ & ~irq_mask;
+
+ pr_debug("Status at end of loop: %08x\n", status);
+ pr_debug_status(status);
+ }
+ pr_debug("MMC IRQ end\n");
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int tmio_mmc_start_data(struct tmio_mmc_host *host,
+ struct mmc_data *data)
+{
+ void __iomem *ctl = host->ctl;
+
+ pr_debug("setup data transfer: blocksize %08x nr_blocks %d\n",
+ data->blksz, data->blocks);
+
+ /* Hardware cannot perform 1 and 2 byte requests in 4 bit mode */
+ if (data->blksz < 4 && host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
+ printk(KERN_ERR "%s: %d byte block unsupported in 4 bit mode\n",
+ mmc_hostname(host->mmc), data->blksz);
+ return -EINVAL;
+ }
+
+ tmio_mmc_init_sg(host, data);
+ host->data = data;
+
+ /* Set transfer length / blocksize */
+ tmio_iowrite16(data->blksz, ctl + CTL_SD_XFER_LEN);
+ tmio_iowrite16(data->blocks, ctl + CTL_XFER_BLK_COUNT);
+
+ return 0;
+}
+
+/* Process requests from the MMC layer */
+static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ int ret;
+
+ if (host->mrq)
+ pr_debug("request not null\n");
+
+ host->mrq = mrq;
+
+ if (mrq->data) {
+ ret = tmio_mmc_start_data(host, mrq->data);
+ if (ret)
+ goto fail;
+ }
+
+ ret = tmio_mmc_start_command(host, mrq->cmd);
+
+ if (!ret)
+ return;
+
+fail:
+ mrq->cmd->error = ret;
+ mmc_request_done(mmc, mrq);
+}
+
+/* Set MMC clock / power.
+ * Note: This controller uses a simple divider scheme therefore it cannot
+ * run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as
+ * MMC wont run that fast, it has to be clocked at 12MHz which is the next
+ * slowest setting.
+ */
+static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ void __iomem *cnf = host->cnf;
+ void __iomem *ctl = host->ctl;
+
+ if (ios->clock)
+ tmio_mmc_set_clock(host, ios->clock);
+
+ /* Power sequence - OFF -> ON -> UP */
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF: /* power down SD bus */
+ tmio_iowrite8(0x00, cnf + CNF_PWR_CTL_2);
+ tmio_mmc_clk_stop(host);
+ break;
+ case MMC_POWER_ON: /* power up SD bus */
+
+ tmio_iowrite8(0x02, cnf + CNF_PWR_CTL_2);
+ break;
+ case MMC_POWER_UP: /* start bus clock */
+ tmio_mmc_clk_start(host);
+ break;
+ }
+
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_1:
+ tmio_iowrite16(0x80e0, ctl + CTL_SD_MEM_CARD_OPT);
+ break;
+ case MMC_BUS_WIDTH_4:
+ tmio_iowrite16(0x00e0, ctl + CTL_SD_MEM_CARD_OPT);
+ break;
+ }
+
+ /* Let things settle. delay taken from winCE driver */
+ udelay(140);
+}
+
+static int tmio_mmc_get_ro(struct mmc_host *mmc)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ void __iomem *ctl = host->ctl;
+
+ return (tmio_ioread16(ctl + CTL_STATUS) & TMIO_STAT_WRPROTECT) ? 0 : 1;
+}
+
+static struct mmc_host_ops tmio_mmc_ops = {
+ .request = tmio_mmc_request,
+ .set_ios = tmio_mmc_set_ios,
+ .get_ro = tmio_mmc_get_ro,
+};
+
+#ifdef CONFIG_PM
+static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+ int ret;
+
+ ret = mmc_suspend_host(mmc, state);
+
+ /* Tell MFD core it can disable us now.*/
+ if (!ret && cell->disable)
+ cell->disable(dev);
+
+ return ret;
+}
+
+static int tmio_mmc_resume(struct platform_device *dev)
+{
+ struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ void __iomem *cnf = host->cnf;
+ int ret = 0;
+
+ /* Enable the MMC/SD Control registers */
+ tmio_iowrite16(SDCREN, cnf + CNF_CMD);
+ tmio_iowrite32(dev->resource[0].start & 0xfffe, cnf + CNF_CTL_BASE);
+
+ /* Tell the MFD core we are ready to be enabled */
+ if (cell->enable) {
+ ret = cell->enable(dev);
+ if (ret)
+ goto out;
+ }
+
+ mmc_resume_host(mmc);
+
+out:
+ return ret;
+}
+#else
+#define tmio_mmc_suspend NULL
+#define tmio_mmc_resume NULL
+#endif
+
+static int __devinit tmio_mmc_probe(struct platform_device *dev)
+{
+ struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
+ struct resource *res_ctl, *res_cnf;
+ struct tmio_mmc_host *host;
+ struct mmc_host *mmc;
+ int ret = -ENOMEM;
+
+ if (dev->num_resources != 3)
+ goto out;
+
+ res_ctl = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ res_cnf = platform_get_resource(dev, IORESOURCE_MEM, 1);
+ if (!res_ctl || !res_cnf) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &dev->dev);
+ if (!mmc)
+ goto out;
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ platform_set_drvdata(dev, mmc);
+
+ host->ctl = ioremap(res_ctl->start, res_ctl->end - res_ctl->start);
+ if (!host->ctl)
+ goto host_free;
+
+ host->cnf = ioremap(res_cnf->start, res_cnf->end - res_cnf->start);
+ if (!host->cnf)
+ goto unmap_ctl;
+
+ mmc->ops = &tmio_mmc_ops;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->f_min = 46875; /* 24000000 / 512 */
+ mmc->f_max = 24000000;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+ /* Enable the MMC/SD Control registers */
+ tmio_iowrite16(SDCREN, host->cnf + CNF_CMD);
+ tmio_iowrite32(dev->resource[0].start & 0xfffe,
+ host->cnf + CNF_CTL_BASE);
+
+ /* Tell the MFD core we are ready to be enabled */
+ if (cell->enable) {
+ ret = cell->enable(dev);
+ if (ret)
+ goto unmap_cnf;
+ }
+
+ /* Disable SD power during suspend */
+ tmio_iowrite8(0x01, host->cnf + CNF_PWR_CTL_3);
+
+ /* The below is required but why? FIXME */
+ tmio_iowrite8(0x1f, host->cnf + CNF_STOP_CLK_CTL);
+
+ /* Power down SD bus*/
+ tmio_iowrite8(0x0, host->cnf + CNF_PWR_CTL_2);
+
+ tmio_mmc_clk_stop(host);
+ reset(host);
+
+ ret = platform_get_irq(dev, 0);
+ if (ret >= 0)
+ host->irq = ret;
+ else
+ goto unmap_cnf;
+
+ disable_mmc_irqs(host->ctl, TMIO_MASK_ALL);
+
+ ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED, "tmio-mmc",
+ host);
+ if (ret)
+ goto unmap_cnf;
+
+ set_irq_type(host->irq, IRQ_TYPE_EDGE_FALLING);
+
+ mmc_add_host(mmc);
+
+ printk(KERN_INFO "%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc),
+ (unsigned long)host->ctl, host->irq);
+
+ /* Unmask the IRQs we want to know about */
+ enable_mmc_irqs(host->ctl, TMIO_MASK_IRQ);
+
+ return 0;
+
+unmap_cnf:
+ iounmap(host->cnf);
+unmap_ctl:
+ iounmap(host->ctl);
+host_free:
+ mmc_free_host(mmc);
+out:
+ return ret;
+}
+
+static int __devexit tmio_mmc_remove(struct platform_device *dev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+
+ platform_set_drvdata(dev, NULL);
+
+ 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);
+ }
+
+ return 0;
+}
+
+/* ------------------- device registration ----------------------- */
+
+static struct platform_driver tmio_mmc_driver = {
+ .driver = {
+ .name = "tmio-mmc",
+ .owner = THIS_MODULE,
+ },
+ .probe = tmio_mmc_probe,
+ .remove = __devexit_p(tmio_mmc_remove),
+ .suspend = tmio_mmc_suspend,
+ .resume = tmio_mmc_resume,
+};
+
+
+static int __init tmio_mmc_init(void)
+{
+ return platform_driver_register(&tmio_mmc_driver);
+}
+
+static void __exit tmio_mmc_exit(void)
+{
+ platform_driver_unregister(&tmio_mmc_driver);
+}
+
+module_init(tmio_mmc_init);
+module_exit(tmio_mmc_exit);
+
+MODULE_DESCRIPTION("Toshiba TMIO SD/MMC driver");
+MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tmio-mmc");
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
new file mode 100644
index 0000000..ba2b424
--- /dev/null
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -0,0 +1,194 @@
+/* Definitons for use with the tmio_mmc.c
+ *
+ * (c) 2004 Ian Molton <spyro@f2s.com>
+ * (c) 2007 Ian Molton <spyro@f2s.com>
+ *
+ * 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.
+ *
+ */
+#define CNF_CMD 0x04
+#define CNF_CTL_BASE 0x10
+#define CNF_INT_PIN 0x3d
+#define CNF_STOP_CLK_CTL 0x40
+#define CNF_GCLK_CTL 0x41
+#define CNF_SD_CLK_MODE 0x42
+#define CNF_PIN_STATUS 0x44
+#define CNF_PWR_CTL_1 0x48
+#define CNF_PWR_CTL_2 0x49
+#define CNF_PWR_CTL_3 0x4a
+#define CNF_CARD_DETECT_MODE 0x4c
+#define CNF_SD_SLOT 0x50
+#define CNF_EXT_GCLK_CTL_1 0xf0
+#define CNF_EXT_GCLK_CTL_2 0xf1
+#define CNF_EXT_GCLK_CTL_3 0xf9
+#define CNF_SD_LED_EN_1 0xfa
+#define CNF_SD_LED_EN_2 0xfe
+
+#define SDCREN 0x2 /* Enable access to MMC CTL regs. (flag in COMMAND_REG)*/
+
+#define CTL_SD_CMD 0x00
+#define CTL_ARG_REG 0x04
+#define CTL_STOP_INTERNAL_ACTION 0x08
+#define CTL_XFER_BLK_COUNT 0xa
+#define CTL_RESPONSE 0x0c
+#define CTL_STATUS 0x1c
+#define CTL_IRQ_MASK 0x20
+#define CTL_SD_CARD_CLK_CTL 0x24
+#define CTL_SD_XFER_LEN 0x26
+#define CTL_SD_MEM_CARD_OPT 0x28
+#define CTL_SD_ERROR_DETAIL_STATUS 0x2c
+#define CTL_SD_DATA_PORT 0x30
+#define CTL_TRANSACTION_CTL 0x34
+#define CTL_RESET_SD 0xe0
+#define CTL_SDIO_REGS 0x100
+#define CTL_CLK_AND_WAIT_CTL 0x138
+#define CTL_RESET_SDIO 0x1e0
+
+/* Definitions for values the CTRL_STATUS register can take. */
+#define TMIO_STAT_CMDRESPEND 0x00000001
+#define TMIO_STAT_DATAEND 0x00000004
+#define TMIO_STAT_CARD_REMOVE 0x00000008
+#define TMIO_STAT_CARD_INSERT 0x00000010
+#define TMIO_STAT_SIGSTATE 0x00000020
+#define TMIO_STAT_WRPROTECT 0x00000080
+#define TMIO_STAT_CARD_REMOVE_A 0x00000100
+#define TMIO_STAT_CARD_INSERT_A 0x00000200
+#define TMIO_STAT_SIGSTATE_A 0x00000400
+#define TMIO_STAT_CMD_IDX_ERR 0x00010000
+#define TMIO_STAT_CRCFAIL 0x00020000
+#define TMIO_STAT_STOPBIT_ERR 0x00040000
+#define TMIO_STAT_DATATIMEOUT 0x00080000
+#define TMIO_STAT_RXOVERFLOW 0x00100000
+#define TMIO_STAT_TXUNDERRUN 0x00200000
+#define TMIO_STAT_CMDTIMEOUT 0x00400000
+#define TMIO_STAT_RXRDY 0x01000000
+#define TMIO_STAT_TXRQ 0x02000000
+#define TMIO_STAT_ILL_FUNC 0x20000000
+#define TMIO_STAT_CMD_BUSY 0x40000000
+#define TMIO_STAT_ILL_ACCESS 0x80000000
+
+/* Define some IRQ masks */
+/* This is the mask used at reset by the chip */
+#define TMIO_MASK_ALL 0x837f031d
+#define TMIO_MASK_READOP (TMIO_STAT_RXRDY | TMIO_STAT_DATAEND | \
+ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT)
+#define TMIO_MASK_WRITEOP (TMIO_STAT_TXRQ | TMIO_STAT_DATAEND | \
+ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT)
+#define TMIO_MASK_CMD (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT | \
+ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT)
+#define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD)
+
+#define enable_mmc_irqs(ctl, i) \
+ do { \
+ u32 mask;\
+ mask = tmio_ioread32((ctl) + CTL_IRQ_MASK); \
+ mask &= ~((i) & TMIO_MASK_IRQ); \
+ tmio_iowrite32(mask, (ctl) + CTL_IRQ_MASK); \
+ } while (0)
+
+#define disable_mmc_irqs(ctl, i) \
+ do { \
+ u32 mask;\
+ mask = tmio_ioread32((ctl) + CTL_IRQ_MASK); \
+ mask |= ((i) & TMIO_MASK_IRQ); \
+ tmio_iowrite32(mask, (ctl) + CTL_IRQ_MASK); \
+ } while (0)
+
+#define ack_mmc_irqs(ctl, i) \
+ do { \
+ u32 mask;\
+ mask = tmio_ioread32((ctl) + CTL_STATUS); \
+ mask &= ~((i) & TMIO_MASK_IRQ); \
+ tmio_iowrite32(mask, (ctl) + CTL_STATUS); \
+ } while (0)
+
+
+struct tmio_mmc_host {
+ void __iomem *cnf;
+ void __iomem *ctl;
+ struct mmc_command *cmd;
+ struct mmc_request *mrq;
+ struct mmc_data *data;
+ struct mmc_host *mmc;
+ int irq;
+
+ /* pio related stuff */
+ struct scatterlist *sg_ptr;
+ unsigned int sg_len;
+ unsigned int sg_off;
+};
+
+#include <linux/scatterlist.h>
+#include <linux/blkdev.h>
+
+static inline void tmio_mmc_init_sg(struct tmio_mmc_host *host,
+ struct mmc_data *data)
+{
+ host->sg_len = data->sg_len;
+ host->sg_ptr = data->sg;
+ host->sg_off = 0;
+}
+
+static inline int tmio_mmc_next_sg(struct tmio_mmc_host *host)
+{
+ host->sg_ptr = sg_next(host->sg_ptr);
+ host->sg_off = 0;
+ return --host->sg_len;
+}
+
+static inline char *tmio_mmc_kmap_atomic(struct tmio_mmc_host *host,
+ unsigned long *flags)
+{
+ struct scatterlist *sg = host->sg_ptr;
+
+ local_irq_save(*flags);
+ return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
+}
+
+static inline void tmio_mmc_kunmap_atomic(struct tmio_mmc_host *host,
+ unsigned long *flags)
+{
+ kunmap_atomic(sg_page(host->sg_ptr), KM_BIO_SRC_IRQ);
+ local_irq_restore(*flags);
+}
+
+#ifdef CONFIG_MMC_DEBUG
+
+#define STATUS_TO_TEXT(a) \
+ do { \
+ if (status & TMIO_STAT_##a) \
+ printk(#a); \
+ } while (0)
+
+void pr_debug_status(u32 status)
+{
+ printk(KERN_DEBUG "status: %08x = ", status);
+ STATUS_TO_TEXT(CARD_REMOVE);
+ STATUS_TO_TEXT(CARD_INSERT);
+ STATUS_TO_TEXT(SIGSTATE);
+ STATUS_TO_TEXT(WRPROTECT);
+ STATUS_TO_TEXT(CARD_REMOVE_A);
+ STATUS_TO_TEXT(CARD_INSERT_A);
+ STATUS_TO_TEXT(SIGSTATE_A);
+ STATUS_TO_TEXT(CMD_IDX_ERR);
+ STATUS_TO_TEXT(STOPBIT_ERR);
+ STATUS_TO_TEXT(ILL_FUNC);
+ STATUS_TO_TEXT(CMD_BUSY);
+ STATUS_TO_TEXT(CMDRESPEND);
+ STATUS_TO_TEXT(DATAEND);
+ STATUS_TO_TEXT(CRCFAIL);
+ STATUS_TO_TEXT(DATATIMEOUT);
+ STATUS_TO_TEXT(CMDTIMEOUT);
+ STATUS_TO_TEXT(RXOVERFLOW);
+ STATUS_TO_TEXT(TXUNDERRUN);
+ STATUS_TO_TEXT(RXRDY);
+ STATUS_TO_TEXT(TXRQ);
+ STATUS_TO_TEXT(ILL_ACCESS);
+ printk("\n");
+}
+
+#else
+#define pr_debug_status(s) do { } while (0)
+#endif
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
new file mode 100644
index 0000000..adda379
--- /dev/null
+++ b/drivers/mmc/host/wbsd.c
@@ -0,0 +1,2047 @@
+/*
+ * linux/drivers/mmc/host/wbsd.c - Winbond W83L51xD SD/MMC driver
+ *
+ * Copyright (C) 2004-2007 Pierre Ossman, 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ *
+ * Warning!
+ *
+ * Changes to the FIFO system should be done with extreme care since
+ * the hardware is full of bugs related to the FIFO. Known issues are:
+ *
+ * - FIFO size field in FSR is always zero.
+ *
+ * - FIFO interrupts tend not to work as they should. Interrupts are
+ * triggered only for full/empty events, not for threshold values.
+ *
+ * - On APIC systems the FIFO empty interrupt is sometimes lost.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/pnp.h>
+#include <linux/highmem.h>
+#include <linux/mmc/host.h>
+#include <linux/scatterlist.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include "wbsd.h"
+
+#define DRIVER_NAME "wbsd"
+
+#define DBG(x...) \
+ pr_debug(DRIVER_NAME ": " x)
+#define DBGF(f, x...) \
+ pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x)
+
+/*
+ * Device resources
+ */
+
+#ifdef CONFIG_PNP
+
+static const struct pnp_device_id pnp_dev_table[] = {
+ { "WEC0517", 0 },
+ { "WEC0518", 0 },
+ { "", 0 },
+};
+
+MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
+
+#endif /* CONFIG_PNP */
+
+static const int config_ports[] = { 0x2E, 0x4E };
+static const int unlock_codes[] = { 0x83, 0x87 };
+
+static const int valid_ids[] = {
+ 0x7112,
+};
+
+#ifdef CONFIG_PNP
+static unsigned int param_nopnp = 0;
+#else
+static const unsigned int param_nopnp = 1;
+#endif
+static unsigned int param_io = 0x248;
+static unsigned int param_irq = 6;
+static int param_dma = 2;
+
+/*
+ * Basic functions
+ */
+
+static inline void wbsd_unlock_config(struct wbsd_host *host)
+{
+ BUG_ON(host->config == 0);
+
+ outb(host->unlock_code, host->config);
+ outb(host->unlock_code, host->config);
+}
+
+static inline void wbsd_lock_config(struct wbsd_host *host)
+{
+ BUG_ON(host->config == 0);
+
+ outb(LOCK_CODE, host->config);
+}
+
+static inline void wbsd_write_config(struct wbsd_host *host, u8 reg, u8 value)
+{
+ BUG_ON(host->config == 0);
+
+ outb(reg, host->config);
+ outb(value, host->config + 1);
+}
+
+static inline u8 wbsd_read_config(struct wbsd_host *host, u8 reg)
+{
+ BUG_ON(host->config == 0);
+
+ outb(reg, host->config);
+ return inb(host->config + 1);
+}
+
+static inline void wbsd_write_index(struct wbsd_host *host, u8 index, u8 value)
+{
+ outb(index, host->base + WBSD_IDXR);
+ outb(value, host->base + WBSD_DATAR);
+}
+
+static inline u8 wbsd_read_index(struct wbsd_host *host, u8 index)
+{
+ outb(index, host->base + WBSD_IDXR);
+ return inb(host->base + WBSD_DATAR);
+}
+
+/*
+ * Common routines
+ */
+
+static void wbsd_init_device(struct wbsd_host *host)
+{
+ u8 setup, ier;
+
+ /*
+ * Reset chip (SD/MMC part) and fifo.
+ */
+ setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+ setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET;
+ wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+
+ /*
+ * Set DAT3 to input
+ */
+ setup &= ~WBSD_DAT3_H;
+ wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+ host->flags &= ~WBSD_FIGNORE_DETECT;
+
+ /*
+ * Read back default clock.
+ */
+ host->clk = wbsd_read_index(host, WBSD_IDX_CLK);
+
+ /*
+ * Power down port.
+ */
+ outb(WBSD_POWER_N, host->base + WBSD_CSR);
+
+ /*
+ * Set maximum timeout.
+ */
+ wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F);
+
+ /*
+ * Test for card presence
+ */
+ if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT)
+ host->flags |= WBSD_FCARD_PRESENT;
+ else
+ host->flags &= ~WBSD_FCARD_PRESENT;
+
+ /*
+ * Enable interesting interrupts.
+ */
+ ier = 0;
+ ier |= WBSD_EINT_CARD;
+ ier |= WBSD_EINT_FIFO_THRE;
+ ier |= WBSD_EINT_CRC;
+ ier |= WBSD_EINT_TIMEOUT;
+ ier |= WBSD_EINT_TC;
+
+ outb(ier, host->base + WBSD_EIR);
+
+ /*
+ * Clear interrupts.
+ */
+ inb(host->base + WBSD_ISR);
+}
+
+static void wbsd_reset(struct wbsd_host *host)
+{
+ u8 setup;
+
+ printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc));
+
+ /*
+ * Soft reset of chip (SD/MMC part).
+ */
+ setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+ setup |= WBSD_SOFT_RESET;
+ wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+}
+
+static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq)
+{
+ unsigned long dmaflags;
+
+ if (host->dma >= 0) {
+ /*
+ * Release ISA DMA controller.
+ */
+ dmaflags = claim_dma_lock();
+ disable_dma(host->dma);
+ clear_dma_ff(host->dma);
+ release_dma_lock(dmaflags);
+
+ /*
+ * Disable DMA on host.
+ */
+ wbsd_write_index(host, WBSD_IDX_DMA, 0);
+ }
+
+ host->mrq = NULL;
+
+ /*
+ * MMC layer might call back into the driver so first unlock.
+ */
+ spin_unlock(&host->lock);
+ mmc_request_done(host->mmc, mrq);
+ spin_lock(&host->lock);
+}
+
+/*
+ * Scatter/gather functions
+ */
+
+static inline void wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data)
+{
+ /*
+ * Get info. about SG list from data structure.
+ */
+ host->cur_sg = data->sg;
+ host->num_sg = data->sg_len;
+
+ host->offset = 0;
+ host->remain = host->cur_sg->length;
+}
+
+static inline int wbsd_next_sg(struct wbsd_host *host)
+{
+ /*
+ * Skip to next SG entry.
+ */
+ host->cur_sg++;
+ host->num_sg--;
+
+ /*
+ * Any entries left?
+ */
+ if (host->num_sg > 0) {
+ host->offset = 0;
+ host->remain = host->cur_sg->length;
+ }
+
+ return host->num_sg;
+}
+
+static inline char *wbsd_sg_to_buffer(struct wbsd_host *host)
+{
+ return sg_virt(host->cur_sg);
+}
+
+static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
+{
+ unsigned int len, i;
+ struct scatterlist *sg;
+ char *dmabuf = host->dma_buffer;
+ char *sgbuf;
+
+ sg = data->sg;
+ len = data->sg_len;
+
+ for (i = 0; i < len; i++) {
+ sgbuf = sg_virt(&sg[i]);
+ memcpy(dmabuf, sgbuf, sg[i].length);
+ dmabuf += sg[i].length;
+ }
+}
+
+static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data)
+{
+ unsigned int len, i;
+ struct scatterlist *sg;
+ char *dmabuf = host->dma_buffer;
+ char *sgbuf;
+
+ sg = data->sg;
+ len = data->sg_len;
+
+ for (i = 0; i < len; i++) {
+ sgbuf = sg_virt(&sg[i]);
+ memcpy(sgbuf, dmabuf, sg[i].length);
+ dmabuf += sg[i].length;
+ }
+}
+
+/*
+ * Command handling
+ */
+
+static inline void wbsd_get_short_reply(struct wbsd_host *host,
+ struct mmc_command *cmd)
+{
+ /*
+ * Correct response type?
+ */
+ if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) {
+ cmd->error = -EILSEQ;
+ return;
+ }
+
+ cmd->resp[0] = wbsd_read_index(host, WBSD_IDX_RESP12) << 24;
+ cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP13) << 16;
+ cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP14) << 8;
+ cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP15) << 0;
+ cmd->resp[1] = wbsd_read_index(host, WBSD_IDX_RESP16) << 24;
+}
+
+static inline void wbsd_get_long_reply(struct wbsd_host *host,
+ struct mmc_command *cmd)
+{
+ int i;
+
+ /*
+ * Correct response type?
+ */
+ if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) {
+ cmd->error = -EILSEQ;
+ return;
+ }
+
+ for (i = 0; i < 4; i++) {
+ cmd->resp[i] =
+ wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24;
+ cmd->resp[i] |=
+ wbsd_read_index(host, WBSD_IDX_RESP2 + i * 4) << 16;
+ cmd->resp[i] |=
+ wbsd_read_index(host, WBSD_IDX_RESP3 + i * 4) << 8;
+ cmd->resp[i] |=
+ wbsd_read_index(host, WBSD_IDX_RESP4 + i * 4) << 0;
+ }
+}
+
+static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd)
+{
+ int i;
+ u8 status, isr;
+
+ /*
+ * Clear accumulated ISR. The interrupt routine
+ * will fill this one with events that occur during
+ * transfer.
+ */
+ host->isr = 0;
+
+ /*
+ * Send the command (CRC calculated by host).
+ */
+ outb(cmd->opcode, host->base + WBSD_CMDR);
+ for (i = 3; i >= 0; i--)
+ outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR);
+
+ cmd->error = 0;
+
+ /*
+ * Wait for the request to complete.
+ */
+ do {
+ status = wbsd_read_index(host, WBSD_IDX_STATUS);
+ } while (status & WBSD_CARDTRAFFIC);
+
+ /*
+ * Do we expect a reply?
+ */
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ /*
+ * Read back status.
+ */
+ isr = host->isr;
+
+ /* Card removed? */
+ if (isr & WBSD_INT_CARD)
+ cmd->error = -ENOMEDIUM;
+ /* Timeout? */
+ else if (isr & WBSD_INT_TIMEOUT)
+ cmd->error = -ETIMEDOUT;
+ /* CRC? */
+ else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC))
+ cmd->error = -EILSEQ;
+ /* All ok */
+ else {
+ if (cmd->flags & MMC_RSP_136)
+ wbsd_get_long_reply(host, cmd);
+ else
+ wbsd_get_short_reply(host, cmd);
+ }
+ }
+}
+
+/*
+ * Data functions
+ */
+
+static void wbsd_empty_fifo(struct wbsd_host *host)
+{
+ struct mmc_data *data = host->mrq->cmd->data;
+ char *buffer;
+ int i, fsr, fifo;
+
+ /*
+ * Handle excessive data.
+ */
+ if (host->num_sg == 0)
+ return;
+
+ buffer = wbsd_sg_to_buffer(host) + host->offset;
+
+ /*
+ * Drain the fifo. This has a tendency to loop longer
+ * than the FIFO length (usually one block).
+ */
+ while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) {
+ /*
+ * The size field in the FSR is broken so we have to
+ * do some guessing.
+ */
+ if (fsr & WBSD_FIFO_FULL)
+ fifo = 16;
+ else if (fsr & WBSD_FIFO_FUTHRE)
+ fifo = 8;
+ else
+ fifo = 1;
+
+ for (i = 0; i < fifo; i++) {
+ *buffer = inb(host->base + WBSD_DFR);
+ buffer++;
+ host->offset++;
+ host->remain--;
+
+ data->bytes_xfered++;
+
+ /*
+ * End of scatter list entry?
+ */
+ if (host->remain == 0) {
+ /*
+ * Get next entry. Check if last.
+ */
+ if (!wbsd_next_sg(host))
+ return;
+
+ buffer = wbsd_sg_to_buffer(host);
+ }
+ }
+ }
+
+ /*
+ * This is a very dirty hack to solve a
+ * hardware problem. The chip doesn't trigger
+ * FIFO threshold interrupts properly.
+ */
+ if ((data->blocks * data->blksz - data->bytes_xfered) < 16)
+ tasklet_schedule(&host->fifo_tasklet);
+}
+
+static void wbsd_fill_fifo(struct wbsd_host *host)
+{
+ struct mmc_data *data = host->mrq->cmd->data;
+ char *buffer;
+ int i, fsr, fifo;
+
+ /*
+ * Check that we aren't being called after the
+ * entire buffer has been transfered.
+ */
+ if (host->num_sg == 0)
+ return;
+
+ buffer = wbsd_sg_to_buffer(host) + host->offset;
+
+ /*
+ * Fill the fifo. This has a tendency to loop longer
+ * than the FIFO length (usually one block).
+ */
+ while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) {
+ /*
+ * The size field in the FSR is broken so we have to
+ * do some guessing.
+ */
+ if (fsr & WBSD_FIFO_EMPTY)
+ fifo = 0;
+ else if (fsr & WBSD_FIFO_EMTHRE)
+ fifo = 8;
+ else
+ fifo = 15;
+
+ for (i = 16; i > fifo; i--) {
+ outb(*buffer, host->base + WBSD_DFR);
+ buffer++;
+ host->offset++;
+ host->remain--;
+
+ data->bytes_xfered++;
+
+ /*
+ * End of scatter list entry?
+ */
+ if (host->remain == 0) {
+ /*
+ * Get next entry. Check if last.
+ */
+ if (!wbsd_next_sg(host))
+ return;
+
+ buffer = wbsd_sg_to_buffer(host);
+ }
+ }
+ }
+
+ /*
+ * The controller stops sending interrupts for
+ * 'FIFO empty' under certain conditions. So we
+ * need to be a bit more pro-active.
+ */
+ tasklet_schedule(&host->fifo_tasklet);
+}
+
+static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
+{
+ u16 blksize;
+ u8 setup;
+ unsigned long dmaflags;
+ unsigned int size;
+
+ /*
+ * Calculate size.
+ */
+ size = data->blocks * data->blksz;
+
+ /*
+ * Check timeout values for overflow.
+ * (Yes, some cards cause this value to overflow).
+ */
+ if (data->timeout_ns > 127000000)
+ wbsd_write_index(host, WBSD_IDX_TAAC, 127);
+ else {
+ wbsd_write_index(host, WBSD_IDX_TAAC,
+ data->timeout_ns / 1000000);
+ }
+
+ if (data->timeout_clks > 255)
+ wbsd_write_index(host, WBSD_IDX_NSAC, 255);
+ else
+ wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks);
+
+ /*
+ * Inform the chip of how large blocks will be
+ * sent. It needs this to determine when to
+ * calculate CRC.
+ *
+ * Space for CRC must be included in the size.
+ * Two bytes are needed for each data line.
+ */
+ if (host->bus_width == MMC_BUS_WIDTH_1) {
+ blksize = data->blksz + 2;
+
+ wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0);
+ wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
+ } else if (host->bus_width == MMC_BUS_WIDTH_4) {
+ blksize = data->blksz + 2 * 4;
+
+ wbsd_write_index(host, WBSD_IDX_PBSMSB,
+ ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH);
+ wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
+ } else {
+ data->error = -EINVAL;
+ return;
+ }
+
+ /*
+ * Clear the FIFO. This is needed even for DMA
+ * transfers since the chip still uses the FIFO
+ * internally.
+ */
+ setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+ setup |= WBSD_FIFO_RESET;
+ wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+
+ /*
+ * DMA transfer?
+ */
+ if (host->dma >= 0) {
+ /*
+ * The buffer for DMA is only 64 kB.
+ */
+ BUG_ON(size > 0x10000);
+ if (size > 0x10000) {
+ data->error = -EINVAL;
+ return;
+ }
+
+ /*
+ * Transfer data from the SG list to
+ * the DMA buffer.
+ */
+ if (data->flags & MMC_DATA_WRITE)
+ wbsd_sg_to_dma(host, data);
+
+ /*
+ * Initialise the ISA DMA controller.
+ */
+ dmaflags = claim_dma_lock();
+ disable_dma(host->dma);
+ clear_dma_ff(host->dma);
+ if (data->flags & MMC_DATA_READ)
+ set_dma_mode(host->dma, DMA_MODE_READ & ~0x40);
+ else
+ set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40);
+ set_dma_addr(host->dma, host->dma_addr);
+ set_dma_count(host->dma, size);
+
+ enable_dma(host->dma);
+ release_dma_lock(dmaflags);
+
+ /*
+ * Enable DMA on the host.
+ */
+ wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE);
+ } else {
+ /*
+ * This flag is used to keep printk
+ * output to a minimum.
+ */
+ host->firsterr = 1;
+
+ /*
+ * Initialise the SG list.
+ */
+ wbsd_init_sg(host, data);
+
+ /*
+ * Turn off DMA.
+ */
+ wbsd_write_index(host, WBSD_IDX_DMA, 0);
+
+ /*
+ * Set up FIFO threshold levels (and fill
+ * buffer if doing a write).
+ */
+ if (data->flags & MMC_DATA_READ) {
+ wbsd_write_index(host, WBSD_IDX_FIFOEN,
+ WBSD_FIFOEN_FULL | 8);
+ } else {
+ wbsd_write_index(host, WBSD_IDX_FIFOEN,
+ WBSD_FIFOEN_EMPTY | 8);
+ wbsd_fill_fifo(host);
+ }
+ }
+
+ data->error = 0;
+}
+
+static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
+{
+ unsigned long dmaflags;
+ int count;
+ u8 status;
+
+ WARN_ON(host->mrq == NULL);
+
+ /*
+ * Send a stop command if needed.
+ */
+ if (data->stop)
+ wbsd_send_command(host, data->stop);
+
+ /*
+ * Wait for the controller to leave data
+ * transfer state.
+ */
+ do {
+ status = wbsd_read_index(host, WBSD_IDX_STATUS);
+ } while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE));
+
+ /*
+ * DMA transfer?
+ */
+ if (host->dma >= 0) {
+ /*
+ * Disable DMA on the host.
+ */
+ wbsd_write_index(host, WBSD_IDX_DMA, 0);
+
+ /*
+ * Turn of ISA DMA controller.
+ */
+ dmaflags = claim_dma_lock();
+ disable_dma(host->dma);
+ clear_dma_ff(host->dma);
+ count = get_dma_residue(host->dma);
+ release_dma_lock(dmaflags);
+
+ data->bytes_xfered = host->mrq->data->blocks *
+ host->mrq->data->blksz - count;
+ data->bytes_xfered -= data->bytes_xfered % data->blksz;
+
+ /*
+ * Any leftover data?
+ */
+ if (count) {
+ printk(KERN_ERR "%s: Incomplete DMA transfer. "
+ "%d bytes left.\n",
+ mmc_hostname(host->mmc), count);
+
+ if (!data->error)
+ data->error = -EIO;
+ } else {
+ /*
+ * Transfer data from DMA buffer to
+ * SG list.
+ */
+ if (data->flags & MMC_DATA_READ)
+ wbsd_dma_to_sg(host, data);
+ }
+
+ if (data->error) {
+ if (data->bytes_xfered)
+ data->bytes_xfered -= data->blksz;
+ }
+ }
+
+ wbsd_request_end(host, host->mrq);
+}
+
+/*****************************************************************************\
+ * *
+ * MMC layer callbacks *
+ * *
+\*****************************************************************************/
+
+static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct wbsd_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd;
+
+ /*
+ * Disable tasklets to avoid a deadlock.
+ */
+ spin_lock_bh(&host->lock);
+
+ BUG_ON(host->mrq != NULL);
+
+ cmd = mrq->cmd;
+
+ host->mrq = mrq;
+
+ /*
+ * Check that there is actually a card in the slot.
+ */
+ if (!(host->flags & WBSD_FCARD_PRESENT)) {
+ cmd->error = -ENOMEDIUM;
+ goto done;
+ }
+
+ if (cmd->data) {
+ /*
+ * The hardware is so delightfully stupid that it has a list
+ * of "data" commands. If a command isn't on this list, it'll
+ * just go back to the idle state and won't send any data
+ * interrupts.
+ */
+ switch (cmd->opcode) {
+ case 11:
+ case 17:
+ case 18:
+ case 20:
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 30:
+ case 42:
+ case 56:
+ break;
+
+ /* ACMDs. We don't keep track of state, so we just treat them
+ * like any other command. */
+ case 51:
+ break;
+
+ default:
+#ifdef CONFIG_MMC_DEBUG
+ printk(KERN_WARNING "%s: Data command %d is not "
+ "supported by this controller.\n",
+ mmc_hostname(host->mmc), cmd->opcode);
+#endif
+ cmd->error = -EINVAL;
+
+ goto done;
+ };
+ }
+
+ /*
+ * Does the request include data?
+ */
+ if (cmd->data) {
+ wbsd_prepare_data(host, cmd->data);
+
+ if (cmd->data->error)
+ goto done;
+ }
+
+ wbsd_send_command(host, cmd);
+
+ /*
+ * If this is a data transfer the request
+ * will be finished after the data has
+ * transfered.
+ */
+ if (cmd->data && !cmd->error) {
+ /*
+ * Dirty fix for hardware bug.
+ */
+ if (host->dma == -1)
+ tasklet_schedule(&host->fifo_tasklet);
+
+ spin_unlock_bh(&host->lock);
+
+ return;
+ }
+
+done:
+ wbsd_request_end(host, mrq);
+
+ spin_unlock_bh(&host->lock);
+}
+
+static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct wbsd_host *host = mmc_priv(mmc);
+ u8 clk, setup, pwr;
+
+ spin_lock_bh(&host->lock);
+
+ /*
+ * Reset the chip on each power off.
+ * Should clear out any weird states.
+ */
+ if (ios->power_mode == MMC_POWER_OFF)
+ wbsd_init_device(host);
+
+ if (ios->clock >= 24000000)
+ clk = WBSD_CLK_24M;
+ else if (ios->clock >= 16000000)
+ clk = WBSD_CLK_16M;
+ else if (ios->clock >= 12000000)
+ clk = WBSD_CLK_12M;
+ else
+ clk = WBSD_CLK_375K;
+
+ /*
+ * Only write to the clock register when
+ * there is an actual change.
+ */
+ if (clk != host->clk) {
+ wbsd_write_index(host, WBSD_IDX_CLK, clk);
+ host->clk = clk;
+ }
+
+ /*
+ * Power up card.
+ */
+ if (ios->power_mode != MMC_POWER_OFF) {
+ pwr = inb(host->base + WBSD_CSR);
+ pwr &= ~WBSD_POWER_N;
+ outb(pwr, host->base + WBSD_CSR);
+ }
+
+ /*
+ * MMC cards need to have pin 1 high during init.
+ * It wreaks havoc with the card detection though so
+ * that needs to be disabled.
+ */
+ setup = wbsd_read_index(host, WBSD_IDX_SETUP);
+ if (ios->chip_select == MMC_CS_HIGH) {
+ BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1);
+ setup |= WBSD_DAT3_H;
+ host->flags |= WBSD_FIGNORE_DETECT;
+ } else {
+ if (setup & WBSD_DAT3_H) {
+ setup &= ~WBSD_DAT3_H;
+
+ /*
+ * We cannot resume card detection immediatly
+ * because of capacitance and delays in the chip.
+ */
+ mod_timer(&host->ignore_timer, jiffies + HZ / 100);
+ }
+ }
+ wbsd_write_index(host, WBSD_IDX_SETUP, setup);
+
+ /*
+ * Store bus width for later. Will be used when
+ * setting up the data transfer.
+ */
+ host->bus_width = ios->bus_width;
+
+ spin_unlock_bh(&host->lock);
+}
+
+static int wbsd_get_ro(struct mmc_host *mmc)
+{
+ struct wbsd_host *host = mmc_priv(mmc);
+ u8 csr;
+
+ spin_lock_bh(&host->lock);
+
+ csr = inb(host->base + WBSD_CSR);
+ csr |= WBSD_MSLED;
+ outb(csr, host->base + WBSD_CSR);
+
+ mdelay(1);
+
+ csr = inb(host->base + WBSD_CSR);
+ csr &= ~WBSD_MSLED;
+ outb(csr, host->base + WBSD_CSR);
+
+ spin_unlock_bh(&host->lock);
+
+ return !!(csr & WBSD_WRPT);
+}
+
+static const struct mmc_host_ops wbsd_ops = {
+ .request = wbsd_request,
+ .set_ios = wbsd_set_ios,
+ .get_ro = wbsd_get_ro,
+};
+
+/*****************************************************************************\
+ * *
+ * Interrupt handling *
+ * *
+\*****************************************************************************/
+
+/*
+ * Helper function to reset detection ignore
+ */
+
+static void wbsd_reset_ignore(unsigned long data)
+{
+ struct wbsd_host *host = (struct wbsd_host *)data;
+
+ BUG_ON(host == NULL);
+
+ DBG("Resetting card detection ignore\n");
+
+ spin_lock_bh(&host->lock);
+
+ host->flags &= ~WBSD_FIGNORE_DETECT;
+
+ /*
+ * Card status might have changed during the
+ * blackout.
+ */
+ tasklet_schedule(&host->card_tasklet);
+
+ spin_unlock_bh(&host->lock);
+}
+
+/*
+ * Tasklets
+ */
+
+static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host)
+{
+ WARN_ON(!host->mrq);
+ if (!host->mrq)
+ return NULL;
+
+ WARN_ON(!host->mrq->cmd);
+ if (!host->mrq->cmd)
+ return NULL;
+
+ WARN_ON(!host->mrq->cmd->data);
+ if (!host->mrq->cmd->data)
+ return NULL;
+
+ return host->mrq->cmd->data;
+}
+
+static void wbsd_tasklet_card(unsigned long param)
+{
+ struct wbsd_host *host = (struct wbsd_host *)param;
+ u8 csr;
+ int delay = -1;
+
+ spin_lock(&host->lock);
+
+ if (host->flags & WBSD_FIGNORE_DETECT) {
+ spin_unlock(&host->lock);
+ return;
+ }
+
+ csr = inb(host->base + WBSD_CSR);
+ WARN_ON(csr == 0xff);
+
+ if (csr & WBSD_CARDPRESENT) {
+ if (!(host->flags & WBSD_FCARD_PRESENT)) {
+ DBG("Card inserted\n");
+ host->flags |= WBSD_FCARD_PRESENT;
+
+ delay = 500;
+ }
+ } else if (host->flags & WBSD_FCARD_PRESENT) {
+ DBG("Card removed\n");
+ host->flags &= ~WBSD_FCARD_PRESENT;
+
+ if (host->mrq) {
+ printk(KERN_ERR "%s: Card removed during transfer!\n",
+ mmc_hostname(host->mmc));
+ wbsd_reset(host);
+
+ host->mrq->cmd->error = -ENOMEDIUM;
+ tasklet_schedule(&host->finish_tasklet);
+ }
+
+ delay = 0;
+ }
+
+ /*
+ * Unlock first since we might get a call back.
+ */
+
+ spin_unlock(&host->lock);
+
+ if (delay != -1)
+ mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
+}
+
+static void wbsd_tasklet_fifo(unsigned long param)
+{
+ struct wbsd_host *host = (struct wbsd_host *)param;
+ struct mmc_data *data;
+
+ spin_lock(&host->lock);
+
+ if (!host->mrq)
+ goto end;
+
+ data = wbsd_get_data(host);
+ if (!data)
+ goto end;
+
+ if (data->flags & MMC_DATA_WRITE)
+ wbsd_fill_fifo(host);
+ else
+ wbsd_empty_fifo(host);
+
+ /*
+ * Done?
+ */
+ if (host->num_sg == 0) {
+ wbsd_write_index(host, WBSD_IDX_FIFOEN, 0);
+ tasklet_schedule(&host->finish_tasklet);
+ }
+
+end:
+ spin_unlock(&host->lock);
+}
+
+static void wbsd_tasklet_crc(unsigned long param)
+{
+ struct wbsd_host *host = (struct wbsd_host *)param;
+ struct mmc_data *data;
+
+ spin_lock(&host->lock);
+
+ if (!host->mrq)
+ goto end;
+
+ data = wbsd_get_data(host);
+ if (!data)
+ goto end;
+
+ DBGF("CRC error\n");
+
+ data->error = -EILSEQ;
+
+ tasklet_schedule(&host->finish_tasklet);
+
+end:
+ spin_unlock(&host->lock);
+}
+
+static void wbsd_tasklet_timeout(unsigned long param)
+{
+ struct wbsd_host *host = (struct wbsd_host *)param;
+ struct mmc_data *data;
+
+ spin_lock(&host->lock);
+
+ if (!host->mrq)
+ goto end;
+
+ data = wbsd_get_data(host);
+ if (!data)
+ goto end;
+
+ DBGF("Timeout\n");
+
+ data->error = -ETIMEDOUT;
+
+ tasklet_schedule(&host->finish_tasklet);
+
+end:
+ spin_unlock(&host->lock);
+}
+
+static void wbsd_tasklet_finish(unsigned long param)
+{
+ struct wbsd_host *host = (struct wbsd_host *)param;
+ struct mmc_data *data;
+
+ spin_lock(&host->lock);
+
+ WARN_ON(!host->mrq);
+ if (!host->mrq)
+ goto end;
+
+ data = wbsd_get_data(host);
+ if (!data)
+ goto end;
+
+ wbsd_finish_data(host, data);
+
+end:
+ spin_unlock(&host->lock);
+}
+
+/*
+ * Interrupt handling
+ */
+
+static irqreturn_t wbsd_irq(int irq, void *dev_id)
+{
+ struct wbsd_host *host = dev_id;
+ int isr;
+
+ isr = inb(host->base + WBSD_ISR);
+
+ /*
+ * Was it actually our hardware that caused the interrupt?
+ */
+ if (isr == 0xff || isr == 0x00)
+ return IRQ_NONE;
+
+ host->isr |= isr;
+
+ /*
+ * Schedule tasklets as needed.
+ */
+ if (isr & WBSD_INT_CARD)
+ tasklet_schedule(&host->card_tasklet);
+ if (isr & WBSD_INT_FIFO_THRE)
+ tasklet_schedule(&host->fifo_tasklet);
+ if (isr & WBSD_INT_CRC)
+ tasklet_hi_schedule(&host->crc_tasklet);
+ if (isr & WBSD_INT_TIMEOUT)
+ tasklet_hi_schedule(&host->timeout_tasklet);
+ if (isr & WBSD_INT_TC)
+ tasklet_schedule(&host->finish_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+/*****************************************************************************\
+ * *
+ * Device initialisation and shutdown *
+ * *
+\*****************************************************************************/
+
+/*
+ * Allocate/free MMC structure.
+ */
+
+static int __devinit wbsd_alloc_mmc(struct device *dev)
+{
+ struct mmc_host *mmc;
+ struct wbsd_host *host;
+
+ /*
+ * Allocate MMC structure.
+ */
+ mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev);
+ if (!mmc)
+ return -ENOMEM;
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+
+ host->dma = -1;
+
+ /*
+ * Set host parameters.
+ */
+ mmc->ops = &wbsd_ops;
+ mmc->f_min = 375000;
+ mmc->f_max = 24000000;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+
+ spin_lock_init(&host->lock);
+
+ /*
+ * Set up timers
+ */
+ init_timer(&host->ignore_timer);
+ host->ignore_timer.data = (unsigned long)host;
+ host->ignore_timer.function = wbsd_reset_ignore;
+
+ /*
+ * Maximum number of segments. Worst case is one sector per segment
+ * so this will be 64kB/512.
+ */
+ mmc->max_hw_segs = 128;
+ mmc->max_phys_segs = 128;
+
+ /*
+ * Maximum request size. Also limited by 64KiB buffer.
+ */
+ mmc->max_req_size = 65536;
+
+ /*
+ * Maximum segment size. Could be one segment with the maximum number
+ * of bytes.
+ */
+ mmc->max_seg_size = mmc->max_req_size;
+
+ /*
+ * Maximum block size. We have 12 bits (= 4095) but have to subtract
+ * space for CRC. So the maximum is 4095 - 4*2 = 4087.
+ */
+ mmc->max_blk_size = 4087;
+
+ /*
+ * Maximum block count. There is no real limit so the maximum
+ * request size will be the only restriction.
+ */
+ mmc->max_blk_count = mmc->max_req_size;
+
+ dev_set_drvdata(dev, mmc);
+
+ return 0;
+}
+
+static void wbsd_free_mmc(struct device *dev)
+{
+ struct mmc_host *mmc;
+ struct wbsd_host *host;
+
+ mmc = dev_get_drvdata(dev);
+ if (!mmc)
+ return;
+
+ host = mmc_priv(mmc);
+ BUG_ON(host == NULL);
+
+ del_timer_sync(&host->ignore_timer);
+
+ mmc_free_host(mmc);
+
+ dev_set_drvdata(dev, NULL);
+}
+
+/*
+ * Scan for known chip id:s
+ */
+
+static int __devinit wbsd_scan(struct wbsd_host *host)
+{
+ int i, j, k;
+ int id;
+
+ /*
+ * Iterate through all ports, all codes to
+ * find hardware that is in our known list.
+ */
+ for (i = 0; i < ARRAY_SIZE(config_ports); i++) {
+ if (!request_region(config_ports[i], 2, DRIVER_NAME))
+ continue;
+
+ for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) {
+ id = 0xFFFF;
+
+ host->config = config_ports[i];
+ host->unlock_code = unlock_codes[j];
+
+ wbsd_unlock_config(host);
+
+ outb(WBSD_CONF_ID_HI, config_ports[i]);
+ id = inb(config_ports[i] + 1) << 8;
+
+ outb(WBSD_CONF_ID_LO, config_ports[i]);
+ id |= inb(config_ports[i] + 1);
+
+ wbsd_lock_config(host);
+
+ for (k = 0; k < ARRAY_SIZE(valid_ids); k++) {
+ if (id == valid_ids[k]) {
+ host->chip_id = id;
+
+ return 0;
+ }
+ }
+
+ if (id != 0xFFFF) {
+ DBG("Unknown hardware (id %x) found at %x\n",
+ id, config_ports[i]);
+ }
+ }
+
+ release_region(config_ports[i], 2);
+ }
+
+ host->config = 0;
+ host->unlock_code = 0;
+
+ return -ENODEV;
+}
+
+/*
+ * Allocate/free io port ranges
+ */
+
+static int __devinit wbsd_request_region(struct wbsd_host *host, int base)
+{
+ if (base & 0x7)
+ return -EINVAL;
+
+ if (!request_region(base, 8, DRIVER_NAME))
+ return -EIO;
+
+ host->base = base;
+
+ return 0;
+}
+
+static void wbsd_release_regions(struct wbsd_host *host)
+{
+ if (host->base)
+ release_region(host->base, 8);
+
+ host->base = 0;
+
+ if (host->config)
+ release_region(host->config, 2);
+
+ host->config = 0;
+}
+
+/*
+ * Allocate/free DMA port and buffer
+ */
+
+static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma)
+{
+ if (dma < 0)
+ return;
+
+ if (request_dma(dma, DRIVER_NAME))
+ goto err;
+
+ /*
+ * We need to allocate a special buffer in
+ * order for ISA to be able to DMA to it.
+ */
+ host->dma_buffer = kmalloc(WBSD_DMA_SIZE,
+ GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN);
+ if (!host->dma_buffer)
+ goto free;
+
+ /*
+ * Translate the address to a physical address.
+ */
+ host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer,
+ WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
+
+ /*
+ * ISA DMA must be aligned on a 64k basis.
+ */
+ if ((host->dma_addr & 0xffff) != 0)
+ goto kfree;
+ /*
+ * ISA cannot access memory above 16 MB.
+ */
+ else if (host->dma_addr >= 0x1000000)
+ goto kfree;
+
+ host->dma = dma;
+
+ return;
+
+kfree:
+ /*
+ * If we've gotten here then there is some kind of alignment bug
+ */
+ BUG_ON(1);
+
+ dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
+ WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
+ host->dma_addr = 0;
+
+ kfree(host->dma_buffer);
+ host->dma_buffer = NULL;
+
+free:
+ free_dma(dma);
+
+err:
+ printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. "
+ "Falling back on FIFO.\n", dma);
+}
+
+static void wbsd_release_dma(struct wbsd_host *host)
+{
+ if (host->dma_addr) {
+ dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
+ WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
+ }
+ kfree(host->dma_buffer);
+ if (host->dma >= 0)
+ free_dma(host->dma);
+
+ host->dma = -1;
+ host->dma_buffer = NULL;
+ host->dma_addr = 0;
+}
+
+/*
+ * Allocate/free IRQ.
+ */
+
+static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq)
+{
+ int ret;
+
+ /*
+ * Set up tasklets. Must be done before requesting interrupt.
+ */
+ tasklet_init(&host->card_tasklet, wbsd_tasklet_card,
+ (unsigned long)host);
+ tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo,
+ (unsigned long)host);
+ tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc,
+ (unsigned long)host);
+ tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout,
+ (unsigned long)host);
+ tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish,
+ (unsigned long)host);
+
+ /*
+ * Allocate interrupt.
+ */
+ ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host);
+ if (ret)
+ return ret;
+
+ host->irq = irq;
+
+ return 0;
+}
+
+static void wbsd_release_irq(struct wbsd_host *host)
+{
+ if (!host->irq)
+ return;
+
+ free_irq(host->irq, host);
+
+ host->irq = 0;
+
+ tasklet_kill(&host->card_tasklet);
+ tasklet_kill(&host->fifo_tasklet);
+ tasklet_kill(&host->crc_tasklet);
+ tasklet_kill(&host->timeout_tasklet);
+ tasklet_kill(&host->finish_tasklet);
+}
+
+/*
+ * Allocate all resources for the host.
+ */
+
+static int __devinit wbsd_request_resources(struct wbsd_host *host,
+ int base, int irq, int dma)
+{
+ int ret;
+
+ /*
+ * Allocate I/O ports.
+ */
+ ret = wbsd_request_region(host, base);
+ if (ret)
+ return ret;
+
+ /*
+ * Allocate interrupt.
+ */
+ ret = wbsd_request_irq(host, irq);
+ if (ret)
+ return ret;
+
+ /*
+ * Allocate DMA.
+ */
+ wbsd_request_dma(host, dma);
+
+ return 0;
+}
+
+/*
+ * Release all resources for the host.
+ */
+
+static void wbsd_release_resources(struct wbsd_host *host)
+{
+ wbsd_release_dma(host);
+ wbsd_release_irq(host);
+ wbsd_release_regions(host);
+}
+
+/*
+ * Configure the resources the chip should use.
+ */
+
+static void wbsd_chip_config(struct wbsd_host *host)
+{
+ wbsd_unlock_config(host);
+
+ /*
+ * Reset the chip.
+ */
+ wbsd_write_config(host, WBSD_CONF_SWRST, 1);
+ wbsd_write_config(host, WBSD_CONF_SWRST, 0);
+
+ /*
+ * Select SD/MMC function.
+ */
+ wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
+
+ /*
+ * Set up card detection.
+ */
+ wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11);
+
+ /*
+ * Configure chip
+ */
+ wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8);
+ wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff);
+
+ wbsd_write_config(host, WBSD_CONF_IRQ, host->irq);
+
+ if (host->dma >= 0)
+ wbsd_write_config(host, WBSD_CONF_DRQ, host->dma);
+
+ /*
+ * Enable and power up chip.
+ */
+ wbsd_write_config(host, WBSD_CONF_ENABLE, 1);
+ wbsd_write_config(host, WBSD_CONF_POWER, 0x20);
+
+ wbsd_lock_config(host);
+}
+
+/*
+ * Check that configured resources are correct.
+ */
+
+static int wbsd_chip_validate(struct wbsd_host *host)
+{
+ int base, irq, dma;
+
+ wbsd_unlock_config(host);
+
+ /*
+ * Select SD/MMC function.
+ */
+ wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
+
+ /*
+ * Read configuration.
+ */
+ base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8;
+ base |= wbsd_read_config(host, WBSD_CONF_PORT_LO);
+
+ irq = wbsd_read_config(host, WBSD_CONF_IRQ);
+
+ dma = wbsd_read_config(host, WBSD_CONF_DRQ);
+
+ wbsd_lock_config(host);
+
+ /*
+ * Validate against given configuration.
+ */
+ if (base != host->base)
+ return 0;
+ if (irq != host->irq)
+ return 0;
+ if ((dma != host->dma) && (host->dma != -1))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Powers down the SD function
+ */
+
+static void wbsd_chip_poweroff(struct wbsd_host *host)
+{
+ wbsd_unlock_config(host);
+
+ wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD);
+ wbsd_write_config(host, WBSD_CONF_ENABLE, 0);
+
+ wbsd_lock_config(host);
+}
+
+/*****************************************************************************\
+ * *
+ * Devices setup and shutdown *
+ * *
+\*****************************************************************************/
+
+static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma,
+ int pnp)
+{
+ struct wbsd_host *host = NULL;
+ struct mmc_host *mmc = NULL;
+ int ret;
+
+ ret = wbsd_alloc_mmc(dev);
+ if (ret)
+ return ret;
+
+ mmc = dev_get_drvdata(dev);
+ host = mmc_priv(mmc);
+
+ /*
+ * Scan for hardware.
+ */
+ ret = wbsd_scan(host);
+ if (ret) {
+ if (pnp && (ret == -ENODEV)) {
+ printk(KERN_WARNING DRIVER_NAME
+ ": Unable to confirm device presence. You may "
+ "experience lock-ups.\n");
+ } else {
+ wbsd_free_mmc(dev);
+ return ret;
+ }
+ }
+
+ /*
+ * Request resources.
+ */
+ ret = wbsd_request_resources(host, base, irq, dma);
+ if (ret) {
+ wbsd_release_resources(host);
+ wbsd_free_mmc(dev);
+ return ret;
+ }
+
+ /*
+ * See if chip needs to be configured.
+ */
+ if (pnp) {
+ if ((host->config != 0) && !wbsd_chip_validate(host)) {
+ printk(KERN_WARNING DRIVER_NAME
+ ": PnP active but chip not configured! "
+ "You probably have a buggy BIOS. "
+ "Configuring chip manually.\n");
+ wbsd_chip_config(host);
+ }
+ } else
+ wbsd_chip_config(host);
+
+ /*
+ * Power Management stuff. No idea how this works.
+ * Not tested.
+ */
+#ifdef CONFIG_PM
+ if (host->config) {
+ wbsd_unlock_config(host);
+ wbsd_write_config(host, WBSD_CONF_PME, 0xA0);
+ wbsd_lock_config(host);
+ }
+#endif
+ /*
+ * Allow device to initialise itself properly.
+ */
+ mdelay(5);
+
+ /*
+ * Reset the chip into a known state.
+ */
+ wbsd_init_device(host);
+
+ mmc_add_host(mmc);
+
+ printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc));
+ if (host->chip_id != 0)
+ printk(" id %x", (int)host->chip_id);
+ printk(" at 0x%x irq %d", (int)host->base, (int)host->irq);
+ if (host->dma >= 0)
+ printk(" dma %d", (int)host->dma);
+ else
+ printk(" FIFO");
+ if (pnp)
+ printk(" PnP");
+ printk("\n");
+
+ return 0;
+}
+
+static void __devexit wbsd_shutdown(struct device *dev, int pnp)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct wbsd_host *host;
+
+ if (!mmc)
+ return;
+
+ host = mmc_priv(mmc);
+
+ mmc_remove_host(mmc);
+
+ /*
+ * Power down the SD/MMC function.
+ */
+ if (!pnp)
+ wbsd_chip_poweroff(host);
+
+ wbsd_release_resources(host);
+
+ wbsd_free_mmc(dev);
+}
+
+/*
+ * Non-PnP
+ */
+
+static int __devinit wbsd_probe(struct platform_device *dev)
+{
+ /* Use the module parameters for resources */
+ return wbsd_init(&dev->dev, param_io, param_irq, param_dma, 0);
+}
+
+static int __devexit wbsd_remove(struct platform_device *dev)
+{
+ wbsd_shutdown(&dev->dev, 0);
+
+ return 0;
+}
+
+/*
+ * PnP
+ */
+
+#ifdef CONFIG_PNP
+
+static int __devinit
+wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id)
+{
+ int io, irq, dma;
+
+ /*
+ * Get resources from PnP layer.
+ */
+ io = pnp_port_start(pnpdev, 0);
+ irq = pnp_irq(pnpdev, 0);
+ if (pnp_dma_valid(pnpdev, 0))
+ dma = pnp_dma(pnpdev, 0);
+ else
+ dma = -1;
+
+ DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma);
+
+ return wbsd_init(&pnpdev->dev, io, irq, dma, 1);
+}
+
+static void __devexit wbsd_pnp_remove(struct pnp_dev *dev)
+{
+ wbsd_shutdown(&dev->dev, 1);
+}
+
+#endif /* CONFIG_PNP */
+
+/*
+ * Power management
+ */
+
+#ifdef CONFIG_PM
+
+static int wbsd_suspend(struct wbsd_host *host, pm_message_t state)
+{
+ BUG_ON(host == NULL);
+
+ return mmc_suspend_host(host->mmc, state);
+}
+
+static int wbsd_resume(struct wbsd_host *host)
+{
+ BUG_ON(host == NULL);
+
+ wbsd_init_device(host);
+
+ return mmc_resume_host(host->mmc);
+}
+
+static int wbsd_platform_suspend(struct platform_device *dev,
+ pm_message_t state)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+ struct wbsd_host *host;
+ int ret;
+
+ if (mmc == NULL)
+ return 0;
+
+ DBGF("Suspending...\n");
+
+ host = mmc_priv(mmc);
+
+ ret = wbsd_suspend(host, state);
+ if (ret)
+ return ret;
+
+ wbsd_chip_poweroff(host);
+
+ return 0;
+}
+
+static int wbsd_platform_resume(struct platform_device *dev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+ struct wbsd_host *host;
+
+ if (mmc == NULL)
+ return 0;
+
+ DBGF("Resuming...\n");
+
+ host = mmc_priv(mmc);
+
+ wbsd_chip_config(host);
+
+ /*
+ * Allow device to initialise itself properly.
+ */
+ mdelay(5);
+
+ return wbsd_resume(host);
+}
+
+#ifdef CONFIG_PNP
+
+static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
+{
+ struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
+ struct wbsd_host *host;
+
+ if (mmc == NULL)
+ return 0;
+
+ DBGF("Suspending...\n");
+
+ host = mmc_priv(mmc);
+
+ return wbsd_suspend(host, state);
+}
+
+static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
+{
+ struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
+ struct wbsd_host *host;
+
+ if (mmc == NULL)
+ return 0;
+
+ DBGF("Resuming...\n");
+
+ host = mmc_priv(mmc);
+
+ /*
+ * See if chip needs to be configured.
+ */
+ if (host->config != 0) {
+ if (!wbsd_chip_validate(host)) {
+ printk(KERN_WARNING DRIVER_NAME
+ ": PnP active but chip not configured! "
+ "You probably have a buggy BIOS. "
+ "Configuring chip manually.\n");
+ wbsd_chip_config(host);
+ }
+ }
+
+ /*
+ * Allow device to initialise itself properly.
+ */
+ mdelay(5);
+
+ return wbsd_resume(host);
+}
+
+#endif /* CONFIG_PNP */
+
+#else /* CONFIG_PM */
+
+#define wbsd_platform_suspend NULL
+#define wbsd_platform_resume NULL
+
+#define wbsd_pnp_suspend NULL
+#define wbsd_pnp_resume NULL
+
+#endif /* CONFIG_PM */
+
+static struct platform_device *wbsd_device;
+
+static struct platform_driver wbsd_driver = {
+ .probe = wbsd_probe,
+ .remove = __devexit_p(wbsd_remove),
+
+ .suspend = wbsd_platform_suspend,
+ .resume = wbsd_platform_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+#ifdef CONFIG_PNP
+
+static struct pnp_driver wbsd_pnp_driver = {
+ .name = DRIVER_NAME,
+ .id_table = pnp_dev_table,
+ .probe = wbsd_pnp_probe,
+ .remove = __devexit_p(wbsd_pnp_remove),
+
+ .suspend = wbsd_pnp_suspend,
+ .resume = wbsd_pnp_resume,
+};
+
+#endif /* CONFIG_PNP */
+
+/*
+ * Module loading/unloading
+ */
+
+static int __init wbsd_drv_init(void)
+{
+ int result;
+
+ printk(KERN_INFO DRIVER_NAME
+ ": Winbond W83L51xD SD/MMC card interface driver\n");
+ printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
+
+#ifdef CONFIG_PNP
+
+ if (!param_nopnp) {
+ result = pnp_register_driver(&wbsd_pnp_driver);
+ if (result < 0)
+ return result;
+ }
+#endif /* CONFIG_PNP */
+
+ if (param_nopnp) {
+ result = platform_driver_register(&wbsd_driver);
+ if (result < 0)
+ return result;
+
+ wbsd_device = platform_device_alloc(DRIVER_NAME, -1);
+ if (!wbsd_device) {
+ platform_driver_unregister(&wbsd_driver);
+ return -ENOMEM;
+ }
+
+ result = platform_device_add(wbsd_device);
+ if (result) {
+ platform_device_put(wbsd_device);
+ platform_driver_unregister(&wbsd_driver);
+ return result;
+ }
+ }
+
+ return 0;
+}
+
+static void __exit wbsd_drv_exit(void)
+{
+#ifdef CONFIG_PNP
+
+ if (!param_nopnp)
+ pnp_unregister_driver(&wbsd_pnp_driver);
+
+#endif /* CONFIG_PNP */
+
+ if (param_nopnp) {
+ platform_device_unregister(wbsd_device);
+
+ platform_driver_unregister(&wbsd_driver);
+ }
+
+ DBG("unloaded\n");
+}
+
+module_init(wbsd_drv_init);
+module_exit(wbsd_drv_exit);
+#ifdef CONFIG_PNP
+module_param_named(nopnp, param_nopnp, uint, 0444);
+#endif
+module_param_named(io, param_io, uint, 0444);
+module_param_named(irq, param_irq, uint, 0444);
+module_param_named(dma, param_dma, int, 0444);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
+MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver");
+
+#ifdef CONFIG_PNP
+MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)");
+#endif
+MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)");
+MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)");
+MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)");
diff --git a/drivers/mmc/host/wbsd.h b/drivers/mmc/host/wbsd.h
new file mode 100644
index 0000000..0877866
--- /dev/null
+++ b/drivers/mmc/host/wbsd.h
@@ -0,0 +1,185 @@
+/*
+ * linux/drivers/mmc/host/wbsd.h - Winbond W83L51xD SD/MMC driver
+ *
+ * Copyright (C) 2004-2007 Pierre Ossman, 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#define LOCK_CODE 0xAA
+
+#define WBSD_CONF_SWRST 0x02
+#define WBSD_CONF_DEVICE 0x07
+#define WBSD_CONF_ID_HI 0x20
+#define WBSD_CONF_ID_LO 0x21
+#define WBSD_CONF_POWER 0x22
+#define WBSD_CONF_PME 0x23
+#define WBSD_CONF_PMES 0x24
+
+#define WBSD_CONF_ENABLE 0x30
+#define WBSD_CONF_PORT_HI 0x60
+#define WBSD_CONF_PORT_LO 0x61
+#define WBSD_CONF_IRQ 0x70
+#define WBSD_CONF_DRQ 0x74
+
+#define WBSD_CONF_PINS 0xF0
+
+#define DEVICE_SD 0x03
+
+#define WBSD_PINS_DAT3_HI 0x20
+#define WBSD_PINS_DAT3_OUT 0x10
+#define WBSD_PINS_GP11_HI 0x04
+#define WBSD_PINS_DETECT_GP11 0x02
+#define WBSD_PINS_DETECT_DAT3 0x01
+
+#define WBSD_CMDR 0x00
+#define WBSD_DFR 0x01
+#define WBSD_EIR 0x02
+#define WBSD_ISR 0x03
+#define WBSD_FSR 0x04
+#define WBSD_IDXR 0x05
+#define WBSD_DATAR 0x06
+#define WBSD_CSR 0x07
+
+#define WBSD_EINT_CARD 0x40
+#define WBSD_EINT_FIFO_THRE 0x20
+#define WBSD_EINT_CRC 0x10
+#define WBSD_EINT_TIMEOUT 0x08
+#define WBSD_EINT_PROGEND 0x04
+#define WBSD_EINT_BUSYEND 0x02
+#define WBSD_EINT_TC 0x01
+
+#define WBSD_INT_PENDING 0x80
+#define WBSD_INT_CARD 0x40
+#define WBSD_INT_FIFO_THRE 0x20
+#define WBSD_INT_CRC 0x10
+#define WBSD_INT_TIMEOUT 0x08
+#define WBSD_INT_PROGEND 0x04
+#define WBSD_INT_BUSYEND 0x02
+#define WBSD_INT_TC 0x01
+
+#define WBSD_FIFO_EMPTY 0x80
+#define WBSD_FIFO_FULL 0x40
+#define WBSD_FIFO_EMTHRE 0x20
+#define WBSD_FIFO_FUTHRE 0x10
+#define WBSD_FIFO_SZMASK 0x0F
+
+#define WBSD_MSLED 0x20
+#define WBSD_POWER_N 0x10
+#define WBSD_WRPT 0x04
+#define WBSD_CARDPRESENT 0x01
+
+#define WBSD_IDX_CLK 0x01
+#define WBSD_IDX_PBSMSB 0x02
+#define WBSD_IDX_TAAC 0x03
+#define WBSD_IDX_NSAC 0x04
+#define WBSD_IDX_PBSLSB 0x05
+#define WBSD_IDX_SETUP 0x06
+#define WBSD_IDX_DMA 0x07
+#define WBSD_IDX_FIFOEN 0x08
+#define WBSD_IDX_STATUS 0x10
+#define WBSD_IDX_RSPLEN 0x1E
+#define WBSD_IDX_RESP0 0x1F
+#define WBSD_IDX_RESP1 0x20
+#define WBSD_IDX_RESP2 0x21
+#define WBSD_IDX_RESP3 0x22
+#define WBSD_IDX_RESP4 0x23
+#define WBSD_IDX_RESP5 0x24
+#define WBSD_IDX_RESP6 0x25
+#define WBSD_IDX_RESP7 0x26
+#define WBSD_IDX_RESP8 0x27
+#define WBSD_IDX_RESP9 0x28
+#define WBSD_IDX_RESP10 0x29
+#define WBSD_IDX_RESP11 0x2A
+#define WBSD_IDX_RESP12 0x2B
+#define WBSD_IDX_RESP13 0x2C
+#define WBSD_IDX_RESP14 0x2D
+#define WBSD_IDX_RESP15 0x2E
+#define WBSD_IDX_RESP16 0x2F
+#define WBSD_IDX_CRCSTATUS 0x30
+#define WBSD_IDX_ISR 0x3F
+
+#define WBSD_CLK_375K 0x00
+#define WBSD_CLK_12M 0x01
+#define WBSD_CLK_16M 0x02
+#define WBSD_CLK_24M 0x03
+
+#define WBSD_DATA_WIDTH 0x01
+
+#define WBSD_DAT3_H 0x08
+#define WBSD_FIFO_RESET 0x04
+#define WBSD_SOFT_RESET 0x02
+#define WBSD_INC_INDEX 0x01
+
+#define WBSD_DMA_SINGLE 0x02
+#define WBSD_DMA_ENABLE 0x01
+
+#define WBSD_FIFOEN_EMPTY 0x20
+#define WBSD_FIFOEN_FULL 0x10
+#define WBSD_FIFO_THREMASK 0x0F
+
+#define WBSD_BLOCK_READ 0x80
+#define WBSD_BLOCK_WRITE 0x40
+#define WBSD_BUSY 0x20
+#define WBSD_CARDTRAFFIC 0x04
+#define WBSD_SENDCMD 0x02
+#define WBSD_RECVRES 0x01
+
+#define WBSD_RSP_SHORT 0x00
+#define WBSD_RSP_LONG 0x01
+
+#define WBSD_CRC_MASK 0x1F
+#define WBSD_CRC_OK 0x05 /* S010E (00101) */
+#define WBSD_CRC_FAIL 0x0B /* S101E (01011) */
+
+#define WBSD_DMA_SIZE 65536
+
+struct wbsd_host
+{
+ struct mmc_host* mmc; /* MMC structure */
+
+ spinlock_t lock; /* Mutex */
+
+ int flags; /* Driver states */
+
+#define WBSD_FCARD_PRESENT (1<<0) /* Card is present */
+#define WBSD_FIGNORE_DETECT (1<<1) /* Ignore card detection */
+
+ struct mmc_request* mrq; /* Current request */
+
+ u8 isr; /* Accumulated ISR */
+
+ struct scatterlist* cur_sg; /* Current SG entry */
+ unsigned int num_sg; /* Number of entries left */
+
+ unsigned int offset; /* Offset into current entry */
+ unsigned int remain; /* Data left in curren entry */
+
+ char* dma_buffer; /* ISA DMA buffer */
+ dma_addr_t dma_addr; /* Physical address for same */
+
+ int firsterr; /* See fifo functions */
+
+ u8 clk; /* Current clock speed */
+ unsigned char bus_width; /* Current bus width */
+
+ int config; /* Config port */
+ u8 unlock_code; /* Code to unlock config */
+
+ int chip_id; /* ID of controller */
+
+ int base; /* I/O port base */
+ int irq; /* Interrupt */
+ int dma; /* DMA channel */
+
+ struct tasklet_struct card_tasklet; /* Tasklet structures */
+ struct tasklet_struct fifo_tasklet;
+ struct tasklet_struct crc_tasklet;
+ struct tasklet_struct timeout_tasklet;
+ struct tasklet_struct finish_tasklet;
+
+ struct timer_list ignore_timer; /* Ignore detection timer */
+};
OpenPOWER on IntegriCloud