Signed-off-by: Reinhard Meyer <u-b...@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.me...@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.me...@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 { _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot