mmc: add generic mmc spi driver
authorThomas Chou <thomas@wytron.com.tw>
Fri, 24 Dec 2010 13:12:21 +0000 (13:12 +0000)
committerAndy Fleming <afleming@freescale.com>
Wed, 13 Apr 2011 11:35:22 +0000 (06:35 -0500)
This patch supports mmc/sd card with spi interface. It is based on
the generic mmc framework. It works with SDHC and supports multi
blocks read/write.

The crc checksum on data packet is enabled with the def,

There is a subcomamnd "mmc_spi" to setup spi bus and cs at run time.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
Signed-off-by: Andy Fleming <afleming@freescale.com>
common/Makefile
common/cmd_mmc_spi.c [new file with mode: 0644]
drivers/mmc/Makefile
drivers/mmc/mmc.c
drivers/mmc/mmc_spi.c [new file with mode: 0644]
include/mmc.h

index 4555716108262abd8fc14463c91e5f1e9a399e74..6f41ce4397d7a4a791aeca4c567e3fa5c6965edc 100644 (file)
@@ -118,6 +118,7 @@ COBJS-$(CONFIG_CMD_MII) += miiphyutil.o
 COBJS-$(CONFIG_CMD_MII) += cmd_mii.o
 COBJS-$(CONFIG_CMD_MISC) += cmd_misc.o
 COBJS-$(CONFIG_CMD_MMC) += cmd_mmc.o
+COBJS-$(CONFIG_CMD_MMC_SPI) += cmd_mmc_spi.o
 COBJS-$(CONFIG_MP) += cmd_mp.o
 COBJS-$(CONFIG_CMD_MTDPARTS) += cmd_mtdparts.o
 COBJS-$(CONFIG_CMD_NAND) += cmd_nand.o
diff --git a/common/cmd_mmc_spi.c b/common/cmd_mmc_spi.c
new file mode 100644 (file)
index 0000000..63fe313
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Command for mmc_spi setup.
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <common.h>
+#include <mmc.h>
+#include <spi.h>
+
+#ifndef CONFIG_MMC_SPI_BUS
+# define CONFIG_MMC_SPI_BUS 0
+#endif
+#ifndef CONFIG_MMC_SPI_CS
+# define CONFIG_MMC_SPI_CS 1
+#endif
+/* in SPI mode, MMC speed limit is 20MHz, while SD speed limit is 25MHz */
+#ifndef CONFIG_MMC_SPI_SPEED
+# define CONFIG_MMC_SPI_SPEED 25000000
+#endif
+/* MMC and SD specs only seem to care that sampling is on the
+ * rising edge ... meaning SPI modes 0 or 3.  So either SPI mode
+ * should be legit.  We'll use mode 0 since the steady state is 0,
+ * which is appropriate for hotplugging, unless the platform data
+ * specify mode 3 (if hardware is not compatible to mode 0).
+ */
+#ifndef CONFIG_MMC_SPI_MODE
+# define CONFIG_MMC_SPI_MODE SPI_MODE_0
+#endif
+
+static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       uint bus = CONFIG_MMC_SPI_BUS;
+       uint cs = CONFIG_MMC_SPI_CS;
+       uint speed = CONFIG_MMC_SPI_SPEED;
+       uint mode = CONFIG_MMC_SPI_MODE;
+       char *endp;
+       struct mmc *mmc;
+
+       if (argc < 2)
+               goto usage;
+
+       cs = simple_strtoul(argv[1], &endp, 0);
+       if (*argv[1] == 0 || (*endp != 0 && *endp != ':'))
+               goto usage;
+       if (*endp == ':') {
+               if (endp[1] == 0)
+                       goto usage;
+               bus = cs;
+               cs = simple_strtoul(endp + 1, &endp, 0);
+               if (*endp != 0)
+                       goto usage;
+       }
+       if (argc >= 3) {
+               speed = simple_strtoul(argv[2], &endp, 0);
+               if (*argv[2] == 0 || *endp != 0)
+                       goto usage;
+       }
+       if (argc >= 4) {
+               mode = simple_strtoul(argv[3], &endp, 16);
+               if (*argv[3] == 0 || *endp != 0)
+                       goto usage;
+       }
+       if (!spi_cs_is_valid(bus, cs)) {
+               printf("Invalid SPI bus %u cs %u\n", bus, cs);
+               return 1;
+       }
+
+       mmc = mmc_spi_init(bus, cs, speed, mode);
+       if (!mmc) {
+               printf("Failed to create MMC Device\n");
+               return 1;
+       }
+       printf("%s: %d at %u:%u hz %u mode %u\n", mmc->name, mmc->block_dev.dev,
+              bus, cs, speed, mode);
+       return 0;
+
+usage:
+       cmd_usage(cmdtp);
+       return 1;
+}
+
+U_BOOT_CMD(
+       mmc_spi,        4,      0,      do_mmc_spi,
+       "mmc_spi setup",
+       "[bus:]cs [hz] [mode]   - setup mmc_spi device"
+);
index 3496f0aa0feea3bf649f3904d8ac52690fbcb3db..9aca3a2bd6c1fb319edc45b54e138c0d16e75942 100644 (file)
@@ -31,6 +31,7 @@ COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o
 COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o
+COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o
 COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
 COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o
index e8bc4b869a2edbdff1496f72dd5c5762c3dd21f2..e8d61c307db3dcaa9a5831b27eb7092624f80ebc 100644 (file)
@@ -117,7 +117,10 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src)
                return 0;
        }
 
-       if (blkcnt > 1) {
+       /* SPI multiblock writes terminate using a special
+        * token, not a STOP_TRANSMISSION request.
+        */
+       if (!mmc_host_is_spi(mmc) && blkcnt > 1) {
                cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
                cmd.cmdarg = 0;
                cmd.resp_type = MMC_RSP_R1b;
@@ -279,7 +282,8 @@ sd_send_op_cond(struct mmc *mmc)
                 * how to manage low voltages SD card is not yet
                 * specified.
                 */
-               cmd.cmdarg = mmc->voltages & 0xff8000;
+               cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 :
+                       (mmc->voltages & 0xff8000);
 
                if (mmc->version == SD_VERSION_2)
                        cmd.cmdarg |= OCR_HCS;
@@ -298,6 +302,18 @@ sd_send_op_cond(struct mmc *mmc)
        if (mmc->version != SD_VERSION_2)
                mmc->version = SD_VERSION_1_0;
 
+       if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
+               cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
+               cmd.resp_type = MMC_RSP_R3;
+               cmd.cmdarg = 0;
+               cmd.flags = 0;
+
+               err = mmc_send_cmd(mmc, &cmd, NULL);
+
+               if (err)
+                       return err;
+       }
+
        mmc->ocr = cmd.response[0];
 
        mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
@@ -318,7 +334,8 @@ int mmc_send_op_cond(struct mmc *mmc)
        do {
                cmd.cmdidx = MMC_CMD_SEND_OP_COND;
                cmd.resp_type = MMC_RSP_R3;
-               cmd.cmdarg = OCR_HCS | mmc->voltages;
+               cmd.cmdarg = OCR_HCS | (mmc_host_is_spi(mmc) ? 0 :
+                                       mmc->voltages);
                cmd.flags = 0;
 
                err = mmc_send_cmd(mmc, &cmd, NULL);
@@ -332,6 +349,18 @@ int mmc_send_op_cond(struct mmc *mmc)
        if (timeout <= 0)
                return UNUSABLE_ERR;
 
+       if (mmc_host_is_spi(mmc)) { /* read OCR for spi */
+               cmd.cmdidx = MMC_CMD_SPI_READ_OCR;
+               cmd.resp_type = MMC_RSP_R3;
+               cmd.cmdarg = 0;
+               cmd.flags = 0;
+
+               err = mmc_send_cmd(mmc, &cmd, NULL);
+
+               if (err)
+                       return err;
+       }
+
        mmc->version = MMC_VERSION_UNKNOWN;
        mmc->ocr = cmd.response[0];
 
@@ -387,6 +416,9 @@ int mmc_change_freq(struct mmc *mmc)
 
        mmc->card_caps = 0;
 
+       if (mmc_host_is_spi(mmc))
+               return 0;
+
        /* Only version 4 supports high-speed */
        if (mmc->version < MMC_VERSION_4)
                return 0;
@@ -460,6 +492,9 @@ int sd_change_freq(struct mmc *mmc)
 
        mmc->card_caps = 0;
 
+       if (mmc_host_is_spi(mmc))
+               return 0;
+
        /* Read the SCR to find out if this card supports higher speeds */
        cmd.cmdidx = MMC_CMD_APP_CMD;
        cmd.resp_type = MMC_RSP_R1;
@@ -610,8 +645,22 @@ int mmc_startup(struct mmc *mmc)
        struct mmc_cmd cmd;
        char ext_csd[512];
 
+#ifdef CONFIG_MMC_SPI_CRC_ON
+       if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
+               cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF;
+               cmd.resp_type = MMC_RSP_R1;
+               cmd.cmdarg = 1;
+               cmd.flags = 0;
+               err = mmc_send_cmd(mmc, &cmd, NULL);
+
+               if (err)
+                       return err;
+       }
+#endif
+
        /* Put the Card in Identify Mode */
-       cmd.cmdidx = MMC_CMD_ALL_SEND_CID;
+       cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID :
+               MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */
        cmd.resp_type = MMC_RSP_R2;
        cmd.cmdarg = 0;
        cmd.flags = 0;
@@ -628,18 +677,20 @@ int mmc_startup(struct mmc *mmc)
         * For SD cards, get the Relatvie Address.
         * This also puts the cards into Standby State
         */
-       cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
-       cmd.cmdarg = mmc->rca << 16;
-       cmd.resp_type = MMC_RSP_R6;
-       cmd.flags = 0;
+       if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
+               cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
+               cmd.cmdarg = mmc->rca << 16;
+               cmd.resp_type = MMC_RSP_R6;
+               cmd.flags = 0;
 
-       err = mmc_send_cmd(mmc, &cmd, NULL);
+               err = mmc_send_cmd(mmc, &cmd, NULL);
 
-       if (err)
-               return err;
+               if (err)
+                       return err;
 
-       if (IS_SD(mmc))
-               mmc->rca = (cmd.response[0] >> 16) & 0xffff;
+               if (IS_SD(mmc))
+                       mmc->rca = (cmd.response[0] >> 16) & 0xffff;
+       }
 
        /* Get the Card-Specific Data */
        cmd.cmdidx = MMC_CMD_SEND_CSD;
@@ -715,14 +766,16 @@ int mmc_startup(struct mmc *mmc)
                mmc->write_bl_len = 512;
 
        /* Select the card, and put it into Transfer Mode */
-       cmd.cmdidx = MMC_CMD_SELECT_CARD;
-       cmd.resp_type = MMC_RSP_R1b;
-       cmd.cmdarg = mmc->rca << 16;
-       cmd.flags = 0;
-       err = mmc_send_cmd(mmc, &cmd, NULL);
+       if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */
+               cmd.cmdidx = MMC_CMD_SELECT_CARD;
+               cmd.resp_type = MMC_RSP_R1b;
+               cmd.cmdarg = mmc->rca << 16;
+               cmd.flags = 0;
+               err = mmc_send_cmd(mmc, &cmd, NULL);
 
-       if (err)
-               return err;
+               if (err)
+                       return err;
+       }
 
        if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) {
                /* check  ext_csd version and capacity */
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
new file mode 100644 (file)
index 0000000..dc7574c
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * generic mmc spi driver
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
+ * Licensed under the GPL-2 or later.
+ */
+#include <common.h>
+#include <malloc.h>
+#include <part.h>
+#include <mmc.h>
+#include <spi.h>
+#include <crc.h>
+#include <linux/crc7.h>
+#include <linux/byteorder/swab.h>
+
+/* MMC/SD in SPI mode reports R1 status always */
+#define R1_SPI_IDLE            (1 << 0)
+#define R1_SPI_ERASE_RESET     (1 << 1)
+#define R1_SPI_ILLEGAL_COMMAND (1 << 2)
+#define R1_SPI_COM_CRC         (1 << 3)
+#define R1_SPI_ERASE_SEQ       (1 << 4)
+#define R1_SPI_ADDRESS         (1 << 5)
+#define R1_SPI_PARAMETER       (1 << 6)
+/* R1 bit 7 is always zero, reuse this bit for error */
+#define R1_SPI_ERROR           (1 << 7)
+
+/* Response tokens used to ack each block written: */
+#define SPI_MMC_RESPONSE_CODE(x)       ((x) & 0x1f)
+#define SPI_RESPONSE_ACCEPTED          ((2 << 1)|1)
+#define SPI_RESPONSE_CRC_ERR           ((5 << 1)|1)
+#define SPI_RESPONSE_WRITE_ERR         ((6 << 1)|1)
+
+/* Read and write blocks start with these tokens and end with crc;
+ * on error, read tokens act like a subset of R2_SPI_* values.
+ */
+#define SPI_TOKEN_SINGLE       0xfe    /* single block r/w, multiblock read */
+#define SPI_TOKEN_MULTI_WRITE  0xfc    /* multiblock write */
+#define SPI_TOKEN_STOP_TRAN    0xfd    /* terminate multiblock write */
+
+/* MMC SPI commands start with a start bit "0" and a transmit bit "1" */
+#define MMC_SPI_CMD(x) (0x40 | (x & 0x3f))
+
+/* bus capability */
+#define MMC_SPI_VOLTAGE (MMC_VDD_32_33 | MMC_VDD_33_34)
+#define MMC_SPI_MIN_CLOCK 400000 /* 400KHz to meet MMC spec */
+
+/* timeout value */
+#define CTOUT 8
+#define RTOUT 3000000 /* 1 sec */
+#define WTOUT 3000000 /* 1 sec */
+
+static uint mmc_spi_sendcmd(struct mmc *mmc, ushort cmdidx, u32 cmdarg)
+{
+       struct spi_slave *spi = mmc->priv;
+       u8 cmdo[7];
+       u8 r1;
+       int i;
+       cmdo[0] = 0xff;
+       cmdo[1] = MMC_SPI_CMD(cmdidx);
+       cmdo[2] = cmdarg >> 24;
+       cmdo[3] = cmdarg >> 16;
+       cmdo[4] = cmdarg >> 8;
+       cmdo[5] = cmdarg;
+       cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01;
+       spi_xfer(spi, sizeof(cmdo) * 8, cmdo, NULL, 0);
+       for (i = 0; i < CTOUT; i++) {
+               spi_xfer(spi, 1 * 8, NULL, &r1, 0);
+               if (i && (r1 & 0x80) == 0) /* r1 response */
+                       break;
+       }
+       debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1);
+       return r1;
+}
+
+static uint mmc_spi_readdata(struct mmc *mmc, void *xbuf,
+                               u32 bcnt, u32 bsize)
+{
+       struct spi_slave *spi = mmc->priv;
+       u8 *buf = xbuf;
+       u8 r1;
+       u16 crc;
+       int i;
+       while (bcnt--) {
+               for (i = 0; i < RTOUT; i++) {
+                       spi_xfer(spi, 1 * 8, NULL, &r1, 0);
+                       if (r1 != 0xff) /* data token */
+                               break;
+               }
+               debug("%s:tok%d %x\n", __func__, i, r1);
+               if (r1 == SPI_TOKEN_SINGLE) {
+                       spi_xfer(spi, bsize * 8, NULL, buf, 0);
+                       spi_xfer(spi, 2 * 8, NULL, &crc, 0);
+#ifdef CONFIG_MMC_SPI_CRC_ON
+                       if (swab16(cyg_crc16(buf, bsize)) != crc) {
+                               debug("%s: CRC error\n", mmc->name);
+                               r1 = R1_SPI_COM_CRC;
+                               break;
+                       }
+#endif
+                       r1 = 0;
+               } else {
+                       r1 = R1_SPI_ERROR;
+                       break;
+               }
+               buf += bsize;
+       }
+       return r1;
+}
+
+static uint mmc_spi_writedata(struct mmc *mmc, const void *xbuf,
+                             u32 bcnt, u32 bsize, int multi)
+{
+       struct spi_slave *spi = mmc->priv;
+       const u8 *buf = xbuf;
+       u8 r1;
+       u16 crc;
+       u8 tok[2];
+       int i;
+       tok[0] = 0xff;
+       tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE;
+       while (bcnt--) {
+#ifdef CONFIG_MMC_SPI_CRC_ON
+               crc = swab16(cyg_crc16((u8 *)buf, bsize));
+#endif
+               spi_xfer(spi, 2 * 8, tok, NULL, 0);
+               spi_xfer(spi, bsize * 8, buf, NULL, 0);
+               spi_xfer(spi, 2 * 8, &crc, NULL, 0);
+               for (i = 0; i < CTOUT; i++) {
+                       spi_xfer(spi, 1 * 8, NULL, &r1, 0);
+                       if ((r1 & 0x10) == 0) /* response token */
+                               break;
+               }
+               debug("%s:tok%d %x\n", __func__, i, r1);
+               if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) {
+                       for (i = 0; i < WTOUT; i++) { /* wait busy */
+                               spi_xfer(spi, 1 * 8, NULL, &r1, 0);
+                               if (i && r1 == 0xff) {
+                                       r1 = 0;
+                                       break;
+                               }
+                       }
+                       if (i == WTOUT) {
+                               debug("%s:wtout %x\n", __func__, r1);
+                               r1 = R1_SPI_ERROR;
+                               break;
+                       }
+               } else {
+                       debug("%s: err %x\n", __func__, r1);
+                       r1 = R1_SPI_COM_CRC;
+                       break;
+               }
+               buf += bsize;
+       }
+       if (multi && bcnt == -1) { /* stop multi write */
+               tok[1] = SPI_TOKEN_STOP_TRAN;
+               spi_xfer(spi, 2 * 8, tok, NULL, 0);
+               for (i = 0; i < WTOUT; i++) { /* wait busy */
+                       spi_xfer(spi, 1 * 8, NULL, &r1, 0);
+                       if (i && r1 == 0xff) {
+                               r1 = 0;
+                               break;
+                       }
+               }
+               if (i == WTOUT) {
+                       debug("%s:wstop %x\n", __func__, r1);
+                       r1 = R1_SPI_ERROR;
+               }
+       }
+       return r1;
+}
+
+static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
+               struct mmc_data *data)
+{
+       struct spi_slave *spi = mmc->priv;
+       u8 r1;
+       int i;
+       int ret = 0;
+       debug("%s:cmd%d %x %x %x\n", __func__,
+             cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags);
+       spi_claim_bus(spi);
+       spi_cs_activate(spi);
+       r1 = mmc_spi_sendcmd(mmc, cmd->cmdidx, cmd->cmdarg);
+       if (r1 == 0xff) { /* no response */
+               ret = NO_CARD_ERR;
+               goto done;
+       } else if (r1 & R1_SPI_COM_CRC) {
+               ret = COMM_ERR;
+               goto done;
+       } else if (r1 & ~R1_SPI_IDLE) { /* other errors */
+               ret = TIMEOUT;
+               goto done;
+       } else if (cmd->resp_type == MMC_RSP_R2) {
+               r1 = mmc_spi_readdata(mmc, cmd->response, 1, 16);
+               for (i = 0; i < 4; i++)
+                       cmd->response[i] = swab32(cmd->response[i]);
+               debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1],
+                     cmd->response[2], cmd->response[3]);
+       } else if (!data) {
+               switch (cmd->cmdidx) {
+               case SD_CMD_APP_SEND_OP_COND:
+               case MMC_CMD_SEND_OP_COND:
+                       cmd->response[0] = (r1 & R1_SPI_IDLE) ? 0 : OCR_BUSY;
+                       break;
+               case SD_CMD_SEND_IF_COND:
+               case MMC_CMD_SPI_READ_OCR:
+                       spi_xfer(spi, 4 * 8, NULL, cmd->response, 0);
+                       cmd->response[0] = swab32(cmd->response[0]);
+                       debug("r32 %x\n", cmd->response[0]);
+                       break;
+               }
+       } else {
+               debug("%s:data %x %x %x\n", __func__,
+                     data->flags, data->blocks, data->blocksize);
+               if (data->flags == MMC_DATA_READ)
+                       r1 = mmc_spi_readdata(mmc, data->dest,
+                               data->blocks, data->blocksize);
+               else if  (data->flags == MMC_DATA_WRITE)
+                       r1 = mmc_spi_writedata(mmc, data->src,
+                               data->blocks, data->blocksize,
+                               (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK));
+               if (r1 & R1_SPI_COM_CRC)
+                       ret = COMM_ERR;
+               else if (r1) /* other errors */
+                       ret = TIMEOUT;
+       }
+done:
+       spi_cs_deactivate(spi);
+       spi_release_bus(spi);
+       return ret;
+}
+
+static void mmc_spi_set_ios(struct mmc *mmc)
+{
+       struct spi_slave *spi = mmc->priv;
+       debug("%s: clock %u\n", __func__, mmc->clock);
+       if (mmc->clock)
+               spi_set_speed(spi, mmc->clock);
+}
+
+static int mmc_spi_init_p(struct mmc *mmc)
+{
+       struct spi_slave *spi = mmc->priv;
+       mmc->clock = 0;
+       spi_set_speed(spi, MMC_SPI_MIN_CLOCK);
+       spi_claim_bus(spi);
+       /* cs deactivated for 100+ clock */
+       spi_xfer(spi, 18 * 8, NULL, NULL, 0);
+       spi_release_bus(spi);
+       return 0;
+}
+
+struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
+{
+       struct mmc *mmc;
+
+       mmc = malloc(sizeof(*mmc));
+       if (!mmc)
+               return NULL;
+       memset(mmc, 0, sizeof(*mmc));
+       mmc->priv = spi_setup_slave(bus, cs, speed, mode);
+       if (!mmc->priv) {
+               free(mmc);
+               return NULL;
+       }
+       sprintf(mmc->name, "MMC_SPI");
+       mmc->send_cmd = mmc_spi_request;
+       mmc->set_ios = mmc_spi_set_ios;
+       mmc->init = mmc_spi_init_p;
+       mmc->host_caps = MMC_MODE_SPI;
+
+       mmc->voltages = MMC_SPI_VOLTAGE;
+       mmc->f_max = speed;
+       mmc->f_min = MMC_SPI_MIN_CLOCK;
+       mmc->block_dev.part_type = PART_TYPE_DOS;
+
+       mmc_register(mmc);
+
+       return mmc;
+}
index fcd0fd1de572c2cf7a54fc0ad1a5414c38c0c549..2cf489f4b9ebcc54a040a275e6b2f2ca2232458c 100644 (file)
@@ -44,6 +44,7 @@
 #define MMC_MODE_HS_52MHz      0x010
 #define MMC_MODE_4BIT          0x100
 #define MMC_MODE_8BIT          0x200
+#define MMC_MODE_SPI           0x400
 
 #define SD_DATA_4BIT   0x00040000
 
@@ -75,6 +76,8 @@
 #define MMC_CMD_WRITE_SINGLE_BLOCK     24
 #define MMC_CMD_WRITE_MULTIPLE_BLOCK   25
 #define MMC_CMD_APP_CMD                        55
+#define MMC_CMD_SPI_READ_OCR           58
+#define MMC_CMD_SPI_CRC_ON_OFF         59
 
 #define SD_CMD_SEND_RELATIVE_ADDR      3
 #define SD_CMD_SWITCH_FUNC             6
@@ -291,6 +294,8 @@ int board_mmc_getcd(u8 *cd, struct mmc *mmc);
 
 #ifdef CONFIG_GENERIC_MMC
 int atmel_mci_init(void *regs);
+#define mmc_host_is_spi(mmc)   ((mmc)->host_caps & MMC_MODE_SPI)
+struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
 #else
 int mmc_legacy_init(int verbose);
 #endif