This is the SD/MMC controller on several of Freescale's more recent parts
---
> Hi Andy,
> Where could we find the driver that works with MMCv4 on at91sam9x?
> Best regards

Pierre, I don't have a driver that works on at91sam9x, I'm afraid.  I have
a driver that nearly works on an mpc837x.  However, some of it is certainly
right, and that may help you.  Also, some of it is wrong (I get intermittent
failures on MMC cards), and maybe someone will look at this driver, and see what
I'm doing wrong.


 Makefile                           |    2 +
 drivers/mmc/Makefile               |   46 ++
 drivers/mmc/fsl_esdhc.c            | 1111 ++++++++++++++++++++++++++++++++++++
 drivers/mmc/fsl_esdhc.h            |  208 +++++++
 include/asm-ppc/arch-mpc83xx/mmc.h |    1 +
 5 files changed, 1368 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/Makefile
 create mode 100644 drivers/mmc/fsl_esdhc.c
 create mode 100644 drivers/mmc/fsl_esdhc.h
 create mode 100644 include/asm-ppc/arch-mpc83xx/mmc.h

diff --git a/Makefile b/Makefile
index e5b4210..cd614a5 100644
--- a/Makefile
+++ b/Makefile
@@ -214,6 +214,7 @@ LIBS += drivers/hwmon/libhwmon.a
 LIBS += drivers/i2c/libi2c.a
 LIBS += drivers/input/libinput.a
 LIBS += drivers/misc/libmisc.a
+LIBS += drivers/mmc/libmmc.a
 LIBS += drivers/mtd/libmtd.a
 LIBS += drivers/mtd/nand/libnand.a
 LIBS += drivers/mtd/nand_legacy/libnand_legacy.a
@@ -383,6 +384,7 @@ TAG_SUBDIRS += drivers/hwmon
 TAG_SUBDIRS += drivers/i2c
 TAG_SUBDIRS += drivers/input
 TAG_SUBDIRS += drivers/misc
+TAG_SUBDIRS += drivers/mmc
 TAG_SUBDIRS += drivers/mtd
 TAG_SUBDIRS += drivers/mtd/nand
 TAG_SUBDIRS += drivers/mtd/nand_legacy
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
new file mode 100644
index 0000000..06e4e88
--- /dev/null
+++ b/drivers/mmc/Makefile
@@ -0,0 +1,46 @@
+#
+# (C) Copyright 2006
+# Wolfgang Denk, DENX Software Engineering, [EMAIL PROTECTED]
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB    := $(obj)libmmc.a
+
+COBJS-y += fsl_esdhc.o
+
+COBJS  := $(COBJS-y)
+SRCS   := $(COBJS:.o=.c)
+OBJS   := $(addprefix $(obj),$(COBJS))
+
+all:   $(LIB)
+
+$(LIB):        $(obj).depend $(OBJS)
+       $(AR) $(ARFLAGS) $@ $(OBJS)
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
new file mode 100644
index 0000000..29af92a
--- /dev/null
+++ b/drivers/mmc/fsl_esdhc.c
@@ -0,0 +1,1111 @@
+/*
+ * Copyright 2007, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Based vaguely on the pxa mmc code:
+ * (C) Copyright 2003
+ * Kyle Harris, Nexus Technologies, Inc. [EMAIL PROTECTED]
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <mmc.h>
+#include <part.h>
+#include <malloc.h>
+#include <asm/io.h>
+
+#include "fsl_esdhc.h"
+
+#ifdef CONFIG_MMC
+DECLARE_GLOBAL_DATA_PTR;
+
+struct fsl_esdhc {
+       uint    dsaddr;
+       uint    blkattr;
+       uint    cmdarg;
+       uint    xfertyp;
+       uint    cmdrsp0;
+       uint    cmdrsp1;
+       uint    cmdrsp2;
+       uint    cmdrsp3;
+       uint    datport;
+       uint    prsstat;
+       uint    proctl;
+       uint    sysctl;
+       uint    irqstat;
+       uint    irqstaten;
+       uint    irqsigen;
+       uint    autoc12err;
+       uint    hostcapblt;
+       uint    wml;
+       char    reserved1[8];
+       uint    fevt;
+       char    reserved2[168];
+       uint    hostver;
+       char    reserved3[780];
+       uint    scr;
+};
+
+enum {
+       MMC_CMD_RSP_NONE,
+       MMC_CMD_R1 = 1,
+       MMC_CMD_R1b,
+       MMC_CMD_R2,
+       MMC_CMD_R3,
+       MMC_CMD_R4,
+       MMC_CMD_R5,
+       MMC_CMD_R5b,
+       MMC_CMD_R6,
+       MMC_CMD_R7
+};
+
+uint   xfertyps[] = {
+       XFERTYP_RSPTYP_NONE,
+       XFERTYP_RSPTYP_48 | XFERTYP_CICEN | XFERTYP_CCCEN,
+       XFERTYP_RSPTYP_48_BUSY | XFERTYP_CICEN | XFERTYP_CCCEN,
+       XFERTYP_RSPTYP_136 | XFERTYP_CCCEN,
+       XFERTYP_RSPTYP_48,
+       XFERTYP_RSPTYP_48,
+       XFERTYP_RSPTYP_48 | XFERTYP_CICEN | XFERTYP_CCCEN,
+       XFERTYP_RSPTYP_48_BUSY | XFERTYP_CICEN | XFERTYP_CCCEN,
+       XFERTYP_RSPTYP_48 | XFERTYP_CICEN | XFERTYP_CCCEN,
+       XFERTYP_RSPTYP_48 | XFERTYP_CICEN | XFERTYP_CCCEN
+};
+
+struct mmc {
+       volatile void * regs;
+       uint version;
+       int high_capacity;
+       uint mode;
+       uint ocr;
+       uint scr[2];
+       uint csd[4];
+       char cid[16];
+       ushort rca;
+       uint tran_speed;
+       uint read_bl_len;
+       uint write_bl_len;
+       u64 capacity;
+       block_dev_desc_t block_dev;
+};
+
+static struct mmc *mmc_dev;
+
+block_dev_desc_t *mmc_get_dev(int dev)
+{
+       return (dev == 0) ? &mmc_dev->block_dev : NULL;
+}
+
+struct mmc_cmd {
+       ushort  cmdidx;
+       int     resp_type;
+       uint    cmdarg;
+       char    response[18];
+       uint    flags;
+};
+
+struct mmc_data {
+       char *  buffer;
+       uint    flags;
+       int     blocks;
+       int     blocksize;
+};
+
+/* Return the XFERTYP flags for a given command and data packet */
+uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
+{
+       uint xfertyp = 0;
+
+       if (data) {
+               xfertyp |= XFERTYP_DPSEL | XFERTYP_DMAEN;
+
+               if (data->blocks > 1) {
+                       xfertyp |= XFERTYP_MSBSEL;
+                       xfertyp |= XFERTYP_BCEN;
+               }
+
+               if (data->flags & MMC_DATA_READ)
+                       xfertyp |= XFERTYP_DTDSEL;
+       }
+
+       xfertyp |= xfertyps[cmd->resp_type];
+
+       return xfertyp;
+}
+
+
+/*
+ * Sends a command out on the bus.  Takes the mmc pointer,
+ * a command pointer, and an optional data pointer.
+ */
+static int
+mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
+{
+       uint    xfertyp;
+       uint    irqstat;
+       volatile struct fsl_esdhc *regs = mmc->regs;
+
+       regs->irqstat = -1;
+
+       sync();
+
+       /* Wait for the bus to be idle */
+       while ((regs->prsstat & PRSSTAT_CICHB) ||
+                       (regs->prsstat & PRSSTAT_CIDHB))
+               sync();
+
+       while (regs->prsstat & PRSSTAT_DLA)
+               sync();
+
+       /* Wait at least 8 SD clock cycles before the next command */
+       /*
+        * Note: This is way more than 8 cycles, but 1ms seems to
+        * resolve timing issues with some cards
+        */
+       udelay(1000);
+
+       /* Set up for a data transfer if we have one */
+       if (data) {
+               uint wml_value;
+               int timeout;
+
+               wml_value = data->blocksize/4;
+
+               if (data->flags & MMC_DATA_READ) {
+                       if (wml_value > 0x10)
+                               wml_value = 0x10;
+
+                       wml_value = 0x100000 | wml_value;
+               } else {
+                       if (wml_value > 0x80)
+                               wml_value = 0x80;
+
+                       wml_value = wml_value << 16 | 0x10;
+               }
+
+               regs->dsaddr = (uint)data->buffer;
+
+               sync();
+
+               regs->wml = wml_value;
+
+               sync();
+
+               regs->blkattr = data->blocks << 16 | data->blocksize;
+
+               sync();
+
+               timeout = __ilog2(mmc->tran_speed/10);
+               timeout -= 13;
+
+               if (timeout > 14)
+                       timeout = 14;
+
+               if (timeout < 0)
+                       timeout = 0;
+
+               regs->sysctl &= ~0x000f0000;
+
+               sync();
+
+               regs->sysctl |= timeout << 16;
+
+               sync();
+       }
+
+       /* Figure out the transfer arguments */
+       xfertyp = esdhc_xfertyp(cmd, data);
+
+       /* Send the command */
+       regs->cmdarg = cmd->cmdarg;
+       sync();
+       regs->xfertyp = XFERTYP_CMD(cmd->cmdidx) | xfertyp;
+
+       sync();
+       /* Wait for the command to complete */
+       while (!(regs->irqstat & IRQSTAT_CC))
+               sync();
+
+       irqstat = regs->irqstat;
+
+       sync();
+
+       regs->irqstat = irqstat;
+
+       sync();
+
+       if (irqstat & CMD_ERR)
+               return COMM_ERR;
+
+       if (irqstat & IRQSTAT_CTOE)
+               return TIMEOUT;
+
+       /* Copy the response to the response buffer */
+       if (cmd->resp_type == MMC_CMD_R2) {
+               ((uint *)(cmd->response))[0] =
+                       (regs->cmdrsp3 << 8) | (regs->cmdrsp2 >> 24);
+               ((uint *)(cmd->response))[1] =
+                       (regs->cmdrsp2 << 8) | (regs->cmdrsp1 >> 24);
+               ((uint *)(cmd->response))[2] =
+                       (regs->cmdrsp1 << 8) | (regs->cmdrsp0 >> 24);
+               ((uint *)(cmd->response))[3] = (regs->cmdrsp0 << 8);
+       } else
+               ((uint *)(cmd->response))[0] = regs->cmdrsp0;
+
+       /* Wait until all of the blocks are transferred */
+       if (data) {
+               do {
+                       irqstat = regs->irqstat;
+
+                       if (irqstat & DATA_ERR)
+                               return COMM_ERR;
+
+                       if (irqstat & IRQSTAT_DTOE)
+                               return TIMEOUT;
+                       sync();
+               } while (!(irqstat & IRQSTAT_TC) &&
+                               (regs->prsstat & PRSSTAT_DLA));
+       }
+
+       regs->irqstat = -1;
+       sync();
+
+       return 0;
+}
+
+void set_sysctl(struct mmc *mmc, int clock)
+{
+       int sdhc_clk = gd->sdhc_clk;
+       int div, pre_div;
+       volatile struct fsl_esdhc *regs = mmc->regs;
+       uint clk;
+
+       if (sdhc_clk / 16 > clock) {
+               for (pre_div = 2; pre_div < 256; pre_div *= 2)
+                       if ((sdhc_clk / pre_div) <= (clock * 16))
+                               break;
+       } else
+               pre_div = 2;
+
+       for (div = 1; div <= 16; div++)
+               if ((sdhc_clk / (div * pre_div)) <= clock)
+                       break;
+
+       pre_div >>= 1;
+       div -= 1;
+
+       clk = (pre_div << 8) | (div << 4);
+       regs->sysctl &= ~0x0000fff0;
+       sync();
+       regs->sysctl |= clk;
+       sync();
+
+       udelay(10000);
+
+       regs->sysctl |= SYSCTL_PEREN;
+}
+
+
+int
+mmc_block_write(ulong dst, uchar *src, int len)
+{
+#warning write not yet implemented
+       return 0;
+}
+
+int mmc_set_blocklen(struct mmc *mmc, int len)
+{
+       struct mmc_cmd cmd;
+
+       cmd.cmdidx = SET_BLOCKLEN;
+       cmd.resp_type = MMC_CMD_R1;
+       cmd.cmdarg = len;
+       cmd.flags = 0;
+
+       return mmc_send_cmd(mmc, &cmd, NULL);
+}
+
+int
+mmc_read(ulong src, uchar *dst, int size)
+{
+       struct mmc_cmd cmd;
+       struct mmc_data data;
+       int err;
+       char * buffer;
+       int stoperr = 0;
+       struct mmc *mmc = mmc_dev;
+       int blklen = mmc->read_bl_len;
+       int startblock = src / blklen;
+       int endblock = (src + size - 1) / blklen;
+       int blkcnt = endblock - startblock + 1;
+
+       /* Make a buffer big enough to hold all the blocks we might read */
+       buffer = malloc(blkcnt * blklen);
+
+       if (!buffer) {
+               printf("Could not allocate buffer for MMC read!\n");
+               return -1;
+       }
+
+#warning deal with larger reads (more than 65536 blks) and partial block reads
+       /* We only support full block reads from the card */
+       err = mmc_set_blocklen(mmc, mmc->read_bl_len);
+
+       if (err)
+               return err;
+
+       if (blkcnt > 1)
+               cmd.cmdidx = READ_MULTIPLE_BLOCKS;
+       else
+               cmd.cmdidx = READ_SINGLE_BLOCK;
+
+       if (mmc->high_capacity)
+               cmd.cmdarg = startblock;
+       else
+               cmd.cmdarg = startblock * blklen;
+
+       cmd.resp_type = MMC_CMD_R1;
+       cmd.flags = 0;
+
+       data.buffer = buffer;
+       data.blocks = blkcnt;
+       data.blocksize = blklen;
+       data.flags = MMC_DATA_READ;
+
+       err = mmc_send_cmd(mmc, &cmd, &data);
+
+       if (blkcnt > 1) {
+               cmd.cmdidx = STOP_TRANSMISSION;
+               cmd.cmdarg = 0;
+               cmd.resp_type = MMC_CMD_R1b;
+               cmd.flags = 0;
+               stoperr = mmc_send_cmd(mmc, &cmd, NULL);
+       }
+
+
+       if (err)
+               return err;
+
+       memcpy(dst, buffer + (src & (blklen - 1)), size);
+
+       free(buffer);
+
+       return stoperr;
+}
+
+int
+mmc_write(uchar *src, ulong dst, int size)
+{
+       return 0;
+}
+
+ulong
+mmc_bread(int dev_num, ulong blknr, ulong blkcnt, void *dst)
+{
+       int err;
+       ulong src = blknr * mmc_dev->read_bl_len;
+
+       err = mmc_read(src, dst, blkcnt * mmc_dev->read_bl_len);
+
+       if (err) {
+               printf("block read failed: %d\n", err);
+               return 0;
+       }
+
+       return blkcnt;
+}
+
+int mmc_go_idle(struct mmc* mmc)
+{
+       struct mmc_cmd cmd;
+       int err;
+
+       udelay(1000);
+
+       cmd.cmdidx = GO_IDLE_STATE;
+       cmd.cmdarg = 0;
+       cmd.resp_type = MMC_CMD_RSP_NONE;
+       cmd.flags = 0;
+
+       err = mmc_send_cmd(mmc, &cmd, NULL);
+
+       if (err)
+               return err;
+
+       udelay(2000);
+
+       return 0;
+}
+
+int
+sd_send_op_cond(struct mmc *mmc)
+{
+       int timeout = 1000;
+       int err;
+       struct mmc_cmd cmd;
+
+       do {
+               cmd.cmdidx = APP_CMD;
+               cmd.resp_type = MMC_CMD_R1;
+               cmd.cmdarg = 0;
+               cmd.flags = 0;
+
+               err = mmc_send_cmd(mmc, &cmd, NULL);
+
+               if (err)
+                       return err;
+
+               cmd.cmdidx = SD_SEND_OP_COND;
+               cmd.resp_type = MMC_CMD_R3;
+               cmd.cmdarg = 0xff8000;
+
+               if (mmc->version == SD_VERSION_2)
+                       cmd.cmdarg |= 0x40000000;
+
+               err = mmc_send_cmd(mmc, &cmd, NULL);
+
+               if (err)
+                       return err;
+
+               udelay(1000);
+       } while ((!(cmd.response[0] & OCR_BUSY)) && timeout--);
+
+       if (timeout <= 0)
+               return UNUSABLE_ERR;
+
+       if (mmc->version != SD_VERSION_2)
+               mmc->version = SD_VERSION_1_0;
+
+       mmc->ocr = ((uint *)(cmd.response))[0];
+
+       mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
+       mmc->rca = 0;
+
+       return 0;
+}
+
+int mmc_send_op_cond(struct mmc *mmc)
+{
+       int timeout = 1000;
+       struct mmc_cmd cmd;
+       int err;
+
+       /* Some cards seem to need this */
+       mmc_go_idle(mmc);
+
+       do {
+               cmd.cmdidx = MMC_SEND_OP_COND;
+               cmd.resp_type = MMC_CMD_R3;
+               cmd.cmdarg = 0x40ff8000;
+               cmd.flags = 0;
+
+               err = mmc_send_cmd(mmc, &cmd, NULL);
+
+               if (err)
+                       return err;
+
+               udelay(1000);
+       } while (!(cmd.response[0] & OCR_BUSY) && timeout--);
+
+       if (timeout <= 0)
+               return UNUSABLE_ERR;
+
+       mmc->version = MMC_VERSION_UNKNOWN;
+       mmc->ocr = ((uint *)(cmd.response))[0];
+
+       mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
+       mmc->rca = 0;
+
+       return 0;
+}
+
+
+int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd)
+{
+       struct mmc_cmd cmd;
+       struct mmc_data data;
+       int err;
+
+       /* Get the Card Status Register */
+       cmd.cmdidx = SEND_EXT_CSD;
+       cmd.resp_type = MMC_CMD_R1;
+       cmd.cmdarg = 0;
+       cmd.flags = 0;
+
+       data.buffer = ext_csd;
+       data.blocks = 1;
+       data.blocksize = 512;
+       data.flags = MMC_DATA_READ;
+
+       err = mmc_send_cmd(mmc, &cmd, &data);
+
+       return err;
+}
+
+
+int mmc_change_freq(struct mmc *mmc)
+{
+       struct mmc_cmd cmd;
+       char ext_csd[512];
+       char cardtype;
+       int err;
+
+       mmc->mode = 0;
+
+       /* Only version 4 supports high-speed */
+       if (mmc->version < MMC_VERSION_4)
+               return 0;
+
+       mmc->mode |= MMC_MODE_4BIT;
+
+       err = mmc_send_ext_csd(mmc, ext_csd);
+
+       if (err)
+               return err;
+
+       if (ext_csd[212] || ext_csd[213] || ext_csd[214] || ext_csd[215])
+               mmc->high_capacity = 1;
+
+       cardtype = ext_csd[196] & 0xf;
+
+       /* Set the card to use High Speed */
+       cmd.cmdidx = MMC_SWITCH;
+       cmd.resp_type = MMC_CMD_R1b;
+       cmd.cmdarg = 0x1b90100;
+       cmd.flags = 0;
+
+       err = mmc_send_cmd(mmc, &cmd, NULL);
+
+       if (err)
+               return err;
+
+       /* Now check to see that it worked */
+       err = mmc_send_ext_csd(mmc, ext_csd);
+
+       if (err)
+               return err;
+
+       /* No high-speed support */
+       if (!ext_csd[185])
+               return 0;
+
+       /* High Speed is set, there are two types: 52MHz and 26MHz */
+       if (cardtype & MMC_HS_52MHZ)
+               mmc->mode |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+       else
+               mmc->mode |= MMC_MODE_HS;
+
+       return 0;
+}
+
+int sd_change_freq(struct mmc *mmc)
+{
+       int err;
+       struct mmc_cmd cmd;
+       uint scr[2];
+       uint switch_status[16];
+       struct mmc_data data;
+       int timeout;
+
+       mmc->mode = 0;
+
+       /* Read the SCR to find out if this card supports higher speeds */
+       cmd.cmdidx = APP_CMD;
+       cmd.resp_type = MMC_CMD_R1;
+       cmd.cmdarg = mmc->rca << 16;
+       cmd.flags = 0;
+
+       err = mmc_send_cmd(mmc, &cmd, NULL);
+
+       if (err)
+               return err;
+
+       cmd.cmdidx = SEND_SCR;
+       cmd.resp_type = MMC_CMD_R1;
+       cmd.cmdarg = 0;
+       cmd.flags = 0;
+
+       timeout = 3;
+
+retry_scr:
+       data.buffer = (char *)&scr;
+       data.blocksize = 8;
+       data.blocks = 1;
+       data.flags = MMC_DATA_READ;
+
+       err = mmc_send_cmd(mmc, &cmd, &data);
+
+       if (err) {
+               if (timeout--)
+                       goto retry_scr;
+
+               return err;
+       }
+
+       mmc->scr[0] = scr[0];
+       mmc->scr[1] = scr[1];
+
+       switch ((mmc->scr[0] >> 24) & 0xf) {
+               case 0:
+                       mmc->version = SD_VERSION_1_0;
+                       break;
+               case 1:
+                       mmc->version = SD_VERSION_1_10;
+                       break;
+               case 2:
+                       mmc->version = SD_VERSION_2;
+                       break;
+               default:
+                       mmc->version = SD_VERSION_1_0;
+                       break;
+       }
+
+       /* Version 1.0 doesn't support switching */
+       if (mmc->version == SD_VERSION_1_0)
+               return 0;
+
+       timeout = 4;
+       while (timeout--) {
+               /* Switch the frequency */
+               cmd.cmdidx = SWITCH_FUNC;
+               cmd.resp_type = MMC_CMD_R1;
+               cmd.cmdarg = 0xfffff1;
+               cmd.flags = 0;
+
+               data.buffer = (char *)&switch_status;
+               data.blocksize = 64;
+               data.blocks = 1;
+               data.flags = MMC_DATA_READ;
+
+               err = mmc_send_cmd(mmc, &cmd, &data);
+
+               if (err)
+                       return err;
+
+               /* The high-speed function is busy.  Try again */
+               if (!switch_status[7] & SD_HIGHSPEED_BUSY)
+                       break;
+       }
+
+       if (mmc->scr[0] & SD_DATA_4BIT)
+               mmc->mode |= MMC_MODE_4BIT;
+
+       /* If high-speed isn't supported, we return */
+       if (!(switch_status[3] & SD_HIGHSPEED_SUPPORTED))
+               return 0;
+
+       cmd.cmdidx = SWITCH_FUNC;
+       cmd.resp_type = MMC_CMD_R1;
+       cmd.cmdarg = 0x80fffff1;
+       cmd.flags = 0;
+
+       data.buffer = (char *)&switch_status;
+       data.blocksize = 64;
+       data.blocks = 1;
+       data.flags = MMC_DATA_READ;
+
+       err = mmc_send_cmd(mmc, &cmd, &data);
+
+       if (err)
+               return err;
+
+       if ((switch_status[4] & 0x0f000000) == 0x01000000)
+               mmc->mode |= MMC_MODE_HS;
+
+       return 0;
+}
+
+/* frequency bases */
+/* divided by 10 to be nice to platforms without floating point */
+int fbase[] = {
+       10000,
+       100000,
+       1000000,
+       10000000,
+};
+
+/* Multiplier values for TRAN_SPEED.  Multiplied by 10 to be nice
+ * to platforms without floating point.
+ */
+int multipliers[] = {
+       0,      /* reserved */
+       10,
+       12,
+       13,
+       15,
+       20,
+       25,
+       30,
+       35,
+       40,
+       45,
+       50,
+       55,
+       60,
+       70,
+       80,
+};
+
+
+int mmc_startup(struct mmc *mmc)
+{
+       int err;
+       uint mult, freq;
+       uint cmult, csize;
+       struct mmc_cmd cmd;
+       volatile struct fsl_esdhc *regs = mmc->regs;
+
+       /* Put the Card in Identify Mode */
+       cmd.cmdidx = ALL_SEND_CID;
+       cmd.resp_type = MMC_CMD_R2;
+       cmd.cmdarg = 0;
+       cmd.flags = 0;
+
+       err = mmc_send_cmd(mmc, &cmd, NULL);
+
+       if (err)
+               return err;
+
+       memcpy(mmc->cid, cmd.response, 16);
+
+       /*
+        * For MMC cards, set the Relative Address.
+        * For SD cards, get the Relatvie Address.
+        * This also puts the cards into Standby State
+        */
+       cmd.cmdidx = SEND_RELATIVE_ADDR;
+       cmd.cmdarg = mmc->rca << 16;
+       cmd.resp_type = MMC_CMD_R6;
+       cmd.flags = 0;
+
+       err = mmc_send_cmd(mmc, &cmd, NULL);
+
+       if (err)
+               return err;
+
+       if (IS_SD(mmc))
+               mmc->rca = (((uint *)(cmd.response))[0] >> 16) & 0xffff;
+
+       /* Get the Card-Specific Data */
+       cmd.cmdidx = SEND_CSD;
+       cmd.resp_type = MMC_CMD_R2;
+       cmd.cmdarg = mmc->rca << 16;
+       cmd.flags = 0;
+
+       err = mmc_send_cmd(mmc, &cmd, NULL);
+
+       if (err)
+               return err;
+
+       mmc->csd[0] = ((uint *)(cmd.response))[0];
+       mmc->csd[1] = ((uint *)(cmd.response))[1];
+       mmc->csd[2] = ((uint *)(cmd.response))[2];
+       mmc->csd[3] = ((uint *)(cmd.response))[3];
+
+       if (mmc->version == MMC_VERSION_UNKNOWN) {
+               int version = (cmd.response[0] >> 2) & 0xf;
+
+               switch (version) {
+                       case 0:
+                               mmc->version = MMC_VERSION_1_2;
+                               break;
+                       case 1:
+                               mmc->version = MMC_VERSION_1_4;
+                               break;
+                       case 2:
+                               mmc->version = MMC_VERSION_2_2;
+                               break;
+                       case 3:
+                               mmc->version = MMC_VERSION_3;
+                               break;
+                       case 4:
+                               mmc->version = MMC_VERSION_4;
+                               break;
+                       default:
+                               mmc->version = MMC_VERSION_1_2;
+                               break;
+               }
+       }
+
+       /* divide frequency by 10, since the mults are 10x bigger */
+       freq = fbase[(cmd.response[3] & 0x7)];
+       mult = multipliers[((cmd.response[3] >> 3) & 0xf)];
+
+       mmc->tran_speed = freq * mult;
+
+       mmc->read_bl_len = 1 << ((((uint *)(cmd.response))[1] >> 16) & 0xf);
+
+       if (IS_SD(mmc))
+               mmc->write_bl_len = mmc->read_bl_len;
+       else
+               mmc->write_bl_len = 1 << ((((uint *)(cmd.response))[3] >> 22) & 
0xf);
+
+       csize = (mmc->csd[1] & 0x3ff) << 2 | (mmc->csd[2] & 0xc0000000) >> 30;
+       cmult = (mmc->csd[2] & 0x00038000) >> 15;
+
+       mmc->capacity = (csize + 1) << (cmult + 2);
+       mmc->capacity *= mmc->read_bl_len;
+
+       if (mmc->read_bl_len > 512)
+               mmc->read_bl_len = 512;
+
+       if (mmc->write_bl_len > 512)
+               mmc->write_bl_len = 512;
+
+       /* Select the card, and put it into Transfer Mode */
+       cmd.cmdidx = SELECT_CARD;
+       cmd.resp_type = MMC_CMD_R1b;
+       cmd.cmdarg = mmc->rca << 16;
+       cmd.flags = 0;
+       err = mmc_send_cmd(mmc, &cmd, NULL);
+
+       if (err)
+               return err;
+
+       if (IS_SD(mmc))
+               err = sd_change_freq(mmc);
+       else
+               err = mmc_change_freq(mmc);
+
+       if (err)
+               return err;
+
+       if (IS_SD(mmc)) {
+               if (mmc->mode & MMC_MODE_4BIT) {
+                       cmd.cmdidx = APP_CMD;
+                       cmd.resp_type = MMC_CMD_R1;
+                       cmd.cmdarg = mmc->rca << 16;
+                       cmd.flags = 0;
+
+                       err = mmc_send_cmd(mmc, &cmd, NULL);
+                       if (err)
+                               return err;
+
+                       cmd.cmdidx = SET_BUS_WIDTH;
+                       cmd.resp_type = MMC_CMD_R1;
+                       cmd.cmdarg = 2;
+                       cmd.flags = 0;
+                       err = mmc_send_cmd(mmc, &cmd, NULL);
+                       if (err)
+                               return err;
+
+                       regs->proctl |= PROCTL_DTW_4;
+               }
+
+               if (mmc->mode & MMC_MODE_HS)
+                       set_sysctl(mmc, 50000000);
+               else
+                       set_sysctl(mmc, 25000000);
+       } else {
+               if (mmc->mode & MMC_MODE_4BIT) {
+                       /* Set the card to use 4 bit*/
+                       cmd.cmdidx = MMC_SWITCH;
+                       cmd.resp_type = MMC_CMD_R1b;
+                       cmd.cmdarg = 0x1b70100;
+                       cmd.flags = 0;
+
+                       err = mmc_send_cmd(mmc, &cmd, NULL);
+
+                       if (err)
+                               return err;
+
+                       regs->proctl |= PROCTL_DTW_4;
+               } else if (mmc->mode & MMC_MODE_8BIT) {
+                       /* Set the card to use 8 bit*/
+                       cmd.cmdidx = MMC_SWITCH;
+                       cmd.resp_type = MMC_CMD_R1b;
+                       cmd.cmdarg = 0x1b70200;
+                       cmd.flags = 0;
+
+                       err = mmc_send_cmd(mmc, &cmd, NULL);
+
+                       if (err)
+                               return err;
+                       
+                       regs->proctl |= PROCTL_DTW_8;
+               }
+
+               if (mmc->mode & MMC_MODE_HS) {
+                       if (mmc->mode & MMC_MODE_HS_52MHz)
+                               set_sysctl(mmc, 52000000);
+                       else
+                               set_sysctl(mmc, 26000000);
+               } else
+                       set_sysctl(mmc, 20000000);
+       }
+
+       /* fill in device description */
+       mmc->block_dev.if_type = IF_TYPE_MMC;
+       mmc->block_dev.part_type = PART_TYPE_DOS;
+       mmc->block_dev.dev = 0;
+       mmc->block_dev.lun = 0;
+       mmc->block_dev.type = 0;
+       mmc->block_dev.blksz = mmc->read_bl_len;
+       mmc->block_dev.lba = mmc->capacity/mmc->read_bl_len;
+       sprintf(mmc->block_dev.vendor,"Man %02x%02x%02x Snr %02x%02x%02x%02x",
+                       mmc->cid[0], mmc->cid[1], mmc->cid[2],
+                       mmc->cid[9], mmc->cid[10], mmc->cid[11], mmc->cid[12]);
+       sprintf(mmc->block_dev.product,"%c%c%c%c%c", mmc->cid[3],
+                       mmc->cid[4], mmc->cid[5], mmc->cid[6], mmc->cid[7]);
+       sprintf(mmc->block_dev.revision,"%d.%d", mmc->cid[8] >> 4,
+                       mmc->cid[8] & 0xf);
+       mmc->block_dev.removable = 1;
+       mmc->block_dev.block_read = mmc_bread;
+
+       init_part(&mmc->block_dev);
+
+       return 0;
+}
+
+int mmc_send_if_cond(struct mmc *mmc)
+{
+       struct mmc_cmd cmd;
+       int err;
+
+       cmd.cmdidx = SEND_IF_COND;
+       cmd.cmdarg = 0x1aa;
+       cmd.resp_type = MMC_CMD_R7;
+       cmd.flags = 0;
+
+       err = mmc_send_cmd(mmc, &cmd, NULL);
+
+       if (err)
+               return err;
+
+       if (((uint *)(cmd.response))[0] != 0x1aa)
+               return UNUSABLE_ERR;
+       else
+               mmc->version = SD_VERSION_2;
+
+       return 0;
+}
+
+int
+mmc_init(int verbose)
+{
+       volatile immap_t *im = (immap_t *) CFG_IMMR;
+       struct fsl_esdhc *regs = (struct fsl_esdhc *)&im->sdhc;
+       int timeout = 1000;
+       struct mmc *mmc;
+       int err;
+
+       mmc = malloc(sizeof(struct mmc));
+
+       mmc->regs = regs;
+
+       if (mmc_dev)
+               free(mmc_dev);
+
+       mmc_dev = mmc;
+
+       regs->sysctl = SYSCTL_HCKEN | SYSCTL_IPGEN;
+
+       /* Set the clock speed */
+       set_sysctl(mmc, 400000);
+
+       /* Disable the BRR and BWR bits in IRQSTAT */
+       regs->irqstaten &= ~(IRQSTATEN_BRR | IRQSTATEN_BWR);
+
+       /* Put the PROCTL reg back to the default */
+       regs->proctl = PROCTL_INIT;
+
+       while (!(regs->prsstat & PRSSTAT_CINS) && timeout--)
+               udelay(1000);
+
+       if (timeout <= 0)
+               return NO_CARD_ERR;
+
+       /* Reset the Card */
+       err = mmc_go_idle(mmc);
+
+       if (err)
+               return err;
+
+       /* Test for SD version 2 */
+       err = mmc_send_if_cond(mmc);
+
+       /* If we got an error other than timeout, we bail */
+       if (err && err != TIMEOUT)
+               return err;
+
+       /* Now try to get the SD card's operating condition */
+       err = sd_send_op_cond(mmc);
+
+       /* If the command timed out, we check for an MMC card */
+       if (err == TIMEOUT) {
+               err = mmc_send_op_cond(mmc);
+
+               if (err) {
+                       printf("Card did not respond to voltage select!\n");
+                       return UNUSABLE_ERR;
+               }
+       }
+
+       err = mmc_startup(mmc);
+
+       if (err)
+               printf("Returning with err %d\n", err);
+
+       return err;
+}
+
+int
+mmc2info(ulong addr)
+{
+       /* Hmm... */
+       return 0;
+}
+
+static void print_mmcinfo(struct mmc *mmc)
+{
+       printf("Manufacturer: %x\n", mmc->cid[0] >> 24);
+       printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff);
+       printf("Name: %c%c%c%c%c\n", mmc->cid[0] & 0xff,
+                       (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
+                       (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
+
+       printf("Tran Speed: %d\n", mmc->tran_speed);
+       printf("Rd Block Len: %d\n", mmc->read_bl_len);
+
+       printf("%s version %d.%d\n", IS_SD(mmc) ? "SD" : "MMC",
+                       (mmc->version >> 4) & 0xf, mmc->version & 0xf);
+
+       printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No");
+       printf("Capacity: %d\n", mmc->capacity);
+
+       printf("Bus Width: %d-bit\n", (mmc->mode & MMC_MODE_4BIT) ? 4 :
+                       (mmc->mode & MMC_MODE_8BIT) ? 8 : 1);
+}
+
+int do_mmcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+       if (mmc_dev)
+               print_mmcinfo(mmc_dev);
+       else
+               printf("Call mmcinit first\n");
+
+       return 0;
+}
+
+U_BOOT_CMD(mmcinfo, 1, 0, do_mmcinfo, "mmcinfo -- display MMC info\n", NULL);
+
+#endif /* CONFIG_MMC */
diff --git a/drivers/mmc/fsl_esdhc.h b/drivers/mmc/fsl_esdhc.h
new file mode 100644
index 0000000..cf5f2d1
--- /dev/null
+++ b/drivers/mmc/fsl_esdhc.h
@@ -0,0 +1,208 @@
+/*
+ * FSL SD/MMC Defines
+ *-------------------------------------------------------------------
+ *
+ * Copyright 2007-2008, Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ *-------------------------------------------------------------------
+ *
+ */
+
+#ifndef  __FSL_ESDHC_H__
+#define        __FSL_ESDHC_H__
+
+#define SD_VERSION_SD  0x20000
+#define SD_VERSION_2   (SD_VERSION_SD | 0x20)
+#define SD_VERSION_1_0 (SD_VERSION_SD | 0x10)
+#define SD_VERSION_1_10        (SD_VERSION_SD | 0x1a)
+#define MMC_VERSION_MMC                0x10000
+#define MMC_VERSION_UNKNOWN    (MMC_VERSION_MMC)
+#define MMC_VERSION_1_2                (MMC_VERSION_MMC | 0x12)
+#define MMC_VERSION_1_4                (MMC_VERSION_MMC | 0x14)
+#define MMC_VERSION_2_2                (MMC_VERSION_MMC | 0x22)
+#define MMC_VERSION_3          (MMC_VERSION_MMC | 0x30)
+#define MMC_VERSION_4          (MMC_VERSION_MMC | 0x40)
+
+#define MMC_MODE_HS            0x001
+#define MMC_MODE_HS_52MHz      0x010
+#define MMC_MODE_4BIT          0x100
+#define MMC_MODE_8BIT          0x200
+
+#define SD_DATA_4BIT   0x00040000
+
+#define IS_SD(x) (mmc->version & SD_VERSION_SD)
+
+#define MMC_DATA_READ          1
+#define MMC_DATA_WRITE         2
+
+#define NO_CARD_ERR            -16 /* No SD/MMC card inserted */
+#define UNUSABLE_ERR           -17 /* Unusable Card */
+#define COMM_ERR               -18 /* Communications Error */
+#define TIMEOUT                        -19
+
+#define GO_IDLE_STATE          0x0
+#define SEND_IF_COND           (8)
+
+#define APP_CMD                        (55)
+
+#define MMC_SEND_OP_COND       (1)
+
+#define SD_SEND_OP_COND                (41)
+
+#define ALL_SEND_CID           (2)
+
+#define SEND_RELATIVE_ADDR     (3)
+
+#define SELECT_CARD            (7)
+
+#define SEND_SCR               (51)
+
+#define SEND_EXT_CSD           (8)
+
+#define SEND_CSD               (9)
+
+#define SEND_STATUS            (13)
+
+#define SWITCH_FUNC            (6)
+#define MMC_SWITCH             (6)
+
+#define SET_BUS_WIDTH          (6)
+
+#define STOP_TRANSMISSION      (12)
+
+#define SET_BLOCKLEN           (16)
+
+#define READ_SINGLE_BLOCK      (17)
+#define READ_MULTIPLE_BLOCKS   (18)
+
+#define SD_HIGHSPEED_BUSY      0x00020000
+#define SD_HIGHSPEED_SUPPORTED 0x00020000
+
+#define MMC_HS_TIMING          0x00000100
+#define MMC_HS_52MHZ           0x2
+
+#define OCR_BUSY       0x80
+#define OCR_HCS                0x40000000
+
+/* FSL eSDHC-specific constants */
+#define SYSCTL                 0x0002e02c
+#define SYSCTL_INITA           0x08000000
+#define SYSCTL_PEREN           0x00000004
+#define SYSCTL_HCKEN           0x00000002
+#define SYSCTL_IPGEN           0x00000001
+
+#define IRQSTAT                        0x0002e030
+#define IRQSTAT_DMAE           (0x10000000)
+#define IRQSTAT_AC12E          (0x01000000)
+#define IRQSTAT_DEBE           (0x00400000)
+#define IRQSTAT_DCE            (0x00200000)
+#define IRQSTAT_DTOE           (0x00100000)
+#define IRQSTAT_CIE            (0x00080000)
+#define IRQSTAT_CEBE           (0x00040000)
+#define IRQSTAT_CCE            (0x00020000)
+#define IRQSTAT_CTOE           (0x00010000)
+#define IRQSTAT_CINT           (0x00000100)
+#define IRQSTAT_CRM            (0x00000080)
+#define IRQSTAT_CINS           (0x00000040)
+#define IRQSTAT_BRR            (0x00000020)
+#define IRQSTAT_BWR            (0x00000010)
+#define IRQSTAT_DINT           (0x00000008)
+#define IRQSTAT_BGE            (0x00000004)
+#define IRQSTAT_TC             (0x00000002)
+#define IRQSTAT_CC             (0x00000001)
+
+#define CMD_ERR                (IRQSTAT_CIE | IRQSTAT_CEBE | IRQSTAT_CCE)
+#define DATA_ERR       (IRQSTAT_DEBE | IRQSTAT_DCE | IRQSTAT_DTOE)
+
+#define IRQSTATEN              0x0002e034
+#define IRQSTATEN_DMAE         (0x10000000)
+#define IRQSTATEN_AC12E                (0x01000000)
+#define IRQSTATEN_DEBE         (0x00400000)
+#define IRQSTATEN_DCE          (0x00200000)
+#define IRQSTATEN_DTOE         (0x00100000)
+#define IRQSTATEN_CIE          (0x00080000)
+#define IRQSTATEN_CEBE         (0x00040000)
+#define IRQSTATEN_CCE          (0x00020000)
+#define IRQSTATEN_CTOE         (0x00010000)
+#define IRQSTATEN_CINT         (0x00000100)
+#define IRQSTATEN_CRM          (0x00000080)
+#define IRQSTATEN_CINS         (0x00000040)
+#define IRQSTATEN_BRR          (0x00000020)
+#define IRQSTATEN_BWR          (0x00000010)
+#define IRQSTATEN_DINT         (0x00000008)
+#define IRQSTATEN_BGE          (0x00000004)
+#define IRQSTATEN_TC           (0x00000002)
+#define IRQSTATEN_CC           (0x00000001)
+
+#define PRSSTAT                        0x0002e024
+#define PRSSTAT_CLSL           (0x00800000)
+#define PRSSTAT_WPSPL          (0x00080000)
+#define PRSSTAT_CDPL           (0x00040000)
+#define PRSSTAT_CINS           (0x00010000)
+#define PRSSTAT_BREN           (0x00000800)
+#define PRSSTAT_DLA            (0x00000004)
+#define PRSSTAT_CICHB          (0x00000002)
+#define PRSSTAT_CIDHB          (0x00000001)
+
+#define PROCTL                 0x0002e028
+#define PROCTL_INIT            0x00000020
+#define PROCTL_DTW_4           0x00000002
+#define PROCTL_DTW_8           0x00000004
+
+#define CMDARG                 0x0002e008
+
+#define XFERTYP                        0x0002e00c
+#define XFERTYP_CMD(x)         ((x & 0x3f) << 24)
+#define XFERTYP_CMDTYP_NORMAL  0x0
+#define XFERTYP_CMDTYP_SUSPEND 0x00400000
+#define XFERTYP_CMDTYP_RESUME  0x00800000
+#define XFERTYP_CMDTYP_ABORT   0x00c00000
+#define XFERTYP_DPSEL          0x00200000
+#define XFERTYP_CICEN          0x00100000
+#define XFERTYP_CCCEN          0x00080000
+#define XFERTYP_RSPTYP_NONE    0
+#define XFERTYP_RSPTYP_136     0x00010000
+#define XFERTYP_RSPTYP_48      0x00020000
+#define XFERTYP_RSPTYP_48_BUSY 0x00030000
+#define XFERTYP_MSBSEL         0x00000020
+#define XFERTYP_DTDSEL         0x00000010
+#define XFERTYP_AC12EN         0x00000004
+#define XFERTYP_BCEN           0x00000002
+#define XFERTYP_DMAEN          0x00000001
+
+#define CINS_TIMEOUT           1000
+
+#define DSADDR         0x2e004
+
+#define CMDRSP0                0x2e010
+#define CMDRSP1                0x2e014
+#define CMDRSP2                0x2e018
+#define CMDRSP3                0x2e01c
+
+#define DATPORT                0x2e020
+
+#define WML            0x2e044
+#define WML_WRITE      0x00010000
+
+#define BLKATTR                0x2e004
+#define BLKATTR_CNT(x) ((x & 0xffff) << 16)
+#define BLKATTR_SIZE(x)        (x & 0x1fff)
+#define MAX_BLK_CNT    0x7fff  /* so malloc will have enough room with 32M */
+
+
+#endif  /* __FSL_ESDHC_H__ */
diff --git a/include/asm-ppc/arch-mpc83xx/mmc.h 
b/include/asm-ppc/arch-mpc83xx/mmc.h
new file mode 100644
index 0000000..d5034f4
--- /dev/null
+++ b/include/asm-ppc/arch-mpc83xx/mmc.h
@@ -0,0 +1 @@
+/* Stub file to satisfy odd requirement for <arch/mmc.h> */
-- 
1.5.4.GIT


-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference 
Don't miss this year's exciting event. There's still time to save $100. 
Use priority code J8TL2D2. 
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
U-Boot-Users mailing list
U-Boot-Users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/u-boot-users

Reply via email to