[U-Boot] [PATCH] AT91SAM9260/9XE: add support for MultiMedia Card Interface (MCI)

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 reinhard.meyer@emk-elektronik.de --- arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c | 20 ++++ 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 | 112 ++++++++++----------- drivers/mmc/atmel_mci.h | 6 +- include/mmc.h | 93 ++++++++++------- 7 files changed, 207 insertions(+), 98 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/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@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..9dfec68 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@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 ARM9 + * - 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> @@ -70,13 +76,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); + pr_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 +90,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 @@ -203,7 +213,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)); + pr_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 +239,14 @@ 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); + pr_debug("mmc: read %lu words, waiting for BLKE\n", wordcount);
do { status = mmci_readl(SR); } while (!(status & MMCI_BIT(BLKE))); - +#if DEBUG putc('.'); +#endif }
out: @@ -294,39 +305,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 +407,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 +465,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 +488,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 +504,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 %u, number of blocks: %u\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 8973bc7..af227cd 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 for SD/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 {

Dear Reinhard,
Could you please rebase your code to master@ git://git.denx.de/u-boot-at91.git ?
Thanks!
BR, Eric
Reinhard Meyer (-VC) wrote:
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 reinhard.meyer@emk-elektronik.de
arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c | 20 ++++ 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 | 112 ++++++++++----------- drivers/mmc/atmel_mci.h | 6 +- include/mmc.h | 93 ++++++++++------- 7 files changed, 207 insertions(+), 98 deletions(-) create mode 100644 doc/README.atmel_mci

Eric Xu schrieb:
Dear Reinhard,
Could you please rebase your code to master@ git://git.denx.de/u-boot-at91.git ?
Thanks!
BR, Eric
Reinhard Meyer (-VC) wrote:
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.
Hi Eric,
I am not a GIT specialist and working in the main GIT tree. How do I rebase into another tree?
Does the patch not apply to your at91.git tree?
Reinhard

Dear Eric Xu,
In message 4C31770B.1020407@atmel.com you wrote:
Could you please rebase your code to master@ git://git.denx.de/u-boot-at91.git ?
Why are you requesting this?
I do not want a situation that patches need to be submitted agaionst a number of different trees or branches, with the unavoidable result that the majority of readers here on the list cannot test the postings bcause they do not apply against the master repository.
Because of that, http://www.denx.de/wiki/U-Boot/Patches states explicitly:
Please use the "master" branch of the mainline U-Boot git repository (git://git.denx.de/u-boot.git) as reference, unless (usually late in a release cycle) there has been an announcement to use the "next" branch of this repository instead.
If your AT91 repository requires a different state, it is your task as custodian to keep it in sync with master, and to resolve any merge conflicts resulting from the remaining differences.
Reinhards patch appears to apply cleanly against master, so it should NOT be changed.
Thanks.
Best regards,
Wolfgang Denk

Dear Wolfgang,
The reason is I tried to apply the patch on top of 1) "master" branch of the mainline U-Boot 2) current "master" of u-boot-at91.git 3) The old "master" of u-boot-at91.git all of them are failed.
Since you said the patch is cleanly against master of mainline U-Boot, I'll rework.
Thanks.
BR, Eric
-----Original Message----- From: Wolfgang Denk [mailto:wd@denx.de] Sent: 2010-7-5 (星期一) 17:59 To: Xu, Hong Cc: reinhard.meyer@emk-elektronik.de; u-boot Subject: Re: [U-Boot] [PATCH] AT91SAM9260/9XE: add support for MultiMedia Card Interface (MCI)
Dear Eric Xu,
In message 4C31770B.1020407@atmel.com you wrote:
Could you please rebase your code to master@ git://git.denx.de/u-boot-at91.git ?
Why are you requesting this?
I do not want a situation that patches need to be submitted agaionst a number of different trees or branches, with the unavoidable result that the majority of readers here on the list cannot test the postings bcause they do not apply against the master repository.
Because of that, http://www.denx.de/wiki/U-Boot/Patches states explicitly:
Please use the "master" branch of the mainline U-Boot git repository (git://git.denx.de/u-boot.git) as reference, unless (usually late in a release cycle) there has been an announcement to use the "next" branch of this repository instead.
If your AT91 repository requires a different state, it is your task as custodian to keep it in sync with master, and to resolve any merge conflicts resulting from the remaining differences.
Reinhards patch appears to apply cleanly against master, so it should NOT be changed.
Thanks.
Best regards,
Wolfgang Denk

Dear "Xu, Hong",
In message 0F5F73E36031FD46AD3F2E6948F0C0A0205C9D@frimb01.corp.atmel.com you wrote:
The reason is I tried to apply the patch on top of
- "master" branch of the mainline U-Boot
- current "master" of u-boot-at91.git
- The old "master" of u-boot-at91.git
all of them are failed.
Since you said the patch is cleanly against master of mainline U-Boot, = I'll rework.
It applies indeed without problems on top of the current master branch (i. e. on top of commit v2010.06-73-g54841ab) :
-> git-am -3 -i -u --whitespace=strip ~/Mail/U-Boot/8433 Commit Body is: -------------------------- AT91SAM9260/9XE: add support for MultiMedia Card Interface (MCI)
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 reinhard.meyer@emk-elektronik.de -------------------------- Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all y Applying: AT91SAM9260/9XE: add support for MultiMedia Card Interface (MCI) ->
Best regards,
Wolfgang Denk

Dear Wolfgang,
It seems the root cause of my failure of applying Reinhard's patch is the exchange server settings. Some of the space/tabs are converted/expanded in a wrong way.
Thanks for the help and hints from you and Mr.Reinhard.
BR, Eric
-----Original Message----- From: Wolfgang Denk [mailto:wd@denx.de] Sent: 2010-7-5 (星期一) 20:46 To: Xu, Hong Cc: reinhard.meyer@emk-elektronik.de; u-boot Subject: Re: 答复: [U-Boot] [PATCH] AT91SAM9260/9XE: add support for MultiMedia Card Interface (MCI)
Dear "Xu, Hong",
In message 0F5F73E36031FD46AD3F2E6948F0C0A0205C9D@frimb01.corp.atmel.com you wrote:
The reason is I tried to apply the patch on top of
- "master" branch of the mainline U-Boot
- current "master" of u-boot-at91.git
- The old "master" of u-boot-at91.git
all of them are failed.
Since you said the patch is cleanly against master of mainline U-Boot, = I'll rework.
It applies indeed without problems on top of the current master branch (i. e. on top of commit v2010.06-73-g54841ab) :
-> git-am -3 -i -u --whitespace=strip ~/Mail/U-Boot/8433 Commit Body is: -------------------------- AT91SAM9260/9XE: add support for MultiMedia Card Interface (MCI)
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),
[...]

Signed-off-by: Reinhard Meyer u-boot@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@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@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 {

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@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@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@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 {

Hello! I have successfully applied your patch on a AT91SAM9G20 cpu and it reads the correct CID & CSD info. At the end of initialization the bread function fails with the following error:
mmc: bread failed, status = 0040c0e5, card status = 00000900
Does someone else received this error or have a hint on this error message?
Thanks, Alex

asc0 wrote:
Hello! I have successfully applied your patch on a AT91SAM9G20 cpu and it reads the correct CID & CSD info. At the end of initialization the bread function fails with the following error:
mmc: bread failed, status = 0040c0e5, card status = 00000900
Does someone else received this error or have a hint on this error message?
a) it works fine on ATNGW100 and AT91SAM9XE-EK. Have you tried other cards? The driver will not work with SDHC cards or cards that do not allow partial reads. Maybe supply us here with the CID and CSD output from the driver.
b) that driver has become obsolete now... A few days ago I managed to get the mmc framework based driver originally by Rob Emanuele running. That one can deal with large cards (still have to test that, a 32GB card is in the mail to me). During the weekend I might post a first patch of that to the list.
Best Regards, Reinhard

Thanks for your reply...
The CID part is: mmc: response: 1a50514d 53202020 1018301a ec007b8b Manufacturer ID: 1A OEM/Application ID: 5051 Product name: MS Product Revision: 1.0 Product Serial Number: 405805804 Manufacturing Date: 07/11
The CSD part is: mmc: response: 005e0032 5f5983cf edb6ff87 9640003f raw CSD data: 005e0032 5f5983cf edb6ff87 9640003f Read block length: 512 (Supports partial reads) Card capacity: 244 Mbytes Warning: Using maximum data timeout mmc: Using 983040 cycles data timeout (DTOR=0x7f) ... MCI_DTOR = 0000007f ... mmc: bread failed, status = 0040c0e5, card status = 00000900 ...
The maximum data timeout is set hard-coded for testing purposes because the bread status has the DTOE bit set. I have also tried a 2GB SD card (no SDHC) with the same error and status code.
Best regards, Alexander
-----Original Message----- From: Reinhard Meyer [mailto:u-boot@emk-elektronik.de] Sent: Friday, August 13, 2010 1:17 PM To: Schleifer, Alexander Cc: u-boot@lists.denx.de Subject: Re: [U-Boot] [PATCH v2a] AT91: add SD/MMC support
asc0 wrote:
Hello! I have successfully applied your patch on a AT91SAM9G20 cpu and it reads the correct CID & CSD info. At the end of initialization the bread function fails with the following error:
mmc: bread failed, status = 0040c0e5, card status = 00000900
Does someone else received this error or have a hint on this error message?
a) it works fine on ATNGW100 and AT91SAM9XE-EK. Have you tried other cards? The driver will not work with SDHC cards or cards that do not allow partial reads. Maybe supply us here with the CID and CSD output from the driver.
b) that driver has become obsolete now... A few days ago I managed to get the mmc framework based driver originally by Rob Emanuele running. That one can deal with large cards (still have to test that, a 32GB card is in the mail to me). During the weekend I might post a first patch of that to the list.
Best Regards, Reinhard

Dear Schleifer, Alexander,
Thanks for your reply...
The CID part is: mmc: response: 1a50514d 53202020 1018301a ec007b8b Manufacturer ID: 1A OEM/Application ID: 5051 Product name: MS Product Revision: 1.0 Product Serial Number: 405805804 Manufacturing Date: 07/11
The CSD part is: mmc: response: 005e0032 5f5983cf edb6ff87 9640003f raw CSD data: 005e0032 5f5983cf edb6ff87 9640003f Read block length: 512 (Supports partial reads) Card capacity: 244 Mbytes Warning: Using maximum data timeout mmc: Using 983040 cycles data timeout (DTOR=0x7f)
I miss the clock messages here. Whats the MCK and which divider is calculated?
mmc: bread failed, status = 0040c0e5, card status = 00000900
0040c0e5= DTOE=1(data time out error) TXBUFE=1 RXBUFF=1(rx buffer full) and other bits
That seems to me like some timing problem, or a hardware problem, where reading data with slow clock (used to get CID/CSD) works, but reading at several MHz (depending on card) does not work. Besides the clock calculation in this driver rounds the divider down, so on a typical system a 25MHz card will receive a 48 MHz clock instead of a 24 MHz. I figured this out while testing the new driver...
If you really want to bother with this now old driver use this excerpt from the new driver:
debug("mci: bus_hz is %u, setting clock %u Hz, block size %u\n", bus_hz, hz, blklen); if (hz > 0) { /* find lowest clkdiv yielding a rate <= than requested */ for (clkdiv=0; clkdiv<255; clkdiv++) { if ((bus_hz / (clkdiv+1) / 2) <= hz) break; } } printf("mci: setting clock %u Hz, block size %u\n", (bus_hz / (clkdiv+1)) / 2, blklen);
If you have two days patience, wait for the new driver, I will be most happy to have a tester ;)
Best Regards, Reinhard

Dear Schleifer, Alexander,
If you have two days patience, wait for the new driver, I will be most happy to have a tester ;)
TOP9000> mmci mci: setting clock 194000 Hz, block size 512 mci: setting clock 24832000 Hz, block size 512 mci: setting clock 194000 Hz, block size 512 mci: setting clock 194000 Hz, block size 512 mci: setting clock 24832000 Hz, block size 512 Device: mci Manufacturer ID: 3 OEM: 5344 Name: SD32G Tran Speed: 25000000 Rd Block Len: 512 SD version 2.0 High Capacity: Yes <<<<< SDHC Capacity: 31914983424 <<<< 32 GB !!! Bus Width: 4-bit TOP9000> mmc read mci 21000000 3b70000 100
MMC read: dev # 0, block # 62324736, count 256 ... mci: setting clock 194000 Hz, block size 512 mci: setting clock 24832000 Hz, block size 512 mci: setting clock 194000 Hz, block size 512 mci: setting clock 194000 Hz, block size 512 mci: setting clock 24832000 Hz, block size 512 256 blocks read: OK TOP9000> mmc read mci 0 3b70000 100
MMC read: dev # 0, block # 62324736, count 256 ... mci: setting clock 194000 Hz, block size 512 mci: setting clock 24832000 Hz, block size 512 mci: setting clock 194000 Hz, block size 512 mci: setting clock 194000 Hz, block size 512 mci: setting clock 24832000 Hz, block size 512 256 blocks read: OK
fatls, however fails, probably cannot handle 32 Gigs :) (fatls works with a SDHC card with 4 Gigs)
Once my other question pertaining to *.h files is answered, I can supply the driver as a patch.
Reinhard

The clock messages are ..
mmc: bus_hz is 99072000, setting clock 150000 Hz, block size 512 mmc: requested clock 150000 is too low; changed to 193500
But I found out, that the AT91Bootstrap initialization impacts the U-Boot code. If I use the AT91Bootstrap (without mci initialization) from NAND flash, the mmc init command successfully reads the card. It fails if I use the AT91Bootstrap with mci initialization.
The new driver may help me out of this - so I would be happy to test your patch ;)
Best Regards, Alex
-----Original Message----- From: Reinhard Meyer [mailto:u-boot@emk-elektronik.de] Sent: Friday, August 13, 2010 8:32 PM To: Schleifer, Alexander Cc: u-boot@lists.denx.de Subject: Re: [U-Boot] [PATCH v2a] AT91: add SD/MMC support
Dear Schleifer, Alexander,
Thanks for your reply...
The CID part is: mmc: response: 1a50514d 53202020 1018301a ec007b8b Manufacturer ID: 1A OEM/Application ID: 5051 Product name: MS Product Revision: 1.0 Product Serial Number: 405805804 Manufacturing Date: 07/11
The CSD part is: mmc: response: 005e0032 5f5983cf edb6ff87 9640003f raw CSD data: 005e0032 5f5983cf edb6ff87 9640003f Read block length: 512 (Supports partial reads) Card capacity: 244 Mbytes Warning: Using maximum data timeout mmc: Using 983040 cycles data timeout (DTOR=0x7f)
I miss the clock messages here. Whats the MCK and which divider is calculated?
mmc: bread failed, status = 0040c0e5, card status = 00000900
0040c0e5= DTOE=1(data time out error) TXBUFE=1 RXBUFF=1(rx buffer full) and other bits
That seems to me like some timing problem, or a hardware problem, where reading data with slow clock (used to get CID/CSD) works, but reading at several MHz (depending on card) does not work. Besides the clock calculation in this driver rounds the divider down, so on a typical system a 25MHz card will receive a 48 MHz clock instead of a 24 MHz. I figured this out while testing the new driver...
If you really want to bother with this now old driver use this excerpt from the new driver:
debug("mci: bus_hz is %u, setting clock %u Hz, block size %u\n", bus_hz, hz, blklen); if (hz > 0) { /* find lowest clkdiv yielding a rate <= than requested */ for (clkdiv=0; clkdiv<255; clkdiv++) { if ((bus_hz / (clkdiv+1) / 2) <= hz) break; } } printf("mci: setting clock %u Hz, block size %u\n", (bus_hz / (clkdiv+1)) / 2, blklen);
If you have two days patience, wait for the new driver, I will be most happy to have a tester ;)
Best Regards, Reinhard

Dear Schleifer, Alexander,
But I found out, that the AT91Bootstrap initialization impacts
the U-Boot code. If I use the AT91Bootstrap (without mci initialization)
from NAND flash, the mmc init command successfully reads the card.
It fails if I use the AT91Bootstrap with mci initialization.
That's unlikely (it has the same init sequences)...
Do you have at91_mci_hw_init(void) in your at91samXXXX_devices.c and do you call that function from your board init?
Can you list me what that bootstrap does initialize differently if mci is enabled?
Best Regards, Reinhard

Dear Reinhard Meyer,
In message 4C690FEC.6040506@emk-elektronik.de you wrote:
Dear Schleifer, Alexander,
But I found out, that the AT91Bootstrap initialization impacts
the U-Boot code. If I use the AT91Bootstrap (without mci initialization)
from NAND flash, the mmc init command successfully reads the card.
It fails if I use the AT91Bootstrap with mci initialization.
That's unlikely (it has the same init sequences)...
Do you have at91_mci_hw_init(void) in your at91samXXXX_devices.c and do you call that function from your board init?
Please fix your mailer. Your quoting is seriously broken!
Best regards,
Wolfgang Denk

Wolfgang Denk wrote:
Dear Reinhard Meyer,
In message 4C690FEC.6040506@emk-elektronik.de you wrote:
Dear Schleifer, Alexander,
But I found out, that the AT91Bootstrap initialization impacts
the U-Boot code. If I use the AT91Bootstrap (without mci initialization)
from NAND flash, the mmc init command successfully reads the card.
It fails if I use the AT91Bootstrap with mci initialization.
That's unlikely (it has the same init sequences)...
Do you have at91_mci_hw_init(void) in your at91samXXXX_devices.c and do you call that function from your board init?
Please fix your mailer. Your quoting is seriously broken!
No, its not. I manually added breaks to that line because it was not wrapped at all - and I did not bother to add extra ">"s.
Reinhard

Dear Reinhard Meyer,
In message 4C691822.8050404@emk-elektronik.de you wrote:
Please fix your mailer. Your quoting is seriously broken!
No, its not. I manually added breaks to that line because it was not wrapped at all - and I did not bother to add extra ">"s.
I wrote "Your quoting is seriously broken", and indeed it is ;-) I was just polite enough to blame it on your mailer...
Best regards,
Wolfgang Denk

Hello Reinhard,
thanks for your reply. I checked the register settings and sorted out, that the SDCBUS was initialized in 4-bit mode by the bootstrap.
So I added the explicit initialization when port A is used:
#if defined(CONFIG_ATMEL_MCI_PORTB) mmci_writel(SDCR, (MMCI_BF(SCDSEL, 1) | MMCI_BF(SCDBUS, 0))); #else mmci_writel(SDCR, (MMCI_BF(SCDSEL, 0) | MMCI_BF(SCDBUS, 0))); #endif
Best regards, Alex
-----Original Message----- From: Reinhard Meyer [mailto:u-boot@emk-elektronik.de] Sent: Montag, 16. August 2010 12:16 To: Schleifer, Alexander Cc: u-boot@lists.denx.de Subject: Re: [U-Boot] [PATCH v2a] AT91: add SD/MMC support
Dear Schleifer, Alexander,
But I found out, that the AT91Bootstrap initialization impacts
the U-Boot code. If I use the AT91Bootstrap (without mci initialization)
from NAND flash, the mmc init command successfully reads the card.
It fails if I use the AT91Bootstrap with mci initialization.
That's unlikely (it has the same init sequences)...
Do you have at91_mci_hw_init(void) in your at91samXXXX_devices.c and do you call that function from your board init?
Can you list me what that bootstrap does initialize differently if mci is enabled?
Best Regards, Reinhard

Dear Schleifer, Alexander,
Hello Reinhard,
thanks for your reply. I checked the register settings and sorted out, that the SDCBUS was initialized in 4-bit mode by the bootstrap.
So I added the explicit initialization when port A is used:
#if defined(CONFIG_ATMEL_MCI_PORTB) mmci_writel(SDCR, (MMCI_BF(SCDSEL, 1) | MMCI_BF(SCDBUS, 0))); #else mmci_writel(SDCR, (MMCI_BF(SCDSEL, 0) | MMCI_BF(SCDBUS, 0))); #endif
Arghh, thats why getting the "geometry" worked, but not a block read.
And you were right, the new driver would have handled that correctly :)
Since currently the new driver is among the "newest" in my series of changes here, I'll have to try to rebase to get it to the "oldest" position.
Reinhard

This patch adds SD/MMC support to AT91SAM9260/9XE/9G20 using the generic MMC framework.
Signed-off-by: Reinhard Meyer u-boot@emk-elektronik.de --- The driver itself is not dependent on any particular AT91 or AVR32 device, but further work in device specific files is required to enable its use for other AT91 (and AVR32) devices: at91<*>_devices.c, hardware.h, at91<*>.h. Patches welcome!
arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c | 21 ++ 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 | 86 +++++ drivers/mmc/Makefile | 5 +- drivers/mmc/atmel_mci.h | 48 +++- drivers/mmc/gen_atmel_mci.c | 353 +++++++++++++++++++++ include/mmc.h | 15 +- 9 files changed, 530 insertions(+), 5 deletions(-) create mode 100644 doc/README.atmel_mci create mode 100644 drivers/mmc/gen_atmel_mci.c
diff --git a/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c b/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c index 77d49ab..2d88481 100644 --- a/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c +++ b/arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c @@ -194,3 +194,24 @@ void at91_macb_hw_init(void) #endif } #endif + +#if defined(CONFIG_ATMEL_MCI) || defined(CONFIG_GENERIC_ATMEL_MCI) +void at91_mci_hw_init(void) +{ + at91_set_a_periph(AT91_PIO_PORTA, 8, 1); /* MCCK */ +#if defined(CONFIG_ATMEL_MCI_PORTB) + at91_set_b_periph(AT91_PIO_PORTA, 1, 1); /* MCCDB */ + at91_set_b_periph(AT91_PIO_PORTA, 0, 1); /* MCDB0 */ + at91_set_b_periph(AT91_PIO_PORTA, 5, 1); /* MCDB1 */ + at91_set_b_periph(AT91_PIO_PORTA, 4, 1); /* MCDB2 */ + at91_set_b_periph(AT91_PIO_PORTA, 3, 1); /* MCDB3 */ +#else + at91_set_a_periph(AT91_PIO_PORTA, 7, 1); /* MCCDA */ + at91_set_a_periph(AT91_PIO_PORTA, 6, 1); /* MCDA0 */ + at91_set_a_periph(AT91_PIO_PORTA, 9, 1); /* MCDA1 */ + at91_set_a_periph(AT91_PIO_PORTA, 10, 1); /* MCDA2 */ + at91_set_a_periph(AT91_PIO_PORTA, 11, 1); /* 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..0067190 100644 --- a/arch/arm/include/asm/arch-at91/at91_common.h +++ b/arch/arm/include/asm/arch-at91/at91_common.h @@ -27,6 +27,7 @@
void at91_can_hw_init(void); void at91_macb_hw_init(void); +void at91_mci_hw_init(void); void at91_serial_hw_init(void); void at91_serial0_hw_init(void); void at91_serial1_hw_init(void); 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..9f732a7 100644 --- a/arch/arm/include/asm/arch-at91/hardware.h +++ b/arch/arm/include/asm/arch-at91/hardware.h @@ -20,6 +20,7 @@ #include <asm/arch-at91/at91rm9200.h> #elif defined(CONFIG_AT91SAM9260) || defined(CONFIG_AT91SAM9G20) #include <asm/arch/at91sam9260.h> +#define AT91_BASE_MCI AT91SAM9260_BASE_MCI #define AT91_BASE_SPI AT91SAM9260_BASE_SPI0 #define AT91_ID_UHP AT91SAM9260_ID_UHP #define AT91_PMC_UHP AT91SAM926x_PMC_UHP diff --git a/doc/README.atmel_mci b/doc/README.atmel_mci new file mode 100644 index 0000000..18b1bdf --- /dev/null +++ b/doc/README.atmel_mci @@ -0,0 +1,86 @@ +How to use SD/MMC cards with Atmel SoCs having MCI hardware +----------------------------------------------------------- +2010-08-16 Reinhard Meyer reinhard.meyer@emk-elektronik.de + +This is a new approach to use Atmel MCI hardware with the +general MMC framework. Therefore it benefits from that +framework's abilities to handle SDHC Cards and the ability +to write blocks. + +- 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 ATMEL devices that have MCI, +including AVR32. + +The generic driver does NOT assign port pins to the MCI block +nor does it start the MCI clock. This has to be handled in a +board/SoC specific manner before the driver is initialized: + +example: this is added to at91sam9260_devices.c: + +#if defined(CONFIG_ATMEL_MCI) || defined(CONFIG_GENERIC_ATMEL_MCI) +void at91_mci_hw_init(void) +{ + at91_set_a_periph(AT91_PIO_PORTA, 8, PUP); /* MCCK */ +#if defined(CONFIG_ATMEL_MCI_PORTB) + at91_set_b_periph(AT91_PIO_PORTA, 1, PUP); /* MCCDB */ + at91_set_b_periph(AT91_PIO_PORTA, 0, PUP); /* MCDB0 */ + at91_set_b_periph(AT91_PIO_PORTA, 5, PUP); /* MCDB1 */ + at91_set_b_periph(AT91_PIO_PORTA, 4, PUP); /* MCDB2 */ + at91_set_b_periph(AT91_PIO_PORTA, 3, PUP); /* MCDB3 */ +#else + at91_set_a_periph(AT91_PIO_PORTA, 7, PUP); /* MCCDA */ + at91_set_a_periph(AT91_PIO_PORTA, 6, PUP); /* MCDA0 */ + at91_set_a_periph(AT91_PIO_PORTA, 9, PUP); /* MCDA1 */ + at91_set_a_periph(AT91_PIO_PORTA, 10, PUP); /* MCDA2 */ + at91_set_a_periph(AT91_PIO_PORTA, 11, PUP); /* MCDA3 */ +#endif +} +#endif + +the board specific file need added: +... +#ifdef CONFIG_GENERIC_ATMEL_MCI +# include <mmc.h> +#endif +... +#ifdef CONFIG_GENERIC_ATMEL_MCI +/* this is a weak define that we are overriding */ +int board_mmc_init(bd_t *bd) +{ + /* Enable clock */ + at91_sys_write(AT91_PMC_PCER, 1 << AT91SAM9260_ID_MCI); + at91_mci_hw_init(); + + /* This calls the atmel_mci_init in gen_atmel_mci.c */ + return atmel_mci_init((void *)AT91_BASE_MCI); +} + +/* this is a weak define that we are overriding */ +int board_mmc_getcd(u8 *cd, struct mmc *mmc) +{ + /* + * the only currently existing use of this function + * (fsl_esdhc.c) suggests this function must return + * *cs = TRUE if a card is NOT detected -> in most + * cases the value of the pin when the detect switch + * closes to GND + */ + *cd = at91_get_gpio_value (CONFIG_SYS_MMC_CD_PIN) ? 1 : 0; + return 0; +} + +#endif + +and the board definition files needs: + +/* SD/MMC card */ +#define CONFIG_MMC 1 +#define CONFIG_GENERIC_MMC 1 +#define CONFIG_GENERIC_ATMEL_MCI 1 +#define CONFIG_ATMEL_MCI_PORTB 1 /* Atmel XE-EK uses port B */ +#define CONFIG_SYS_MMC_CD_PIN AT91_PIN_PC9 +#define CONFIG_CMD_MMC 1 + diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 6fa04b8..3180776 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -25,12 +25,13 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libmmc.a
-COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o -COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o +COBJS-$(CONFIG_GENERIC_MMC) += mmc.o +COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o +COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o
COBJS := $(COBJS-y) diff --git a/drivers/mmc/atmel_mci.h b/drivers/mmc/atmel_mci.h index 5b4f5c9..823a77d 100644 --- a/drivers/mmc/atmel_mci.h +++ b/drivers/mmc/atmel_mci.h @@ -22,7 +22,45 @@ #ifndef __CPU_AT32AP_ATMEL_MCI_H__ #define __CPU_AT32AP_ATMEL_MCI_H__
-/* Atmel MultiMedia Card Interface (MCI) registers */ +#ifndef __ASSEMBLY__ + +/* + * Structure for struct SoC access. + * Names starting with '_' are fillers. + */ +typedef struct atmel_mci { + /* reg Offset */ + u32 cr; /* 0x00 */ + u32 mr; /* 0x04 */ + u32 dtor; /* 0x08 */ + u32 sdcr; /* 0x0c */ + u32 argr; /* 0x10 */ + u32 cmdr; /* 0x14 */ + u32 _18; /* 0x18 */ + u32 _1c; /* 0x1c */ + u32 rspr; /* 0x20 */ + u32 rspr1; /* 0x24 */ + u32 rspr2; /* 0x28 */ + u32 rspr3; /* 0x2c */ + u32 rdr; /* 0x30 */ + u32 tdr; /* 0x34 */ + u32 _38; /* 0x38 */ + u32 _3c; /* 0x3c */ + u32 sr; /* 0x40 */ + u32 ier; /* 0x44 */ + u32 idr; /* 0x48 */ + u32 imr; /* 0x4c */ +} atmel_mci_t; + +#endif /* __ASSEMBLY__ */ + +/* + * NOTICE: Use of registers offsets is depreciated. + * These defines will be removed once the old driver + * is taken out of commision. + * + * Atmel MultiMedia Card Interface (MCI) registers + */ #define MMCI_CR 0x0000 #define MMCI_MR 0x0004 #define MMCI_DTOR 0x0008 @@ -192,7 +230,13 @@ << MMCI_##name##_OFFSET)) \ | MMCI_BF(name,value))
-/* Register access macros */ +/* + * NOTICE: Use of registers offsets is depreciated. + * These defines will be removed once the old driver + * is taken out of commision. + * + * Register access macros + */ #define mmci_readl(reg) \ readl((void *)MMCI_BASE + MMCI_##reg) #define mmci_writel(reg,value) \ diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c new file mode 100644 index 0000000..6754efa --- /dev/null +++ b/drivers/mmc/gen_atmel_mci.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2010 + * Rob Emanuele rob@emanuele.us + * Reinhard Meyer, EMK Elektronik reinhard.meyer@emk-elektronik.de + * + * Original Driver: + * Copyright (C) 2004-2006 Atmel Corporation + * + * 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 <common.h> +#include <mmc.h> +#include <part.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/byteorder.h> +#include <asm/arch/clk.h> +#include <asm/arch/memory-map.h> +#include "atmel_mci.h" + +#ifndef CONFIG_SYS_MMC_CLK_OD +# define CONFIG_SYS_MMC_CLK_OD 150000 +#endif + +#define MMC_DEFAULT_BLKLEN 512 + +#if defined(CONFIG_ATMEL_MCI_PORTB) +# define MCI_BUS 1 +#else +# define MCI_BUS 0 +#endif + +static int initialized = 0; + +/* + * print command and status: + * + * always when DEBUG is defined + * on command errors + */ +static void dump_cmd(u32 cmdr, u32 arg, u32 status, const char* msg) +{ + printf("gen_atmel_mci: CMDR %08x (%2u) ARGR %08x (SR: %08x) %s\n", + cmdr, cmdr&0x3F, arg, status, msg); +} + +/* Setup for MCI Clock and Block Size */ +static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) +{ + atmel_mci_t *mci = (atmel_mci_t *)mmc->priv; + u32 bus_hz = get_mci_clk_rate(); + u32 clkdiv = 255; + + debug("mci: bus_hz is %u, setting clock %u Hz, block size %u\n", + bus_hz, hz, blklen); + if (hz > 0) { + /* find lowest clkdiv yielding a rate <= than requested */ + for (clkdiv=0; clkdiv<255; clkdiv++) { + if ((bus_hz / (clkdiv+1) / 2) <= hz) + break; + } + } + printf("mci: setting clock %u Hz, block size %u\n", + (bus_hz / (clkdiv+1)) / 2, blklen); + + blklen &= 0xfffc; + /* On some platforms RDPROOF and WRPROOF are ignored */ + writel((MMCI_BF(CLKDIV, clkdiv) + | MMCI_BF(BLKLEN, blklen) + | MMCI_BIT(RDPROOF) + | MMCI_BIT(WRPROOF)), &mci->mr); + initialized = 1; +} + +/* Return the CMDR with flags for a given command and data packet */ +static u32 mci_encode_cmd( + struct mmc_cmd *cmd, struct mmc_data *data, u32* error_flags) +{ + u32 cmdr = 0; + + /* Default Flags for Errors */ + *error_flags |= (MMCI_BIT(DTOE) | MMCI_BIT(RDIRE) | MMCI_BIT(RENDE) | + MMCI_BIT(RINDE) | MMCI_BIT(RTOE)); + + /* Default Flags for the Command */ + cmdr |= MMCI_BIT(MAXLAT); + + if (data) { + cmdr |= MMCI_BF(TRCMD, 1); + if (data->blocks > 1) + cmdr |= MMCI_BF(TRTYP, 1); + if (data->flags & MMC_DATA_READ) + cmdr |= MMCI_BIT(TRDIR); + } + + if (cmd->resp_type & MMC_RSP_CRC) + *error_flags |= MMCI_BIT(RCRCE); + if (cmd->resp_type & MMC_RSP_136) + cmdr |= MMCI_BF(RSPTYP, 2); + else if (cmd->resp_type & MMC_RSP_BUSY) + cmdr |= MMCI_BF(RSPTYP, 3); + else if (cmd->resp_type & MMC_RSP_PRESENT) + cmdr |= MMCI_BF(RSPTYP, 1); + + return cmdr | MMCI_BF(CMDNB, cmd->cmdidx); +} + +/* entered into function pointer in mci_send_cmd */ +static u32 mci_data_read(atmel_mci_t *mci, u32* data, u32 error_flags) +{ + u32 status; + + do { + status = readl(&mci->sr); + if (status & (error_flags | MMCI_BIT(OVRE))) + goto io_fail; + } while (!(status & MMCI_BIT(RXRDY))); + + if (status & MMCI_BIT(RXRDY)) { + *data = readl(&mci->rdr); + status = 0; + } +io_fail: + return status; +} + +/* entered into function pointer in mci_send_cmd */ +static u32 mci_data_write(atmel_mci_t *mci, u32* data, u32 error_flags) +{ + u32 status; + + do { + status = readl(&mci->sr); + if (status & (error_flags | MMCI_BIT(UNRE))) + goto io_fail; + } while (!(status & MMCI_BIT(TXRDY))); + + if (status & MMCI_BIT(TXRDY)) { + writel(*data, &mci->tdr); + status = 0; + } +io_fail: + return status; +} + +/* + * entered into mmc structure during driver init + * + * Sends a command out on the bus and deals with the block data. + * Takes the mmc pointer, a command pointer, and an optional data pointer. + */ +static int +mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + atmel_mci_t *mci = (atmel_mci_t *)mmc->priv; + u32 cmdr; + u32 error_flags = 0; + u32 status; + + if (!initialized) { + puts ("MCI not initialized!\n"); + return COMM_ERR; + } + + /* Figure out the transfer arguments */ + cmdr = mci_encode_cmd(cmd, data, &error_flags); + + /* Send the command */ + writel(cmd->cmdarg, &mci->argr); + writel(cmdr, &mci->cmdr); + +#ifdef DEBUG + dump_cmd(cmdr, cmd->cmdarg, 0, "DEBUG"); +#endif + + /* Wait for the command to complete */ + while (!((status = readl(&mci->sr)) & MMCI_BIT(CMDRDY))); + + if (status & error_flags) { + dump_cmd(cmdr, cmd->cmdarg, status, "Command Failed"); + return COMM_ERR; + } + + /* Copy the response to the response buffer */ + if (cmd->resp_type & MMC_RSP_136) { + cmd->response[0] = readl(&mci->rspr); + cmd->response[1] = readl(&mci->rspr1); + cmd->response[2] = readl(&mci->rspr2); + cmd->response[3] = readl(&mci->rspr3); + } else + cmd->response[0] = readl(&mci->rspr); + + /* transfer all of the blocks */ + if (data) { + u32 word_count, block_count; + u32* ioptr; + u32 sys_blocksize, dummy, i; + u32 (*mci_data_op) + (atmel_mci_t *mci, u32* data, u32 error_flags); + + if (data->flags & MMC_DATA_READ) { + mci_data_op = mci_data_read; + sys_blocksize = mmc->read_bl_len; + ioptr = (u32*)data->dest; + } else { + mci_data_op = mci_data_write; + sys_blocksize = mmc->write_bl_len; + ioptr = (u32*)data->src; + } + + status = 0; + for (block_count = 0; + block_count < data->blocks && !status; + block_count++) { + word_count = 0; + do { + status = mci_data_op(mci, ioptr, error_flags); + word_count++; + ioptr++; + } while (!status && word_count < (data->blocksize/4)); +#ifdef DEBUG + if (data->flags & MMC_DATA_READ) + { + printf("Read Data:\n"); + print_buffer(0, data->dest, 1, + word_count*4, 0); + } +#endif +#ifdef DEBUG + if (!status && word_count < (sys_blocksize / 4)) + printf("filling rest of block...\n"); +#endif + /* fill the rest of a full block */ + while (!status && word_count < (sys_blocksize / 4)) { + status = mci_data_op(mci, &dummy, + error_flags); + word_count++; + } + if (status) { + dump_cmd(cmdr, cmd->cmdarg, status, + "Data Transfer Failed"); + return COMM_ERR; + } + } + + /* Wait for Transfer End */ + i = 0; + do { + status = readl(&mci->sr); + + if (status & error_flags) { + dump_cmd(cmdr, cmd->cmdarg, status, + "DTIP Wait Failed"); + return COMM_ERR; + } + i++; + } while ((status & MMCI_BIT(DTIP)) && i < 10000); + if (status & MMCI_BIT(DTIP)) { + dump_cmd(cmdr, cmd->cmdarg, status, + "XFER DTIP never unset, ignoring"); + } + } + + return 0; +} + +/* entered into mmc structure during driver init */ +static void mci_set_ios(struct mmc *mmc) +{ + atmel_mci_t *mci = (atmel_mci_t *)mmc->priv; + int busw = (mmc->bus_width == 4) ? 1 : 0; + + /* Set the clock speed */ + mci_set_mode(mmc, mmc->clock, MMC_DEFAULT_BLKLEN); + + /* + * set the bus width and select slot for this interface + * there is no capability for multiple slots on the same interface yet + * Bitfield SCDBUS needs to be expanded to 2 bits for 8-bit buses + */ + writel(MMCI_BF(SCDBUS, busw) | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); +} + +/* entered into mmc structure during driver init */ +static int mci_init(struct mmc *mmc) +{ + atmel_mci_t *mci = (atmel_mci_t *)mmc->priv; + + /* Initialize controller */ + writel(MMCI_BIT(SWRST), &mci->cr); /* soft reset */ + writel(MMCI_BIT(PWSDIS), &mci->cr); /* disable power save */ + writel(MMCI_BIT(MCIEN), &mci->cr); /* enable mci */ + + /* Initial Time-outs */ + writel(0x5f, &mci->dtor); + /* Disable Interrupts */ + writel(~0UL, &mci->idr); + + /* Set default clocks and blocklen */ + mci_set_mode(mmc, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN); + + return 0; +} + +/* + * this is the only exported function + * + * call it with the MCI register base address + */ +int atmel_mci_init(void *regs) +{ + struct mmc *mmc = malloc(sizeof(struct mmc)); + + if (!mmc) + return -1; + strcpy(mmc->name, "mci"); + mmc->priv = regs; + mmc->send_cmd = mci_send_cmd; + mmc->set_ios = mci_set_ios; + mmc->init = mci_init; + + /* need to be able to pass these in on a board by board basis */ + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->host_caps = MMC_MODE_4BIT; + /* + * min and max frequencies determined by + * max and min of clock divider + */ + mmc->f_min = get_mci_clk_rate() / (2*256); + mmc->f_max = get_mci_clk_rate() / (2*1); + + mmc_register(mmc); + + return 0; +} diff --git a/include/mmc.h b/include/mmc.h index fcb237e..9f94f42 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -179,6 +179,16 @@ struct mmc_cid { char pnm[7]; };
+/* + * WARNING! + * + * This structure is used by atmel_mci.c only. + * It works for the AVR32 architecture but NOT + * for ARM/AT91 architectures. + * Its use is highly depreciated. + * After the atmel_mci.c driver for AVR32 has + * been replaced this structure will be removed. + */ struct mmc_csd { u8 csd_structure:2, @@ -275,7 +285,10 @@ int mmc_set_dev(int dev_num); void print_mmc_devices(char separator); int board_mmc_getcd(u8 *cd, struct mmc *mmc);
-#ifndef CONFIG_GENERIC_MMC +#ifdef CONFIG_GENERIC_MMC +int atmel_mci_init(void *regs); +#else int mmc_legacy_init(int verbose); #endif + #endif /* _MMC_H_ */

Reinhard Meyer schrieb:
This patch adds SD/MMC support to AT91SAM9260/9XE/9G20 using the generic MMC framework.
Signed-off-by: Reinhard Meyer u-boot@emk-elektronik.de
The driver itself is not dependent on any particular AT91 or AVR32 device, but further work in device specific files is required to enable its use for other AT91 (and AVR32) devices: at91<*>_devices.c, hardware.h, at91<*>.h. Patches welcome!
arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c | 21 ++ 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 | 86 +++++ drivers/mmc/Makefile | 5 +- drivers/mmc/atmel_mci.h | 48 +++- drivers/mmc/gen_atmel_mci.c | 353 +++++++++++++++++++++ include/mmc.h | 15 +- 9 files changed, 530 insertions(+), 5 deletions(-)
Applied to u-boot-atmel/next Thanks, Reinhard

Hi Alex,
Thanks to Reinhard, his previous work does well on SAM9260 SAM9G20 SAM9XE SAM9RL (I tested all of them). Since he said he'll provide a more fancy one, I'm just on hold :-)
For your issue, could you try (If you're using the original EK board from Atmel)
Remove R42
- or -
In at91_mci_hw_init() @ arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c , Add a line
at91_set_pio_input(AT91_PIO_PORTA, 2, 0);
This will let the CLK line of dataflash appear high resistance state. (By default, the CLK line of dataflash and SD slot are connected together by a 0R resistor)
BR, Eric
-----Original Message----- From: u-boot-bounces@lists.denx.de [mailto:u-boot-bounces@lists.denx.de] On Behalf Of asc0 Sent: 2010?8?13? 18:19 To: u-boot@lists.denx.de Subject: Re: [U-Boot] [PATCH v2a] AT91: add SD/MMC support
Hello! I have successfully applied your patch on a AT91SAM9G20 cpu and it reads the correct CID & CSD info. At the end of initialization the bread function fails with the following error:
mmc: bread failed, status = 0040c0e5, card status = 00000900
Does someone else received this error or have a hint on this error message?
Thanks, Alex -- View this message in context: http://old.nabble.com/-U-Boot---PATCH--AT91SAM9260-9XE%3A-add- support-for-MultiMedia-Card-Interface-%28MCI%29-tp29068839p294 27373.html Sent from the Uboot - Users mailing list archive at Nabble.com.
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

Hi Eric,
Thanks for your reply. I sorted out, that the AT91Bootstrap impacts the U-Boot. See http://lists.denx.de/pipermail/u-boot/2010-August/075646.html. I am using a hardware which is based on the EK board from Atmel and I will try out the new driver patch from Reinhard to see if it helps me out.
Best regards, Alex
-----Original Message----- From: Xu, Hong [mailto:Hong.Xu@atmel.com] Sent: Monday, August 16, 2010 4:04 AM To: Schleifer, Alexander Cc: u-boot@lists.denx.de; u-boot@emk-elektronik.de Subject: RE: [U-Boot] [PATCH v2a] AT91: add SD/MMC support
Hi Alex,
Thanks to Reinhard, his previous work does well on SAM9260 SAM9G20 SAM9XE SAM9RL (I tested all of them). Since he said he'll provide a more fancy one, I'm just on hold :-)
For your issue, could you try (If you're using the original EK board from Atmel)
Remove R42
- or -
In at91_mci_hw_init() @ arch/arm/cpu/arm926ejs/at91/at91sam9260_devices.c , Add a line
at91_set_pio_input(AT91_PIO_PORTA, 2, 0);
This will let the CLK line of dataflash appear high resistance state. (By default, the CLK line of dataflash and SD slot are connected together by a 0R resistor)
BR, Eric
-----Original Message----- From: u-boot-bounces@lists.denx.de [mailto:u-boot-bounces@lists.denx.de] On Behalf Of asc0 Sent: 2010?8?13? 18:19 To: u-boot@lists.denx.de Subject: Re: [U-Boot] [PATCH v2a] AT91: add SD/MMC support
Hello! I have successfully applied your patch on a AT91SAM9G20 cpu and it reads the correct CID & CSD info. At the end of initialization the bread function fails with the following error:
mmc: bread failed, status = 0040c0e5, card status = 00000900
Does someone else received this error or have a hint on this error message?
Thanks, Alex -- View this message in context: http://old.nabble.com/-U-Boot---PATCH--AT91SAM9260-9XE%3A-add- support-for-MultiMedia-Card-Interface-%28MCI%29-tp29068839p294 27373.html Sent from the Uboot - Users mailing list archive at Nabble.com.
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot
participants (8)
-
asc0
-
Eric Xu
-
Reinhard Meyer
-
Reinhard Meyer
-
Reinhard Meyer (-VC)
-
Schleifer, Alexander
-
Wolfgang Denk
-
Xu, Hong