[U-Boot] [PATCH v2a] AT91: add SD/MMC support

Reinhard Meyer u-boot at emk-elektronik.de
Mon Aug 9 17:45:48 CEST 2010


This patch adds support for the Atmel MultiMedia Card Interface (MCI)
in AT91SAM9260 and AT91SAM9XE SoCs and preserves the original support
for the AVR32AP700x SoCs.
It patches the original atmel_mci.c driver to remove byte order and
toolchain dependencies and makes it a common MCI driver for
Atmels AVR32AP7000 and AT91SAM9260 based SoCs.
The driver itself is not specific to those SoCs and should work with
other Atmel devices having an identical MultiMedia Card Interface (MCI),
provided the functions get_mci_clk_rate() and <SoC>_mci_hw_init() are
defined.

Tested on AT91SAM9XE-EK (AT91SAM9XE512)
Tested on ATNGW100 (AVR32AP7000)

Signed-off-by: Reinhard Meyer <u-boot at emk-elektronik.de>
---
Sorry, my mouse slipped and sent v2 prematurely :)

 arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c |   20 +++
 arch/arm/include/asm/arch-at91/at91_common.h      |    1 +
 arch/arm/include/asm/arch-at91/clk.h              |    5 +
 arch/arm/include/asm/arch-at91/hardware.h         |    1 +
 doc/README.atmel_mci                              |   68 +++++++++++
 drivers/mmc/atmel_mci.c                           |  134 ++++++++++-----------
 drivers/mmc/atmel_mci.h                           |    6 +-
 include/mmc.h                                     |   93 +++++++++------
 8 files changed, 217 insertions(+), 111 deletions(-)
 create mode 100644 doc/README.atmel_mci

diff --git a/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c b/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
index 77d49ab..9cef832 100644
--- a/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
+++ b/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c
@@ -194,3 +194,23 @@ void at91_macb_hw_init(void)
 #endif
 }
 #endif
+
+#ifdef CONFIG_ATMEL_MCI
+void at91_mci_hw_init(void)
+{
+	at91_set_a_periph(AT91_PIO_PORTA, 8, 0);	/* MCCK */
+#if defined(CONFIG_ATMEL_MCI_PORTB)
+	at91_set_b_periph(AT91_PIO_PORTA, 1, 0);	/* MCCDB */
+	at91_set_b_periph(AT91_PIO_PORTA, 0, 0);	/* MCDB0 */
+	at91_set_b_periph(AT91_PIO_PORTA, 5, 0);	/* MCDB1 */
+	at91_set_b_periph(AT91_PIO_PORTA, 4, 0);	/* MCDB2 */
+	at91_set_b_periph(AT91_PIO_PORTA, 3, 0);	/* MCDB3 */
+#else
+	at91_set_a_periph(AT91_PIO_PORTA, 7, 0);	/* MCCDA */
+	at91_set_a_periph(AT91_PIO_PORTA, 6, 0);	/* MCDA0 */
+	at91_set_a_periph(AT91_PIO_PORTA, 9, 0);	/* MCDA1 */
+	at91_set_a_periph(AT91_PIO_PORTA, 10, 0);	/* MCDA2 */
+	at91_set_a_periph(AT91_PIO_PORTA, 11, 0);	/* MCDA3 */
+#endif
+}
+#endif
diff --git a/arch/arm/include/asm/arch-at91/at91_common.h b/arch/arm/include/asm/arch-at91/at91_common.h
index 01840ee..90337eb 100644
--- a/arch/arm/include/asm/arch-at91/at91_common.h
+++ b/arch/arm/include/asm/arch-at91/at91_common.h
@@ -35,5 +35,6 @@ void at91_serial3_hw_init(void);
 void at91_spi0_hw_init(unsigned long cs_mask);
 void at91_spi1_hw_init(unsigned long cs_mask);
 void at91_uhp_hw_init(void);
+void at91_mci_hw_init(void);
 
 #endif /* AT91_COMMON_H */
diff --git a/arch/arm/include/asm/arch-at91/clk.h b/arch/arm/include/asm/arch-at91/clk.h
index f642dd9..457e6c9 100644
--- a/arch/arm/include/asm/arch-at91/clk.h
+++ b/arch/arm/include/asm/arch-at91/clk.h
@@ -59,5 +59,10 @@ static inline unsigned long get_twi_clk_rate(unsigned int dev_id)
 	return get_mck_clk_rate();
 }
 
+static inline unsigned long get_mci_clk_rate(void)
+{
+	return get_mck_clk_rate();
+}
+
 int at91_clock_init(unsigned long main_clock);
 #endif /* __ASM_ARM_ARCH_CLK_H__ */
diff --git a/arch/arm/include/asm/arch-at91/hardware.h b/arch/arm/include/asm/arch-at91/hardware.h
index 4ddb315..224b285 100644
--- a/arch/arm/include/asm/arch-at91/hardware.h
+++ b/arch/arm/include/asm/arch-at91/hardware.h
@@ -23,6 +23,7 @@
 #define AT91_BASE_SPI	AT91SAM9260_BASE_SPI0
 #define AT91_ID_UHP	AT91SAM9260_ID_UHP
 #define AT91_PMC_UHP	AT91SAM926x_PMC_UHP
+#define MMCI_BASE	AT91SAM9260_BASE_MCI
 #elif defined(CONFIG_AT91SAM9261) || defined(CONFIG_AT91SAM9G10)
 #include <asm/arch/at91sam9261.h>
 #define AT91_BASE_SPI	AT91SAM9261_BASE_SPI0
diff --git a/doc/README.atmel_mci b/doc/README.atmel_mci
new file mode 100644
index 0000000..15b3cc0
--- /dev/null
+++ b/doc/README.atmel_mci
@@ -0,0 +1,68 @@
+How to use SD/MMC cards with Atmel SoCs having MCI hardware
+-----------------------------------------------------------
+2010-07-04 Reinhard Meyer <reinhard.meyer at emk-elektronik.de>
+
+The drivers/mmc/atmel_mci.c file which originally worked only
+with the AVR32 architecture SoCs like AVR32AP700x has been
+updated to also work with the AT91SAM9260 compatible architectures:
+
+- AT91SAM9XE512 (tested, will definitely work with XE128 and XE256)
+- AT91SAM9260 (not tested, but MCI is to AT91SAM9XE)
+- AT91SAM9G20 (not tested, should work)
+
+It should work with all other AT91SAM9<xxx> devices that have MCI
+provided that a correct version of the following function is added
+to their specific XXX_devices file:
+
+(this example is from at91sam9260_devices.c)
+
+#ifdef CONFIG_ATMEL_MCI
+void at91_mci_hw_init(void)
+{
+	at91_set_a_periph(AT91_PIO_PORTA, 8, 0);	/* MCCK */
+#if defined(CONFIG_ATMEL_MCI_PORTB)
+	at91_set_b_periph(AT91_PIO_PORTA, 1, 0);	/* MCCDB */
+	at91_set_b_periph(AT91_PIO_PORTA, 0, 0);	/* MCDB0 */
+	at91_set_b_periph(AT91_PIO_PORTA, 5, 0);	/* MCDB1 */
+	at91_set_b_periph(AT91_PIO_PORTA, 4, 0);	/* MCDB2 */
+	at91_set_b_periph(AT91_PIO_PORTA, 3, 0);	/* MCDB3 */
+#else
+	at91_set_a_periph(AT91_PIO_PORTA, 7, 0);	/* MCCDA */
+	at91_set_a_periph(AT91_PIO_PORTA, 6, 0);	/* MCDA0 */
+	at91_set_a_periph(AT91_PIO_PORTA, 9, 0);	/* MCDA1 */
+	at91_set_a_periph(AT91_PIO_PORTA, 10, 0);	/* MCDA2 */
+	at91_set_a_periph(AT91_PIO_PORTA, 11, 0);	/* MCDA3 */
+#endif
+
+the board specific files need added:
+
+#ifdef CONFIG_ATMEL_MCI
+static void mci_hw_init(void)
+{
+	/* Enable clock */
+	at91_sys_write(AT91_PMC_PCER, 1 << AT91SAM9260_ID_MCI);
+
+	at91_mci_hw_init();
+}
+#endif
+
+int board_init(void)
+{
+	...
+#ifdef CONFIG_ATMEL_MCI
+	mci_hw_init();
+#endif
+	...
+	return 0;
+}
+
+and the board definition files needs:
+/* for the driver itself */
+#define CONFIG_MMC		1
+#define CONFIG_ATMEL_MCI	1
+#define CONFIG_ATMEL_MCI_PORTB	1	/* to use port B, undefine for port A */
+/* to use the cards */
+#define CONFIG_CMD_EXT2		1
+#define CONFIG_CMD_FAT		1
+#define CONFIG_CMD_MMC		1
+
diff --git a/drivers/mmc/atmel_mci.c b/drivers/mmc/atmel_mci.c
index 3946ffe..2d9e3c7 100644
--- a/drivers/mmc/atmel_mci.c
+++ b/drivers/mmc/atmel_mci.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2004-2006 Atmel Corporation
- *
+ * Copyright (C) 2010 EMK Elektronik <reinhard.meyer at emk-elektronik.de>
  * See file CREDITS for list of people who contributed to this
  * project.
  *
@@ -19,6 +19,12 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  * MA 02111-1307 USA
  */
+/*
+ * Notes:
+ * - modified to work with AVR32 and AT91
+ * - this driver handles cards in 1 bit bus width only,
+ * which is enough to load a kernel or similiar file
+ */
 #include <common.h>
 
 #include <part.h>
@@ -32,12 +38,6 @@
 
 #include "atmel_mci.h"
 
-#ifdef DEBUG
-#define pr_debug(fmt, args...) printf(fmt, ##args)
-#else
-#define pr_debug(...) do { } while(0)
-#endif
-
 #ifndef CONFIG_SYS_MMC_CLK_OD
 #define CONFIG_SYS_MMC_CLK_OD		150000
 #endif
@@ -70,13 +70,13 @@ static void mci_set_mode(unsigned long hz, unsigned long blklen)
 	bus_hz = get_mci_clk_rate();
 	clkdiv = (bus_hz / hz) / 2 - 1;
 
-	pr_debug("mmc: setting clock %lu Hz, block size %lu\n",
-		 hz, blklen);
+	debug("mmc: bus_hz is %lu, setting clock %lu Hz, block size %lu\n",
+		 bus_hz, hz, blklen);
 
 	if (clkdiv & ~255UL) {
 		clkdiv = 255;
-		printf("mmc: clock %lu too low; setting CLKDIV to 255\n",
-			hz);
+		printf("mmc: requested clock %lu is too low; changed to %lu\n",
+			hz, (bus_hz / (clkdiv+1)) / 2);
 	}
 
 	blklen &= 0xfffc;
@@ -84,6 +84,10 @@ static void mci_set_mode(unsigned long hz, unsigned long blklen)
 			 | MMCI_BF(BLKLEN, blklen)
 			 | MMCI_BIT(RDPROOF)
 			 | MMCI_BIT(WRPROOF)));
+#if defined(CONFIG_ATMEL_MCI_PORTB)
+	mmci_writel(SDCR, (MMCI_BF(SCDSEL, 1)
+			 | MMCI_BF(SCDBUS, 0)));
+#endif
 }
 
 #define RESP_NO_CRC	1
@@ -114,7 +118,7 @@ mmc_cmd(unsigned long cmd, unsigned long arg,
 	unsigned long error_flags;
 	u32 status;
 
-	pr_debug("mmc: CMD%lu 0x%lx (flags 0x%lx)\n",
+	debug("mmc: CMD%lu 0x%lx (flags 0x%lx)\n",
 		 cmd, arg, flags);
 
 	error_flags = ERROR_FLAGS;
@@ -135,7 +139,7 @@ mmc_cmd(unsigned long cmd, unsigned long arg,
 		status = mmci_readl(SR);
 	} while (!(status & MMCI_BIT(CMDRDY)));
 
-	pr_debug("mmc: status 0x%08x\n", status);
+	debug("mmc: status 0x%08x\n", status);
 
 	if (status & error_flags) {
 		printf("mmc: command %lu failed (status: 0x%08x)\n",
@@ -144,13 +148,13 @@ mmc_cmd(unsigned long cmd, unsigned long arg,
 	}
 
 	if (response_words)
-		pr_debug("mmc: response:");
+		debug("mmc: response:");
 
 	for (i = 0; i < response_words; i++) {
 		response[i] = mmci_readl(RSPR);
-		pr_debug(" %08lx", response[i]);
+		debug(" %08lx", response[i]);
 	}
-	pr_debug("\n");
+	debug("\n");
 
 	return 0;
 }
@@ -192,7 +196,7 @@ mmc_bread(int dev, unsigned long start, lbaint_t blkcnt,
 	if (blkcnt == 0)
 		return 0;
 
-	pr_debug("mmc_bread: dev %d, start %lx, blkcnt %lx\n",
+	debug("mmc_bread: dev %d, start %lx, blkcnt %lx\n",
 		 dev, start, blkcnt);
 
 	/* Put the device into Transfer state */
@@ -203,7 +207,7 @@ mmc_bread(int dev, unsigned long start, lbaint_t blkcnt,
 	ret = mmc_cmd(MMC_CMD_SET_BLOCKLEN, mmc_blkdev.blksz, resp, R1 | NCR);
 	if (ret) goto out;
 
-	pr_debug("MCI_DTOR = %08lx\n", mmci_readl(DTOR));
+	debug("MCI_DTOR = %08x\n", mmci_readl(DTOR));
 
 	for (i = 0; i < blkcnt; i++, start++) {
 		ret = mmc_cmd(MMC_CMD_READ_SINGLE_BLOCK,
@@ -229,13 +233,16 @@ mmc_bread(int dev, unsigned long start, lbaint_t blkcnt,
 			}
 		} while(wordcount < (mmc_blkdev.blksz / 4));
 
-		pr_debug("mmc: read %u words, waiting for BLKE\n", wordcount);
+		debug("mmc: read %lu words, waiting for BLKE\n", wordcount);
 
 		do {
 			status = mmci_readl(SR);
 		} while (!(status & MMCI_BIT(BLKE)));
-
-		putc('.');
+#if DEBUG
+		/* print the first block only */
+		if (i==0)
+			print_buffer(0, buffer, 1, mmc_blkdev.blksz, 0);
+#endif
 	}
 
 out:
@@ -294,39 +301,26 @@ static void mmc_dump_cid(const struct mmc_cid *cid)
 	       cid->mdt >> 4, cid->mdt & 0x0f);
 }
 
-static void mmc_dump_csd(const struct mmc_csd *csd)
+static void mmc_parse_csd(struct mmc_csd *csd, int verbose)
 {
-	unsigned long *csd_raw = (unsigned long *)csd;
-	printf("CSD data: %08lx %08lx %08lx %08lx\n",
-	       csd_raw[0], csd_raw[1], csd_raw[2], csd_raw[3]);
-	printf("CSD structure version:   1.%u\n", csd->csd_structure);
-	printf("MMC System Spec version: %u\n", csd->spec_vers);
-	printf("Card command classes:    %03x\n", csd->ccc);
-	printf("Read block length:       %u\n", 1 << csd->read_bl_len);
-	if (csd->read_bl_partial)
-		puts("Supports partial reads\n");
-	else
-		puts("Does not support partial reads\n");
-	printf("Write block length:      %u\n", 1 << csd->write_bl_len);
-	if (csd->write_bl_partial)
-		puts("Supports partial writes\n");
-	else
-		puts("Does not support partial writes\n");
-	if (csd->wp_grp_enable)
-		printf("Supports group WP:      %u\n", csd->wp_grp_size + 1);
-	else
-		puts("Does not support group WP\n");
-	printf("Card capacity:		%u bytes\n",
-	       (csd->c_size + 1) * (1 << (csd->c_size_mult + 2)) *
-	       (1 << csd->read_bl_len));
-	printf("File format:            %u/%u\n",
-	       csd->file_format_grp, csd->file_format);
-	puts("Write protection:        ");
-	if (csd->perm_write_protect)
-		puts(" permanent");
-	if (csd->tmp_write_protect)
-		puts(" temporary");
-	putc('\n');
+	csd->taac = (csd->raw[0] >> 16) & 0xff;
+	csd->nsac = (csd->raw[0] >> 8) & 0xff;
+	csd->read_bl_len = (csd->raw[1] >> 16) & 0x0f;
+	csd->read_bl_partial = (csd->raw[1] >> 15) & 0x01;
+	csd->c_size = ((csd->raw[1] << 2) & 0x0ffc) | ((csd->raw[2] >> 30) & 0x03);
+	csd->c_size_mult = (csd->raw[2] >> 15) & 0x07;
+	csd->blocks = (csd->c_size+1) * (1 << (csd->c_size_mult+2));
+	csd->blocksize = 1 << csd->read_bl_len;
+
+	if (verbose) {
+		printf("raw CSD data: %08x %08x %08x %08x\n",
+		       csd->raw[0], csd->raw[1], csd->raw[2], csd->raw[3]);
+		printf("Read block length: %u\n", 1 << csd->read_bl_len);
+		if (csd->read_bl_partial)
+			puts("  (Supports partial reads)\n");
+		printf("Card capacity: %u Mbytes\n",
+		       (csd->blocks>>12) * (csd->blocksize>>8));
+	}
 }
 
 static int mmc_idle_cards(void)
@@ -409,7 +403,7 @@ static int mmc_init_card(struct mmc_cid *cid, int verbose)
 	return ret;
 }
 
-static void mci_set_data_timeout(struct mmc_csd *csd)
+static void mci_set_data_timeout(const struct mmc_csd *csd)
 {
 	static const unsigned int dtomul_to_shift[] = {
 		0, 4, 7, 8, 10, 12, 16, 20,
@@ -467,7 +461,6 @@ int mmc_legacy_init(int verbose)
 {
 	struct mmc_cid cid;
 	struct mmc_csd csd;
-	unsigned int max_blksz;
 	int ret;
 
 	/* Initialize controller */
@@ -491,8 +484,7 @@ int mmc_legacy_init(int verbose)
 	ret = mmc_cmd(MMC_CMD_SEND_CSD, mmc_rca << 16, &csd, R2 | NCR);
 	if (ret)
 		return ret;
-	if (verbose)
-		mmc_dump_csd(&csd);
+	mmc_parse_csd(&csd, verbose);
 
 	mci_set_data_timeout(&csd);
 
@@ -508,26 +500,28 @@ int mmc_legacy_init(int verbose)
 	sprintf((char *)mmc_blkdev.revision, "%x %x",
 		cid.prv >> 4, cid.prv & 0x0f);
 
-	/*
-	 * If we can't use 512 byte blocks, refuse to deal with the
-	 * card. Tons of code elsewhere seems to depend on this.
-	 */
-	max_blksz = 1 << csd.read_bl_len;
-	if (max_blksz < 512 || (max_blksz > 512 && !csd.read_bl_partial)) {
+	mmc_blkdev.blksz = csd.blocksize;
+	mmc_blkdev.lba = csd.blocks;
+
+	/* if the card supports partial reads, decrease the block size to 512 */
+	while (mmc_blkdev.blksz > 512 && csd.read_bl_partial) {
+		mmc_blkdev.blksz >>= 1;
+		mmc_blkdev.lba <<= 1;
+	}
+	if (mmc_blkdev.blksz != csd.blocksize) {
+		printf ("mmc: blocksize reduced to %lu, number of blocks: %lu\n",
+			mmc_blkdev.blksz, mmc_blkdev.lba);
+	}
+
+	/* fail if blocksize != 512 */
+	if (mmc_blkdev.blksz != 512) {
 		printf("Card does not support 512 byte reads, aborting.\n");
 		return -ENODEV;
 	}
-	mmc_blkdev.blksz = 512;
-	mmc_blkdev.lba = (csd.c_size + 1) * (1 << (csd.c_size_mult + 2));
 
 	mci_set_mode(CONFIG_SYS_MMC_CLK_PP, mmc_blkdev.blksz);
 
-#if 0
-	if (fat_register_device(&mmc_blkdev, 1))
-		printf("Could not register MMC fat device\n");
-#else
 	init_part(&mmc_blkdev);
-#endif
 
 	return 0;
 }
diff --git a/drivers/mmc/atmel_mci.h b/drivers/mmc/atmel_mci.h
index 5b4f5c9..8632cb4 100644
--- a/drivers/mmc/atmel_mci.h
+++ b/drivers/mmc/atmel_mci.h
@@ -19,8 +19,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  * MA 02111-1307 USA
  */
-#ifndef __CPU_AT32AP_ATMEL_MCI_H__
-#define __CPU_AT32AP_ATMEL_MCI_H__
+#ifndef __ATMEL_MCI_H__
+#define __ATMEL_MCI_H__
 
 /* Atmel MultiMedia Card Interface (MCI) registers */
 #define MMCI_CR					0x0000
@@ -198,4 +198,4 @@
 #define mmci_writel(reg,value)				\
 	writel((value), (void *)MMCI_BASE + MMCI_##reg)
 
-#endif /* __CPU_AT32AP_ATMEL_MCI_H__ */
+#endif /* __ATMEL_MCI_H__ */
diff --git a/include/mmc.h b/include/mmc.h
index fcb237e..c739986 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -179,44 +179,61 @@ struct mmc_cid {
 	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;
+/*
+ * CSD structure forSD/MMC  cards upto 4GB
+ *
+ * Bitfields in the 128 Bit answer from the card
+ * (bit 127 is bit 31 of first u32
+ * bit 0 is bit 0 of last u32)
+ * csd_structure:127..126
+ * spec_vers:125..122
+ * rsvd1:121..120
+ * taac:119..112
+ * nsac:111..104
+ * tran_speed:103..96
+ * ccc:95..84
+ * read_bl_len:83..80
+ * read_bl_partial:79
+ * write_blk_misalign:78
+ * read_blk_misalign:77
+ * dsr_imp:76
+ * rsvd2:75..74
+ * c_size:73..62 - crosses u32 boundary!
+ * vdd_r_curr_min:61..59
+ * vdd_r_curr_max:58..56
+ * vdd_w_curr_min:55..53
+ * vdd_w_curr_max:52..50
+ * c_size_mult:49..47
+ * sector_size:46..42
+ * erase_grp_size:41..37
+ * wp_grp_size:36..32
+ * wp_grp_enable:31
+ * default_ecc:30..29
+ * r2w_factor:28..26
+ * write_bl_len:25..22
+ * write_bl_partial:21
+ * rsvd3:20..16
+ * file_format_grp:15
+ * copy:14
+ * perm_write_protect:13
+ * tmp_write_protect:12
+ * file_format:11..10
+ * ecc:9..8
+ * crc:7..1
+ * one:0
+ */
+struct mmc_csd {
+	/* raw data */
+	u32 raw[4];
+	/* parsed values we need to read a card */
+	u8 taac;
+	u8 nsac;
+	u8 read_bl_len;
+	u8 read_bl_partial;
+	u32 c_size;
+	u8 c_size_mult;
+	u32 blocks;
+	u32 blocksize;
 };
 
 struct mmc_cmd {
-- 
1.5.6.5





More information about the U-Boot mailing list