[U-Boot] [PATCH 06/10] Add MMC Framework

Andy Fleming afleming at freescale.com
Thu Oct 30 23:35:35 CET 2008


Here's a new framework (based roughly off the linux one) for managing
MMC controllers.  It handles all of the standard SD/MMC transactions,
leaving the host drivers to implement only what is necessary to
deal with their specific hardware.

This also hooks the infrastructure into the PowerPC board code
(similar to how the ethernet infrastructure now hooks in)

Some of this code was contributed by Dave Liu <daveliu at freescale.com>

Signed-off-by: Andy Fleming <afleming at freescale.com>
---
 common/cmd_mmc.c     |  122 +++++++
 drivers/mmc/Makefile |    1 +
 drivers/mmc/mmc.c    |  926 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/mmc.h        |  174 +++++++++-
 lib_ppc/board.c      |    9 +
 5 files changed, 1223 insertions(+), 9 deletions(-)
 create mode 100644 drivers/mmc/mmc.c

diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
index 21873d6..f27d7ef 100644
--- a/common/cmd_mmc.c
+++ b/common/cmd_mmc.c
@@ -25,6 +25,7 @@
 #include <command.h>
 #include <mmc.h>
 
+#ifndef CONFIG_GENERIC_MMC
 int do_mmc (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
 {
 	if (mmc_legacy_init (1) != 0) {
@@ -39,3 +40,124 @@ U_BOOT_CMD(
 	"mmcinit - init mmc card\n",
 	NULL
 );
+#endif /* !CONFIG_GENERIC_MMC */
+
+static void print_mmcinfo(struct mmc *mmc)
+{
+	printf("Device: %s\n", mmc->name);
+	printf("Manufacturer ID: %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: %lld\n", mmc->capacity);
+
+	printf("Bus Width: %d-bit\n", mmc->bus_width);
+}
+
+int do_mmcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	struct mmc *mmc;
+	int dev_num;
+
+	if (argc < 2)
+		dev_num = 0;
+	else
+		dev_num = simple_strtoul(argv[1], NULL, 0);
+
+	mmc = find_mmc_device(dev_num);
+
+	if (mmc) {
+		mmc_init(mmc);
+
+		print_mmcinfo(mmc);
+	}
+
+	return 0;
+}
+
+U_BOOT_CMD(mmcinfo, 2, 0, do_mmcinfo, "mmcinfo <dev num>-- display MMC info\n",
+		NULL);
+
+int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	int rc = 0;
+
+	switch (argc) {
+	case 0:
+	case 1:
+	case 3:
+	case 4:
+		printf("Usage:\n%s\n", cmdtp->usage);
+		return 1;
+
+	case 2:
+		if (!strcmp(argv[1], "list")) {
+			print_mmc_devices('\n');
+			return 0;
+		}
+		return 1;
+	default: /* at least 5 args */
+		if (strcmp(argv[1], "read") == 0) {
+			int dev = simple_strtoul(argv[2], NULL, 10);
+			void *addr = (void *)simple_strtoul(argv[3], NULL, 16);
+			u32 cnt = simple_strtoul(argv[5], NULL, 16);
+			u32 n;
+			u32 blk = simple_strtoul(argv[4], NULL, 16);
+			struct mmc *mmc = find_mmc_device(dev);
+
+			printf("\nMMC read: dev # %d, block # %d, count %d ... ",
+				dev, blk, cnt);
+
+			mmc_init(mmc);
+
+			n = mmc->block_dev.block_read(dev, blk, cnt, addr);
+
+			/* flush cache after read */
+			flush_cache((ulong)addr, cnt * 512); //FIXME
+
+			printf("%d blocks read: %s\n",
+				n, (n==cnt) ? "OK" : "ERROR");
+			return (n == cnt) ? 0 : 1;
+		} else if (strcmp(argv[1], "write") == 0) {
+			int dev = simple_strtoul(argv[2], NULL, 10);
+			void *addr = (void *)simple_strtoul(argv[3], NULL, 16);
+			u32 cnt = simple_strtoul(argv[5], NULL, 16);
+			u32 n;
+			struct mmc *mmc = find_mmc_device(dev);
+
+			int blk = simple_strtoul(argv[4], NULL, 16);
+
+			printf("\nMMC write: dev # %d, block # %d, count %d ... ",
+				dev, blk, cnt);
+
+			mmc_init(mmc);
+
+			n = mmc->block_dev.block_write(dev, blk, cnt, addr);
+
+			printf("%d blocks written: %s\n",
+				n, (n == cnt) ? "OK" : "ERROR");
+			return (n == cnt) ? 0 : 1;
+		} else {
+			printf("Usage:\n%s\n", cmdtp->usage);
+			rc = 1;
+		}
+
+		return rc;
+	}
+}
+
+U_BOOT_CMD(
+	mmc, 6, 1, do_mmcops,
+	"mmc	- MMC sub system\n",
+	"mmc read <device num> addr blk# cnt\n"
+	"mmc write <device num> addr blk# cnt\n"
+	"mmc list - lists available devices\n");
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 3dc031b..d5a969c 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -25,6 +25,7 @@ include $(TOPDIR)/config.mk
 
 LIB	:= $(obj)libmmc.a
 
+COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
 
 COBJS	:= $(COBJS-y)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
new file mode 100644
index 0000000..cd4eb77
--- /dev/null
+++ b/drivers/mmc/mmc.c
@@ -0,0 +1,926 @@
+/*
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Based vaguely on the Linux code
+ *
+ * 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 <mmc.h>
+
+static struct mmc *mmc_devices;
+static int cur_dev_num = -1;
+
+int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
+{
+	return mmc->send_cmd(mmc, cmd, data);
+}
+
+int mmc_set_blocklen(struct mmc *mmc, int len)
+{
+	struct mmc_cmd cmd;
+
+	cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
+	cmd.resp_type = MMC_CMD_R1;
+	cmd.cmdarg = len;
+	cmd.flags = 0;
+
+	return mmc_send_cmd(mmc, &cmd, NULL);
+}
+
+struct mmc *find_mmc_device(int dev_num)
+{
+	struct mmc *m = mmc_devices;
+
+	if (!m) {
+		printf("No MMC devices registered!\n");
+		return NULL;
+	}
+
+	do {
+		if (m->block_dev.dev == dev_num)
+			return m;
+
+		m = m->next;
+	} while (m->next != mmc_devices);
+
+	printf("MMC Device %d not found\n", dev_num);
+
+	return NULL;
+}
+
+static ulong
+mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src)
+{
+	struct mmc_cmd cmd;
+	struct mmc_data data;
+	int err;
+	int stoperr = 0;
+	struct mmc *mmc = find_mmc_device(dev_num);
+	int blklen;
+
+	if (!mmc)
+		return -1;
+
+	blklen = mmc->write_bl_len;
+
+	err = mmc_set_blocklen(mmc, mmc->write_bl_len);
+
+	if (err) {
+		printf("set write bl len failed\n\r");
+		return err;
+	}
+
+	if (blkcnt > 1)
+		cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+	else
+		cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
+
+	if (mmc->high_capacity)
+		cmd.cmdarg = start;
+	else
+		cmd.cmdarg = start * blklen;
+
+	cmd.resp_type = MMC_CMD_R1;
+	cmd.flags = 0;
+
+	data.src = src;
+	data.blocks = blkcnt;
+	data.blocksize = blklen;
+	data.flags = MMC_DATA_WRITE;
+
+	err = mmc_send_cmd(mmc, &cmd, &data);
+
+	if (err) {
+		printf("mmc write failed\n\r");
+		return err;
+	}
+
+	if (blkcnt > 1) {
+		cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+		cmd.cmdarg = 0;
+		cmd.resp_type = MMC_CMD_R1b;
+		cmd.flags = 0;
+		stoperr = mmc_send_cmd(mmc, &cmd, NULL);
+	}
+
+	return blkcnt;
+}
+
+int mmc_read_block(struct mmc *mmc, void *dst, uint blocknum)
+{
+	struct mmc_cmd cmd;
+	struct mmc_data data;
+
+	cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
+
+	if (mmc->high_capacity)
+		cmd.cmdarg = blocknum;
+	else
+		cmd.cmdarg = blocknum * mmc->read_bl_len;
+
+	cmd.resp_type = MMC_CMD_R1;
+	cmd.flags = 0;
+
+	data.dest = dst;
+	data.blocks = 1;
+	data.blocksize = mmc->read_bl_len;
+	data.flags = MMC_DATA_READ;
+
+	return mmc_send_cmd(mmc, &cmd, &data);
+}
+
+int mmc_read(struct mmc *mmc, ulong src, uchar *dst, int size)
+{
+	char *buffer;
+	int i;
+	int blklen = mmc->read_bl_len;
+	int startblock = src / blklen;
+	int endblock = (src + size - 1) / blklen;
+	int err = 0;
+
+	/* Make a buffer big enough to hold all the blocks we might read */
+	buffer = malloc(blklen);
+
+	if (!buffer) {
+		printf("Could not allocate buffer for MMC read!\n");
+		return -1;
+	}
+
+	/* We always do full block reads from the card */
+	err = mmc_set_blocklen(mmc, mmc->read_bl_len);
+
+	if (err)
+		return err;
+
+	for (i = startblock; i <= endblock; i++) {
+		int segment_size;
+		int offset;
+
+		err = mmc_read_block(mmc, buffer, i);
+
+		if (err)
+			goto free_buffer;
+
+		/*
+		 * The first block may not be aligned, so we
+		 * copy from the desired point in the block
+		 */
+		offset = (src & (blklen - 1));
+		segment_size = MIN(blklen - offset, size);
+
+		memcpy(dst, buffer + offset, segment_size);
+
+		dst += segment_size;
+		src += segment_size;
+		size -= segment_size;
+	}
+
+free_buffer:
+	free(buffer);
+
+	return err;
+}
+
+static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
+{
+	int err;
+	ulong src;
+	struct mmc *mmc = find_mmc_device(dev_num);
+
+	if (!mmc)
+		return 0;
+
+	src = start * mmc->read_bl_len;
+
+	err = mmc_read(mmc, src, dst, blkcnt * mmc->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 = MMC_CMD_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 = MMC_CMD_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_CMD_APP_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_CMD_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 = MMC_CMD_SEND_EXT_CSD;
+	cmd.resp_type = MMC_CMD_R1;
+	cmd.cmdarg = 0;
+	cmd.flags = 0;
+
+	data.dest = 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->caps = 0;
+
+	/* Only version 4 supports high-speed */
+	if (mmc->version < MMC_VERSION_4)
+		return 0;
+
+	mmc->caps |= 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_CMD_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->caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+	else
+		mmc->caps |= 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->caps = 0;
+
+	/* Read the SCR to find out if this card supports higher speeds */
+	cmd.cmdidx = MMC_CMD_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 = SD_CMD_APP_SEND_SCR;
+	cmd.resp_type = MMC_CMD_R1;
+	cmd.cmdarg = 0;
+	cmd.flags = 0;
+
+	timeout = 3;
+
+retry_scr:
+	data.dest = (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 = SD_CMD_SWITCH_FUNC;
+		cmd.resp_type = MMC_CMD_R1;
+		cmd.cmdarg = 0xfffff1;
+		cmd.flags = 0;
+
+		data.dest = (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->caps |= MMC_MODE_4BIT;
+
+	/* If high-speed isn't supported, we return */
+	if (!(switch_status[3] & SD_HIGHSPEED_SUPPORTED))
+		return 0;
+
+	cmd.cmdidx = SD_CMD_SWITCH_FUNC;
+	cmd.resp_type = MMC_CMD_R1;
+	cmd.cmdarg = 0x80fffff1;
+	cmd.flags = 0;
+
+	data.dest = (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->caps |= 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,
+};
+
+void mmc_set_ios(struct mmc *mmc)
+{
+	mmc->set_ios(mmc);
+}
+
+void mmc_set_clock(struct mmc *mmc, uint clock)
+{
+	mmc->clock = clock;
+
+	mmc_set_ios(mmc);
+}
+
+void mmc_set_bus_width(struct mmc *mmc, uint width)
+{
+	mmc->bus_width = width;
+
+	mmc_set_ios(mmc);
+}
+
+int mmc_startup(struct mmc *mmc)
+{
+	int err;
+	uint mult, freq;
+	u64 cmult, csize;
+	struct mmc_cmd cmd;
+
+	/* Put the Card in Identify Mode */
+	cmd.cmdidx = MMC_CMD_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 = SD_CMD_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 = MMC_CMD_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);
+
+	if (mmc->high_capacity) {
+		csize = (mmc->csd[1] & 0x3f) << 16
+			| (mmc->csd[2] & 0xffff0000) >> 16;
+		cmult = 8;
+	} else {
+		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 = MMC_CMD_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->caps & MMC_MODE_4BIT) {
+			cmd.cmdidx = MMC_CMD_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 = SD_CMD_APP_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;
+
+			mmc_set_bus_width(mmc, 4);
+		}
+
+		if (mmc->caps & MMC_MODE_HS)
+			mmc_set_clock(mmc, 12000000);
+		else
+			mmc_set_clock(mmc, 12000000);
+	} else {
+		if (mmc->caps & MMC_MODE_4BIT) {
+			/* Set the card to use 4 bit*/
+			cmd.cmdidx = MMC_CMD_SWITCH;
+			cmd.resp_type = MMC_CMD_R1b;
+			cmd.cmdarg = 0x1b70100;
+			cmd.flags = 0;
+
+			err = mmc_send_cmd(mmc, &cmd, NULL);
+
+			if (err)
+				return err;
+
+			mmc_set_bus_width(mmc, 4);
+		} else if (mmc->caps & MMC_MODE_8BIT) {
+			/* Set the card to use 8 bit*/
+			cmd.cmdidx = MMC_CMD_SWITCH;
+			cmd.resp_type = MMC_CMD_R1b;
+			cmd.cmdarg = 0x1b70200;
+			cmd.flags = 0;
+
+			err = mmc_send_cmd(mmc, &cmd, NULL);
+
+			if (err)
+				return err;
+
+			mmc_set_bus_width(mmc, 8);
+		}
+
+		if (mmc->caps & MMC_MODE_HS) {
+			if (mmc->caps & MMC_MODE_HS_52MHz)
+				mmc_set_clock(mmc, 52000000);
+			else
+				mmc_set_clock(mmc, 26000000);
+		} else
+			mmc_set_clock(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 = cur_dev_num++;
+	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;
+	mmc->block_dev.block_write = mmc_bwrite;
+
+	init_part(&mmc->block_dev);
+
+	return 0;
+}
+
+int mmc_send_if_cond(struct mmc *mmc)
+{
+	struct mmc_cmd cmd;
+	int err;
+
+	cmd.cmdidx = SD_CMD_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_register(struct mmc *mmc)
+{
+	struct mmc *m;
+
+	if (!mmc_devices)
+		mmc_devices = mmc;
+	else {
+		for (m = mmc_devices; m->next != mmc_devices; m=m->next);
+		m->next = mmc;
+	}
+
+	mmc->next = mmc_devices;
+
+	return 0;
+}
+
+block_dev_desc_t *mmc_get_dev(int dev)
+{
+	struct mmc *mmc = find_mmc_device(dev);
+
+	return &mmc->block_dev;
+}
+
+int mmc_init(struct mmc *mmc)
+{
+	int err;
+
+	err = mmc->init(mmc);
+
+	if (err)
+		return 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)
+		mmc->initialized = 1;
+
+	return err;
+}
+
+/*
+ * CPU and board-specific MMC initializations.  Aliased function
+ * signals caller to move on
+ */
+static int __def_mmc_init(bd_t *bis)
+{
+	return -1;
+}
+
+int cpu_mmc_init(bd_t *bis) __attribute((weak, alias("__def_mmc_init")));
+int board_mmc_init(bd_t *bis) __attribute((weak, alias("__def_mmc_init")));
+
+void print_mmc_devices(char separator)
+{
+	struct mmc *m = mmc_devices;
+
+	do {
+		if (m != mmc_devices)
+			printf("%c ", separator);
+
+		printf("%s: %d", m->name, m->block_dev.dev);
+		m = m->next;
+	} while (m->next != mmc_devices);
+
+	printf("\n");
+}
+
+int mmc_initialize(bd_t *bis)
+{
+	mmc_devices = NULL;
+	cur_dev_num = 0;
+
+	if (board_mmc_init(bis) < 0)
+		cpu_mmc_init(bis);
+
+	if (!mmc_devices)
+		puts ("No MMC devices found.\n");
+	else
+		print_mmc_devices(',');
+
+	return 0;
+}
diff --git a/include/mmc.h b/include/mmc.h
index 7a03ed2..aed449c 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -1,6 +1,8 @@
 /*
- * (C) Copyright 2000-2003
- * Wolfgang Denk, DENX Software Engineering, wd at denx.de.
+ * Copyright 2008, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Based (loosely) on the Linux code
  *
  * See file CREDITS for list of people who contributed to this
  * project.
@@ -24,32 +26,186 @@
 #ifndef _MMC_H_
 #define _MMC_H_
 
-/* MMC command numbers */
+#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 MMC_CMD_GO_IDLE_STATE		0
 #define MMC_CMD_SEND_OP_COND		1
 #define MMC_CMD_ALL_SEND_CID		2
 #define MMC_CMD_SET_RELATIVE_ADDR	3
 #define MMC_CMD_SET_DSR			4
+#define MMC_CMD_SWITCH			6
 #define MMC_CMD_SELECT_CARD		7
+#define MMC_CMD_SEND_EXT_CSD		8
 #define MMC_CMD_SEND_CSD		9
 #define MMC_CMD_SEND_CID		10
+#define MMC_CMD_STOP_TRANSMISSION	12
 #define MMC_CMD_SEND_STATUS		13
 #define MMC_CMD_SET_BLOCKLEN		16
 #define MMC_CMD_READ_SINGLE_BLOCK	17
 #define MMC_CMD_READ_MULTIPLE_BLOCK	18
-#define MMC_CMD_WRITE_BLOCK		24
+#define MMC_CMD_WRITE_SINGLE_BLOCK	24
+#define MMC_CMD_WRITE_MULTIPLE_BLOCK	25
 #define MMC_CMD_APP_CMD			55
 
-/* SD Card command numbers */
 #define SD_CMD_SEND_RELATIVE_ADDR	3
-#define SD_CMD_SWITCH			6
+#define SD_CMD_SWITCH_FUNC		6
 #define SD_CMD_SEND_IF_COND		8
 
 #define SD_CMD_APP_SET_BUS_WIDTH	6
 #define SD_CMD_APP_SEND_OP_COND		41
+#define SD_CMD_APP_SEND_SCR		51
 
-int mmc_legacy_init(int verbose);
-int mmc_read(ulong src, uchar *dst, int size);
-int mmc_write(uchar *src, ulong dst, int size);
+#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
 
+#define R1_ILLEGAL_COMMAND		(1 << 22)
+#define R1_APP_CMD			(1 << 5)
+
+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
+};
+
+struct mmc_cid {
+	unsigned long psn;
+	unsigned short oid;
+	unsigned char mid;
+	unsigned char prv;
+	unsigned char mdt;
+	char pnm[7];
+};
+
+struct mmc_csd
+{
+	u8	csd_structure:2,
+		spec_vers:4,
+		rsvd1:2;
+	u8	taac;
+	u8	nsac;
+	u8	tran_speed;
+	u16	ccc:12,
+		read_bl_len:4;
+	u64	read_bl_partial:1,
+		write_blk_misalign:1,
+		read_blk_misalign:1,
+		dsr_imp:1,
+		rsvd2:2,
+		c_size:12,
+		vdd_r_curr_min:3,
+		vdd_r_curr_max:3,
+		vdd_w_curr_min:3,
+		vdd_w_curr_max:3,
+		c_size_mult:3,
+		sector_size:5,
+		erase_grp_size:5,
+		wp_grp_size:5,
+		wp_grp_enable:1,
+		default_ecc:2,
+		r2w_factor:3,
+		write_bl_len:4,
+		write_bl_partial:1,
+		rsvd3:5;
+	u8	file_format_grp:1,
+		copy:1,
+		perm_write_protect:1,
+		tmp_write_protect:1,
+		file_format:2,
+		ecc:2;
+	u8	crc:7;
+	u8	one:1;
+};
+
+struct mmc_cmd {
+	ushort cmdidx;
+	int resp_type;
+	uint cmdarg;
+	char response[18];
+	uint flags;
+};
+
+struct mmc_data {
+	char *dest;
+	const char *src; /* src buffers don't get written to */
+	uint flags;
+	int blocks;
+	int blocksize;
+};
+
+struct mmc {
+	char name[32];
+	void *priv;
+	uint initialized;
+	uint version;
+	int high_capacity;
+	uint bus_width;
+	uint clock;
+	uint caps;
+	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;
+	int (*send_cmd)(struct mmc *mmc,
+			struct mmc_cmd *cmd, struct mmc_data *data);
+	void (*set_ios)(struct mmc *mmc);
+	int (*init)(struct mmc *mmc);
+	struct mmc *next;
+};
+
+int mmc_register(struct mmc *mmc);
+int mmc_initialize(bd_t *bis);
+int mmc_init(struct mmc *mmc);
+int mmc_read(struct mmc *mmc, ulong src, uchar *dst, int size);
+struct mmc *find_mmc_device(int dev_num);
+void print_mmc_devices(char separator);
+
+#ifndef CONFIG_GENERIC_MMC
+int mmc_legacy_init(int verbose);
+#endif
 #endif /* _MMC_H_ */
diff --git a/lib_ppc/board.c b/lib_ppc/board.c
index ce07c4e..e304d8c 100644
--- a/lib_ppc/board.c
+++ b/lib_ppc/board.c
@@ -51,6 +51,9 @@
 #include <status_led.h>
 #endif
 #include <net.h>
+#ifdef CONFIG_GENERIC_MMC
+#include <mmc.h>
+#endif
 #include <serial.h>
 #ifdef CONFIG_SYS_ALLOC_DPRAM
 #if !defined(CONFIG_CPM2)
@@ -1083,6 +1086,12 @@ void board_init_r (gd_t *id, ulong dest_addr)
 	scsi_init ();
 #endif
 
+#ifdef CONFIG_GENERIC_MMC
+	WATCHDOG_RESET ();
+	puts ("MMC:  ");
+	mmc_initialize (bd);
+#endif
+
 #if defined(CONFIG_CMD_DOC)
 	WATCHDOG_RESET ();
 	puts ("DOC:   ");
-- 
1.5.4.GIT



More information about the U-Boot mailing list