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

Thomas Chou thomas at wytron.com.tw
Fri Apr 23 04:53:34 CEST 2010


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 at 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 at 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



More information about the U-Boot mailing list