All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform
@ 2009-04-30  9:14 Li, Jiebing
  2009-04-30  9:16 ` [PATCH 1/2] " Li, Jiebing
  2009-04-30  9:19 ` [PATCH 2/2] " Li, Jiebing
  0 siblings, 2 replies; 8+ messages in thread
From: Li, Jiebing @ 2009-04-30  9:14 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: linux-kernel, Johnson, Charles F, Zhu, Daniel, Yuan, Hang,
	Pasrija, Geeta, Li, Jiebing

Hi Pierre,

I wish to submit two patches for Intel low power platform "Moorestown". The below is the description of the patches:

1. Patch 1 enables CE-ATA device support on Moorestown platform.
2. Patch 2 enables SDIO OSPM suspend/resume support for SDIO devices applied on Moorestown.
3. Added some silicon/hardware specific restrictions.

This is the first time Moorestown related patches are submitted, so I'm ready for code review and will try my best to do updates before the code can be finally accepted.

Thanks a lot!

Regards,
Jiebing Li

^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH 1/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform
  2009-04-30  9:14 [PATCH 0/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform Li, Jiebing
@ 2009-04-30  9:16 ` Li, Jiebing
  2009-04-30 13:44   ` Alan Cox
  2009-05-10 18:29   ` Pierre Ossman
  2009-04-30  9:19 ` [PATCH 2/2] " Li, Jiebing
  1 sibling, 2 replies; 8+ messages in thread
From: Li, Jiebing @ 2009-04-30  9:16 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: linux-kernel, Johnson, Charles F, Zhu, Daniel, Yuan, Hang,
	Pasrija, Geeta, Li, Jiebing


This patch enables CE-ATA HDD support including device initialization and  data read/write.
Validation has been passed with TOSHIBA, SAMSUNG and HITACHI devices.

And this patch also works for Moorestown platform by dealing with slicon/hardware limitations:
1. Slot number and first BAR values are fixed instead of reading from PCI configuration space.
2. Clock speed is restricted to normal-speed.
3. ADMA operation is disabled by adding quirks.

Signed-off-by: JiebingLi <jiebing.li@intel.com>
Signed-off-by: FengTang <feng.tang@intel.com>
Signed-off-by: GermanMonroy (german.monroy@intel.com>

---

 drivers/mmc/Kconfig          |   19 ++
 drivers/mmc/card/block.c     |  117 +++++++++++-
 drivers/mmc/core/Makefile    |    3 +-
 drivers/mmc/core/bus.c       |   10 +
 drivers/mmc/core/ceata.c     |  449 ++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/core/ceata.h     |   29 +++
 drivers/mmc/core/core.c      |   25 +++-
 drivers/mmc/core/mmc.c       |   56 +++++-
 drivers/mmc/core/mmc_ops.c   |   33 +++
 drivers/mmc/core/mmc_ops.h   |    2 +-
 drivers/mmc/core/sd.c        |    8 +
 drivers/mmc/core/sdio.c      |   13 ++
 drivers/mmc/host/sdhci-pci.c |   57 +++++-
 drivers/mmc/host/sdhci.c     |   14 +-
 drivers/mmc/host/sdhci.h     |    1 +
 include/linux/mmc/card.h     |   26 +++
 include/linux/mmc/mmc.h      |  111 +++++++++++
 17 files changed, 951 insertions(+), 22 deletions(-)
 create mode 100644 drivers/mmc/core/ceata.c
 create mode 100644 drivers/mmc/core/ceata.h

diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index f2eeb38..3d1c83b 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -19,6 +19,25 @@ config MMC_DEBUG
          This is an option for use by developers; most people should
          say N here.  This enables MMC core and driver debugging.

+config MRST_MMC_WR
+       bool "MMC/SD/CEATA workaround for Moorestown"
+       depends on MMC
+       help
+         This is an option for Moorestown developers to add workaround
+         in the code due to Silicon issues.
+
+config MMC_CEATA_WR
+       bool "disable SDIO device search to support CEATA"
+       depends on MMC
+       help
+         This disables SDIO device search process to support various
+         brands of CE-ATA devices as some CE-ATA devices may pretends
+         itself as a SDIO device.
+
+         If you want SDIO support, you should say N here and if you
+         want to support as many as kinds of CE-ATA devices,you should
+         say Y. If you want to support both SDIO and CE-ATA, just say N.
+
 if MMC

 source "drivers/mmc/core/Kconfig"
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index fe8041e..022a9db 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -226,6 +226,98 @@ static u32 get_card_status(struct mmc_card *card, struct request *req)
        return cmd.resp[0];
 }

+static int ceata_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;
+       int rw, err;
+
+       rw = rq_data_dir(req) == READ ? 0 : 1;
+
+       mmc_claim_host(card->host);
+
+       do {
+               /* 1. Send ATA command via ceata's mmc cmd 60*/
+               err = ceata_read_write_cmd(card,
+                       rw, req->sector, req->nr_sectors);
+
+               if (err)
+                       goto cmd_err;
+
+               /* 2. Transfer data via DMA using ceata's mmc cmd 61 */
+               memset(&brq, 0, sizeof(struct mmc_blk_request));
+
+               brq.mrq.cmd = &brq.cmd;
+               brq.mrq.data = &brq.data;
+               /* No stop command required for CEATA transfers */
+               brq.mrq.stop = NULL;
+
+               /*
+                * New controllers claim to support 4k mmc block transfers,
+                * but no known CEATA hdd supports it.
+                * Playing it safe at 512 for now
+                */
+               brq.data.blksz = 512;
+               brq.data.blocks = req->nr_sectors;
+               brq.data.flags = rw ? MMC_DATA_WRITE : MMC_DATA_READ;
+
+               if (brq.data.blocks > card->host->max_blk_count)
+                       brq.data.blocks = card->host->max_blk_count;
+
+               brq.cmd.opcode = MMC_CEATA_RW_MULTI_BLK;
+               brq.cmd.arg = (rw << 31) | req->nr_sectors;
+               brq.cmd.flags = MMC_CMD_ADTC | (rw ? MMC_RSP_R1B : MMC_RSP_R1);
+
+               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);
+
+               if (brq.cmd.error) {
+                       printk(KERN_ERR "%s: error %d sending read/write command\n",
+                       req->rq_disk->disk_name, brq.cmd.error);
+                       goto cmd_err;
+               }
+
+               if (brq.data.error) {
+                       printk(KERN_ERR "%s: error %d transferring data\n",
+                       req->rq_disk->disk_name, brq.data.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:
+       mmc_release_host(card->host);
+
+       spin_lock_irq(&md->lock);
+       while (ret)
+               ret = __blk_end_request(req, -EIO,
+                       req->current_nr_sectors << 9);
+
+       spin_unlock_irq(&md->lock);
+
+       return 0;
+
+}
+
 static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
 {
        struct mmc_blk_data *md = mq->data;
@@ -449,6 +541,9 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)

 static inline int mmc_blk_readonly(struct mmc_card *card)
 {
+       if (mmc_card_ceata(card))
+               return 0;
+
        return mmc_card_readonly(card) ||
               !(card->csd.cmdclass & CCC_BLOCK_WRITE);
 }
@@ -489,7 +584,11 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
        if (ret)
                goto err_putdisk;

-       md->queue.issue_fn = mmc_blk_issue_rq;
+       if (mmc_card_ceata(card))
+               md->queue.issue_fn = ceata_blk_issue_rq;
+       else
+               md->queue.issue_fn = mmc_blk_issue_rq;
+
        md->queue.data = md;

        md->disk->major = MMC_BLOCK_MAJOR;
@@ -511,11 +610,20 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
         * messages to tell when the card is present.
         */

-       sprintf(md->disk->disk_name, "mmcblk%d", devidx);
+       snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),
+               "mmcblk%d", devidx);

        blk_queue_hardsect_size(md->queue.queue, 512);

-       if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
+       if (mmc_card_ceata(card)) {
+               /* max_lba[0] covers all current ceata devices */
+               WARN_ON(card->ceata_info->max_lba[1]);
+               /*
+                * Capacity from IDENTIFY_DEVICE comes in 512 byte
+                * sectors.
+                */
+               set_capacity(md->disk, card->ceata_info->max_lba[0]);
+       } else if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
                /*
                 * The EXT_CSD sector count is in number or 512 byte
                 * sectors.
@@ -575,7 +683,8 @@ static int mmc_blk_probe(struct mmc_card *card)
        /*
         * Check that the card supports the command class(es) we need.
         */
-       if (!(card->csd.cmdclass & CCC_BLOCK_READ))
+       if (!(card->csd.cmdclass & CCC_BLOCK_READ) &&
+       !mmc_card_ceata(card))
                return -ENODEV;

        md = mmc_blk_alloc(card);
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 889e5f8..3c31f32 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -10,6 +10,7 @@ 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
+                                  sdio_cis.o sdio_io.o sdio_irq.o \
+                                  ceata.o

 mmc_core-$(CONFIG_DEBUG_FS)    += debugfs.o
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index bdb165f..5f66c5a 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -36,6 +36,8 @@ static ssize_t mmc_type_show(struct device *dev,
                return sprintf(buf, "SD\n");
        case MMC_TYPE_SDIO:
                return sprintf(buf, "SDIO\n");
+       case MMC_TYPE_CEATA:
+               return snprintf(buf, sizeof(buf), "CEATA\n");
        default:
                return -EFAULT;
        }
@@ -73,6 +75,9 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
        case MMC_TYPE_SDIO:
                type = "SDIO";
                break;
+       case MMC_TYPE_CEATA:
+               type = "CEATA";
+               break;
        default:
                type = NULL;
        }
@@ -190,6 +195,8 @@ static void mmc_release_card(struct device *dev)
        if (card->info)
                kfree(card->info);

+       kfree(card->ceata_info);
+
        kfree(card);
 }

@@ -238,6 +245,9 @@ int mmc_add_card(struct mmc_card *card)
        case MMC_TYPE_SDIO:
                type = "SDIO";
                break;
+       case MMC_TYPE_CEATA:
+               type = "CEATA";
+               break;
        default:
                type = "?";
                break;
diff --git a/drivers/mmc/core/ceata.c b/drivers/mmc/core/ceata.c
new file mode 100644
index 0000000..49fb80c
--- /dev/null
+++ b/drivers/mmc/core/ceata.c
@@ -0,0 +1,449 @@
+/*
+ * Intel MMC/SD/CEATA driver
+ * Copyright (C) 2008, Intel Corporation.
+ * Author: Li Jiebing (jiebing.li@intel.com)
+ *        Tang Feng  (feng.tang@intel.com)
+ *        Monroy German (german.monroy@intel.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
+#include <linux/scatterlist.h>
+
+#include "core.h"
+#include "mmc_ops.h"
+
+/*
+ * Poll ATA status register until the device is ready for the next
+ * ATA command (command completion)
+ */
+static int ceata_wait_until_ready(struct mmc_card *card)
+{
+       u8 status;
+       u8 error;
+       char *type;
+       /*
+        * Almost all the time this function will return immediately
+        * or within a few milliseconds.  However the Toshiba and
+        * Samsung hdds have been known to take up to 1.5 s to clear
+        * their ATA busy bit, either when the hdd is used for the first
+        * time or sporadically during normal use.  Leaving 30 s for
+        * a corner case.
+        */
+       int limit = 30000;
+       int wait = 0;
+
+       for (wait = 0; wait < limit; wait++) {
+               mmc_fast_io(card, 0, CEATA_REG_STATUS, &status, 0x0);
+
+               if (status & CEATA_STATUS_ERR) {
+                       mmc_fast_io(card, 0, CEATA_REG_ERROR, &error, 0x0);
+
+                       switch (error) {
+                       case CEATA_ERROR_ICRC:
+                               type = "Interface CRC";
+                               break;
+                       case CEATA_ERROR_UNC:
+                               type = "Uncorrectable data";
+                               break;
+                       case CEATA_ERROR_IDNF:
+                               type = "Sector not found";
+                               break;
+                       case CEATA_ERROR_ABRT:
+                               type = "Command Aborted";
+                               break;
+                       default:
+                               type = "Unknown";
+                       }
+                       printk(KERN_ERR "%s: CE-ATA device error: %s\n",
+                                       mmc_hostname(card->host), type);
+                       return -EIO;
+
+               }
+
+               if (!(status & CEATA_STATUS_BSY) &&
+                               (status & CEATA_STATUS_DRDY))
+                       goto exit;
+               mmc_delay(1);
+       }
+
+       printk(KERN_ERR "%s: after %d ms wait, CE-ATA still busy\n",
+                       mmc_hostname(card->host), wait);
+       return -ETIMEDOUT;
+
+exit:
+       if (wait > 0)
+               pr_debug("%s: polled CE-ATA status during %d ms\n",
+                               mmc_hostname(card->host), wait);
+       return 0;
+}
+
+/*
+ * Transfer multiple ATA registers (task file) in one MMC operation
+ */
+static int ceata_cmd60(struct mmc_card *card, u8 rw, void *ptaskfile,
+               u8 reg_addr, u8 bytelen)
+{
+       struct mmc_request mrq;
+       struct mmc_command cmd;
+       struct mmc_data data;
+       struct scatterlist sg;
+
+       BUG_ON(!card);
+       BUG_ON(!card->host);
+       BUG_ON(!ptaskfile);
+
+       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 = MMC_CEATA_RW_MULTI_REG;
+       cmd.arg = (rw << 31) | (reg_addr << 16) | (bytelen);
+       cmd.flags = MMC_CMD_ADTC | (rw ? MMC_RSP_R1B : MMC_RSP_R1);
+
+       data.blksz = bytelen;
+       data.blocks = 1;
+       data.flags = rw ? MMC_DATA_WRITE : MMC_DATA_READ;
+       data.sg = &sg;
+       data.sg_len = 1;
+
+       sg_init_one(&sg, ptaskfile, bytelen);
+
+       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;
+}
+
+/*
+ * Transfer the data for a previously issued ATA taskfile
+ */
+static int ceata_cmd61(struct mmc_card *card, u8 rw, void *buffer,
+               u16 bytelen)
+{
+       struct mmc_request mrq;
+       struct mmc_command cmd;
+       struct mmc_data data;
+       struct scatterlist sg;
+
+       BUG_ON(!card);
+       BUG_ON(!card->host);
+       BUG_ON(bytelen % 512);
+
+       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;
+       mrq.stop = NULL;
+
+       /* MMC transfer size currently fixed at 512 */
+       data.blksz = 512;
+       data.blocks = bytelen / 512;
+       data.flags = rw ? MMC_DATA_WRITE : MMC_DATA_READ;
+       data.sg = &sg;
+       data.sg_len = 1;
+
+       cmd.opcode = MMC_CEATA_RW_MULTI_BLK;
+       cmd.arg = (rw << 31) | data.blocks;
+       cmd.flags = MMC_CMD_ADTC | (rw ? MMC_RSP_R1B : MMC_RSP_R1);
+
+       sg_init_one(&sg, buffer, bytelen);
+
+       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;
+}
+
+/*
+ * Check the CEATA reset signature
+ */
+static int ceata_check_signature(struct mmc_card *card)
+{
+       u8      *buffer;
+       u8      reg_addr;
+       int     ret = 0;
+
+       ret = ceata_wait_until_ready(card);
+       if (ret)
+               return ret;
+
+       buffer = kzalloc(CEATA_TASKFILE_BYTELEN, GFP_KERNEL);
+       if (!buffer) {
+               printk(KERN_ERR "%s: could not allocate a buffer to hold "
+                       "the CE-ATA signature.\n", mmc_hostname(card->host));
+               return -ENOMEM;
+       }
+
+       reg_addr = 0;
+       ret = ceata_cmd60(card, 0, buffer, reg_addr, CEATA_TASKFILE_BYTELEN);
+       if (ret)
+               goto err;
+
+       if (buffer[12] != CEATA_SIGNATURE_BYTE_12 ||
+                       buffer[13] != CEATA_SIGNATURE_BYTE_13)
+               printk(KERN_WARNING "%s: device missing CE-ATA signature\n",
+                               mmc_hostname(card->host));
+
+err:
+       kfree(buffer);
+       return ret;
+}
+
+/*
+ * Retrieve device information and capabilities
+ */
+static int ceata_identify_dev(struct mmc_card *card)
+{
+       u8      *cmd_buf;
+       u32     *data_buf;
+       u8      reg_addr = 0;
+
+       struct  ceata_card_info *info;
+       struct  ceata_task *ptask;
+       int     ret = 0;
+
+       u16     *ptemp;
+
+       /* Issue identify_dev cmd through cmd 60 */
+       cmd_buf = kzalloc(CEATA_TASKFILE_BYTELEN, GFP_KERNEL);
+       data_buf = kzalloc(CEATA_IDENTIFY_DEV_BYTELEN, GFP_KERNEL);
+       if (!cmd_buf | !data_buf) {
+               printk(KERN_ERR "%s: could not allocate buffers to "
+                       "identify CE-ATA device.\n", mmc_hostname(card->host));
+               return -ENOMEM;
+       }
+
+       ptask = (struct ceata_task *)cmd_buf;
+       ptask->cmd_status = CEATA_CMD_IDENTIFY_DEV;
+
+       ret = ceata_wait_until_ready(card);
+       if (ret)
+               goto exit;
+
+       ret = ceata_cmd60(card, 1, cmd_buf, reg_addr, CEATA_TASKFILE_BYTELEN);
+       if (ret) {
+               printk(KERN_ERR "%s: Unable to send identify command to "
+                               "CE-ATA device\n", mmc_hostname(card->host));
+               goto exit;
+       }
+
+       ret = ceata_cmd61(card, 0, data_buf, CEATA_IDENTIFY_DEV_BYTELEN);
+       if (ret) {
+               printk(KERN_ERR "%s: Unable to receive identify data from "
+                              "CE-ATA device\n", mmc_hostname(card->host));
+               goto exit;
+       }
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info) {
+               printk(KERN_ERR "%s: could not allocate a buffer to hold "
+                       "the CE-ATA dev info.\n", mmc_hostname(card->host));
+               return -ENOMEM;
+       }
+       card->ceata_info = info;
+
+       ptemp = (u16 *)data_buf;
+
+       memcpy(info->serialnum, ptemp + 10, 20);
+       info->serialnum[20] = 0;
+       memcpy(info->fw_ver, ptemp + 23, 8);
+       info->fw_ver[8] = 0;
+       memcpy(info->model_num, ptemp + 27, 40);
+       info->model_num[40] = 0;
+
+       info->major = ptemp[80];
+       info->max_lba[0] = data_buf[50];    /* units are 512-byte sectors */
+       info->max_lba[1] = data_buf[51];
+
+       info->seclen = ptemp[106]; /* power of 2 */
+
+       memcpy(info->global_id, ptemp + 108, 4);
+       info->features = ptemp[206];
+       info->max_writes = ptemp[207];
+
+       pr_debug("%s: CE-ATA identify device output:\n",
+                       mmc_hostname(card->host));
+       pr_debug(" serial: %s\n", info->serialnum);
+       pr_debug(" firmware: %s\n", info->fw_ver);
+       pr_debug(" model: %s\n", info->model_num);
+       pr_debug(" major = 0x%x, secsize = 0x%x, features = 0x%04x\n",
+                       info->major, info->seclen, info->features);
+       pr_debug(" max_writes = %d, max_lba = 0x%08x%08x\n",
+                       info->max_writes, info->max_lba[1], info->max_lba[0]);
+
+       BUG_ON(info->seclen >= 32);
+
+exit:
+       kfree(cmd_buf);
+       kfree(data_buf);
+
+       return ret;
+}
+
+static int ceata_sw_reset(struct mmc_card *card)
+{
+       int ret = 0;
+
+       mmc_fast_io(card, 1, CEATA_REG_CONTROL, NULL,
+                       CEATA_CONTROL_SRST | CEATA_CONTROL_NIEN);
+       mmc_fast_io(card, 1, CEATA_REG_CONTROL, NULL,
+                       ~CEATA_CONTROL_SRST | CEATA_CONTROL_NIEN);
+
+       ret = ceata_wait_until_ready(card);
+
+       return ret;
+}
+
+int ceata_flush_cache(struct mmc_card *card)
+{
+       u8      *cmd_buf;
+       u8      reg_addr = 0;
+
+       struct  ceata_task *ptask;
+       int     ret = 0;
+
+       cmd_buf = kzalloc(CEATA_TASKFILE_BYTELEN, GFP_KERNEL);
+       if (!cmd_buf) {
+               printk(KERN_ERR "%s: could not allocate a buffer for the "
+                       "flush cache command.\n", mmc_hostname(card->host));
+               return -ENOMEM;
+       }
+       ptask = (struct ceata_task *)cmd_buf;
+       ptask->cmd_status = CEATA_CMD_FLUSH_CACHE_EXT;
+
+       ret = ceata_wait_until_ready(card);
+       if (ret)
+               return ret;
+
+       ret = ceata_cmd60(card, 1, cmd_buf, reg_addr, CEATA_TASKFILE_BYTELEN);
+       if (ret)
+               printk(KERN_ERR "%s: Error issuing CE-ATA flush cache "
+                               "command\n", mmc_hostname(card->host));
+
+       return ret;
+}
+
+int ceata_init_card(struct mmc_card *card, struct device_type *type)
+{
+       int ret = 0;
+
+       card->type = MMC_TYPE_CEATA;
+
+#ifdef CONFIG_MRST_MMC_WR
+       ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+               EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_1);
+       if (ret)
+               return ret;
+
+       mmc_set_bus_width(card->host, MMC_BUS_WIDTH_1);
+#endif
+
+       ret = mmc_switch(card, 0, EXT_CSD_CMD_SET, EXT_CSD_CMD_SET_CEATA);
+       if (ret)
+               return ret;
+
+       ret = ceata_sw_reset(card);
+       if (ret)
+               return ret;
+
+       ret = ceata_check_signature(card);
+       if (ret)
+               return ret;
+
+       ret = ceata_identify_dev(card);
+       if (ret)
+               return ret;
+
+       card->dev.type = type;
+
+       mmc_card_set_blockaddr(card);
+
+       return ret;
+}
+
+int ceata_read_write_cmd(struct mmc_card *card, int rw,
+       sector_t lba, u32 secs)
+{
+       u8      *cmd_buf, *plba;
+       u8      reg_addr = 0;
+       struct  ceata_task *ptask;
+       int     ret;
+
+
+       if (secs == 0) {
+               printk(KERN_ERR "%s: received a NULL length request\n",
+               mmc_hostname(card->host));
+               return -EINVAL;
+       }
+
+       ret = ceata_wait_until_ready(card);
+       if (ret)
+               return ret;
+
+       cmd_buf = kzalloc(CEATA_TASKFILE_BYTELEN, GFP_KERNEL);
+       if (!cmd_buf) {
+               printk(KERN_ERR "%s: could not allocate a buffer for the "
+                       "read/write dma command.\n", mmc_hostname(card->host));
+               return -ENOMEM;
+       }
+       ptask = (struct ceata_task *)cmd_buf;
+       ptask->cmd_status = rw ?
+               CEATA_CMD_WRITE_DMA_EXT :
+               CEATA_CMD_READ_DMA_EXT;
+       ptask->sec = (u8)(secs & 0xff);
+       ptask->sec_exp = (u8)(secs & 0xff00) >> 8;
+
+       plba = (u8 *)&lba;
+       ptask->lba_low      = plba[0];
+       ptask->lba_mid      = plba[1];
+       ptask->lba_high     = plba[2];
+       ptask->lba_low_exp  = plba[3];
+#ifdef CONFIG_LBD
+       ptask->lba_mid_exp  = plba[4];
+       ptask->lba_high_exp = plba[5];
+#endif
+
+       ret = ceata_cmd60(card, 1, cmd_buf, reg_addr, CEATA_TASKFILE_BYTELEN);
+       if (ret)
+               printk(KERN_ERR "%s: error during CE-ATA %s dma command\n",
+                               mmc_hostname(card->host),
+                               rw ? "write" : "read");
+
+       kfree(cmd_buf);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ceata_read_write_cmd);
diff --git a/drivers/mmc/core/ceata.h b/drivers/mmc/core/ceata.h
new file mode 100644
index 0000000..b685497
--- /dev/null
+++ b/drivers/mmc/core/ceata.h
@@ -0,0 +1,29 @@
+/*
+ * Intel MMC/SD/CEATA driver
+ * Copyright (C) 2008, Intel Corporation.
+ * Author: Li Jiebing (jiebing.li@intel.com)
+ *        Tang Feng  (feng.tang@intel.com)
+ *        Monroy German (german.monroy@intel.com)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _MMC_CORE_CEATA_H
+#define _MMC_CORE_CEATA_H
+
+int ceata_init_card(struct mmc_card *card, struct device_type *type);
+int ceata_flush_cache(struct mmc_card *card);
+
+#endif
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index fa073ab..2aa638d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -248,6 +248,18 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
        unsigned int mult;

        /*
+        * CEATA hdds could take as long as 35 s in corner cases,
+        * according to at least one datasheet.
+        * TODO: implement longer timeout in SW, since max HW timeout
+        * is just ~2 s (2^27 / 48MHz)
+        */
+       if (mmc_card_ceata(card)) {
+               data->timeout_ns = 2000000000;
+               data->timeout_clks = 0;
+               return;
+       }
+
+       /*
         * SDIO cards only define an upper 1 s limit on access.
         */
        if (mmc_card_sdio(card)) {
@@ -715,8 +727,12 @@ static void mmc_power_up(struct mmc_host *host)
        /*
         * This delay must be at least 74 clock sizes, or 1 ms, or the
         * time required to reach a stable voltage.
+        *
+        * Three different CEATA hdds from Toshiba, Samsung
+        * and Hitachi fail when the delay is less than 20 ms, but
+        * work with 40 ms (doubling just in case).
         */
-       mmc_delay(2);
+       mmc_delay(80);
 }

 static void mmc_power_off(struct mmc_host *host)
@@ -870,6 +886,11 @@ void mmc_rescan(struct work_struct *work)
                mmc_power_up(host);
                mmc_go_idle(host);

+/*
+ * TOSHIBA HDD is supported by disabling SDIO device search. As TOSHIBA
+ * HDD pretends itself as a SDIO device and then initialization fails.
+ */
+#ifndef CONFIG_MMC_CEATA_WR
                mmc_send_if_cond(host, host->ocr_avail);

                /*
@@ -881,7 +902,7 @@ void mmc_rescan(struct work_struct *work)
                                mmc_power_off(host);
                        goto out;
                }
-
+#endif
                /*
                 * ...then normal SD...
                 */
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 06084db..6920fbd 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -19,6 +19,7 @@
 #include "core.h"
 #include "bus.h"
 #include "mmc_ops.h"
+#include "ceata.h"

 static const unsigned int tran_exp[] = {
        10000,          100000,         1000000,        10000000,
@@ -121,7 +122,7 @@ static int mmc_decode_csd(struct mmc_card *card)
         * v1.2 has extra information in bits 15, 11 and 10.
         */
        csd_struct = UNSTUFF_BITS(resp, 126, 2);
-       if (csd_struct != 1 && csd_struct != 2) {
+       if (csd_struct != 1 && csd_struct != 2 && csd_struct != 3) {
                printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
                        mmc_hostname(card->host), csd_struct);
                return -EINVAL;
@@ -241,6 +242,8 @@ static int mmc_read_ext_csd(struct mmc_card *card)
                goto out;
        }

+       card->ext_csd.s_cmd_set = ext_csd[EXT_CSD_S_CMD_SET];
+
 out:
        kfree(ext_csd);

@@ -285,6 +288,39 @@ static struct device_type mmc_type = {
        .groups = mmc_attr_groups,
 };

+MMC_DEV_ATTR(capacity, "%d KB\n", card->ceata_info->max_lba[0] / 2);
+MMC_DEV_ATTR(seclen, "%d bytes\n", 1 << card->ceata_info->seclen);
+MMC_DEV_ATTR(globalid, "%08x%08x\n", card->ceata_info->global_id[1],
+       card->ceata_info->global_id[0]);
+MMC_DEV_ATTR(serial2, "%s\n", card->ceata_info->serialnum);
+MMC_DEV_ATTR(fwrev2, "%s\n", card->ceata_info->fw_ver);
+MMC_DEV_ATTR(model, "%s\n", card->ceata_info->model_num);
+
+
+static struct attribute *mmc_ceata_attrs[] = {
+       &dev_attr_capacity.attr,
+       &dev_attr_seclen.attr,
+       &dev_attr_globalid.attr,
+       &dev_attr_serial2.attr,
+       &dev_attr_fwrev2.attr,
+       &dev_attr_model.attr,
+       NULL,
+};
+
+static struct attribute_group mmc_ceata_attr_group = {
+       .attrs = mmc_ceata_attrs,
+};
+
+static struct attribute_group *ceata_attr_groups[] = {
+       &mmc_std_attr_group,
+       &mmc_ceata_attr_group,
+       NULL,
+};
+
+static struct device_type ceata_type = {
+       .groups = ceata_attr_groups,
+};
+
 /*
  * Handle the detection and initialisation of a card.
  *
@@ -401,6 +437,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                        goto free_card;
        }

+/*
+ * temporaily avoid MMC cards to switch to HS timing
+ * which doesn't work yet due to existing Silicon bug
+ */
+#ifndef CONFIG_MRST_MMC_WR
        /*
         * Activate high speed (if supported)
         */
@@ -415,7 +456,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,

                mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
        }
-
+#endif
        /*
         * Compute bus speed.
         */
@@ -454,6 +495,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                mmc_set_bus_width(card->host, bus_width);
        }

+       /*
+        * CEATA hdds are a subset of MMC cards
+        */
+       if (card->ext_csd.s_cmd_set & EXT_CSD_CMD_SET_CEATA) {
+               err = ceata_init_card(card, &ceata_type);
+               if (err)
+                       goto free_card;
+       }
+
        if (!oldcard)
                host->card = card;

@@ -518,6 +568,8 @@ static void mmc_suspend(struct mmc_host *host)
        BUG_ON(!host->card);

        mmc_claim_host(host);
+       if (mmc_card_ceata(host->card))
+               ceata_flush_cache(host->card);
        if (!mmc_host_is_spi(host))
                mmc_deselect_cards(host);
        host->card->state &= ~MMC_STATE_HIGHSPEED;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 34ce270..64906e4 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -402,3 +402,36 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
        return 0;
 }

+/*
+ * use to issue FAST_IO cmd
+ * READ: pdata points to buffer to receive data
+ * WRITE: data contains the data to be written to the card
+ * rw: 0 for read, 1 for write
+ */
+int mmc_fast_io(struct mmc_card *card, u8 rw, u8 reg_addr, u8 *read, u8 write)
+{
+       int err;
+       struct mmc_command cmd;
+
+       BUG_ON(!card);
+       BUG_ON(!card->host);
+       WARN_ON(reg_addr >= 128);   /* reg_addr only contains 7 bits */
+
+       memset(&cmd, 0, sizeof(struct mmc_command));
+
+       cmd.opcode = MMC_FAST_IO;
+       cmd.arg = (card->rca << 16) |
+               (rw << 15)|
+               (reg_addr << 8) | write;
+       cmd.flags = MMC_RSP_R4 | MMC_CMD_AC;
+
+       err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+       if (err)
+               return err;
+
+       if ((!rw) && read)
+               *read = cmd.resp[0];
+
+       return 0;
+}
+
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 17854bf..af584f6 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -25,6 +25,6 @@ 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);
-
+int mmc_fast_io(struct mmc_card *card, u8 rw, u8 reg_addr, u8 *read, u8 write);
 #endif

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index cd81c39..56336ce 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -234,6 +234,7 @@ out:
        return err;
 }

+#ifndef CONFIG_MRST_MMC_WR
 /*
  * Test if the card supports high-speed mode and, if so, switch to it.
  */
@@ -281,6 +282,7 @@ out:

        return err;
 }
+#endif

 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]);
@@ -460,12 +462,18 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
                        goto free_card;
        }

+/*
+ * temporarily avoiding SD cards to switch to HS timing
+ * which doesn't work yet due to existing Silicon bug
+ */
+#ifndef CONFIG_MRST_MMC_WR
        /*
         * Attempt to change to high-speed (if supported)
         */
        err = mmc_switch_hs(card);
        if (err)
                goto free_card;
+#endif

        /*
         * Compute bus speed.
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index fb99ccf..3f31f67 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -164,6 +164,7 @@ static int sdio_enable_wide(struct mmc_card *card)
        return 0;
 }

+#ifndef CONFIG_MRST_MMC_WR
 /*
  * Test if the card supports high-speed mode and, if so, switch to it.
  */
@@ -193,6 +194,7 @@ static int sdio_enable_hs(struct mmc_card *card)

        return 0;
 }
+#endif

 /*
  * Host is being removed. Free up the current card.
@@ -362,6 +364,11 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
        if (err)
                goto remove;

+/*
+ * temporarily avoiding SDIO cards to switch to HS timing
+ * which doesn't work yet due to existing Silicon bug
+ */
+#ifndef CONFIG_MRST_MMC_WR
        /*
         * Switch to high-speed (if supported).
         */
@@ -383,6 +390,12 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
        } else {
                mmc_set_clock(host, card->cis.max_dtr);
        }
+#else
+       if (card->cis.max_dtr > 24000000)
+               card->cis.max_dtr = 24000000;
+
+       mmc_set_clock(host, card->cis.max_dtr);
+#endif

        /*
         * Switch to wider bus (if supported).
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index cd37962..312ec69 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -284,6 +284,13 @@ static const struct sdhci_pci_fixes sdhci_jmicron = {
        .resume         = jmicron_resume,
 };

+/*
+ * ADMA operation is disabled for Moorestown platform.
+ */
+static const struct sdhci_pci_fixes sdhci_intel_mrst = {
+       .quirks         = SDHCI_QUIRK_BROKEN_ADMA,
+};
+
 static const struct pci_device_id pci_ids[] __devinitdata = {
        {
                .vendor         = PCI_VENDOR_ID_RICOH,
@@ -349,6 +356,22 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
                .driver_data    = (kernel_ulong_t)&sdhci_jmicron,
        },

+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_MRST_SD0,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_mrst,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_INTEL,
+               .device         = PCI_DEVICE_ID_INTEL_MRST_SD1,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (kernel_ulong_t)&sdhci_intel_mrst,
+       },
+
        {       /* Generic SD host controller */
                PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
        },
@@ -614,22 +637,40 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
        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 number is fixed to 2 by Moorestown architecture
+        */
+       if (pdev->device == PCI_DEVICE_ID_INTEL_MRST_SD0 ||
+               pdev->device == PCI_DEVICE_ID_INTEL_MRST_SD1) {
+               slots = 1;
+       } else {
+               ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots);
+
+               if (ret)
+                       return ret;
+
+               slots = PCI_SLOT_INFO_SLOTS(slots) + 1;
+       }

-       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 is fixed to 0 by Moorestown architecture
+        */
+       if (pdev->device == PCI_DEVICE_ID_INTEL_MRST_SD0 ||
+               pdev->device == PCI_DEVICE_ID_INTEL_MRST_SD1) {
+               first_bar = 0;
+       } else {
+               ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar);
+               if (ret)
+                       return ret;

-       first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK;
+               first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK;
+       }

        if (first_bar > 5) {
                dev_err(&pdev->dev, "Invalid first BAR. Aborting.\n");
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9234be2..a2804f1 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1123,12 +1123,18 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

        ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);

-       if (ios->bus_width == MMC_BUS_WIDTH_4)
+       if (ios->bus_width == MMC_BUS_WIDTH_8) {
+               ctrl |= SDHCI_CTRL_8BITBUS;
                ctrl |= SDHCI_CTRL_4BITBUS;
-       else
+       } else if (ios->bus_width == MMC_BUS_WIDTH_4) {
+               ctrl &= ~SDHCI_CTRL_8BITBUS;
+               ctrl |= SDHCI_CTRL_4BITBUS;
+       } else {
+               ctrl &= ~SDHCI_CTRL_8BITBUS;
                ctrl &= ~SDHCI_CTRL_4BITBUS;
+       }

-       if (ios->timing == MMC_TIMING_SD_HS)
+       if (ios->timing == MMC_TIMING_SD_HS || ios->timing == MMC_TIMING_MMC_HS)
                ctrl |= SDHCI_CTRL_HISPD;
        else
                ctrl &= ~SDHCI_CTRL_HISPD;
@@ -1724,7 +1730,7 @@ int sdhci_add_host(struct sdhci_host *host)
        mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;

        if (caps & SDHCI_CAN_DO_HISPD)
-               mmc->caps |= MMC_CAP_SD_HIGHSPEED;
+               mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;

        if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
                mmc->caps |= MMC_CAP_NEEDS_POLL;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index f20a834..fa87b8b 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -65,6 +65,7 @@
 #define  SDHCI_CTRL_LED                0x01
 #define  SDHCI_CTRL_4BITBUS    0x02
 #define  SDHCI_CTRL_HISPD      0x04
+#define  SDHCI_CTRL_8BITBUS    0x20
 #define  SDHCI_CTRL_DMA_MASK   0x18
 #define   SDHCI_CTRL_SDMA      0x00
 #define   SDHCI_CTRL_ADMA1     0x08
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 403aa50..e5d8a9e 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -42,6 +42,7 @@ struct mmc_csd {
 struct mmc_ext_csd {
        unsigned int            hs_max_dtr;
        unsigned int            sectors;
+       unsigned char           s_cmd_set;
 };

 struct sd_scr {
@@ -72,6 +73,19 @@ struct sdio_cis {
        unsigned int            max_dtr;
 };

+struct ceata_card_info {
+       u8  serialnum[21];
+       u8  fw_ver[9];
+       u8  model_num[41];
+       u16 major;
+       u16 seclen;         /* power of 2, 12 means 4096 */
+       u32 max_lba[2];
+       u32 global_id[2];
+       u16 features;
+       u16 max_writes;
+       u16 integrity;
+};
+
 struct mmc_host;
 struct sdio_func;
 struct sdio_func_tuple;
@@ -89,6 +103,7 @@ struct mmc_card {
 #define MMC_TYPE_MMC           0               /* MMC card */
 #define MMC_TYPE_SD            1               /* SD card */
 #define MMC_TYPE_SDIO          2               /* SDIO card */
+#define MMC_TYPE_CEATA         3               /* CEATA card */
        unsigned int            state;          /* (our) card state */
 #define MMC_STATE_PRESENT      (1<<0)          /* present in sysfs */
 #define MMC_STATE_READONLY     (1<<1)          /* card is read-only */
@@ -104,6 +119,10 @@ struct mmc_card {
        struct sd_scr           scr;            /* extra SD information */
        struct sd_switch_caps   sw_caps;        /* switch (CMD6) caps */

+       /* ceata info */
+       struct ceata_card_info  *ceata_info;
+
+       /* sdio related info */
        unsigned int            sdio_funcs;     /* number of SDIO functions */
        struct sdio_cccr        cccr;           /* common card info */
        struct sdio_cis         cis;            /* common tuple info */
@@ -118,6 +137,7 @@ struct mmc_card {
 #define mmc_card_mmc(c)                ((c)->type == MMC_TYPE_MMC)
 #define mmc_card_sd(c)         ((c)->type == MMC_TYPE_SD)
 #define mmc_card_sdio(c)       ((c)->type == MMC_TYPE_SDIO)
+#define mmc_card_ceata(c)      ((c)->type == MMC_TYPE_CEATA)

 #define mmc_card_present(c)    ((c)->state & MMC_STATE_PRESENT)
 #define mmc_card_readonly(c)   ((c)->state & MMC_STATE_READONLY)
@@ -136,6 +156,12 @@ struct mmc_card {
 #define mmc_get_drvdata(c)     dev_get_drvdata(&(c)->dev)
 #define mmc_set_drvdata(c,d)   dev_set_drvdata(&(c)->dev, d)

+/**
+ * CE-ATA taskfile access
+ */
+extern int ceata_read_write_cmd(struct mmc_card *card, int rw,
+       sector_t lba, u32 secs);
+
 /*
  * MMC device driver (e.g., Flash card, I/O card...)
  */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 14b81f3..399e195 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -254,6 +254,8 @@ struct _mmc_csd {
 #define EXT_CSD_CARD_TYPE      196     /* RO */
 #define EXT_CSD_REV            192     /* RO */
 #define EXT_CSD_SEC_CNT                212     /* RO, 4 bytes */
+#define EXT_CSD_CMD_SET                191     /* RW */
+#define EXT_CSD_S_CMD_SET      504     /* RO */

 /*
  * EXT_CSD field definitions
@@ -262,6 +264,8 @@ struct _mmc_csd {
 #define EXT_CSD_CMD_SET_NORMAL         (1<<0)
 #define EXT_CSD_CMD_SET_SECURE         (1<<1)
 #define EXT_CSD_CMD_SET_CPSECURE       (1<<2)
+#define EXT_CSD_CMD_SET_SECURE_2_0     (1<<3)
+#define EXT_CSD_CMD_SET_CEATA          (1<<4)

 #define EXT_CSD_CARD_TYPE_26   (1<<0)  /* Card can run at 26MHz */
 #define EXT_CSD_CARD_TYPE_52   (1<<1)  /* Card can run at 52MHz */
@@ -279,5 +283,112 @@ struct _mmc_csd {
 #define MMC_SWITCH_MODE_CLEAR_BITS     0x02    /* Clear bits which are 1 in value */
 #define MMC_SWITCH_MODE_WRITE_BYTE     0x03    /* Set target to value */

+struct ceata_task {
+       u8  resv1;
+       u8  fexp;       /* feature exp */
+       u8  sec_exp;
+       u8  lba_low_exp;
+       u8  lba_mid_exp;
+       u8  lba_high_exp;
+       u8  control;
+       u8  resv2[2];
+       u8  feature_err;
+       u8  sec;
+       u8  lba_low;
+       u8  lba_mid;
+       u8  lba_high;
+       u8  dev_head;
+       u8  cmd_status;
+} __attribute__((packed));
+
+/*
+ * CE-ATA MMC commands
+ */
+
+#define MMC_CEATA_RW_MULTI_REG         60 /* adtc     R1/R1b for R/W */
+#define MMC_CEATA_RW_MULTI_BLK         61 /* adtc     R1/R1b for R/W */
+
+/*
+ * CE-ATA definitions
+ */
+#define CEATA_SIGNATURE_BYTE_12                0xCE
+#define CEATA_SIGNATURE_BYTE_13                0xAA
+
+#define CEATA_TASKFILE_BYTELEN         16
+#define CEATA_IDENTIFY_DEV_BYTELEN     512
+
+/*
+ * CE-ATA registers
+ */
+#define CEATA_REG_FEATURES_EXP         1
+#define CEATA_REG_SEC_COUNT_EXP                2
+#define CEATA_REG_LBALOW_EXP           3
+#define CEATA_REG_LBAMID_EXP           4
+#define CEATA_REG_LBAHIGH_EXP          5
+#define CEATA_REG_CONTROL              6
+#define CEATA_REG_FEATURES             9   /* write */
+#define CEATA_REG_ERROR                        9   /* read */
+#define CEATA_REG_SEC_COUNT            10
+#define CEATA_REG_LBALOW               11
+#define CEATA_REG_LBAMID               12
+#define CEATA_REG_LBAHIGH              13
+#define CEATA_REG_DEVICE_HEAD          14
+#define CEATA_REG_CMD                  15  /* write */
+#define CEATA_REG_STATUS               15  /* read */
+
+/*
+ * CE-ATA register fields
+ */
+#define CEATA_STATUS_BSY               (1<<7)  /* Busy */
+#define CEATA_STATUS_DRDY              (1<<6)  /* Device Ready */
+#define CEATA_STATUS_DRQ               (1<<3)  /* Data Request */
+#define CEATA_STATUS_ERR               (1<<0)  /* Error */
+
+#define CEATA_ERROR_ICRC               (1<<7)  /* Interface CRC error (w) */
+#define CEATA_ERROR_UNC                (1<<6)  /* Uncorrectable data error (r) */
+#define CEATA_ERROR_IDNF               (1<<4)  /* ID (sector) not found */
+#define CEATA_ERROR_ABRT               (1<<2)  /* Aborted Command */
+
+#define CEATA_CONTROL_SRST             (1<<2)  /* ATA software reset */
+#define CEATA_CONTROL_NIEN             (1<<1)  /* Neg cmd comp int enable */
+
+/*
+ * CE-ATA commands
+ */
+#define CEATA_CMD_IDENTIFY_DEV         0xec    /* data in */
+#define CEATA_CMD_READ_DMA_EXT         0x25    /* data in */
+#define CEATA_CMD_WRITE_DMA_EXT                0x35    /* data out */
+#define CEATA_CMD_STANDBY_IMME         0xe0    /* No data */
+#define CEATA_CMD_FLUSH_CACHE_EXT      0xea    /* No data */
+
+/*
+ * CE-ATA status and control registers
+ *  M = mandatory   O = optional
+ *  RO = read-only  RW = read-write
+ */
+#define CEATA_REG_SCRTEMPC             0x80    /* O, RO */
+#define CEATA_REG_SCRTEMPMAXP          0x84    /* O, RO */
+#define CEATA_REG_SCRTEMPMINP          0x88    /* O, RO */
+#define CEATA_REG_SCRSTATUS            0x8C    /* O, RO */
+#define CEATA_REG_SCRREALLOCSA         0x90    /* O, RO */
+#define CEATA_REG_SCRERETRACTSA                0x94    /* O, RO */
+#define CEATA_REG_SCRCAPABILITIES      0x98    /* M, RO */
+#define CEATA_REG_SCRCONTROL           0xC0    /* M, RW */
+
+/*
+ * Identify Device data structure.  16-bit word addresses
+ */
+#define CEATA_CAP_SERIAL_NUMBER                10  /* 20 B */
+#define CEATA_CAP_FIRMWARE_REV         23  /* 8 B */
+#define CEATA_CAP_MODEL_NUMBER         27  /* 40 B */
+#define CEATA_CAP_MAJOR_VER            80
+#define CEATA_CAP_MAX_LBA              100 /* 4 B */
+#define CEATA_CAP_SECTOR_SIZE          106
+#define CEATA_CAP_GLOBAL_UNIQUE_ID     108 /* optional, 4 B */
+#define CEATA_CAP_FEATURES             206
+#define CEATA_CAP_MAX_WRITE_PER_ADDR   207 /* optional */
+#define CEATA_CAP_INTEGRITY_WORD       255
+
+
 #endif  /* MMC_MMC_PROTOCOL_H */

--
1.6.0

-----Original Message-----
From: Li, Jiebing
Sent: Thursday, April 30, 2009 5:14 PM
To: Pierre Ossman
Cc: linux-kernel@vger.kernel.org; Johnson, Charles F; Zhu, Daniel; Yuan, Hang; Pasrija, Geeta; Li, Jiebing
Subject: [PATCH 0/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform

Hi Pierre,

I wish to submit two patches for Intel low power platform "Moorestown". The below is the description of the patches:

1. Patch 1 enables CE-ATA device support on Moorestown platform.
2. Patch 2 enables SDIO OSPM suspend/resume support for SDIO devices applied on Moorestown.
3. Added some silicon/hardware specific restrictions.

This is the first time Moorestown related patches are submitted, so I'm ready for code review and will try my best to do updates before the code can be finally accepted.

Thanks a lot!

Regards,
Jiebing Li

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* RE: [PATCH 2/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform
  2009-04-30  9:14 [PATCH 0/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform Li, Jiebing
  2009-04-30  9:16 ` [PATCH 1/2] " Li, Jiebing
@ 2009-04-30  9:19 ` Li, Jiebing
  2009-04-30 12:11   ` Matthew Garrett
  2009-05-10 18:42   ` Pierre Ossman
  1 sibling, 2 replies; 8+ messages in thread
From: Li, Jiebing @ 2009-04-30  9:19 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: linux-kernel, Johnson, Charles F, Zhu, Daniel, Yuan, Hang,
	Pasrija, Geeta, Li, Jiebing


This patch enables support of SDIO bus driver suspend/resume operation and supply sysfs interface for user
to call suspend/resume selectively.Remind that this function should work together with SDIO device driver's
suspend/resume function.

And Moorestown's specific code is added into this patch to enable the second SDIO slot of the host controller.

Signed-off-by: JiebingLi <jiebing.li@intel.com>

---

 drivers/mmc/core/Kconfig      |    9 +
 drivers/mmc/core/sdio.c       |  467 ++++++++++++++++++++++++++++++++++++++++-
 drivers/mmc/core/sdio_bus.c   |   26 +++
 drivers/mmc/host/Kconfig      |    8 +
 drivers/mmc/host/sdhci-pci.c  |   20 ++-
 drivers/mmc/host/sdhci.c      |   14 ++-
 drivers/mmc/host/sdhci.h      |    2 +
 include/linux/mmc/card.h      |   11 +
 include/linux/mmc/sdio_func.h |    6 +
 9 files changed, 551 insertions(+), 12 deletions(-)

diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index ab37a6d..eaa5fcf 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -14,3 +14,12 @@ config MMC_UNSAFE_RESUME
          This option is usually just for embedded systems which use
          a MMC/SD card for rootfs. Most people should say N here.

+config SDIO_SUSPEND
+       bool "SDIO selective suspend/resume"
+       depends on MMC && PM
+       help
+         If you say Y here, you can use driver calls or the sysfs
+         "power/level" file to suspend or resume the SDIO
+         peripherals.
+
+         If you are unsure about this, say N here.
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 3f31f67..1d1d785 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -24,6 +24,215 @@
 #include "sdio_ops.h"
 #include "sdio_cis.h"

+#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
+
+#ifdef CONFIG_SDIO_SUSPEND
+
+static int sdio_suspend_func(struct mmc_card *card,
+        struct sdio_func *func, pm_message_t msg)
+{
+       struct device *dev;
+       int error = 0;
+
+       if (!func)
+               return -EINVAL;
+
+       dev = &func->dev;
+       BUG_ON(!dev);
+
+       down(&dev->sem);
+
+       if (dev->bus)
+               if (dev->bus->suspend)
+                       error = dev->bus->suspend(dev, msg);
+
+       up(&dev->sem);
+
+       return error;
+}
+
+static int sdio_resume_func(struct mmc_card *card, struct sdio_func *func)
+{
+       struct device *dev;
+       int error = 0;
+
+       if (!func)
+               return -EINVAL;
+
+       dev = &func->dev;
+       BUG_ON(!dev);
+
+       down(&dev->sem);
+
+       if (dev->bus)
+               if (dev->bus->resume)
+                       error = dev->bus->resume(dev);
+
+       up(&dev->sem);
+
+       return error;
+}
+
+/*
+ * This routine handles external suspend request coming from sysfs
+ */
+int sdio_external_suspend_device(struct mmc_card *card, pm_message_t msg)
+{
+       int ret = 0;
+       int i = 0;
+
+       BUG_ON(!card->host);
+       BUG_ON(!card->sdio_func);
+
+       mutex_lock(&card->pm_mutex);
+       if (!mmc_card_present(card) ||
+               mmc_card_suspended(card))
+               goto done;
+
+       /* suspend all funcs of the SDIO device */
+       for (; i < card->sdio_funcs; i++) {
+               ret = sdio_suspend_func(card, card->sdio_func[i], msg);
+               if (ret != 0)
+                       break;
+       }
+
+       if (ret == 0)
+               ret = mmc_suspend_host(card->host, msg);
+
+       if (ret == 0)
+               mmc_card_set_suspended(card);
+
+done:
+       mutex_unlock(&card->pm_mutex);
+
+       return ret;
+}
+
+/*
+ * This routine handles external resume request coming from sysfs
+ */
+int sdio_external_resume_device(struct mmc_card *card)
+{
+       int ret = 0;
+       int i = 0;
+
+       BUG_ON(!card->host);
+       BUG_ON(!card->sdio_func);
+
+       mutex_lock(&card->pm_mutex);
+
+       if (!mmc_card_present(card)) {
+               ret = -ENODEV;
+               goto done;
+       }
+
+       if (mmc_card_suspended(card)) {
+               ret = mmc_resume_host(card->host);
+
+               if (ret == 0) {
+                       for (i = 0; i < card->sdio_funcs; i++) {
+                               ret = sdio_resume_func(card,
+                                        card->sdio_func[i]);
+                               if (ret != 0)
+                                       break;
+                       }
+               }
+
+               if (ret == 0)
+                       mmc_card_clear_suspended(card);
+       }
+
+done:
+       mutex_unlock(&card->pm_mutex);
+
+       return ret;
+}
+
+static const char power_group[] = "power";
+
+static const char resume_string[] = "resume";
+static const char suspend_string[] = "suspend";
+
+static ssize_t
+show_level(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mmc_card *card = container_of(dev, struct mmc_card, dev);
+       const char *p = suspend_string;
+
+       BUG_ON(!card);
+
+       if (mmc_card_suspended(card))
+               p = suspend_string;
+       else
+               p = resume_string;
+
+       return sprintf(buf, "%s\n", p);
+}
+
+static ssize_t
+set_level(struct device *dev, struct device_attribute *attr,
+       const char *buf, size_t count)
+{
+       struct mmc_card *card = container_of(dev, struct mmc_card, dev);
+       int len = count;
+       char *cp;
+       int ret = 0;
+
+       BUG_ON(!card);
+
+       cp = memchr(buf, '\n', count);
+       if (cp)
+               len = cp - buf;
+
+       down(&dev->sem);
+
+       if (len == sizeof resume_string - 1 &&
+               strncmp(buf, resume_string, len) == 0) {
+               ret = sdio_external_resume_device(card);
+       } else if (len == sizeof suspend_string - 1 &&
+               strncmp(buf, suspend_string, len) == 0) {
+               ret = sdio_external_suspend_device(card, PMSG_SUSPEND);
+       } else {
+               ret = -EINVAL;
+       }
+
+       up(&dev->sem);
+
+       return (ret < 0 ? ret : count);
+}
+
+static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
+
+void sdio_remove_sysfs_file(struct mmc_card *card)
+{
+       struct device *dev = &card->dev;
+
+       sysfs_remove_file_from_group(&dev->kobj,
+               &dev_attr_level.attr,
+               power_group);
+}
+
+int sdio_create_sysfs_file(struct mmc_card *card)
+{
+       int ret;
+       struct device *dev = &card->dev;
+
+       ret = sysfs_add_file_to_group(&dev->kobj,
+               &dev_attr_level.attr,
+               power_group);
+
+       if (ret)
+               goto error;
+
+       return 0;
+
+error:
+       sdio_remove_sysfs_file(card);
+       return ret;
+}
+
+#endif /* CONFIG_SDIO_SUSPEND */
+
 static int sdio_read_fbr(struct sdio_func *func)
 {
        int ret;
@@ -197,6 +406,97 @@ static int sdio_enable_hs(struct mmc_card *card)
 #endif

 /*
+ * Handle the re-initialization of a SDIO card.
+ */
+static int mmc_sdio_reinit_card(struct mmc_host *host,
+       struct mmc_card *oldcard)
+{
+       int err = 0;
+       u16 funcs;
+       u32 ocr;
+       struct mmc_card *card;
+
+       BUG_ON(!host);
+       WARN_ON(!host->claimed);
+
+       if (!oldcard)
+               goto err;
+
+       card = oldcard;
+
+       err = mmc_send_io_op_cond(host, 0, &ocr);
+       if (err)
+               goto remove;
+
+       /*
+        * Inform the card of the voltage
+        */
+       err = mmc_send_io_op_cond(host, host->ocr, &ocr);
+       if (err)
+               goto remove;
+
+       /*
+        * For SPI, enable CRC as appropriate.
+        */
+       if (mmc_host_is_spi(host)) {
+               err = mmc_spi_set_crc(host, use_spi_crc);
+               if (err)
+                       goto remove;
+       }
+
+       funcs = (ocr & 0x70000000) >> 28;
+
+       if (funcs != card->sdio_funcs)
+               printk(KERN_INFO "funcs number is changed from OCR register after suspend!\n");
+
+       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 CIS tuples.
+        */
+       err = sdio_read_cccr(card);
+       if (err)
+               goto remove;
+
+#ifdef CONFIG_MRST_MMC_WR
+       /* restricting to 24MHz for Langwell A0 */
+       if (card->cis.max_dtr > 24000000)
+               card->cis.max_dtr = 24000000;
+#endif
+       mmc_set_clock(host, card->cis.max_dtr);
+
+       /*
+        * Switch to wider bus (if supported).
+        */
+       err = sdio_enable_wide(card);
+       if (err)
+               goto remove;
+
+       host->card = card;
+
+       return 0;
+
+remove:
+err:
+       return err;
+}
+
+/*
  * Host is being removed. Free up the current card.
  */
 static void mmc_sdio_remove(struct mmc_host *host)
@@ -213,6 +513,10 @@ static void mmc_sdio_remove(struct mmc_host *host)
                }
        }

+#ifdef CONFIG_SDIO_SUSPEND
+       sdio_remove_sysfs_file(host->card);
+#endif
+
        mmc_remove_card(host->card);
        host->card = NULL;
 }
@@ -246,9 +550,73 @@ static void mmc_sdio_detect(struct mmc_host *host)
 }


+/*
+ * Suspend callback from host.
+ */
+static void mmc_sdio_suspend(struct mmc_host *host)
+{
+       int err;
+       u8 reg = 0;
+
+       BUG_ON(!host);
+       BUG_ON(!host->card);
+
+       mmc_claim_host(host);
+
+       if (!mmc_host_is_spi(host)) {
+               /*
+                * I/O reset to go back to the Initialization state
+                */
+               err = mmc_io_rw_direct(host->card, 0, 0,
+                        SDIO_CCCR_ABORT, 0, &reg);
+
+               if (err)
+                       reg = 0x08;
+               else
+                       reg |= 0x08;
+
+               mmc_io_rw_direct(host->card, 1, 0, SDIO_CCCR_ABORT, reg, NULL);
+       }
+
+       mmc_release_host(host);
+
+       printk(KERN_INFO "%s: SDIO device is suspended\n",
+               mmc_hostname(host));
+}
+
+/*
+ * Resume callback from host.
+ */
+static void mmc_sdio_resume(struct mmc_host *host)
+{
+       int err;
+
+       BUG_ON(!host);
+       BUG_ON(!host->card);
+
+       mmc_claim_host(host);
+
+       err = mmc_sdio_reinit_card(host, host->card);
+
+       mmc_release_host(host);
+
+       if (err) {
+               mmc_sdio_remove(host);
+
+               mmc_claim_host(host);
+               mmc_detach_bus(host);
+               mmc_release_host(host);
+       } else {
+               printk(KERN_INFO "%s: SDIO device is resumed\n",
+                       mmc_hostname(host));
+       }
+}
+
 static const struct mmc_bus_ops mmc_sdio_ops = {
        .remove = mmc_sdio_remove,
        .detect = mmc_sdio_detect,
+       .suspend = mmc_sdio_suspend,
+       .resume = mmc_sdio_resume,
 };


@@ -325,6 +693,10 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
                goto err;
        }

+#ifdef CONFIG_SDIO_SUSPEND
+       mutex_init(&card->pm_mutex);
+#endif
+
        card->type = MMC_TYPE_SDIO;
        card->sdio_funcs = funcs;

@@ -364,10 +736,6 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
        if (err)
                goto remove;

-/*
- * temporarily avoiding SDIO cards to switch to HS timing
- * which doesn't work yet due to existing Silicon bug
- */
 #ifndef CONFIG_MRST_MMC_WR
        /*
         * Switch to high-speed (if supported).
@@ -391,12 +759,12 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
                mmc_set_clock(host, card->cis.max_dtr);
        }
 #else
+       /* restricting to 24MHz for Langwell A0 */
        if (card->cis.max_dtr > 24000000)
                card->cis.max_dtr = 24000000;

        mmc_set_clock(host, card->cis.max_dtr);
 #endif
-
        /*
         * Switch to wider bus (if supported).
         */
@@ -423,6 +791,14 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
                goto remove_added;

        /*
+        * create the user interface to call suspend/resume
+        * from susfs
+        */
+#ifdef CONFIG_SDIO_SUSPEND
+       sdio_create_sysfs_file(host->card);
+#endif
+
+       /*
         * ...then the SDIO functions.
         */
        for (i = 0;i < funcs;i++) {
@@ -452,3 +828,84 @@ err:
        return err;
 }

+/*
+ * warn device driver and perform a SDIO device reset.
+ * Assume that device driver knows hot to handle resets.
+ */
+int sdio_reset_device(struct mmc_card *card)
+{
+       int ret = 0;
+       int i = 0;
+       u8 reg = 0;
+
+       BUG_ON(!card);
+       BUG_ON(!card->host);
+       BUG_ON(!card->sdio_func);
+
+       if (!mmc_card_present(card) ||
+               mmc_card_suspended(card)) {
+               dev_dbg(&card->dev, "device reset not allowed\n");
+               return -EINVAL;
+       }
+
+       for (; i < card->sdio_funcs; i++) {
+               struct sdio_func *func = card->sdio_func[i];
+               struct sdio_driver *drv;
+
+               if (func && func->dev.driver) {
+                       drv = to_sdio_driver(func->dev.driver);
+                       if (drv->pre_reset) {
+                               ret = (drv->pre_reset)(func);
+                               if (ret)
+                                       break;
+                       }
+               }
+       }
+
+       if (ret)
+               goto err;
+
+       /* reset SDIO card via CMD52 */
+       mmc_claim_host(card->host);
+
+       ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_ABORT, 0, &reg);
+
+       if (ret)
+               reg = 0x08;
+       else
+               reg |= 0x08;
+
+       mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_ABORT, reg, NULL);
+
+       /* re-enumerate the device */
+       ret = mmc_sdio_reinit_card(card->host, card);
+
+       mmc_release_host(card->host);
+
+       if (ret)
+               goto err;
+
+       for (i = card->sdio_funcs - 1; i >= 0; i--) {
+               struct sdio_func *func = card->sdio_func[i];
+               struct sdio_driver *drv;
+
+               if (func && func->dev.driver) {
+                       drv = to_sdio_driver(func->dev.driver);
+                       if (drv->post_reset) {
+                               ret = (drv->post_reset)(func);
+                               if (ret)
+                                       break;
+                       }
+               }
+       }
+
+       if (ret)
+               goto err;
+
+       return 0;
+
+err:
+       return -EINVAL;
+
+}
+EXPORT_SYMBOL_GPL(sdio_reset_device);
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index 46284b5..3f53bae 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -156,6 +156,30 @@ static int sdio_bus_remove(struct device *dev)
        return 0;
 }

+static int sdio_bus_suspend(struct device *dev, pm_message_t state)
+{
+       struct sdio_driver *drv = to_sdio_driver(dev->driver);
+       struct sdio_func *func = dev_to_sdio_func(dev);
+       int ret = 0;
+
+       if (dev->driver && drv->suspend)
+               ret = drv->suspend(func, state);
+
+       return ret;
+}
+
+static int sdio_bus_resume(struct device *dev)
+{
+       struct sdio_driver *drv = to_sdio_driver(dev->driver);
+       struct sdio_func *func = dev_to_sdio_func(dev);
+       int ret = 0;
+
+       if (dev->driver && drv->resume)
+               ret = drv->resume(func);
+
+       return ret;
+}
+
 static struct bus_type sdio_bus_type = {
        .name           = "sdio",
        .dev_attrs      = sdio_dev_attrs,
@@ -163,6 +187,8 @@ static struct bus_type sdio_bus_type = {
        .uevent         = sdio_bus_uevent,
        .probe          = sdio_bus_probe,
        .remove         = sdio_bus_remove,
+       .suspend        = sdio_bus_suspend,
+       .resume         = sdio_bus_resume,
 };

 int sdio_register_bus(void)
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index b4cf691..0c9d2eb 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -44,6 +44,14 @@ config MMC_SDHCI_IO_ACCESSORS
          This is silent Kconfig symbol that is selected by the drivers that
          need to overwrite SDHCI IO memory accessors.

+config MMC_SDHCI_MRST_SDIO1
+       bool
+       depends on MMC_SDHCI
+       help
+         This enables Moorestown SD host controller's 2nd SDIO slot.
+
+         If unsure, say N.
+
 config MMC_SDHCI_PCI
        tristate "SDHCI support on PCI bus"
        depends on MMC_SDHCI && PCI
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 312ec69..2debbcd 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -288,7 +288,8 @@ static const struct sdhci_pci_fixes sdhci_jmicron = {
  * ADMA operation is disabled for Moorestown platform.
  */
 static const struct sdhci_pci_fixes sdhci_intel_mrst = {
-       .quirks         = SDHCI_QUIRK_BROKEN_ADMA,
+       .quirks         = SDHCI_QUIRK_BROKEN_ADMA |
+                        SDHCI_QUIRK_MRST_RESTRICTION,
 };

 static const struct pci_device_id pci_ids[] __devinitdata = {
@@ -396,11 +397,14 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host)
        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");
+       if (!(host->quirks & SDHCI_QUIRK_MRST_RESTRICTION)) {
+               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_BIT_MASK(32));
@@ -642,7 +646,11 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
         */
        if (pdev->device == PCI_DEVICE_ID_INTEL_MRST_SD0 ||
                pdev->device == PCI_DEVICE_ID_INTEL_MRST_SD1) {
+#ifndef CONFIG_MMC_SDHCI_MRST_SDIO1
                slots = 1;
+#else
+               slots = 2;
+#endif
        } else {
                ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots);

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index a2804f1..4d4ad6d 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -914,6 +914,17 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
        if (cmd->data)
                flags |= SDHCI_CMD_DATA;

+#ifdef CONFIG_MMC_SDHCI_MRST_SDIO1
+       if (host->quirks & SDHCI_QUIRK_MRST_RESTRICTION) {
+               u16 clk;
+
+               clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+
+               clk |= SDHCI_CLOCK_CARD_EN;
+               sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+       }
+#endif
+
        sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
 }

@@ -1788,7 +1799,8 @@ int sdhci_add_host(struct sdhci_host *host)
        } else {
                mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
                                SDHCI_MAX_BLOCK_SHIFT;
-               if (mmc->max_blk_size >= 3) {
+               if ((mmc->max_blk_size >= 3) &&
+                       !(host->quirks & SDHCI_QUIRK_MRST_RESTRICTION)) {
                        printk(KERN_WARNING "%s: Invalid maximum block size, "
                                "assuming 512 bytes\n", mmc_hostname(mmc));
                        mmc->max_blk_size = 0;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index fa87b8b..50be698 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -227,6 +227,8 @@ struct sdhci_host {
 #define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET           (1<<19)
 /* Controller has to be forced to use block size of 2048 bytes */
 #define SDHCI_QUIRK_FORCE_BLK_SZ_2048                  (1<<20)
+/* Controller of Moorestown specific restriction */
+#define SDHCI_QUIRK_MRST_RESTRICTION                   (1<<21)

        int                     irq;            /* Device IRQ */
        void __iomem *          ioaddr;         /* Mapped address */
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index e5d8a9e..d4adc6a 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -109,6 +109,7 @@ struct mmc_card {
 #define MMC_STATE_READONLY     (1<<1)          /* card is read-only */
 #define MMC_STATE_HIGHSPEED    (1<<2)          /* card is in high speed mode */
 #define MMC_STATE_BLOCKADDR    (1<<3)          /* card uses block-addressing */
+#define MMC_STATE_SUSPENDED    (1<<4)          /* card suspended */

        u32                     raw_cid[4];     /* raw card CID */
        u32                     raw_csd[4];     /* raw card CSD */
@@ -132,6 +133,10 @@ struct mmc_card {
        struct sdio_func_tuple  *tuples;        /* unknown common tuples */

        struct dentry           *debugfs_root;
+
+#ifdef CONFIG_SDIO_SUSPEND
+       struct mutex            pm_mutex;
+#endif
 };

 #define mmc_card_mmc(c)                ((c)->type == MMC_TYPE_MMC)
@@ -143,12 +148,18 @@ struct mmc_card {
 #define mmc_card_readonly(c)   ((c)->state & MMC_STATE_READONLY)
 #define mmc_card_highspeed(c)  ((c)->state & MMC_STATE_HIGHSPEED)
 #define mmc_card_blockaddr(c)  ((c)->state & MMC_STATE_BLOCKADDR)
+#define mmc_card_suspended(c)  ((c)->state & MMC_STATE_SUSPENDED)

 #define mmc_card_set_present(c)        ((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
 #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
 #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)

+#ifdef CONFIG_SDIO_SUSPEND
+#define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED)
+#define mmc_card_clear_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED)
+#endif
+
 #define mmc_card_name(c)       ((c)->cid.prod_name)
 #define mmc_card_id(c)         (dev_name(&(c)->dev))

diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
index 451bdfc..a733fa5 100644
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -77,6 +77,11 @@ struct sdio_driver {

        int (*probe)(struct sdio_func *, const struct sdio_device_id *);
        void (*remove)(struct sdio_func *);
+       int (*suspend)(struct sdio_func *, pm_message_t);
+       int (*resume)(struct sdio_func *);
+
+       int (*pre_reset)(struct sdio_func *);
+       int (*post_reset)(struct sdio_func *);

        struct device_driver drv;
 };
@@ -150,5 +155,6 @@ extern unsigned char sdio_f0_readb(struct sdio_func *func,
 extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b,
        unsigned int addr, int *err_ret);

+extern int sdio_reset_device(struct mmc_card *card);
 #endif

--
1.6.0

-----Original Message-----
From: Li, Jiebing
Sent: Thursday, April 30, 2009 5:14 PM
To: Pierre Ossman
Cc: linux-kernel@vger.kernel.org; Johnson, Charles F; Zhu, Daniel; Yuan, Hang; Pasrija, Geeta; Li, Jiebing
Subject: [PATCH 0/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform

Hi Pierre,

I wish to submit two patches for Intel low power platform "Moorestown". The below is the description of the patches:

1. Patch 1 enables CE-ATA device support on Moorestown platform.
2. Patch 2 enables SDIO OSPM suspend/resume support for SDIO devices applied on Moorestown.
3. Added some silicon/hardware specific restrictions.

This is the first time Moorestown related patches are submitted, so I'm ready for code review and will try my best to do updates before the code can be finally accepted.

Thanks a lot!

Regards,
Jiebing Li

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform
  2009-04-30  9:19 ` [PATCH 2/2] " Li, Jiebing
@ 2009-04-30 12:11   ` Matthew Garrett
  2009-05-10 18:42   ` Pierre Ossman
  1 sibling, 0 replies; 8+ messages in thread
From: Matthew Garrett @ 2009-04-30 12:11 UTC (permalink / raw)
  To: Li, Jiebing
  Cc: Pierre Ossman, linux-kernel, Johnson, Charles F, Zhu, Daniel,
	Yuan, Hang, Pasrija, Geeta

On Thu, Apr 30, 2009 at 05:19:06PM +0800, Li, Jiebing wrote:
> 
> This patch enables support of SDIO bus driver suspend/resume operation and supply sysfs interface for user
> to call suspend/resume selectively.Remind that this function should work together with SDIO device driver's
> suspend/resume function.

We used to have similar functionality in the PCI layer, but it got 
removed on the assumption that this kind of thing should be handled by 
the drivers being sufficiently intelligent. What's the use case for 
this?

> And Moorestown's specific code is added into this patch to enable the second SDIO slot of the host controller.

That should probably be sent as a separate patch.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform
  2009-04-30  9:16 ` [PATCH 1/2] " Li, Jiebing
@ 2009-04-30 13:44   ` Alan Cox
  2009-05-10 18:29   ` Pierre Ossman
  1 sibling, 0 replies; 8+ messages in thread
From: Alan Cox @ 2009-04-30 13:44 UTC (permalink / raw)
  To: Li, Jiebing
  Cc: Pierre Ossman, linux-kernel, Johnson, Charles F, Zhu, Daniel,
	Yuan, Hang, Pasrija, Geeta, Li, Jiebing

> +       ptemp = (u16 *)data_buf;
> +
> +       memcpy(info->serialnum, ptemp + 10, 20);
> +       info->serialnum[20] = 0;
> +       memcpy(info->fw_ver, ptemp + 23, 8);
> +       info->fw_ver[8] = 0;
> +       memcpy(info->model_num, ptemp + 27, 40);
> +       info->model_num[40] = 0;
> +
> +       info->major = ptemp[80];
> +       info->max_lba[0] = data_buf[50];    /* units are 512-byte sectors */
> +       info->max_lba[1] = data_buf[51];

What happens here on a big endian system ?


> +int ceata_flush_cache(struct mmc_card *card)
> +{
>
> +       ret = ceata_cmd60(card, 1, cmd_buf, reg_addr, CEATA_TASKFILE_BYTELEN);
> +       if (ret)
> +               printk(KERN_ERR "%s: Error issuing CE-ATA flush cache "
> +                               "command\n", mmc_hostname(card->host));

FLUSH_CACHE_EXT in standard ATA returns an error and the number of the
failed sector on error. That means it has to be retried to continue
flushing the rest of the cache after a bad block - is CE-ATA defined
differently here - otherwise this seems insufficient ?


> +#ifndef CONFIG_MRST_MMC_WR

Really any board/chipset specific handling needs to be done with flags on
the mmc_host not by ifdefs - otherwise you can't build a fairly generic
kernel any more.

> +       /*
> +        * first BAR is fixed to 0 by Moorestown architecture
> +        */
> +       if (pdev->device == PCI_DEVICE_ID_INTEL_MRST_SD0 ||
> +               pdev->device == PCI_DEVICE_ID_INTEL_MRST_SD1) {
> +               first_bar = 0;

and at this point you could set a workaround flag and propogate it into
the relevant mmc_host


> +/*
> + * CE-ATA register fields
> + */
> +#define CEATA_STATUS_BSY               (1<<7)  /* Busy */
> +#define CEATA_STATUS_DRDY              (1<<6)  /* Device Ready */
> +#define CEATA_STATUS_DRQ               (1<<3)  /* Data Request */
> +#define CEATA_STATUS_ERR               (1<<0)  /* Error */
> +
> +#define CEATA_ERROR_ICRC               (1<<7)  /* Interface CRC error (w) */
> +#define CEATA_ERROR_UNC                (1<<6)  /* Uncorrectable data error (r) */
> +#define CEATA_ERROR_IDNF               (1<<4)  /* ID (sector) not found */
> +#define CEATA_ERROR_ABRT               (1<<2)  /* Aborted Command */
> +
> +#define CEATA_CONTROL_SRST             (1<<2)  /* ATA software reset */
> +#define CEATA_CONTROL_NIEN             (1<<1)  /* Neg cmd comp int enable */

These bits are defined in ata.h and used by the various ATA layers (and
old IDE driver) - could you re-use them or not ?

> +#define CEATA_CMD_IDENTIFY_DEV         0xec    /* data in */
> +#define CEATA_CMD_READ_DMA_EXT         0x25    /* data in */
> +#define CEATA_CMD_WRITE_DMA_EXT                0x35    /* data out */
> +#define CEATA_CMD_STANDBY_IMME         0xe0    /* No data */
> +#define CEATA_CMD_FLUSH_CACHE_EXT      0xea    /* No data */

Ditto


On the more general question of "should CE-ATA use libata for the ATA
work" I'd agree with this current approach. It seems unlikely CE-ATA
devices are going to grow into full-stack ATA devices in a hurry and the
CE-ATA mode within mmc as done here is therefore much smaller and neater.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform
  2009-04-30  9:16 ` [PATCH 1/2] " Li, Jiebing
  2009-04-30 13:44   ` Alan Cox
@ 2009-05-10 18:29   ` Pierre Ossman
  1 sibling, 0 replies; 8+ messages in thread
From: Pierre Ossman @ 2009-05-10 18:29 UTC (permalink / raw)
  To: Li, Jiebing
  Cc: linux-kernel, Johnson, Charles F, Zhu, Daniel, Yuan, Hang,
	Pasrija, Geeta, Li, Jiebing

[-- Attachment #1: Type: text/plain, Size: 10714 bytes --]

On Thu, 30 Apr 2009 17:16:47 +0800
"Li, Jiebing" <jiebing.li@intel.com> wrote:

> 
> This patch enables CE-ATA HDD support including device initialization and  data read/write.
> Validation has been passed with TOSHIBA, SAMSUNG and HITACHI devices.
> 

I got the impression that CE-ATA was more or less dead. Are there any
devices still being produced?

> And this patch also works for Moorestown platform by dealing with slicon/hardware limitations:
> 1. Slot number and first BAR values are fixed instead of reading from PCI configuration space.
> 2. Clock speed is restricted to normal-speed.
> 3. ADMA operation is disabled by adding quirks.

Don't mix things in one patch. You'll need to split things up and do
one change at a time.

> diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
> index f2eeb38..3d1c83b 100644
> --- a/drivers/mmc/Kconfig
> +++ b/drivers/mmc/Kconfig
> @@ -19,6 +19,25 @@ config MMC_DEBUG
>           This is an option for use by developers; most people should
>           say N here.  This enables MMC core and driver debugging.
> 
> +config MRST_MMC_WR
> +       bool "MMC/SD/CEATA workaround for Moorestown"
> +       depends on MMC
> +       help
> +         This is an option for Moorestown developers to add workaround
> +         in the code due to Silicon issues.

No. This should be handled at runtime and through proper detection of
the buggy device. And if it's just for unreleased silicon, then it
shouldn't be in the mainline kernel at all.

> +config MMC_CEATA_WR
> +       bool "disable SDIO device search to support CEATA"
> +       depends on MMC
> +       help
> +         This disables SDIO device search process to support various
> +         brands of CE-ATA devices as some CE-ATA devices may pretends
> +         itself as a SDIO device.
> +
> +         If you want SDIO support, you should say N here and if you
> +         want to support as many as kinds of CE-ATA devices,you should
> +         say Y. If you want to support both SDIO and CE-ATA, just say N.
> +

What kind of SDIO device do these cards present? Can't we support that
as well and avoid this hack?

> +static int ceata_blk_issue_rq(struct mmc_queue *mq, struct request *req)
> +{

This routine shares a lot of code with the MMC/SD handler. Do a better
separation so that we don't have all this code duplication (with
associated lack of bug fixes).

> @@ -449,6 +541,9 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
> 
>  static inline int mmc_blk_readonly(struct mmc_card *card)
>  {
> +       if (mmc_card_ceata(card))
> +               return 0;
> +

The read-only flag would not be set on CEATA, so this is meaningless.

> @@ -511,11 +610,20 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
>          * messages to tell when the card is present.
>          */
> 
> -       sprintf(md->disk->disk_name, "mmcblk%d", devidx);
> +       snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),
> +               "mmcblk%d", devidx);
> 

Unrelated change. Send a separate patch.

>         blk_queue_hardsect_size(md->queue.queue, 512);
> 
> -       if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
> +       if (mmc_card_ceata(card)) {
> +               /* max_lba[0] covers all current ceata devices */
> +               WARN_ON(card->ceata_info->max_lba[1]);

If the standard defines max_lba[1], we might as well be prepared to
handle it.

> @@ -575,7 +683,8 @@ static int mmc_blk_probe(struct mmc_card *card)
>         /*
>          * Check that the card supports the command class(es) we need.
>          */
> -       if (!(card->csd.cmdclass & CCC_BLOCK_READ))
> +       if (!(card->csd.cmdclass & CCC_BLOCK_READ) &&
> +       !mmc_card_ceata(card))
>                 return -ENODEV;
> 

Indentation.

> diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
> index bdb165f..5f66c5a 100644
> --- a/drivers/mmc/core/bus.c
> +++ b/drivers/mmc/core/bus.c
> @@ -36,6 +36,8 @@ static ssize_t mmc_type_show(struct device *dev,
>                 return sprintf(buf, "SD\n");
>         case MMC_TYPE_SDIO:
>                 return sprintf(buf, "SDIO\n");
> +       case MMC_TYPE_CEATA:
> +               return snprintf(buf, sizeof(buf), "CEATA\n");
>         default:
>                 return -EFAULT;
>         }

CEATA is a sub-type of MMC, and compatible enough that only mmc_block
needs to care about the difference. I'm not sure we need a new type
here.

> diff --git a/drivers/mmc/core/ceata.c b/drivers/mmc/core/ceata.c

All of the stuff in this file seems to be things that only mmc_block
needs. As such, move it into that module and don't fill up the core
with it.

It's probably still a good idea to have all of it in it's own file
though.

> +
> +       printk(KERN_ERR "%s: after %d ms wait, CE-ATA still busy\n",
> +                       mmc_hostname(card->host), wait);

This is a lie. As the command takes some time to execute, just assuming
that the loops have taken roughly 1 ms is incorrect.

> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index fa073ab..2aa638d 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -248,6 +248,18 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
>         unsigned int mult;
> 
>         /*
> +        * CEATA hdds could take as long as 35 s in corner cases,
> +        * according to at least one datasheet.
> +        * TODO: implement longer timeout in SW, since max HW timeout
> +        * is just ~2 s (2^27 / 48MHz)
> +        */

There is no way you can handle this in software as you'll be unable to
read the response from the card.

Does the CE-ATA spec really allow timeouts larger than the MMC spec.?
Or have the card vendors just decided to ignore the specs?

> @@ -715,8 +727,12 @@ static void mmc_power_up(struct mmc_host *host)
>         /*
>          * This delay must be at least 74 clock sizes, or 1 ms, or the
>          * time required to reach a stable voltage.
> +        *
> +        * Three different CEATA hdds from Toshiba, Samsung
> +        * and Hitachi fail when the delay is less than 20 ms, but
> +        * work with 40 ms (doubling just in case).
>          */
> -       mmc_delay(2);
> +       mmc_delay(80);
>  }
> 

How do they fail?

> @@ -121,7 +122,7 @@ static int mmc_decode_csd(struct mmc_card *card)
>          * v1.2 has extra information in bits 15, 11 and 10.
>          */
>         csd_struct = UNSTUFF_BITS(resp, 126, 2);
> -       if (csd_struct != 1 && csd_struct != 2) {
> +       if (csd_struct != 1 && csd_struct != 2 && csd_struct != 3) {
>                 printk(KERN_ERR "%s: unrecognised CSD structure version %d\n",
>                         mmc_hostname(card->host), csd_struct);
>                 return -EINVAL;

This should probably also go in a separate patch.

> @@ -285,6 +288,39 @@ static struct device_type mmc_type = {
>         .groups = mmc_attr_groups,
>  };
> 
> +MMC_DEV_ATTR(capacity, "%d KB\n", card->ceata_info->max_lba[0] / 2);
> +MMC_DEV_ATTR(seclen, "%d bytes\n", 1 << card->ceata_info->seclen);

Redundant. You'll get this info from the block layer.

> +MMC_DEV_ATTR(globalid, "%08x%08x\n", card->ceata_info->global_id[1],
> +       card->ceata_info->global_id[0]);
> +MMC_DEV_ATTR(serial2, "%s\n", card->ceata_info->serialnum);
> +MMC_DEV_ATTR(fwrev2, "%s\n", card->ceata_info->fw_ver);
> +MMC_DEV_ATTR(model, "%s\n", card->ceata_info->model_num);

Have you made sure these have the same names and formats as the libata
counterparts?

> @@ -614,22 +637,40 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
>         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 number is fixed to 2 by Moorestown architecture
> +        */
> +       if (pdev->device == PCI_DEVICE_ID_INTEL_MRST_SD0 ||
> +               pdev->device == PCI_DEVICE_ID_INTEL_MRST_SD1) {
> +               slots = 1;

Comment and code do not match. And how many slots are in the slots field
in the PCI configuration?

> -       ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar);
> -       if (ret)
> -               return ret;
> +       /*
> +        * first BAR is fixed to 0 by Moorestown architecture
> +        */
> +       if (pdev->device == PCI_DEVICE_ID_INTEL_MRST_SD0 ||
> +               pdev->device == PCI_DEVICE_ID_INTEL_MRST_SD1) {
> +               first_bar = 0;

Same here, what's in the PCI config?

Both should be handled by quirks anyway.

> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 9234be2..a2804f1 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -1123,12 +1123,18 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> 
>         ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
> 
> -       if (ios->bus_width == MMC_BUS_WIDTH_4)
> +       if (ios->bus_width == MMC_BUS_WIDTH_8) {
> +               ctrl |= SDHCI_CTRL_8BITBUS;
>                 ctrl |= SDHCI_CTRL_4BITBUS;
> -       else
> +       } else if (ios->bus_width == MMC_BUS_WIDTH_4) {
> +               ctrl &= ~SDHCI_CTRL_8BITBUS;
> +               ctrl |= SDHCI_CTRL_4BITBUS;
> +       } else {
> +               ctrl &= ~SDHCI_CTRL_8BITBUS;
>                 ctrl &= ~SDHCI_CTRL_4BITBUS;
> +       }

This is not in any spec I've seen. :)

> 
> -       if (ios->timing == MMC_TIMING_SD_HS)
> +       if (ios->timing == MMC_TIMING_SD_HS || ios->timing == MMC_TIMING_MMC_HS)
>                 ctrl |= SDHCI_CTRL_HISPD;
>         else
>                 ctrl &= ~SDHCI_CTRL_HISPD;
> @@ -1724,7 +1730,7 @@ int sdhci_add_host(struct sdhci_host *host)
>         mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
> 
>         if (caps & SDHCI_CAN_DO_HISPD)
> -               mmc->caps |= MMC_CAP_SD_HIGHSPEED;
> +               mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
> 
>         if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
>                 mmc->caps |= MMC_CAP_NEEDS_POLL;

No, this isn't safe.

Rgds
-- 
     -- Pierre Ossman

  WARNING: This correspondence is being monitored by the
  Swedish government. Make sure your server uses encryption
  for SMTP traffic and consider using PGP for end-to-end
  encryption.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform
  2009-04-30  9:19 ` [PATCH 2/2] " Li, Jiebing
  2009-04-30 12:11   ` Matthew Garrett
@ 2009-05-10 18:42   ` Pierre Ossman
  2009-05-20 10:55     ` Li, Jiebing
  1 sibling, 1 reply; 8+ messages in thread
From: Pierre Ossman @ 2009-05-10 18:42 UTC (permalink / raw)
  To: Li, Jiebing
  Cc: linux-kernel, Johnson, Charles F, Zhu, Daniel, Yuan, Hang,
	Pasrija, Geeta, Li, Jiebing

[-- Attachment #1: Type: text/plain, Size: 3096 bytes --]

On Thu, 30 Apr 2009 17:19:06 +0800
"Li, Jiebing" <jiebing.li@intel.com> wrote:

> 
> This patch enables support of SDIO bus driver suspend/resume operation and supply sysfs interface for user
> to call suspend/resume selectively.Remind that this function should work together with SDIO device driver's
> suspend/resume function.
> 

As Matthew asked, what's the use case?

> And Moorestown's specific code is added into this patch to enable the second SDIO slot of the host controller.
> 

Again, stop mixing unrelated things in the same patch.

> @@ -452,3 +828,84 @@ err:
>         return err;
>  }
> 
> +/*
> + * warn device driver and perform a SDIO device reset.
> + * Assume that device driver knows hot to handle resets.
> + */
> +int sdio_reset_device(struct mmc_card *card)
> +{

Use case?

> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index b4cf691..0c9d2eb 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -44,6 +44,14 @@ config MMC_SDHCI_IO_ACCESSORS
>           This is silent Kconfig symbol that is selected by the drivers that
>           need to overwrite SDHCI IO memory accessors.
> 
> +config MMC_SDHCI_MRST_SDIO1
> +       bool
> +       depends on MMC_SDHCI
> +       help
> +         This enables Moorestown SD host controller's 2nd SDIO slot.
> +
> +         If unsure, say N.
> +

Detect this at runtime.

> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index a2804f1..4d4ad6d 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -914,6 +914,17 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
>         if (cmd->data)
>                 flags |= SDHCI_CMD_DATA;
> 
> +#ifdef CONFIG_MMC_SDHCI_MRST_SDIO1
> +       if (host->quirks & SDHCI_QUIRK_MRST_RESTRICTION) {
> +               u16 clk;
> +
> +               clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +
> +               clk |= SDHCI_CLOCK_CARD_EN;
> +               sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +       }
> +#endif
> +
>         sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
>  }
> 

Huh? Is this some kind of multiplexed nonsense?

> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index fa87b8b..50be698 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -227,6 +227,8 @@ struct sdhci_host {
>  #define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET           (1<<19)
>  /* Controller has to be forced to use block size of 2048 bytes */
>  #define SDHCI_QUIRK_FORCE_BLK_SZ_2048                  (1<<20)
> +/* Controller of Moorestown specific restriction */
> +#define SDHCI_QUIRK_MRST_RESTRICTION                   (1<<21)
> 

No. Quirks should be defined in a way that allows reuse.

-- 
     -- Pierre Ossman

  WARNING: This correspondence is being monitored by the
  Swedish government. Make sure your server uses encryption
  for SMTP traffic and consider using PGP for end-to-end
  encryption.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH 2/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform
  2009-05-10 18:42   ` Pierre Ossman
@ 2009-05-20 10:55     ` Li, Jiebing
  0 siblings, 0 replies; 8+ messages in thread
From: Li, Jiebing @ 2009-05-20 10:55 UTC (permalink / raw)
  To: Pierre Ossman, Alan Cox, Matthew Garrett, Arjan van de Ven; +Cc: linux-kernel

Hi all,

Thanks a lot for your comments and instructions! I will modify the code, prepare for the use cases and re-submit the patches in next days.


Rgds,
Jiebing 

-----Original Message-----
From: Pierre Ossman [mailto:pierre@ossman.eu] 
Sent: Monday, May 11, 2009 2:42 AM
To: Li, Jiebing
Cc: linux-kernel@vger.kernel.org; Johnson, Charles F; Zhu, Daniel; Yuan, Hang; Pasrija, Geeta; Li, Jiebing
Subject: Re: [PATCH 2/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform

On Thu, 30 Apr 2009 17:19:06 +0800
"Li, Jiebing" <jiebing.li@intel.com> wrote:

> 
> This patch enables support of SDIO bus driver suspend/resume operation and supply sysfs interface for user
> to call suspend/resume selectively.Remind that this function should work together with SDIO device driver's
> suspend/resume function.
> 

As Matthew asked, what's the use case?

> And Moorestown's specific code is added into this patch to enable the second SDIO slot of the host controller.
> 

Again, stop mixing unrelated things in the same patch.

> @@ -452,3 +828,84 @@ err:
>         return err;
>  }
> 
> +/*
> + * warn device driver and perform a SDIO device reset.
> + * Assume that device driver knows hot to handle resets.
> + */
> +int sdio_reset_device(struct mmc_card *card)
> +{

Use case?

> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index b4cf691..0c9d2eb 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -44,6 +44,14 @@ config MMC_SDHCI_IO_ACCESSORS
>           This is silent Kconfig symbol that is selected by the drivers that
>           need to overwrite SDHCI IO memory accessors.
> 
> +config MMC_SDHCI_MRST_SDIO1
> +       bool
> +       depends on MMC_SDHCI
> +       help
> +         This enables Moorestown SD host controller's 2nd SDIO slot.
> +
> +         If unsure, say N.
> +

Detect this at runtime.

> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index a2804f1..4d4ad6d 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -914,6 +914,17 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
>         if (cmd->data)
>                 flags |= SDHCI_CMD_DATA;
> 
> +#ifdef CONFIG_MMC_SDHCI_MRST_SDIO1
> +       if (host->quirks & SDHCI_QUIRK_MRST_RESTRICTION) {
> +               u16 clk;
> +
> +               clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
> +
> +               clk |= SDHCI_CLOCK_CARD_EN;
> +               sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +       }
> +#endif
> +
>         sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
>  }
> 

Huh? Is this some kind of multiplexed nonsense?

> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index fa87b8b..50be698 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -227,6 +227,8 @@ struct sdhci_host {
>  #define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET           (1<<19)
>  /* Controller has to be forced to use block size of 2048 bytes */
>  #define SDHCI_QUIRK_FORCE_BLK_SZ_2048                  (1<<20)
> +/* Controller of Moorestown specific restriction */
> +#define SDHCI_QUIRK_MRST_RESTRICTION                   (1<<21)
> 

No. Quirks should be defined in a way that allows reuse.

-- 
     -- Pierre Ossman

  WARNING: This correspondence is being monitored by the
  Swedish government. Make sure your server uses encryption
  for SMTP traffic and consider using PGP for end-to-end
  encryption.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2009-05-20 10:56 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-04-30  9:14 [PATCH 0/2] MMC: MMC/SD/CE-ATA/SDIO driver for Intel Moorestown platform Li, Jiebing
2009-04-30  9:16 ` [PATCH 1/2] " Li, Jiebing
2009-04-30 13:44   ` Alan Cox
2009-05-10 18:29   ` Pierre Ossman
2009-04-30  9:19 ` [PATCH 2/2] " Li, Jiebing
2009-04-30 12:11   ` Matthew Garrett
2009-05-10 18:42   ` Pierre Ossman
2009-05-20 10:55     ` Li, Jiebing

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.