[U-Boot] [PATCH] mmc: add generic mmc spi driver

Thomas Chou
2010-04-23T02:53:34Z

This patch supports mmc/sd card with spi interface.
I have tested with sd and mmc cards. But there is still ocr issue
with SDHC.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
drivers/mmc/Makefile | 1 +
drivers/mmc/mmc_spi.c | 252 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 253 insertions(+), 0 deletions(-)
create mode 100644 drivers/mmc/mmc_spi.c

diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 1b8f5bd..d03eb47 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -31,6 +31,7 @@ LIB := $(obj)libmmc.a
COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
+COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o
COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
new file mode 100644
index 0000000..76c5977
--- /dev/null
+++ b/drivers/mmc/mmc_spi.c
@@ -0,0 +1,252 @@
+/*
+ * 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 <asm/errno.h>
+
+#define CTOUT 0x10
+#define RTOUT 0x10000
+#define WTOUT 0x10000
+
+static uint mmc_spi_sendcmd(struct mmc *mmc, u8 cmdidx, u32 cmdarg)
+{
+ u8 cmdo[6];
+ u8 r1;
+ int i;
+ cmdo[0] = 0x40 + cmdidx;
+ cmdo[1] = cmdarg >> 24;
+ cmdo[2] = cmdarg >> 16;
+ cmdo[3] = cmdarg >> 8;
+ cmdo[4] = cmdarg;
+ cmdo[5] = 0x95; /* crc valid only for cmd00 */
+ spi_xfer(mmc->priv, 6 * 8, cmdo, NULL, SPI_XFER_BEGIN);
+ for (i = 0; i < CTOUT; i++) {
+ spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+ if ((r1 & 0x80) == 0)
+ break;
+ }
+ debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1);
+ return r1;
+}
+
+static uint mmc_spi_readdata(struct mmc *mmc, char *buf,
+ u32 bcnt, u32 bsize)
+{
+ u8 r1;
+ u8 crc[2];
+ int i;
+ while (bcnt--) {
+ for (i = 0; i < RTOUT; i++) {
+ spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+ if (r1 != 0xff)
+ break;
+ }
+ debug("%s:tok%d %x\n", __func__, i, r1);
+ if (r1 == 0xfe) {
+ spi_xfer(mmc->priv, bsize * 8, NULL, buf, 0);
+ buf += bsize;
+ spi_xfer(mmc->priv, 2 * 8, NULL, crc, 0);
+ r1 = 0;
+ } else
+ break;
+ }
+ return r1;
+}
+
+static uint mmc_spi_writedata(struct mmc *mmc, const char *buf,
+ u32 bcnt, u32 bsize)
+{
+ u8 r1;
+ u8 tok[2] = { 0xff, 0xfe };
+ u8 crc[2];
+ int i;
+ while (bcnt--) {
+ spi_xfer(mmc->priv, 2 * 8, tok, NULL, 0);
+ spi_xfer(mmc->priv, bsize * 8, buf, NULL, 0);
+ buf += bsize;
+ spi_xfer(mmc->priv, 2 * 8, crc, NULL, 0);
+ spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+ if (r1 == 0x05) {
+ for (i = 0; i < WTOUT; i++) {
+ spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+ if (r1 == 0xff) {
+ r1 = 0;
+ break;
+ }
+ }
+ if (i == WTOUT) {
+ debug("%s:wtout %x\n", __func__, r1);
+ r1 = 0x04;
+ break;
+ }
+ } else
+ break;
+ }
+ return r1;
+}
+
+static uint mmc_spi_writeblock(struct mmc *mmc, const char *buf,
+ u32 bcnt, u32 bsize)
+{
+ u8 r1;
+ u8 tok[2] = { 0xff, 0xfc };
+ u8 stop[2] = { 0xff, 0xfd };
+ u8 crc[2];
+ int i;
+ while (bcnt--) {
+ spi_xfer(mmc->priv, 2 * 8, tok, NULL, 0);
+ spi_xfer(mmc->priv, bsize * 8, buf, NULL, 0);
+ buf += bsize;
+ spi_xfer(mmc->priv, 2 * 8, crc, NULL, 0);
+ spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+ if (r1 == 0x05) {
+ for (i = 0; i < WTOUT; i++) {
+ spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+ if (r1 == 0xff) {
+ r1 = 0;
+ break;
+ }
+ }
+ if (i == WTOUT) {
+ debug("%s:wtout %x\n", __func__, r1);
+ r1 = 0x04;
+ break;
+ }
+ }
+ }
+ if (bcnt == 0 && r1 == 0) {
+ spi_xfer(mmc->priv, 2 * 8, stop, NULL, 0);
+ for (i = 0; i < WTOUT; i++) {
+ spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+ if (r1 == 0xff) {
+ r1 = 0;
+ break;
+ }
+ }
+ if (i == WTOUT) {
+ debug("%s:wtout %x\n", __func__, r1);
+ r1 = 0x04;
+ }
+ }
+ return r1;
+}
+
+static inline uint rswab(u8 *d)
+{
+ return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ u8 r1;
+ u8 resp[16];
+ int i;
+ debug("%s:cmd%d %x %x %x\n", __func__,
+ cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags);
+ if (cmd->cmdidx == MMC_CMD_ALL_SEND_CID)
+ cmd->cmdidx = MMC_CMD_SEND_CID;
+ r1 = mmc_spi_sendcmd(mmc, cmd->cmdidx, cmd->cmdarg);
+ if (cmd->cmdidx == 55 && (r1 & 0x04))
+ return TIMEOUT;
+ if ((cmd->cmdidx == MMC_CMD_SEND_OP_COND ||
+ cmd->cmdidx == SD_CMD_APP_SEND_OP_COND) &&
+ cmd->resp_type == MMC_RSP_R3) {
+ r1 = mmc_spi_sendcmd(mmc, 58, 0);
+ spi_xfer(mmc->priv, 4 * 8, NULL, resp, 0);
+ cmd->response[0] = rswab(resp);
+ debug("ocr %x\n", cmd->response[0]);
+ } else if (cmd->resp_type == MMC_RSP_R2) {
+ r1 = mmc_spi_readdata(mmc, resp, 1, 16);
+ for (i = 0; i < 4; i++)
+ cmd->response[i] = rswab(resp + i * 4);
+ debug("r136 %x %x %x %x\n", cmd->response[0], cmd->response[1],
+ cmd->response[2], cmd->response[3]);
+ } else if (cmd->resp_type == MMC_RSP_R1) {
+ cmd->response[0] = 0;
+ if (r1 & 0x7e)
+ cmd->response[0] |= (1 << 19);
+ if (r1 & 0x40)
+ cmd->response[0] |= (1 << 31);
+ if (r1 & 0x20)
+ cmd->response[0] |= (1 << 30);
+ if (r1 & 0x10)
+ cmd->response[0] |= (1 << 28);
+ if (r1 & 0x08)
+ cmd->response[0] |= (1 << 23);
+ if (r1 & 0x04)
+ cmd->response[0] |= (1 << 22);
+ if (r1 & 0x02)
+ cmd->response[0] |= (1 << 13);
+ }
+ if (data) {
+ 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) {
+ if (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)
+ r1 = mmc_spi_writeblock(mmc, data->src,
+ data->blocks, data->blocksize);
+ else
+ r1 = mmc_spi_writedata(mmc, data->src,
+ data->blocks, data->blocksize);
+ }
+ }
+ return 0;
+}
+
+static void mmc_spi_set_ios(struct mmc *mmc)
+{
+ debug("%s:\n", __func__);
+}
+
+static int mmc_spi_init_p(struct mmc *mmc)
+{
+ u8 d = 0xff;
+ int i;
+ debug("%s:\n", __func__);
+ spi_release_bus(mmc->priv);
+ for (i = 0; i < 16; i++) /* power on initialization */
+ spi_xfer(mmc->priv, 1 * 8, &d, NULL, 0);
+ spi_claim_bus(mmc->priv);
+ return 0;
+}
+
+int mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
+{
+ struct mmc *mmc = NULL;
+
+ mmc = malloc(sizeof(struct mmc));
+ if (!mmc)
+ return -ENOMEM;
+ sprintf(mmc->name, "mmc_spi");
+ mmc->priv = spi_setup_slave(bus, cs, speed, mode);
+ if (!mmc->priv) {
+ free(mmc);
+ return -ENOMEM;
+ }
+ mmc->send_cmd = mmc_spi_request;
+ mmc->set_ios = mmc_spi_set_ios;
+ mmc->init = mmc_spi_init_p;
+ mmc->host_caps = 0;
+
+ mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->f_max = speed;
+ mmc->f_min = mmc->f_max >> 9;
+ mmc->block_dev.part_type = PART_TYPE_DOS;
+
+ mmc_register(mmc);
+
+ return 0;
+}
--
1.6.6.1