[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