[U-Boot] [PATCH 5/8] Add MMC Framework

Andy Fleming afleming at freescale.com
Fri Jan 30 22:45:41 CET 2009


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     |  132 +++++++
 drivers/mmc/Makefile |    1 +
 drivers/mmc/mmc.c    |  930 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/mmc.h        |  190 ++++++++++-
 lib_ppc/board.c      |    9 +
 5 files changed, 1253 insertions(+), 9 deletions(-)
 create mode 100644 drivers/mmc/mmc.c

diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
index 75a75e5..71f4971 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,134 @@ U_BOOT_CMD(
 	"init mmc card",
 	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 3:
+		if (strcmp(argv[1], "rescan") == 0) {
+			int dev = simple_strtoul(argv[2], NULL, 10);
+			struct mmc *mmc = find_mmc_device(dev);
+
+			mmc_init(mmc);
+
+			return 0;
+		}
+
+	case 0:
+	case 1:
+	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 rescan <device num>\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..96186d9
--- /dev/null
+++ b/drivers/mmc/mmc.c
@@ -0,0 +1,930 @@
+/*
+ * 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 <linux/list.h>
+#include <mmc.h>
+
+static struct list_head 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_RSP_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;
+	struct list_head *entry;
+
+	list_for_each(entry, &mmc_devices) {
+		m = list_entry(entry, struct mmc, link);
+
+		if (m->block_dev.dev == dev_num)
+			return m;
+	}
+
+	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_RSP_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_RSP_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_RSP_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, u64 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;
+	int i;
+	struct mmc *mmc = find_mmc_device(dev_num);
+
+	if (!mmc)
+		return 0;
+
+	/* We always do full block reads from the card */
+	err = mmc_set_blocklen(mmc, mmc->read_bl_len);
+
+	if (err) {
+		return 0;
+	}
+
+	for (i = start; i < start + blkcnt; i++, dst += mmc->read_bl_len) {
+		err = mmc_read_block(mmc, dst, i);
+
+		if (err) {
+			printf("block read failed: %d\n", err);
+			return i - start;
+		}
+	}
+
+	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_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_RSP_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_RSP_R3;
+		cmd.cmdarg = mmc->voltages;
+
+		if (mmc->version == SD_VERSION_2)
+			cmd.cmdarg |= OCR_HCS;
+
+		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_RSP_R3;
+		cmd.cmdarg = OCR_HCS | mmc->voltages;
+		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_RSP_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_switch(struct mmc *mmc, u8 set, u8 index, u8 value)
+{
+	struct mmc_cmd cmd;
+
+	cmd.cmdidx = MMC_CMD_SWITCH;
+	cmd.resp_type = MMC_RSP_R1b;
+	cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+		(index << 16) |
+		(value << 8);
+	cmd.flags = 0;
+
+	return mmc_send_cmd(mmc, &cmd, NULL);
+}
+
+int mmc_change_freq(struct mmc *mmc)
+{
+	char ext_csd[512];
+	char cardtype;
+	int err;
+
+	mmc->card_caps = 0;
+
+	/* Only version 4 supports high-speed */
+	if (mmc->version < MMC_VERSION_4)
+		return 0;
+
+	mmc->card_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;
+
+	err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1);
+
+	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->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+	else
+		mmc->card_caps |= MMC_MODE_HS;
+
+	return 0;
+}
+
+int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
+{
+	struct mmc_cmd cmd;
+	struct mmc_data data;
+
+	/* Switch the frequency */
+	cmd.cmdidx = SD_CMD_SWITCH_FUNC;
+	cmd.resp_type = MMC_RSP_R1;
+	cmd.cmdarg = (mode << 31) | 0xffffff;
+	cmd.cmdarg &= ~(0xf << (group * 4));
+	cmd.cmdarg |= value << (group * 4);
+	cmd.flags = 0;
+
+	data.dest = (char *)resp;
+	data.blocksize = 64;
+	data.blocks = 1;
+	data.flags = MMC_DATA_READ;
+
+	return mmc_send_cmd(mmc, &cmd, &data);
+}
+
+
+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->card_caps = 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;
+	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_RSP_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--) {
+		err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1,
+				(u8 *)&switch_status);
+
+		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->card_caps |= MMC_MODE_4BIT;
+
+	/* If high-speed isn't supported, we return */
+	if (!(switch_status[3] & SD_HIGHSPEED_SUPPORTED))
+		return 0;
+
+	err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)&switch_status);
+
+	if (err)
+		return err;
+
+	if ((switch_status[4] & 0x0f000000) == 0x01000000)
+		mmc->card_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)
+{
+	if (clock > mmc->f_max)
+		clock = mmc->f_max;
+
+	if (clock < mmc->f_min)
+		clock = mmc->f_min;
+
+	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_RSP_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_RSP_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_RSP_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_RSP_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;
+
+	/* Restrict card's capabilities by what the host can do */
+	mmc->card_caps &= mmc->host_caps;
+
+	if (IS_SD(mmc)) {
+		if (mmc->card_caps & MMC_MODE_4BIT) {
+			cmd.cmdidx = MMC_CMD_APP_CMD;
+			cmd.resp_type = MMC_RSP_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_RSP_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->card_caps & MMC_MODE_HS)
+			mmc_set_clock(mmc, 50000000);
+		else
+			mmc_set_clock(mmc, 25000000);
+	} else {
+		if (mmc->card_caps & MMC_MODE_4BIT) {
+			/* Set the card to use 4 bit*/
+			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+					EXT_CSD_BUS_WIDTH,
+					EXT_CSD_BUS_WIDTH_4);
+
+			if (err)
+				return err;
+
+			mmc_set_bus_width(mmc, 4);
+		} else if (mmc->card_caps & MMC_MODE_8BIT) {
+			/* Set the card to use 8 bit*/
+			err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+					EXT_CSD_BUS_WIDTH,
+					EXT_CSD_BUS_WIDTH_8);
+
+			if (err)
+				return err;
+
+			mmc_set_bus_width(mmc, 8);
+		}
+
+		if (mmc->card_caps & MMC_MODE_HS) {
+			if (mmc->card_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.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);
+	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;
+	/* We set the bit if the host supports voltages between 2.7 and 3.6 V */
+	cmd.cmdarg = ((mmc->voltages & 0xff8000) != 0) << 8 | 0xaa;
+	cmd.resp_type = MMC_RSP_R7;
+	cmd.flags = 0;
+
+	err = mmc_send_cmd(mmc, &cmd, NULL);
+
+	if (err)
+		return err;
+
+	if ((((uint *)(cmd.response))[0] & 0xff) != 0xaa)
+		return UNUSABLE_ERR;
+	else
+		mmc->version = SD_VERSION_2;
+
+	return 0;
+}
+
+int mmc_register(struct mmc *mmc)
+{
+	/* Setup the universal parts of the block interface just once */
+	mmc->block_dev.if_type = IF_TYPE_MMC;
+	mmc->block_dev.dev = cur_dev_num++;
+	mmc->block_dev.removable = 1;
+	mmc->block_dev.block_read = mmc_bread;
+	mmc->block_dev.block_write = mmc_bwrite;
+
+	INIT_LIST_HEAD (&mmc->link);
+
+	list_add_tail (&mmc->link, &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;
+		}
+	}
+
+	return mmc_startup(mmc);
+}
+
+/*
+ * 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;
+	struct list_head *entry;
+
+	list_for_each(entry, &mmc_devices) {
+		m = list_entry(entry, struct mmc, link);
+
+		printf("%s: %d", m->name, m->block_dev.dev);
+
+		if (entry->next != &mmc_devices)
+			printf("%c ", separator);
+	}
+
+	printf("\n");
+}
+
+int mmc_initialize(bd_t *bis)
+{
+	INIT_LIST_HEAD (&mmc_devices);
+	cur_dev_num = 0;
+
+	if (board_mmc_init(bis) < 0)
+		cpu_mmc_init(bis);
+
+	print_mmc_devices(',');
+
+	return 0;
+}
diff --git a/include/mmc.h b/include/mmc.h
index e41033f..b9b27ba 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,36 +26,149 @@
 #ifndef _MMC_H_
 #define _MMC_H_
 
-/* MMC command numbers */
+#include <linux/list.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 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
+
+/* SCR definitions in different words */
+#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 MMC_VDD_165_195		0x00000080	/* VDD voltage 1.65 - 1.95 */
+#define MMC_VDD_20_21		0x00000100	/* VDD voltage 2.0 ~ 2.1 */
+#define MMC_VDD_21_22		0x00000200	/* VDD voltage 2.1 ~ 2.2 */
+#define MMC_VDD_22_23		0x00000400	/* VDD voltage 2.2 ~ 2.3 */
+#define MMC_VDD_23_24		0x00000800	/* VDD voltage 2.3 ~ 2.4 */
+#define MMC_VDD_24_25		0x00001000	/* VDD voltage 2.4 ~ 2.5 */
+#define MMC_VDD_25_26		0x00002000	/* VDD voltage 2.5 ~ 2.6 */
+#define MMC_VDD_26_27		0x00004000	/* VDD voltage 2.6 ~ 2.7 */
+#define MMC_VDD_27_28		0x00008000	/* VDD voltage 2.7 ~ 2.8 */
+#define MMC_VDD_28_29		0x00010000	/* VDD voltage 2.8 ~ 2.9 */
+#define MMC_VDD_29_30		0x00020000	/* VDD voltage 2.9 ~ 3.0 */
+#define MMC_VDD_30_31		0x00040000	/* VDD voltage 3.0 ~ 3.1 */
+#define MMC_VDD_31_32		0x00080000	/* VDD voltage 3.1 ~ 3.2 */
+#define MMC_VDD_32_33		0x00100000	/* VDD voltage 3.2 ~ 3.3 */
+#define MMC_VDD_33_34		0x00200000	/* VDD voltage 3.3 ~ 3.4 */
+#define MMC_VDD_34_35		0x00400000	/* VDD voltage 3.4 ~ 3.5 */
+#define MMC_VDD_35_36		0x00800000	/* VDD voltage 3.5 ~ 3.6 */
+
+#define MMC_SWITCH_MODE_CMD_SET		0x00 /* Change the command set */
+#define MMC_SWITCH_MODE_SET_BITS	0x01 /* Set bits in EXT_CSD byte
+						addressed by index which are
+						1 in value field */
+#define MMC_SWITCH_MODE_CLEAR_BITS	0x02 /* Clear bits in EXT_CSD byte
+						addressed by index, which are
+						1 in value field */
+#define MMC_SWITCH_MODE_WRITE_BYTE	0x03 /* Set target byte to value */
+
+#define SD_SWITCH_CHECK		0
+#define SD_SWITCH_SWITCH	1
+
+/*
+ * EXT_CSD fields
+ */
+
+#define EXT_CSD_BUS_WIDTH	183	/* R/W */
+#define EXT_CSD_HS_TIMING	185	/* R/W */
+#define EXT_CSD_CARD_TYPE	196	/* RO */
+#define EXT_CSD_REV		192	/* RO */
+#define EXT_CSD_SEC_CNT		212	/* RO, 4 bytes */
+
+/*
+ * EXT_CSD field definitions
+ */
+
+#define EXT_CSD_CMD_SET_NORMAL		(1<<0)
+#define EXT_CSD_CMD_SET_SECURE		(1<<1)
+#define EXT_CSD_CMD_SET_CPSECURE	(1<<2)
+
+#define EXT_CSD_CARD_TYPE_26	(1<<0)	/* Card can run at 26MHz */
+#define EXT_CSD_CARD_TYPE_52	(1<<1)	/* Card can run at 52MHz */
+
+#define EXT_CSD_BUS_WIDTH_1	0	/* Card is in 1 bit mode */
+#define EXT_CSD_BUS_WIDTH_4	1	/* Card is in 4 bit mode */
+#define EXT_CSD_BUS_WIDTH_8	2	/* Card is in 8 bit mode */
 
 #define R1_ILLEGAL_COMMAND		(1 << 22)
 #define R1_APP_CMD			(1 << 5)
 
-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 MMC_RSP_PRESENT (1 << 0)
+#define MMC_RSP_136     (1 << 1)                /* 136 bit response */
+#define MMC_RSP_CRC     (1 << 2)                /* expect valid crc */
+#define MMC_RSP_BUSY    (1 << 3)                /* card may send busy */
+#define MMC_RSP_OPCODE  (1 << 4)                /* response contains opcode */
+
+#define MMC_RSP_NONE    (0)
+#define MMC_RSP_R1      (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R1b	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE| \
+			MMC_RSP_BUSY)
+#define MMC_RSP_R2      (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
+#define MMC_RSP_R3      (MMC_RSP_PRESENT)
+#define MMC_RSP_R4      (MMC_RSP_PRESENT)
+#define MMC_RSP_R5      (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R6      (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+#define MMC_RSP_R7      (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+
 
 struct mmc_cid {
 	unsigned long psn;
@@ -104,4 +219,61 @@ struct mmc_csd
 	u8	one:1;
 };
 
+struct mmc_cmd {
+	ushort cmdidx;
+	uint resp_type;
+	uint cmdarg;
+	char response[18];
+	uint flags;
+};
+
+struct mmc_data {
+	union {
+		char *dest;
+		const char *src; /* src buffers don't get written to */
+	};
+	uint flags;
+	uint blocks;
+	uint blocksize;
+};
+
+struct mmc {
+	struct list_head link;
+	char name[32];
+	void *priv;
+	uint voltages;
+	uint version;
+	uint f_min;
+	uint f_max;
+	int high_capacity;
+	uint bus_width;
+	uint clock;
+	uint card_caps;
+	uint host_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);
+};
+
+int mmc_register(struct mmc *mmc);
+int mmc_initialize(bd_t *bis);
+int mmc_init(struct mmc *mmc);
+int mmc_read(struct mmc *mmc, u64 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 df1cf13..65bcb4b 100644
--- a/lib_ppc/board.c
+++ b/lib_ppc/board.c
@@ -48,6 +48,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)
@@ -1078,6 +1081,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