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

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



Signed-off-by: Reinhard Meyer <u-boot at emk-elektronik.de>
---
 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 {



More information about the U-Boot mailing list