[U-Boot] [PATCH 00/10] Add MMC Framework

Here's a new framework (based roughly off the linux one) for managing MMC controllers. It handles all of the standard SD/MMC transactions, leaving the host drivers to implement only what is necessary to deal with their specific hardware.
We make a number of slow steps toward it before dropping it all in.
Then we add a new driver which uses it, and board support for that driver
I have no means of testing (or even compiling) the other drivers which I mucked around with to get to this point, so I'd appreciate any testing the pxa and atmel people could do to make sure I didn't screw them up.
It should be fairly straightforward for anyone to port those drivers to this new framework, but as I have no means of testing them, I didn't want to risk that kind of breakage.

There were several, now there is one (two if you count the lower-case versions).
Signed-off-by: Andy Fleming afleming@freescale.com --- board/delta/nand.c | 2 -- board/zylonite/nand.c | 2 -- common/cmd_bedbug.c | 4 ---- common/cmd_elf.c | 4 ---- cpu/ixp/npe/include/IxEthDB_p.h | 2 -- include/common.h | 3 +++ include/usbdcore.h | 8 -------- 7 files changed, 3 insertions(+), 22 deletions(-)
diff --git a/board/delta/nand.c b/board/delta/nand.c index 14382f5..aff7c54 100644 --- a/board/delta/nand.c +++ b/board/delta/nand.c @@ -46,8 +46,6 @@ # define DFC_DEBUG3(fmt, args...) #endif
-#define MIN(x, y) ((x < y) ? x : y) - /* These really don't belong here, as they are specific to the NAND Model */ static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
diff --git a/board/zylonite/nand.c b/board/zylonite/nand.c index 895fb2b..899445e 100644 --- a/board/zylonite/nand.c +++ b/board/zylonite/nand.c @@ -46,8 +46,6 @@ # define DFC_DEBUG3(fmt, args...) #endif
-#define MIN(x, y) ((x < y) ? x : y) - /* These really don't belong here, as they are specific to the NAND Model */ static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
diff --git a/common/cmd_bedbug.c b/common/cmd_bedbug.c index 3e597f9..e6277c9 100644 --- a/common/cmd_bedbug.c +++ b/common/cmd_bedbug.c @@ -13,10 +13,6 @@
DECLARE_GLOBAL_DATA_PTR;
-#ifndef MAX -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#endif - extern void show_regs __P ((struct pt_regs *)); extern int run_command __P ((const char *, int)); extern char console_buffer[]; diff --git a/common/cmd_elf.c b/common/cmd_elf.c index 3ebb6d9..4d8e1d2 100644 --- a/common/cmd_elf.c +++ b/common/cmd_elf.c @@ -23,10 +23,6 @@ DECLARE_GLOBAL_DATA_PTR; #endif
-#ifndef MAX -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#endif - int valid_elf_image (unsigned long addr); unsigned long load_elf_image (unsigned long addr);
diff --git a/cpu/ixp/npe/include/IxEthDB_p.h b/cpu/ixp/npe/include/IxEthDB_p.h index e7c67ae..ccec7ea 100644 --- a/cpu/ixp/npe/include/IxEthDB_p.h +++ b/cpu/ixp/npe/include/IxEthDB_p.h @@ -193,8 +193,6 @@ extern int overflowEvent; #define LEFT (-1)
/* macros */ -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - #define IX_ETH_DB_CHECK_PORT_EXISTS(portID) \ { \ if ((portID) >= IX_ETH_DB_NUMBER_OF_PORTS) \ diff --git a/include/common.h b/include/common.h index b8a654a..df64bf0 100644 --- a/include/common.h +++ b/include/common.h @@ -177,6 +177,9 @@ typedef void (interrupt_handler_t)(void *); ({ typeof (X) __x = (X), __y = (Y); \ (__x > __y) ? __x : __y; })
+#define MIN(x, y) min(x, y) +#define MAX(x, y) max(x, y) +
/** * container_of - cast a member of a structure out to the containing structure diff --git a/include/usbdcore.h b/include/usbdcore.h index cb2be72..206dbbc 100644 --- a/include/usbdcore.h +++ b/include/usbdcore.h @@ -126,14 +126,6 @@ }) #endif
-#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif -#ifndef MAX -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#endif - - /* * Structure member address manipulation macros. * These are used by client code (code using the urb_link routines), since

MMC cards are not memory, so we stop treating them that way.
Signed-off-by: Andy Fleming afleming@freescale.com --- common/cmd_mem.c | 43 ------------------------------------------- cpu/arm720t/lpc2292/mmc.c | 26 -------------------------- cpu/pxa/mmc.c | 14 -------------- drivers/mmc/atmel_mci.c | 15 --------------- include/mmc.h | 1 - 5 files changed, 0 insertions(+), 99 deletions(-)
diff --git a/common/cmd_mem.c b/common/cmd_mem.c index d7666c2..960f753 100644 --- a/common/cmd_mem.c +++ b/common/cmd_mem.c @@ -29,9 +29,6 @@
#include <common.h> #include <command.h> -#if defined(CONFIG_CMD_MMC) -#include <mmc.h> -#endif #ifdef CONFIG_HAS_DATAFLASH #include <dataflash.h> #endif @@ -404,46 +401,6 @@ int do_mem_cp ( cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) } #endif
-#if defined(CONFIG_CMD_MMC) - if (mmc2info(dest)) { - int rc; - - puts ("Copy to MMC... "); - switch (rc = mmc_write ((uchar *)addr, dest, count*size)) { - case 0: - putc ('\n'); - return 1; - case -1: - puts ("failed\n"); - return 1; - default: - printf ("%s[%d] FIXME: rc=%d\n",__FILE__,__LINE__,rc); - return 1; - } - puts ("done\n"); - return 0; - } - - if (mmc2info(addr)) { - int rc; - - puts ("Copy from MMC... "); - switch (rc = mmc_read (addr, (uchar *)dest, count*size)) { - case 0: - putc ('\n'); - return 1; - case -1: - puts ("failed\n"); - return 1; - default: - printf ("%s[%d] FIXME: rc=%d\n",__FILE__,__LINE__,rc); - return 1; - } - puts ("done\n"); - return 0; - } -#endif - #ifdef CONFIG_HAS_DATAFLASH /* Check if we are copying from RAM or Flash to DataFlash */ if (addr_dataflash(dest) && !addr_dataflash(addr)){ diff --git a/cpu/arm720t/lpc2292/mmc.c b/cpu/arm720t/lpc2292/mmc.c index fd7f149..792a884 100644 --- a/cpu/arm720t/lpc2292/mmc.c +++ b/cpu/arm720t/lpc2292/mmc.c @@ -128,30 +128,4 @@ int mmc_init(int verbose) return ret; }
-int mmc_write(uchar * src, ulong dst, int size) -{ -#ifdef MMC_DEBUG - printf("mmc_write: src=%p, dst=%lu, size=%u\n", src, dst, size); -#endif - /* Since mmc2info always returns 0 this function will never be called */ - return 0; -} - -int mmc_read(ulong src, uchar * dst, int size) -{ -#ifdef MMC_DEBUG - printf("mmc_read: src=%lu, dst=%p, size=%u\n", src, dst, size); -#endif - /* Since mmc2info always returns 0 this function will never be called */ - return 0; -} - -int mmc2info(ulong addr) -{ - /* This function is used by cmd_cp to determine if source or destination - address resides on MMC-card or not. We do not support copy to and from - MMC-card so we always return 0. */ - return 0; -} - #endif /* CONFIG_MMC */ diff --git a/cpu/pxa/mmc.c b/cpu/pxa/mmc.c index d735c8d..1f0d488 100644 --- a/cpu/pxa/mmc.c +++ b/cpu/pxa/mmc.c @@ -645,18 +645,4 @@ mmc_init(int verbose) return rc; }
-int mmc_ident(block_dev_desc_t * dev) -{ - return 0; -} - -int mmc2info(ulong addr) -{ - if (addr >= CONFIG_SYS_MMC_BASE - && addr < CONFIG_SYS_MMC_BASE + (mmc_dev.lba * mmc_dev.blksz)) { - return 1; - } - return 0; -} - #endif /* CONFIG_MMC */ diff --git a/drivers/mmc/atmel_mci.c b/drivers/mmc/atmel_mci.c index 3aa92f2..4028256 100644 --- a/drivers/mmc/atmel_mci.c +++ b/drivers/mmc/atmel_mci.c @@ -531,18 +531,3 @@ int mmc_init(int verbose)
return 0; } - -int mmc_read(ulong src, uchar *dst, int size) -{ - return -ENOSYS; -} - -int mmc_write(uchar *src, ulong dst, int size) -{ - return -ENOSYS; -} - -int mmc2info(ulong addr) -{ - return 0; -} diff --git a/include/mmc.h b/include/mmc.h index 19c76fe..26b9553 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -52,6 +52,5 @@ int mmc_init(int verbose); int mmc_read(ulong src, uchar *dst, int size); int mmc_write(uchar *src, ulong dst, int size); -int mmc2info(ulong addr);
#endif /* _MMC_H_ */

This is to get it out of the way of incoming MMC framework
Signed-off-by: Andy Fleming afleming@freescale.com --- common/cmd_mmc.c | 2 +- cpu/arm720t/lpc2292/mmc.c | 4 ++-- cpu/pxa/mmc.c | 2 +- drivers/mmc/atmel_mci.c | 2 +- include/mmc.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index 25c9702..21873d6 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -27,7 +27,7 @@
int do_mmc (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { - if (mmc_init (1) != 0) { + if (mmc_legacy_init (1) != 0) { printf ("No MMC card found\n"); return 1; } diff --git a/cpu/arm720t/lpc2292/mmc.c b/cpu/arm720t/lpc2292/mmc.c index 792a884..beaffe9 100644 --- a/cpu/arm720t/lpc2292/mmc.c +++ b/cpu/arm720t/lpc2292/mmc.c @@ -93,12 +93,12 @@ static int mmc_hw_get_parameters(void) return 0; }
-int mmc_init(int verbose) +int mmc_legacy_init(int verbose) { int ret = -ENODEV;
if (verbose) - printf("mmc_init\n"); + printf("mmc_legacy_init\n");
spi_init(); /* this meeds to be done twice */ diff --git a/cpu/pxa/mmc.c b/cpu/pxa/mmc.c index 1f0d488..7199e89 100644 --- a/cpu/pxa/mmc.c +++ b/cpu/pxa/mmc.c @@ -543,7 +543,7 @@ static void mmc_decode_csd(uint32_t * resp)
int /****************************************************/ -mmc_init(int verbose) +mmc_legacy_init(int verbose) /****************************************************/ { int retries, rc = -ENODEV; diff --git a/drivers/mmc/atmel_mci.c b/drivers/mmc/atmel_mci.c index 4028256..3946ffe 100644 --- a/drivers/mmc/atmel_mci.c +++ b/drivers/mmc/atmel_mci.c @@ -463,7 +463,7 @@ static void mci_set_data_timeout(struct mmc_csd *csd) dtocyc << shift, dtor); }
-int mmc_init(int verbose) +int mmc_legacy_init(int verbose) { struct mmc_cid cid; struct mmc_csd csd; diff --git a/include/mmc.h b/include/mmc.h index 26b9553..47cc053 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -49,7 +49,7 @@ #define SD_CMD_APP_SET_BUS_WIDTH 6 #define SD_CMD_APP_SEND_OP_COND 41
-int mmc_init(int verbose); +int mmc_legacy_init(int verbose); int mmc_read(ulong src, uchar *dst, int size); int mmc_write(uchar *src, ulong dst, int size);

The current MMC infrastructure relies on the existence of an arch-specific header file. This isn't necessary, and a couple drivers were forced to implement dummy files to meet this requirement. Instead, we move the stuff in those header files into a more appropriate place, and eliminate the stubs and the #include of asm/arch/mmc.h
Signed-off-by: Andy Fleming afleming@freescale.com --- cpu/pxa/mmc.c | 2 + cpu/pxa/mmc.h | 189 +++++++++++++++++++++++++++++++ include/asm-arm/arch-lpc2292/mmc.h | 22 ---- include/asm-arm/arch-pxa/mmc.h | 189 ------------------------------- include/asm-avr32/arch-at32ap700x/mmc.h | 77 ------------- include/mmc.h | 1 - 6 files changed, 191 insertions(+), 289 deletions(-) create mode 100644 cpu/pxa/mmc.h delete mode 100644 include/asm-arm/arch-lpc2292/mmc.h delete mode 100644 include/asm-arm/arch-pxa/mmc.h delete mode 100644 include/asm-avr32/arch-at32ap700x/mmc.h
diff --git a/cpu/pxa/mmc.c b/cpu/pxa/mmc.c index 7199e89..8f5277e 100644 --- a/cpu/pxa/mmc.c +++ b/cpu/pxa/mmc.c @@ -28,6 +28,8 @@ #include <asm/arch/hardware.h> #include <part.h>
+#include "mmc.h" + #ifdef CONFIG_MMC
extern int fat_register_device(block_dev_desc_t * dev_desc, int part_no); diff --git a/cpu/pxa/mmc.h b/cpu/pxa/mmc.h new file mode 100644 index 0000000..85e144b --- /dev/null +++ b/cpu/pxa/mmc.h @@ -0,0 +1,189 @@ +/* + * linux/drivers/mmc/mmc_pxa.h + * + * Author: Vladimir Shebordaev, Igor Oblakov + * Copyright: MontaVista Software Inc. + * + * $Id: mmc_pxa.h,v 0.3.1.6 2002/09/25 19:25:48 ted Exp ted $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __MMC_PXA_P_H__ +#define __MMC_PXA_P_H__ + +/* PXA-250 MMC controller registers */ + +/* MMC_STRPCL */ +#define MMC_STRPCL_STOP_CLK (0x0001UL) +#define MMC_STRPCL_START_CLK (0x0002UL) + +/* MMC_STAT */ +#define MMC_STAT_END_CMD_RES (0x0001UL << 13) +#define MMC_STAT_PRG_DONE (0x0001UL << 12) +#define MMC_STAT_DATA_TRAN_DONE (0x0001UL << 11) +#define MMC_STAT_CLK_EN (0x0001UL << 8) +#define MMC_STAT_RECV_FIFO_FULL (0x0001UL << 7) +#define MMC_STAT_XMIT_FIFO_EMPTY (0x0001UL << 6) +#define MMC_STAT_RES_CRC_ERROR (0x0001UL << 5) +#define MMC_STAT_SPI_READ_ERROR_TOKEN (0x0001UL << 4) +#define MMC_STAT_CRC_READ_ERROR (0x0001UL << 3) +#define MMC_STAT_CRC_WRITE_ERROR (0x0001UL << 2) +#define MMC_STAT_TIME_OUT_RESPONSE (0x0001UL << 1) +#define MMC_STAT_READ_TIME_OUT (0x0001UL) + +#define MMC_STAT_ERRORS (MMC_STAT_RES_CRC_ERROR|MMC_STAT_SPI_READ_ERROR_TOKEN\ + |MMC_STAT_CRC_READ_ERROR|MMC_STAT_TIME_OUT_RESPONSE\ + |MMC_STAT_READ_TIME_OUT|MMC_STAT_CRC_WRITE_ERROR) + +/* MMC_CLKRT */ +#define MMC_CLKRT_20MHZ (0x0000UL) +#define MMC_CLKRT_10MHZ (0x0001UL) +#define MMC_CLKRT_5MHZ (0x0002UL) +#define MMC_CLKRT_2_5MHZ (0x0003UL) +#define MMC_CLKRT_1_25MHZ (0x0004UL) +#define MMC_CLKRT_0_625MHZ (0x0005UL) +#define MMC_CLKRT_0_3125MHZ (0x0006UL) + +/* MMC_SPI */ +#define MMC_SPI_DISABLE (0x00UL) +#define MMC_SPI_EN (0x01UL) +#define MMC_SPI_CS_EN (0x01UL << 2) +#define MMC_SPI_CS_ADDRESS (0x01UL << 3) +#define MMC_SPI_CRC_ON (0x01UL << 1) + +/* MMC_CMDAT */ +#define MMC_CMDAT_SD_4DAT (0x0001UL << 8) +#define MMC_CMDAT_MMC_DMA_EN (0x0001UL << 7) +#define MMC_CMDAT_INIT (0x0001UL << 6) +#define MMC_CMDAT_BUSY (0x0001UL << 5) +#define MMC_CMDAT_BCR (0x0003UL << 5) +#define MMC_CMDAT_STREAM (0x0001UL << 4) +#define MMC_CMDAT_BLOCK (0x0000UL << 4) +#define MMC_CMDAT_WRITE (0x0001UL << 3) +#define MMC_CMDAT_READ (0x0000UL << 3) +#define MMC_CMDAT_DATA_EN (0x0001UL << 2) +#define MMC_CMDAT_R0 (0) +#define MMC_CMDAT_R1 (0x0001UL) +#define MMC_CMDAT_R2 (0x0002UL) +#define MMC_CMDAT_R3 (0x0003UL) + +/* MMC_RESTO */ +#define MMC_RES_TO_MAX (0x007fUL) /* [6:0] */ + +/* MMC_RDTO */ +#define MMC_READ_TO_MAX (0x0ffffUL) /* [15:0] */ + +/* MMC_BLKLEN */ +#define MMC_BLK_LEN_MAX (0x03ffUL) /* [9:0] */ + +/* MMC_PRTBUF */ +#define MMC_PRTBUF_BUF_PART_FULL (0x01UL) +#define MMC_PRTBUF_BUF_FULL (0x00UL ) + +/* MMC_I_MASK */ +#define MMC_I_MASK_TXFIFO_WR_REQ (0x01UL << 6) +#define MMC_I_MASK_RXFIFO_RD_REQ (0x01UL << 5) +#define MMC_I_MASK_CLK_IS_OFF (0x01UL << 4) +#define MMC_I_MASK_STOP_CMD (0x01UL << 3) +#define MMC_I_MASK_END_CMD_RES (0x01UL << 2) +#define MMC_I_MASK_PRG_DONE (0x01UL << 1) +#define MMC_I_MASK_DATA_TRAN_DONE (0x01UL) +#define MMC_I_MASK_ALL (0x07fUL) + + +/* MMC_I_REG */ +#define MMC_I_REG_TXFIFO_WR_REQ (0x01UL << 6) +#define MMC_I_REG_RXFIFO_RD_REQ (0x01UL << 5) +#define MMC_I_REG_CLK_IS_OFF (0x01UL << 4) +#define MMC_I_REG_STOP_CMD (0x01UL << 3) +#define MMC_I_REG_END_CMD_RES (0x01UL << 2) +#define MMC_I_REG_PRG_DONE (0x01UL << 1) +#define MMC_I_REG_DATA_TRAN_DONE (0x01UL) +#define MMC_I_REG_ALL (0x007fUL) + +/* MMC_CMD */ +#define MMC_CMD_INDEX_MAX (0x006fUL) /* [5:0] */ +#define CMD(x) (x) + +#define MMC_DEFAULT_RCA 1 + +#define MMC_BLOCK_SIZE 512 +#define MMC_MAX_BLOCK_SIZE 512 + +#define MMC_R1_IDLE_STATE 0x01 +#define MMC_R1_ERASE_STATE 0x02 +#define MMC_R1_ILLEGAL_CMD 0x04 +#define MMC_R1_COM_CRC_ERR 0x08 +#define MMC_R1_ERASE_SEQ_ERR 0x01 +#define MMC_R1_ADDR_ERR 0x02 +#define MMC_R1_PARAM_ERR 0x04 + +#define MMC_R1B_WP_ERASE_SKIP 0x0002 +#define MMC_R1B_ERR 0x0004 +#define MMC_R1B_CC_ERR 0x0008 +#define MMC_R1B_CARD_ECC_ERR 0x0010 +#define MMC_R1B_WP_VIOLATION 0x0020 +#define MMC_R1B_ERASE_PARAM 0x0040 +#define MMC_R1B_OOR 0x0080 +#define MMC_R1B_IDLE_STATE 0x0100 +#define MMC_R1B_ERASE_RESET 0x0200 +#define MMC_R1B_ILLEGAL_CMD 0x0400 +#define MMC_R1B_COM_CRC_ERR 0x0800 +#define MMC_R1B_ERASE_SEQ_ERR 0x1000 +#define MMC_R1B_ADDR_ERR 0x2000 +#define MMC_R1B_PARAM_ERR 0x4000 + +typedef struct mmc_cid +{ +/* FIXME: BYTE_ORDER */ + uchar year:4, + month:4; + uchar sn[3]; + uchar fwrev:4, + hwrev:4; + uchar name[6]; + uchar id[3]; +} mmc_cid_t; + +typedef struct mmc_csd +{ + uint8_t csd_structure:2, + spec_ver:4, + rsvd1:2; + uint8_t taac; + uint8_t nsac; + uint8_t tran_speed; + uint16_t ccc:12, + read_bl_len:4; + uint64_t 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, + erase_blk_en:1, + sector_size:7, + wp_grp_size:7, + wp_grp_enable:1, + default_ecc:2, + r2w_factor:3, + write_bl_len:4, + write_bl_partial:1, + rsvd3:4, + content_prot_app:1; + uint8_t file_format_grp:1, + copy:1, + perm_write_protect:1, + tmp_write_protect:1, + file_format:2, + ecc:2; +} mmc_csd_t; + +#endif /* __MMC_PXA_P_H__ */ diff --git a/include/asm-arm/arch-lpc2292/mmc.h b/include/asm-arm/arch-lpc2292/mmc.h deleted file mode 100644 index e664a5f..0000000 --- a/include/asm-arm/arch-lpc2292/mmc.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * A dummy header file for use with the LPC2292 port to keep the - * compiler happy. - * - * 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 - */ -#ifndef _MMC_ARM_TDM_H_ -#define _MMC_ARM_TDM_H_ -#endif /* _MMC_ARM_TDM_H_ */ diff --git a/include/asm-arm/arch-pxa/mmc.h b/include/asm-arm/arch-pxa/mmc.h deleted file mode 100644 index 85e144b..0000000 --- a/include/asm-arm/arch-pxa/mmc.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * linux/drivers/mmc/mmc_pxa.h - * - * Author: Vladimir Shebordaev, Igor Oblakov - * Copyright: MontaVista Software Inc. - * - * $Id: mmc_pxa.h,v 0.3.1.6 2002/09/25 19:25:48 ted Exp ted $ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef __MMC_PXA_P_H__ -#define __MMC_PXA_P_H__ - -/* PXA-250 MMC controller registers */ - -/* MMC_STRPCL */ -#define MMC_STRPCL_STOP_CLK (0x0001UL) -#define MMC_STRPCL_START_CLK (0x0002UL) - -/* MMC_STAT */ -#define MMC_STAT_END_CMD_RES (0x0001UL << 13) -#define MMC_STAT_PRG_DONE (0x0001UL << 12) -#define MMC_STAT_DATA_TRAN_DONE (0x0001UL << 11) -#define MMC_STAT_CLK_EN (0x0001UL << 8) -#define MMC_STAT_RECV_FIFO_FULL (0x0001UL << 7) -#define MMC_STAT_XMIT_FIFO_EMPTY (0x0001UL << 6) -#define MMC_STAT_RES_CRC_ERROR (0x0001UL << 5) -#define MMC_STAT_SPI_READ_ERROR_TOKEN (0x0001UL << 4) -#define MMC_STAT_CRC_READ_ERROR (0x0001UL << 3) -#define MMC_STAT_CRC_WRITE_ERROR (0x0001UL << 2) -#define MMC_STAT_TIME_OUT_RESPONSE (0x0001UL << 1) -#define MMC_STAT_READ_TIME_OUT (0x0001UL) - -#define MMC_STAT_ERRORS (MMC_STAT_RES_CRC_ERROR|MMC_STAT_SPI_READ_ERROR_TOKEN\ - |MMC_STAT_CRC_READ_ERROR|MMC_STAT_TIME_OUT_RESPONSE\ - |MMC_STAT_READ_TIME_OUT|MMC_STAT_CRC_WRITE_ERROR) - -/* MMC_CLKRT */ -#define MMC_CLKRT_20MHZ (0x0000UL) -#define MMC_CLKRT_10MHZ (0x0001UL) -#define MMC_CLKRT_5MHZ (0x0002UL) -#define MMC_CLKRT_2_5MHZ (0x0003UL) -#define MMC_CLKRT_1_25MHZ (0x0004UL) -#define MMC_CLKRT_0_625MHZ (0x0005UL) -#define MMC_CLKRT_0_3125MHZ (0x0006UL) - -/* MMC_SPI */ -#define MMC_SPI_DISABLE (0x00UL) -#define MMC_SPI_EN (0x01UL) -#define MMC_SPI_CS_EN (0x01UL << 2) -#define MMC_SPI_CS_ADDRESS (0x01UL << 3) -#define MMC_SPI_CRC_ON (0x01UL << 1) - -/* MMC_CMDAT */ -#define MMC_CMDAT_SD_4DAT (0x0001UL << 8) -#define MMC_CMDAT_MMC_DMA_EN (0x0001UL << 7) -#define MMC_CMDAT_INIT (0x0001UL << 6) -#define MMC_CMDAT_BUSY (0x0001UL << 5) -#define MMC_CMDAT_BCR (0x0003UL << 5) -#define MMC_CMDAT_STREAM (0x0001UL << 4) -#define MMC_CMDAT_BLOCK (0x0000UL << 4) -#define MMC_CMDAT_WRITE (0x0001UL << 3) -#define MMC_CMDAT_READ (0x0000UL << 3) -#define MMC_CMDAT_DATA_EN (0x0001UL << 2) -#define MMC_CMDAT_R0 (0) -#define MMC_CMDAT_R1 (0x0001UL) -#define MMC_CMDAT_R2 (0x0002UL) -#define MMC_CMDAT_R3 (0x0003UL) - -/* MMC_RESTO */ -#define MMC_RES_TO_MAX (0x007fUL) /* [6:0] */ - -/* MMC_RDTO */ -#define MMC_READ_TO_MAX (0x0ffffUL) /* [15:0] */ - -/* MMC_BLKLEN */ -#define MMC_BLK_LEN_MAX (0x03ffUL) /* [9:0] */ - -/* MMC_PRTBUF */ -#define MMC_PRTBUF_BUF_PART_FULL (0x01UL) -#define MMC_PRTBUF_BUF_FULL (0x00UL ) - -/* MMC_I_MASK */ -#define MMC_I_MASK_TXFIFO_WR_REQ (0x01UL << 6) -#define MMC_I_MASK_RXFIFO_RD_REQ (0x01UL << 5) -#define MMC_I_MASK_CLK_IS_OFF (0x01UL << 4) -#define MMC_I_MASK_STOP_CMD (0x01UL << 3) -#define MMC_I_MASK_END_CMD_RES (0x01UL << 2) -#define MMC_I_MASK_PRG_DONE (0x01UL << 1) -#define MMC_I_MASK_DATA_TRAN_DONE (0x01UL) -#define MMC_I_MASK_ALL (0x07fUL) - - -/* MMC_I_REG */ -#define MMC_I_REG_TXFIFO_WR_REQ (0x01UL << 6) -#define MMC_I_REG_RXFIFO_RD_REQ (0x01UL << 5) -#define MMC_I_REG_CLK_IS_OFF (0x01UL << 4) -#define MMC_I_REG_STOP_CMD (0x01UL << 3) -#define MMC_I_REG_END_CMD_RES (0x01UL << 2) -#define MMC_I_REG_PRG_DONE (0x01UL << 1) -#define MMC_I_REG_DATA_TRAN_DONE (0x01UL) -#define MMC_I_REG_ALL (0x007fUL) - -/* MMC_CMD */ -#define MMC_CMD_INDEX_MAX (0x006fUL) /* [5:0] */ -#define CMD(x) (x) - -#define MMC_DEFAULT_RCA 1 - -#define MMC_BLOCK_SIZE 512 -#define MMC_MAX_BLOCK_SIZE 512 - -#define MMC_R1_IDLE_STATE 0x01 -#define MMC_R1_ERASE_STATE 0x02 -#define MMC_R1_ILLEGAL_CMD 0x04 -#define MMC_R1_COM_CRC_ERR 0x08 -#define MMC_R1_ERASE_SEQ_ERR 0x01 -#define MMC_R1_ADDR_ERR 0x02 -#define MMC_R1_PARAM_ERR 0x04 - -#define MMC_R1B_WP_ERASE_SKIP 0x0002 -#define MMC_R1B_ERR 0x0004 -#define MMC_R1B_CC_ERR 0x0008 -#define MMC_R1B_CARD_ECC_ERR 0x0010 -#define MMC_R1B_WP_VIOLATION 0x0020 -#define MMC_R1B_ERASE_PARAM 0x0040 -#define MMC_R1B_OOR 0x0080 -#define MMC_R1B_IDLE_STATE 0x0100 -#define MMC_R1B_ERASE_RESET 0x0200 -#define MMC_R1B_ILLEGAL_CMD 0x0400 -#define MMC_R1B_COM_CRC_ERR 0x0800 -#define MMC_R1B_ERASE_SEQ_ERR 0x1000 -#define MMC_R1B_ADDR_ERR 0x2000 -#define MMC_R1B_PARAM_ERR 0x4000 - -typedef struct mmc_cid -{ -/* FIXME: BYTE_ORDER */ - uchar year:4, - month:4; - uchar sn[3]; - uchar fwrev:4, - hwrev:4; - uchar name[6]; - uchar id[3]; -} mmc_cid_t; - -typedef struct mmc_csd -{ - uint8_t csd_structure:2, - spec_ver:4, - rsvd1:2; - uint8_t taac; - uint8_t nsac; - uint8_t tran_speed; - uint16_t ccc:12, - read_bl_len:4; - uint64_t 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, - erase_blk_en:1, - sector_size:7, - wp_grp_size:7, - wp_grp_enable:1, - default_ecc:2, - r2w_factor:3, - write_bl_len:4, - write_bl_partial:1, - rsvd3:4, - content_prot_app:1; - uint8_t file_format_grp:1, - copy:1, - perm_write_protect:1, - tmp_write_protect:1, - file_format:2, - ecc:2; -} mmc_csd_t; - -#endif /* __MMC_PXA_P_H__ */ diff --git a/include/asm-avr32/arch-at32ap700x/mmc.h b/include/asm-avr32/arch-at32ap700x/mmc.h deleted file mode 100644 index 9caba91..0000000 --- a/include/asm-avr32/arch-at32ap700x/mmc.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 - */ -#ifndef __ASM_AVR32_MMC_H -#define __ASM_AVR32_MMC_H - -struct mmc_cid { - unsigned long psn; - unsigned short oid; - unsigned char mid; - unsigned char prv; - unsigned char mdt; - 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; -}; - -#define R1_ILLEGAL_COMMAND (1 << 22) -#define R1_APP_CMD (1 << 5) - -#endif /* __ASM_AVR32_MMC_H */ diff --git a/include/mmc.h b/include/mmc.h index 47cc053..7a03ed2 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -23,7 +23,6 @@
#ifndef _MMC_H_ #define _MMC_H_ -#include <asm/arch/mmc.h>
/* MMC command numbers */ #define MMC_CMD_GO_IDLE_STATE 0

These names are being taken over by the new MMC framework. Hopefuly the PXA can be easily ported, and these functions will go away entirely.
Signed-off-by: Andy Fleming afleming@freescale.com --- cpu/pxa/mmc.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/cpu/pxa/mmc.c b/cpu/pxa/mmc.c index 8f5277e..de77c12 100644 --- a/cpu/pxa/mmc.c +++ b/cpu/pxa/mmc.c @@ -218,7 +218,7 @@ mmc_block_write(ulong dst, uchar * src, int len)
int /****************************************************/ -mmc_read(ulong src, uchar * dst, int size) +pxa_mmc_read(long src, uchar * dst, int size) /****************************************************/ { ulong end, part_start, part_end, part_len, aligned_start, aligned_end; @@ -294,7 +294,7 @@ mmc_read(ulong src, uchar * dst, int size)
int /****************************************************/ -mmc_write(uchar * src, ulong dst, int size) +pxa_mmc_write(uchar * src, ulong dst, int size) /****************************************************/ { ulong end, part_start, part_end, part_len, aligned_start, aligned_end; @@ -375,7 +375,7 @@ mmc_write(uchar * src, ulong dst, int size) return 0; }
-ulong +static ulong /****************************************************/ mmc_bread(int dev_num, ulong blknr, lbaint_t blkcnt, void *dst) /****************************************************/ @@ -383,7 +383,7 @@ mmc_bread(int dev_num, ulong blknr, lbaint_t blkcnt, void *dst) int mmc_block_size = MMC_BLOCK_SIZE; ulong src = blknr * mmc_block_size + CONFIG_SYS_MMC_BASE;
- mmc_read(src, (uchar *) dst, blkcnt * mmc_block_size); + pxa_mmc_read(src, (uchar *) dst, blkcnt * mmc_block_size); return blkcnt; }

Here's a new framework (based roughly off the linux one) for managing MMC controllers. It handles all of the standard SD/MMC transactions, leaving the host drivers to implement only what is necessary to deal with their specific hardware.
This also hooks the infrastructure into the PowerPC board code (similar to how the ethernet infrastructure now hooks in)
Some of this code was contributed by Dave Liu daveliu@freescale.com
Signed-off-by: Andy Fleming afleming@freescale.com --- common/cmd_mmc.c | 122 +++++++ drivers/mmc/Makefile | 1 + drivers/mmc/mmc.c | 926 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 174 +++++++++- lib_ppc/board.c | 9 + 5 files changed, 1223 insertions(+), 9 deletions(-) create mode 100644 drivers/mmc/mmc.c
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index 21873d6..f27d7ef 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -25,6 +25,7 @@ #include <command.h> #include <mmc.h>
+#ifndef CONFIG_GENERIC_MMC int do_mmc (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { if (mmc_legacy_init (1) != 0) { @@ -39,3 +40,124 @@ U_BOOT_CMD( "mmcinit - init mmc card\n", NULL ); +#endif /* !CONFIG_GENERIC_MMC */ + +static void print_mmcinfo(struct mmc *mmc) +{ + printf("Device: %s\n", mmc->name); + printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24); + printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff); + printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff, + (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, + (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); + + printf("Tran Speed: %d\n", mmc->tran_speed); + printf("Rd Block Len: %d\n", mmc->read_bl_len); + + printf("%s version %d.%d\n", IS_SD(mmc) ? "SD" : "MMC", + (mmc->version >> 4) & 0xf, mmc->version & 0xf); + + printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No"); + printf("Capacity: %lld\n", mmc->capacity); + + printf("Bus Width: %d-bit\n", mmc->bus_width); +} + +int do_mmcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + struct mmc *mmc; + int dev_num; + + if (argc < 2) + dev_num = 0; + else + dev_num = simple_strtoul(argv[1], NULL, 0); + + mmc = find_mmc_device(dev_num); + + if (mmc) { + mmc_init(mmc); + + print_mmcinfo(mmc); + } + + return 0; +} + +U_BOOT_CMD(mmcinfo, 2, 0, do_mmcinfo, "mmcinfo <dev num>-- display MMC info\n", + NULL); + +int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int rc = 0; + + switch (argc) { + case 0: + case 1: + case 3: + case 4: + printf("Usage:\n%s\n", cmdtp->usage); + return 1; + + case 2: + if (!strcmp(argv[1], "list")) { + print_mmc_devices('\n'); + return 0; + } + return 1; + default: /* at least 5 args */ + if (strcmp(argv[1], "read") == 0) { + int dev = simple_strtoul(argv[2], NULL, 10); + void *addr = (void *)simple_strtoul(argv[3], NULL, 16); + u32 cnt = simple_strtoul(argv[5], NULL, 16); + u32 n; + u32 blk = simple_strtoul(argv[4], NULL, 16); + struct mmc *mmc = find_mmc_device(dev); + + printf("\nMMC read: dev # %d, block # %d, count %d ... ", + dev, blk, cnt); + + mmc_init(mmc); + + n = mmc->block_dev.block_read(dev, blk, cnt, addr); + + /* flush cache after read */ + flush_cache((ulong)addr, cnt * 512); //FIXME + + printf("%d blocks read: %s\n", + n, (n==cnt) ? "OK" : "ERROR"); + return (n == cnt) ? 0 : 1; + } else if (strcmp(argv[1], "write") == 0) { + int dev = simple_strtoul(argv[2], NULL, 10); + void *addr = (void *)simple_strtoul(argv[3], NULL, 16); + u32 cnt = simple_strtoul(argv[5], NULL, 16); + u32 n; + struct mmc *mmc = find_mmc_device(dev); + + int blk = simple_strtoul(argv[4], NULL, 16); + + printf("\nMMC write: dev # %d, block # %d, count %d ... ", + dev, blk, cnt); + + mmc_init(mmc); + + n = mmc->block_dev.block_write(dev, blk, cnt, addr); + + printf("%d blocks written: %s\n", + n, (n == cnt) ? "OK" : "ERROR"); + return (n == cnt) ? 0 : 1; + } else { + printf("Usage:\n%s\n", cmdtp->usage); + rc = 1; + } + + return rc; + } +} + +U_BOOT_CMD( + mmc, 6, 1, do_mmcops, + "mmc - MMC sub system\n", + "mmc read <device num> addr blk# cnt\n" + "mmc write <device num> addr blk# cnt\n" + "mmc list - lists available devices\n"); diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 3dc031b..d5a969c 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -25,6 +25,7 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libmmc.a
+COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
COBJS := $(COBJS-y) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c new file mode 100644 index 0000000..cd4eb77 --- /dev/null +++ b/drivers/mmc/mmc.c @@ -0,0 +1,926 @@ +/* + * Copyright 2008, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the Linux code + * + * 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 <config.h> +#include <common.h> +#include <command.h> +#include <mmc.h> +#include <part.h> +#include <malloc.h> +#include <mmc.h> + +static struct mmc *mmc_devices; +static int cur_dev_num = -1; + +int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + return mmc->send_cmd(mmc, cmd, data); +} + +int mmc_set_blocklen(struct mmc *mmc, int len) +{ + struct mmc_cmd cmd; + + cmd.cmdidx = MMC_CMD_SET_BLOCKLEN; + cmd.resp_type = MMC_CMD_R1; + cmd.cmdarg = len; + cmd.flags = 0; + + return mmc_send_cmd(mmc, &cmd, NULL); +} + +struct mmc *find_mmc_device(int dev_num) +{ + struct mmc *m = mmc_devices; + + if (!m) { + printf("No MMC devices registered!\n"); + return NULL; + } + + do { + if (m->block_dev.dev == dev_num) + return m; + + m = m->next; + } while (m->next != mmc_devices); + + printf("MMC Device %d not found\n", dev_num); + + return NULL; +} + +static ulong +mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src) +{ + struct mmc_cmd cmd; + struct mmc_data data; + int err; + int stoperr = 0; + struct mmc *mmc = find_mmc_device(dev_num); + int blklen; + + if (!mmc) + return -1; + + blklen = mmc->write_bl_len; + + err = mmc_set_blocklen(mmc, mmc->write_bl_len); + + if (err) { + printf("set write bl len failed\n\r"); + return err; + } + + if (blkcnt > 1) + cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; + else + cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; + + if (mmc->high_capacity) + cmd.cmdarg = start; + else + cmd.cmdarg = start * blklen; + + cmd.resp_type = MMC_CMD_R1; + cmd.flags = 0; + + data.src = src; + data.blocks = blkcnt; + data.blocksize = blklen; + data.flags = MMC_DATA_WRITE; + + err = mmc_send_cmd(mmc, &cmd, &data); + + if (err) { + printf("mmc write failed\n\r"); + return err; + } + + if (blkcnt > 1) { + cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; + cmd.cmdarg = 0; + cmd.resp_type = MMC_CMD_R1b; + cmd.flags = 0; + stoperr = mmc_send_cmd(mmc, &cmd, NULL); + } + + return blkcnt; +} + +int mmc_read_block(struct mmc *mmc, void *dst, uint blocknum) +{ + struct mmc_cmd cmd; + struct mmc_data data; + + cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK; + + if (mmc->high_capacity) + cmd.cmdarg = blocknum; + else + cmd.cmdarg = blocknum * mmc->read_bl_len; + + cmd.resp_type = MMC_CMD_R1; + cmd.flags = 0; + + data.dest = dst; + data.blocks = 1; + data.blocksize = mmc->read_bl_len; + data.flags = MMC_DATA_READ; + + return mmc_send_cmd(mmc, &cmd, &data); +} + +int mmc_read(struct mmc *mmc, ulong src, uchar *dst, int size) +{ + char *buffer; + int i; + int blklen = mmc->read_bl_len; + int startblock = src / blklen; + int endblock = (src + size - 1) / blklen; + int err = 0; + + /* Make a buffer big enough to hold all the blocks we might read */ + buffer = malloc(blklen); + + if (!buffer) { + printf("Could not allocate buffer for MMC read!\n"); + return -1; + } + + /* We always do full block reads from the card */ + err = mmc_set_blocklen(mmc, mmc->read_bl_len); + + if (err) + return err; + + for (i = startblock; i <= endblock; i++) { + int segment_size; + int offset; + + err = mmc_read_block(mmc, buffer, i); + + if (err) + goto free_buffer; + + /* + * The first block may not be aligned, so we + * copy from the desired point in the block + */ + offset = (src & (blklen - 1)); + segment_size = MIN(blklen - offset, size); + + memcpy(dst, buffer + offset, segment_size); + + dst += segment_size; + src += segment_size; + size -= segment_size; + } + +free_buffer: + free(buffer); + + return err; +} + +static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) +{ + int err; + ulong src; + struct mmc *mmc = find_mmc_device(dev_num); + + if (!mmc) + return 0; + + src = start * mmc->read_bl_len; + + err = mmc_read(mmc, src, dst, blkcnt * mmc->read_bl_len); + + if (err) { + printf("block read failed: %d\n", err); + return 0; + } + + return blkcnt; +} + +int mmc_go_idle(struct mmc* mmc) +{ + struct mmc_cmd cmd; + int err; + + udelay(1000); + + cmd.cmdidx = MMC_CMD_GO_IDLE_STATE; + cmd.cmdarg = 0; + cmd.resp_type = MMC_CMD_RSP_NONE; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + udelay(2000); + + return 0; +} + +int +sd_send_op_cond(struct mmc *mmc) +{ + int timeout = 1000; + int err; + struct mmc_cmd cmd; + + do { + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_CMD_R1; + cmd.cmdarg = 0; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + cmd.cmdidx = SD_CMD_APP_SEND_OP_COND; + cmd.resp_type = MMC_CMD_R3; + cmd.cmdarg = 0xff8000; + + if (mmc->version == SD_VERSION_2) + cmd.cmdarg |= 0x40000000; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + udelay(1000); + } while ((!(cmd.response[0] & OCR_BUSY)) && timeout--); + + if (timeout <= 0) + return UNUSABLE_ERR; + + if (mmc->version != SD_VERSION_2) + mmc->version = SD_VERSION_1_0; + + mmc->ocr = ((uint *)(cmd.response))[0]; + + mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); + mmc->rca = 0; + + return 0; +} + +int mmc_send_op_cond(struct mmc *mmc) +{ + int timeout = 1000; + struct mmc_cmd cmd; + int err; + + /* Some cards seem to need this */ + mmc_go_idle(mmc); + + do { + cmd.cmdidx = MMC_CMD_SEND_OP_COND; + cmd.resp_type = MMC_CMD_R3; + cmd.cmdarg = 0x40ff8000; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + udelay(1000); + } while (!(cmd.response[0] & OCR_BUSY) && timeout--); + + if (timeout <= 0) + return UNUSABLE_ERR; + + mmc->version = MMC_VERSION_UNKNOWN; + mmc->ocr = ((uint *)(cmd.response))[0]; + + mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); + mmc->rca = 0; + + return 0; +} + + +int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd) +{ + struct mmc_cmd cmd; + struct mmc_data data; + int err; + + /* Get the Card Status Register */ + cmd.cmdidx = MMC_CMD_SEND_EXT_CSD; + cmd.resp_type = MMC_CMD_R1; + cmd.cmdarg = 0; + cmd.flags = 0; + + data.dest = ext_csd; + data.blocks = 1; + data.blocksize = 512; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + + return err; +} + + +int mmc_change_freq(struct mmc *mmc) +{ + struct mmc_cmd cmd; + char ext_csd[512]; + char cardtype; + int err; + + mmc->caps = 0; + + /* Only version 4 supports high-speed */ + if (mmc->version < MMC_VERSION_4) + return 0; + + mmc->caps |= MMC_MODE_4BIT; + + err = mmc_send_ext_csd(mmc, ext_csd); + + if (err) + return err; + + if (ext_csd[212] || ext_csd[213] || ext_csd[214] || ext_csd[215]) + mmc->high_capacity = 1; + + cardtype = ext_csd[196] & 0xf; + + /* Set the card to use High Speed */ + cmd.cmdidx = MMC_CMD_SWITCH; + cmd.resp_type = MMC_CMD_R1b; + cmd.cmdarg = 0x1b90100; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + /* Now check to see that it worked */ + err = mmc_send_ext_csd(mmc, ext_csd); + + if (err) + return err; + + /* No high-speed support */ + if (!ext_csd[185]) + return 0; + + /* High Speed is set, there are two types: 52MHz and 26MHz */ + if (cardtype & MMC_HS_52MHZ) + mmc->caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + else + mmc->caps |= MMC_MODE_HS; + + return 0; +} + +int sd_change_freq(struct mmc *mmc) +{ + int err; + struct mmc_cmd cmd; + uint scr[2]; + uint switch_status[16]; + struct mmc_data data; + int timeout; + + mmc->caps = 0; + + /* Read the SCR to find out if this card supports higher speeds */ + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_CMD_R1; + cmd.cmdarg = mmc->rca << 16; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + cmd.cmdidx = SD_CMD_APP_SEND_SCR; + cmd.resp_type = MMC_CMD_R1; + cmd.cmdarg = 0; + cmd.flags = 0; + + timeout = 3; + +retry_scr: + data.dest = (char *)&scr; + data.blocksize = 8; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + + if (err) { + if (timeout--) + goto retry_scr; + + return err; + } + + mmc->scr[0] = scr[0]; + mmc->scr[1] = scr[1]; + + switch ((mmc->scr[0] >> 24) & 0xf) { + case 0: + mmc->version = SD_VERSION_1_0; + break; + case 1: + mmc->version = SD_VERSION_1_10; + break; + case 2: + mmc->version = SD_VERSION_2; + break; + default: + mmc->version = SD_VERSION_1_0; + break; + } + + /* Version 1.0 doesn't support switching */ + if (mmc->version == SD_VERSION_1_0) + return 0; + + timeout = 4; + while (timeout--) { + /* Switch the frequency */ + cmd.cmdidx = SD_CMD_SWITCH_FUNC; + cmd.resp_type = MMC_CMD_R1; + cmd.cmdarg = 0xfffff1; + cmd.flags = 0; + + data.dest = (char *)&switch_status; + data.blocksize = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + + if (err) + return err; + + /* The high-speed function is busy. Try again */ + if (!switch_status[7] & SD_HIGHSPEED_BUSY) + break; + } + + if (mmc->scr[0] & SD_DATA_4BIT) + mmc->caps |= MMC_MODE_4BIT; + + /* If high-speed isn't supported, we return */ + if (!(switch_status[3] & SD_HIGHSPEED_SUPPORTED)) + return 0; + + cmd.cmdidx = SD_CMD_SWITCH_FUNC; + cmd.resp_type = MMC_CMD_R1; + cmd.cmdarg = 0x80fffff1; + cmd.flags = 0; + + data.dest = (char *)&switch_status; + data.blocksize = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + + if (err) + return err; + + if ((switch_status[4] & 0x0f000000) == 0x01000000) + mmc->caps |= MMC_MODE_HS; + + return 0; +} + +/* frequency bases */ +/* divided by 10 to be nice to platforms without floating point */ +int fbase[] = { + 10000, + 100000, + 1000000, + 10000000, +}; + +/* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice + * to platforms without floating point. + */ +int multipliers[] = { + 0, /* reserved */ + 10, + 12, + 13, + 15, + 20, + 25, + 30, + 35, + 40, + 45, + 50, + 55, + 60, + 70, + 80, +}; + +void mmc_set_ios(struct mmc *mmc) +{ + mmc->set_ios(mmc); +} + +void mmc_set_clock(struct mmc *mmc, uint clock) +{ + mmc->clock = clock; + + mmc_set_ios(mmc); +} + +void mmc_set_bus_width(struct mmc *mmc, uint width) +{ + mmc->bus_width = width; + + mmc_set_ios(mmc); +} + +int mmc_startup(struct mmc *mmc) +{ + int err; + uint mult, freq; + u64 cmult, csize; + struct mmc_cmd cmd; + + /* Put the Card in Identify Mode */ + cmd.cmdidx = MMC_CMD_ALL_SEND_CID; + cmd.resp_type = MMC_CMD_R2; + cmd.cmdarg = 0; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + memcpy(mmc->cid, cmd.response, 16); + + /* + * For MMC cards, set the Relative Address. + * For SD cards, get the Relatvie Address. + * This also puts the cards into Standby State + */ + cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR; + cmd.cmdarg = mmc->rca << 16; + cmd.resp_type = MMC_CMD_R6; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + if (IS_SD(mmc)) + mmc->rca = (((uint *)(cmd.response))[0] >> 16) & 0xffff; + + /* Get the Card-Specific Data */ + cmd.cmdidx = MMC_CMD_SEND_CSD; + cmd.resp_type = MMC_CMD_R2; + cmd.cmdarg = mmc->rca << 16; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + mmc->csd[0] = ((uint *)(cmd.response))[0]; + mmc->csd[1] = ((uint *)(cmd.response))[1]; + mmc->csd[2] = ((uint *)(cmd.response))[2]; + mmc->csd[3] = ((uint *)(cmd.response))[3]; + + if (mmc->version == MMC_VERSION_UNKNOWN) { + int version = (cmd.response[0] >> 2) & 0xf; + + switch (version) { + case 0: + mmc->version = MMC_VERSION_1_2; + break; + case 1: + mmc->version = MMC_VERSION_1_4; + break; + case 2: + mmc->version = MMC_VERSION_2_2; + break; + case 3: + mmc->version = MMC_VERSION_3; + break; + case 4: + mmc->version = MMC_VERSION_4; + break; + default: + mmc->version = MMC_VERSION_1_2; + break; + } + } + + /* divide frequency by 10, since the mults are 10x bigger */ + freq = fbase[(cmd.response[3] & 0x7)]; + mult = multipliers[((cmd.response[3] >> 3) & 0xf)]; + + mmc->tran_speed = freq * mult; + + mmc->read_bl_len = 1 << ((((uint *)(cmd.response))[1] >> 16) & 0xf); + + if (IS_SD(mmc)) + mmc->write_bl_len = mmc->read_bl_len; + else + mmc->write_bl_len = 1 << ((((uint *)(cmd.response))[3] >> 22) & 0xf); + + if (mmc->high_capacity) { + csize = (mmc->csd[1] & 0x3f) << 16 + | (mmc->csd[2] & 0xffff0000) >> 16; + cmult = 8; + } else { + csize = (mmc->csd[1] & 0x3ff) << 2 + | (mmc->csd[2] & 0xc0000000) >> 30; + cmult = (mmc->csd[2] & 0x00038000) >> 15; + } + + mmc->capacity = (csize + 1) << (cmult + 2); + mmc->capacity *= mmc->read_bl_len; + + if (mmc->read_bl_len > 512) + mmc->read_bl_len = 512; + + if (mmc->write_bl_len > 512) + mmc->write_bl_len = 512; + + /* Select the card, and put it into Transfer Mode */ + cmd.cmdidx = MMC_CMD_SELECT_CARD; + cmd.resp_type = MMC_CMD_R1b; + cmd.cmdarg = mmc->rca << 16; + cmd.flags = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + if (IS_SD(mmc)) + err = sd_change_freq(mmc); + else + err = mmc_change_freq(mmc); + + if (err) + return err; + + if (IS_SD(mmc)) { + if (mmc->caps & MMC_MODE_4BIT) { + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_CMD_R1; + cmd.cmdarg = mmc->rca << 16; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + + cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; + cmd.resp_type = MMC_CMD_R1; + cmd.cmdarg = 2; + cmd.flags = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + + mmc_set_bus_width(mmc, 4); + } + + if (mmc->caps & MMC_MODE_HS) + mmc_set_clock(mmc, 12000000); + else + mmc_set_clock(mmc, 12000000); + } else { + if (mmc->caps & MMC_MODE_4BIT) { + /* Set the card to use 4 bit*/ + cmd.cmdidx = MMC_CMD_SWITCH; + cmd.resp_type = MMC_CMD_R1b; + cmd.cmdarg = 0x1b70100; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + mmc_set_bus_width(mmc, 4); + } else if (mmc->caps & MMC_MODE_8BIT) { + /* Set the card to use 8 bit*/ + cmd.cmdidx = MMC_CMD_SWITCH; + cmd.resp_type = MMC_CMD_R1b; + cmd.cmdarg = 0x1b70200; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + mmc_set_bus_width(mmc, 8); + } + + if (mmc->caps & MMC_MODE_HS) { + if (mmc->caps & MMC_MODE_HS_52MHz) + mmc_set_clock(mmc, 52000000); + else + mmc_set_clock(mmc, 26000000); + } else + mmc_set_clock(mmc, 20000000); + } + + /* fill in device description */ + mmc->block_dev.if_type = IF_TYPE_MMC; + mmc->block_dev.part_type = PART_TYPE_DOS; + mmc->block_dev.dev = cur_dev_num++; + mmc->block_dev.lun = 0; + mmc->block_dev.type = 0; + mmc->block_dev.blksz = mmc->read_bl_len; + mmc->block_dev.lba = mmc->capacity/mmc->read_bl_len; + sprintf(mmc->block_dev.vendor,"Man %02x%02x%02x Snr %02x%02x%02x%02x", + mmc->cid[0], mmc->cid[1], mmc->cid[2], + mmc->cid[9], mmc->cid[10], mmc->cid[11], mmc->cid[12]); + sprintf(mmc->block_dev.product,"%c%c%c%c%c", mmc->cid[3], + mmc->cid[4], mmc->cid[5], mmc->cid[6], mmc->cid[7]); + sprintf(mmc->block_dev.revision,"%d.%d", mmc->cid[8] >> 4, + mmc->cid[8] & 0xf); + mmc->block_dev.removable = 1; + mmc->block_dev.block_read = mmc_bread; + mmc->block_dev.block_write = mmc_bwrite; + + init_part(&mmc->block_dev); + + return 0; +} + +int mmc_send_if_cond(struct mmc *mmc) +{ + struct mmc_cmd cmd; + int err; + + cmd.cmdidx = SD_CMD_SEND_IF_COND; + cmd.cmdarg = 0x1aa; + cmd.resp_type = MMC_CMD_R7; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + if (((uint *)(cmd.response))[0] != 0x1aa) + return UNUSABLE_ERR; + else + mmc->version = SD_VERSION_2; + + return 0; +} + +int mmc_register(struct mmc *mmc) +{ + struct mmc *m; + + if (!mmc_devices) + mmc_devices = mmc; + else { + for (m = mmc_devices; m->next != mmc_devices; m=m->next); + m->next = mmc; + } + + mmc->next = mmc_devices; + + return 0; +} + +block_dev_desc_t *mmc_get_dev(int dev) +{ + struct mmc *mmc = find_mmc_device(dev); + + return &mmc->block_dev; +} + +int mmc_init(struct mmc *mmc) +{ + int err; + + err = mmc->init(mmc); + + if (err) + return err; + + /* Reset the Card */ + err = mmc_go_idle(mmc); + + if (err) + return err; + + /* Test for SD version 2 */ + err = mmc_send_if_cond(mmc); + + /* If we got an error other than timeout, we bail */ + if (err && err != TIMEOUT) + return err; + + /* Now try to get the SD card's operating condition */ + err = sd_send_op_cond(mmc); + + /* If the command timed out, we check for an MMC card */ + if (err == TIMEOUT) { + err = mmc_send_op_cond(mmc); + + if (err) { + printf("Card did not respond to voltage select!\n"); + return UNUSABLE_ERR; + } + } + + err = mmc_startup(mmc); + + if (!err) + mmc->initialized = 1; + + return err; +} + +/* + * CPU and board-specific MMC initializations. Aliased function + * signals caller to move on + */ +static int __def_mmc_init(bd_t *bis) +{ + return -1; +} + +int cpu_mmc_init(bd_t *bis) __attribute((weak, alias("__def_mmc_init"))); +int board_mmc_init(bd_t *bis) __attribute((weak, alias("__def_mmc_init"))); + +void print_mmc_devices(char separator) +{ + struct mmc *m = mmc_devices; + + do { + if (m != mmc_devices) + printf("%c ", separator); + + printf("%s: %d", m->name, m->block_dev.dev); + m = m->next; + } while (m->next != mmc_devices); + + printf("\n"); +} + +int mmc_initialize(bd_t *bis) +{ + mmc_devices = NULL; + cur_dev_num = 0; + + if (board_mmc_init(bis) < 0) + cpu_mmc_init(bis); + + if (!mmc_devices) + puts ("No MMC devices found.\n"); + else + print_mmc_devices(','); + + return 0; +} diff --git a/include/mmc.h b/include/mmc.h index 7a03ed2..aed449c 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -1,6 +1,8 @@ /* - * (C) Copyright 2000-2003 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * Copyright 2008, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based (loosely) on the Linux code * * See file CREDITS for list of people who contributed to this * project. @@ -24,32 +26,186 @@ #ifndef _MMC_H_ #define _MMC_H_
-/* MMC command numbers */ +#define SD_VERSION_SD 0x20000 +#define SD_VERSION_2 (SD_VERSION_SD | 0x20) +#define SD_VERSION_1_0 (SD_VERSION_SD | 0x10) +#define SD_VERSION_1_10 (SD_VERSION_SD | 0x1a) +#define MMC_VERSION_MMC 0x10000 +#define MMC_VERSION_UNKNOWN (MMC_VERSION_MMC) +#define MMC_VERSION_1_2 (MMC_VERSION_MMC | 0x12) +#define MMC_VERSION_1_4 (MMC_VERSION_MMC | 0x14) +#define MMC_VERSION_2_2 (MMC_VERSION_MMC | 0x22) +#define MMC_VERSION_3 (MMC_VERSION_MMC | 0x30) +#define MMC_VERSION_4 (MMC_VERSION_MMC | 0x40) + +#define MMC_MODE_HS 0x001 +#define MMC_MODE_HS_52MHz 0x010 +#define MMC_MODE_4BIT 0x100 +#define MMC_MODE_8BIT 0x200 + +#define SD_DATA_4BIT 0x00040000 + +#define IS_SD(x) (mmc->version & SD_VERSION_SD) + +#define MMC_DATA_READ 1 +#define MMC_DATA_WRITE 2 + +#define NO_CARD_ERR -16 /* No SD/MMC card inserted */ +#define UNUSABLE_ERR -17 /* Unusable Card */ +#define COMM_ERR -18 /* Communications Error */ +#define TIMEOUT -19 + #define MMC_CMD_GO_IDLE_STATE 0 #define MMC_CMD_SEND_OP_COND 1 #define MMC_CMD_ALL_SEND_CID 2 #define MMC_CMD_SET_RELATIVE_ADDR 3 #define MMC_CMD_SET_DSR 4 +#define MMC_CMD_SWITCH 6 #define MMC_CMD_SELECT_CARD 7 +#define MMC_CMD_SEND_EXT_CSD 8 #define MMC_CMD_SEND_CSD 9 #define MMC_CMD_SEND_CID 10 +#define MMC_CMD_STOP_TRANSMISSION 12 #define MMC_CMD_SEND_STATUS 13 #define MMC_CMD_SET_BLOCKLEN 16 #define MMC_CMD_READ_SINGLE_BLOCK 17 #define MMC_CMD_READ_MULTIPLE_BLOCK 18 -#define MMC_CMD_WRITE_BLOCK 24 +#define MMC_CMD_WRITE_SINGLE_BLOCK 24 +#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25 #define MMC_CMD_APP_CMD 55
-/* SD Card command numbers */ #define SD_CMD_SEND_RELATIVE_ADDR 3 -#define SD_CMD_SWITCH 6 +#define SD_CMD_SWITCH_FUNC 6 #define SD_CMD_SEND_IF_COND 8
#define SD_CMD_APP_SET_BUS_WIDTH 6 #define SD_CMD_APP_SEND_OP_COND 41 +#define SD_CMD_APP_SEND_SCR 51
-int mmc_legacy_init(int verbose); -int mmc_read(ulong src, uchar *dst, int size); -int mmc_write(uchar *src, ulong dst, int size); +#define SD_HIGHSPEED_BUSY 0x00020000 +#define SD_HIGHSPEED_SUPPORTED 0x00020000 + +#define MMC_HS_TIMING 0x00000100 +#define MMC_HS_52MHZ 0x2 + +#define OCR_BUSY 0x80 +#define OCR_HCS 0x40000000
+#define R1_ILLEGAL_COMMAND (1 << 22) +#define R1_APP_CMD (1 << 5) + +enum { + MMC_CMD_RSP_NONE, + MMC_CMD_R1 = 1, + MMC_CMD_R1b, + MMC_CMD_R2, + MMC_CMD_R3, + MMC_CMD_R4, + MMC_CMD_R5, + MMC_CMD_R5b, + MMC_CMD_R6, + MMC_CMD_R7 +}; + +struct mmc_cid { + unsigned long psn; + unsigned short oid; + unsigned char mid; + unsigned char prv; + unsigned char mdt; + 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; +}; + +struct mmc_cmd { + ushort cmdidx; + int resp_type; + uint cmdarg; + char response[18]; + uint flags; +}; + +struct mmc_data { + char *dest; + const char *src; /* src buffers don't get written to */ + uint flags; + int blocks; + int blocksize; +}; + +struct mmc { + char name[32]; + void *priv; + uint initialized; + uint version; + int high_capacity; + uint bus_width; + uint clock; + uint caps; + uint ocr; + uint scr[2]; + uint csd[4]; + char cid[16]; + ushort rca; + uint tran_speed; + uint read_bl_len; + uint write_bl_len; + u64 capacity; + block_dev_desc_t block_dev; + int (*send_cmd)(struct mmc *mmc, + struct mmc_cmd *cmd, struct mmc_data *data); + void (*set_ios)(struct mmc *mmc); + int (*init)(struct mmc *mmc); + struct mmc *next; +}; + +int mmc_register(struct mmc *mmc); +int mmc_initialize(bd_t *bis); +int mmc_init(struct mmc *mmc); +int mmc_read(struct mmc *mmc, ulong src, uchar *dst, int size); +struct mmc *find_mmc_device(int dev_num); +void print_mmc_devices(char separator); + +#ifndef CONFIG_GENERIC_MMC +int mmc_legacy_init(int verbose); +#endif #endif /* _MMC_H_ */ diff --git a/lib_ppc/board.c b/lib_ppc/board.c index ce07c4e..e304d8c 100644 --- a/lib_ppc/board.c +++ b/lib_ppc/board.c @@ -51,6 +51,9 @@ #include <status_led.h> #endif #include <net.h> +#ifdef CONFIG_GENERIC_MMC +#include <mmc.h> +#endif #include <serial.h> #ifdef CONFIG_SYS_ALLOC_DPRAM #if !defined(CONFIG_CPM2) @@ -1083,6 +1086,12 @@ void board_init_r (gd_t *id, ulong dest_addr) scsi_init (); #endif
+#ifdef CONFIG_GENERIC_MMC + WATCHDOG_RESET (); + puts ("MMC: "); + mmc_initialize (bd); +#endif + #if defined(CONFIG_CMD_DOC) WATCHDOG_RESET (); puts ("DOC: ");

This uses the new MMC framework
Some contributions by Dave Liu daveliu@freescale.com
Signed-off-by: Andy Fleming afleming@freescale.com --- drivers/mmc/Makefile | 1 + drivers/mmc/fsl_esdhc.c | 344 +++++++++++++++++++++++++++++++++++++++++++++++ include/fsl_esdhc.h | 136 +++++++++++++++++++ 3 files changed, 481 insertions(+), 0 deletions(-) create mode 100644 drivers/mmc/fsl_esdhc.c create mode 100644 include/fsl_esdhc.h
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index d5a969c..040be8d 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -27,6 +27,7 @@ LIB := $(obj)libmmc.a
COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o +COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c new file mode 100644 index 0000000..f6c6fff --- /dev/null +++ b/drivers/mmc/fsl_esdhc.c @@ -0,0 +1,344 @@ +/* + * Copyright 2007, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the pxa mmc code: + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * + * 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 <config.h> +#include <common.h> +#include <command.h> +#include <mmc.h> +#include <part.h> +#include <malloc.h> +#include <mmc.h> +#include <fsl_esdhc.h> +#include <asm/io.h> + + +DECLARE_GLOBAL_DATA_PTR; + +struct fsl_esdhc { + uint dsaddr; + uint blkattr; + uint cmdarg; + uint xfertyp; + uint cmdrsp0; + uint cmdrsp1; + uint cmdrsp2; + uint cmdrsp3; + uint datport; + uint prsstat; + uint proctl; + uint sysctl; + uint irqstat; + uint irqstaten; + uint irqsigen; + uint autoc12err; + uint hostcapblt; + uint wml; + char reserved1[8]; + uint fevt; + char reserved2[168]; + uint hostver; + char reserved3[780]; + uint scr; +}; + +static uint xfertyps[] = { + XFERTYP_RSPTYP_NONE, + XFERTYP_RSPTYP_48 | XFERTYP_CICEN | XFERTYP_CCCEN, + XFERTYP_RSPTYP_48_BUSY | XFERTYP_CICEN | XFERTYP_CCCEN, + XFERTYP_RSPTYP_136 | XFERTYP_CCCEN, + XFERTYP_RSPTYP_48, + XFERTYP_RSPTYP_48, + XFERTYP_RSPTYP_48 | XFERTYP_CICEN | XFERTYP_CCCEN, + XFERTYP_RSPTYP_48_BUSY | XFERTYP_CICEN | XFERTYP_CCCEN, + XFERTYP_RSPTYP_48 | XFERTYP_CICEN | XFERTYP_CCCEN, + XFERTYP_RSPTYP_48 | XFERTYP_CICEN | XFERTYP_CCCEN +}; + +/* Return the XFERTYP flags for a given command and data packet */ +uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) +{ + uint xfertyp = 0; + + if (data) { + xfertyp |= XFERTYP_DPSEL | XFERTYP_DMAEN; + + if (data->blocks > 1) { + xfertyp |= XFERTYP_MSBSEL; + xfertyp |= XFERTYP_BCEN; + } + + if (data->flags & MMC_DATA_READ) + xfertyp |= XFERTYP_DTDSEL; + } + + xfertyp |= xfertyps[cmd->resp_type]; + + return xfertyp; +} + +static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) +{ + uint wml_value; + int timeout; + struct fsl_esdhc *regs = mmc->priv; + u32 temp; + + wml_value = data->blocksize/4; + + if (data->flags & MMC_DATA_READ) { + if (wml_value > 0x10) + wml_value = 0x10; + + wml_value = 0x100000 | wml_value; + + out_be32(®s->dsaddr, (u32)data->dest); + } else { + if (wml_value > 0x80) + wml_value = 0x80; + if ((in_be32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { + printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); + return TIMEOUT; + } + wml_value = wml_value << 16 | 0x10; + out_be32(®s->dsaddr, (u32)data->src); + } + + out_be32(®s->wml, wml_value); + + out_be32(®s->blkattr, data->blocks << 16 | data->blocksize); + + /* Calculate the timeout period for data transactions */ + timeout = __ilog2(mmc->tran_speed/10); + timeout -= 13; + + if (timeout > 14) + timeout = 14; + + if (timeout < 0) + timeout = 0; + + temp = in_be32(®s->sysctl); +#define SYSCTL_TIMEOUT_MASK 0x000f0000 + temp &= ~(SYSCTL_TIMEOUT_MASK); + temp |= timeout << 16; + out_be32(®s->sysctl, temp); + + return 0; +} + + +/* + * Sends a command out on the bus. Takes the mmc pointer, + * a command pointer, and an optional data pointer. + */ +static int +esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + uint xfertyp; + uint irqstat; + volatile struct fsl_esdhc *regs = mmc->priv; + + out_be32(®s->irqstat, -1); + + sync(); + + /* Wait for the bus to be idle */ + while ((in_be32(®s->prsstat) & PRSSTAT_CICHB) || + (in_be32(®s->prsstat) & PRSSTAT_CIDHB)); + + while (in_be32(®s->prsstat) & PRSSTAT_DLA); + + /* Wait at least 8 SD clock cycles before the next command */ + /* + * Note: This is way more than 8 cycles, but 1ms seems to + * resolve timing issues with some cards + */ + udelay(1000); + + /* Set up for a data transfer if we have one */ + if (data) { + int err; + + err = esdhc_setup_data(mmc, data); + if(err) + return err; + } + + /* Figure out the transfer arguments */ + xfertyp = esdhc_xfertyp(cmd, data); + + /* Send the command */ + out_be32(®s->cmdarg, cmd->cmdarg); + out_be32(®s->xfertyp, XFERTYP_CMD(cmd->cmdidx) | xfertyp); + + /* Wait for the command to complete */ + while (!(in_be32(®s->irqstat) & IRQSTAT_CC)); + + irqstat = in_be32(®s->irqstat); + out_be32(®s->irqstat, irqstat); + + if (irqstat & CMD_ERR) + return COMM_ERR; + + if (irqstat & IRQSTAT_CTOE) + return TIMEOUT; + + /* Copy the response to the response buffer */ + if (cmd->resp_type == MMC_CMD_R2) { + u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0; + + cmdrsp3 = in_be32(®s->cmdrsp3); + cmdrsp2 = in_be32(®s->cmdrsp2); + cmdrsp1 = in_be32(®s->cmdrsp1); + cmdrsp0 = in_be32(®s->cmdrsp0); + ((uint *)(cmd->response))[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24); + ((uint *)(cmd->response))[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24); + ((uint *)(cmd->response))[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24); + ((uint *)(cmd->response))[3] = (cmdrsp0 << 8); + } else + ((uint *)(cmd->response))[0] = in_be32(®s->cmdrsp0); + + /* Wait until all of the blocks are transferred */ + if (data) { + do { + irqstat = in_be32(®s->irqstat); + + if (irqstat & DATA_ERR) + return COMM_ERR; + + if (irqstat & IRQSTAT_DTOE) + return TIMEOUT; + } while (!(irqstat & IRQSTAT_TC) && + (in_be32(®s->prsstat) & PRSSTAT_DLA)); + } + + out_be32(®s->irqstat, -1); + + return 0; +} + +void set_sysctl(struct mmc *mmc, uint clock) +{ + int sdhc_clk = gd->sdhc_clk; + int div, pre_div; + volatile struct fsl_esdhc *regs = mmc->priv; + uint clk; + u32 temp; + + if (sdhc_clk / 16 > clock) { + for (pre_div = 2; pre_div < 256; pre_div *= 2) + if ((sdhc_clk / pre_div) <= (clock * 16)) + break; + } else + pre_div = 2; + + for (div = 1; div <= 16; div++) + if ((sdhc_clk / (div * pre_div)) <= clock) + break; + + pre_div >>= 1; + div -= 1; + + clk = (pre_div << 8) | (div << 4); + + temp = in_be32(®s->sysctl); +#define SYSCTL_CLOCK_MASK 0x00000fff + temp &= SYSCTL_CLOCK_MASK; + temp |= clk; + out_be32(®s->sysctl, temp); + + udelay(10000); + + setbits_be32(®s->sysctl, SYSCTL_PEREN); +} + +static void esdhc_set_ios(struct mmc *mmc) +{ + struct fsl_esdhc *regs = mmc->priv; + + /* Set the clock speed */ + set_sysctl(mmc, mmc->clock); + + /* Set the bus width */ + clrbits_be32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); + + if (mmc->bus_width == 4) + setbits_be32(®s->proctl, PROCTL_DTW_4); + else if (mmc->bus_width == 8) + setbits_be32(®s->proctl, PROCTL_DTW_8); +} + +static int esdhc_init(struct mmc *mmc) +{ + struct fsl_esdhc *regs = mmc->priv; + int timeout = 1000; + + /* Enable cache snooping */ + out_be32(®s->scr, 0x00000040); + + out_be32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); + + /* Set the initial clock speed */ + set_sysctl(mmc, 400000); + + /* Disable the BRR and BWR bits in IRQSTAT */ + clrbits_be32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR); + + /* Put the PROCTL reg back to the default */ + out_be32(®s->proctl, PROCTL_INIT); + + while (!(in_be32(®s->prsstat) & PRSSTAT_CINS) && --timeout) + udelay(1000); + + if (timeout <= 0) + return NO_CARD_ERR; + + return 0; +} + +static int esdhc_initialize(bd_t *bis) +{ + struct fsl_esdhc *regs = (struct fsl_esdhc *)CFG_FSL_ESDHC_ADDR; + struct mmc *mmc; + + mmc = malloc(sizeof(struct mmc)); + + sprintf(mmc->name, "FSL_ESDHC"); + mmc->priv = regs; + mmc->send_cmd = esdhc_send_cmd; + mmc->set_ios = esdhc_set_ios; + mmc->init = esdhc_init; + + mmc_register(mmc); + + return 0; +} + +int fsl_esdhc_mmc_init(bd_t *bis) +{ + return esdhc_initialize(bis); +} diff --git a/include/fsl_esdhc.h b/include/fsl_esdhc.h new file mode 100644 index 0000000..6a504e8 --- /dev/null +++ b/include/fsl_esdhc.h @@ -0,0 +1,136 @@ +/* + * FSL SD/MMC Defines + *------------------------------------------------------------------- + * + * Copyright 2007-2008, Freescale Semiconductor, Inc + * + * 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 + * + *------------------------------------------------------------------- + * + */ + +#ifndef __FSL_ESDHC_H__ +#define __FSL_ESDHC_H__ + +/* FSL eSDHC-specific constants */ +#define SYSCTL 0x0002e02c +#define SYSCTL_INITA 0x08000000 +#define SYSCTL_PEREN 0x00000004 +#define SYSCTL_HCKEN 0x00000002 +#define SYSCTL_IPGEN 0x00000001 + +#define IRQSTAT 0x0002e030 +#define IRQSTAT_DMAE (0x10000000) +#define IRQSTAT_AC12E (0x01000000) +#define IRQSTAT_DEBE (0x00400000) +#define IRQSTAT_DCE (0x00200000) +#define IRQSTAT_DTOE (0x00100000) +#define IRQSTAT_CIE (0x00080000) +#define IRQSTAT_CEBE (0x00040000) +#define IRQSTAT_CCE (0x00020000) +#define IRQSTAT_CTOE (0x00010000) +#define IRQSTAT_CINT (0x00000100) +#define IRQSTAT_CRM (0x00000080) +#define IRQSTAT_CINS (0x00000040) +#define IRQSTAT_BRR (0x00000020) +#define IRQSTAT_BWR (0x00000010) +#define IRQSTAT_DINT (0x00000008) +#define IRQSTAT_BGE (0x00000004) +#define IRQSTAT_TC (0x00000002) +#define IRQSTAT_CC (0x00000001) + +#define CMD_ERR (IRQSTAT_CIE | IRQSTAT_CEBE | IRQSTAT_CCE) +#define DATA_ERR (IRQSTAT_DEBE | IRQSTAT_DCE | IRQSTAT_DTOE) + +#define IRQSTATEN 0x0002e034 +#define IRQSTATEN_DMAE (0x10000000) +#define IRQSTATEN_AC12E (0x01000000) +#define IRQSTATEN_DEBE (0x00400000) +#define IRQSTATEN_DCE (0x00200000) +#define IRQSTATEN_DTOE (0x00100000) +#define IRQSTATEN_CIE (0x00080000) +#define IRQSTATEN_CEBE (0x00040000) +#define IRQSTATEN_CCE (0x00020000) +#define IRQSTATEN_CTOE (0x00010000) +#define IRQSTATEN_CINT (0x00000100) +#define IRQSTATEN_CRM (0x00000080) +#define IRQSTATEN_CINS (0x00000040) +#define IRQSTATEN_BRR (0x00000020) +#define IRQSTATEN_BWR (0x00000010) +#define IRQSTATEN_DINT (0x00000008) +#define IRQSTATEN_BGE (0x00000004) +#define IRQSTATEN_TC (0x00000002) +#define IRQSTATEN_CC (0x00000001) + +#define PRSSTAT 0x0002e024 +#define PRSSTAT_CLSL (0x00800000) +#define PRSSTAT_WPSPL (0x00080000) +#define PRSSTAT_CDPL (0x00040000) +#define PRSSTAT_CINS (0x00010000) +#define PRSSTAT_BREN (0x00000800) +#define PRSSTAT_DLA (0x00000004) +#define PRSSTAT_CICHB (0x00000002) +#define PRSSTAT_CIDHB (0x00000001) + +#define PROCTL 0x0002e028 +#define PROCTL_INIT 0x00000020 +#define PROCTL_DTW_4 0x00000002 +#define PROCTL_DTW_8 0x00000004 + +#define CMDARG 0x0002e008 + +#define XFERTYP 0x0002e00c +#define XFERTYP_CMD(x) ((x & 0x3f) << 24) +#define XFERTYP_CMDTYP_NORMAL 0x0 +#define XFERTYP_CMDTYP_SUSPEND 0x00400000 +#define XFERTYP_CMDTYP_RESUME 0x00800000 +#define XFERTYP_CMDTYP_ABORT 0x00c00000 +#define XFERTYP_DPSEL 0x00200000 +#define XFERTYP_CICEN 0x00100000 +#define XFERTYP_CCCEN 0x00080000 +#define XFERTYP_RSPTYP_NONE 0 +#define XFERTYP_RSPTYP_136 0x00010000 +#define XFERTYP_RSPTYP_48 0x00020000 +#define XFERTYP_RSPTYP_48_BUSY 0x00030000 +#define XFERTYP_MSBSEL 0x00000020 +#define XFERTYP_DTDSEL 0x00000010 +#define XFERTYP_AC12EN 0x00000004 +#define XFERTYP_BCEN 0x00000002 +#define XFERTYP_DMAEN 0x00000001 + +#define CINS_TIMEOUT 1000 + +#define DSADDR 0x2e004 + +#define CMDRSP0 0x2e010 +#define CMDRSP1 0x2e014 +#define CMDRSP2 0x2e018 +#define CMDRSP3 0x2e01c + +#define DATPORT 0x2e020 + +#define WML 0x2e044 +#define WML_WRITE 0x00010000 + +#define BLKATTR 0x2e004 +#define BLKATTR_CNT(x) ((x & 0xffff) << 16) +#define BLKATTR_SIZE(x) (x & 0x1fff) +#define MAX_BLK_CNT 0x7fff /* so malloc will have enough room with 32M */ + +int fsl_esdhc_mmc_init(bd_t *bis); + +#endif /* __FSL_ESDHC_H__ */

Signed-off-by: Andy Fleming afleming@freescale.com --- board/freescale/mpc837xemds/mpc837xemds.c | 19 +++++++++++++++++++ cpu/mpc83xx/cpu.c | 14 ++++++++++++++ include/asm-ppc/immap_83xx.h | 2 ++ include/configs/MPC837XEMDS.h | 15 +++++++++++++++ 4 files changed, 50 insertions(+), 0 deletions(-)
diff --git a/board/freescale/mpc837xemds/mpc837xemds.c b/board/freescale/mpc837xemds/mpc837xemds.c index acf8ada..57747be 100644 --- a/board/freescale/mpc837xemds/mpc837xemds.c +++ b/board/freescale/mpc837xemds/mpc837xemds.c @@ -22,6 +22,7 @@
int board_early_init_f(void) { + struct immap __iomem *im = (struct immap __iomem *)CONFIG_SYS_IMMR; u8 *bcsr = (u8 *)CONFIG_SYS_BCSR;
/* Enable flash write */ @@ -29,6 +30,24 @@ int board_early_init_f(void) /* Clear all of the interrupt of BCSR */ bcsr[0xe] = 0xff;
+#ifdef CONFIG_MMC + bcsr[0xc] |= 0x4c; + +#define SICRL_USB_B_MASK 0x30000000 +#define SICRL_USB_B_SD 0x20000000 +#define SICRH_GPIO2_E_MASK 0x00000c00 +#define SICRH_GPIO2_E_SD 0x00000800 +#define SICRH_SPI_MASK 0x00000003 +#define SICRH_SPI_SD 0x00000001 + + im->sysconf.sicrl &= ~SICRL_USB_B_MASK; + im->sysconf.sicrl |= SICRL_USB_B_SD; + + im->sysconf.sicrh &= ~(SICRH_GPIO2_E_MASK | SICRH_SPI_MASK); + im->sysconf.sicrh |= SICRH_GPIO2_E_SD | SICRH_SPI_SD; + +#endif + #ifdef CONFIG_FSL_SERDES immap_t *immr = (immap_t *)CONFIG_SYS_IMMR; u32 spridr = in_be32(&immr->sysconf.spridr); diff --git a/cpu/mpc83xx/cpu.c b/cpu/mpc83xx/cpu.c index 5e885ab..af180fb 100644 --- a/cpu/mpc83xx/cpu.c +++ b/cpu/mpc83xx/cpu.c @@ -33,6 +33,7 @@ #include <asm/processor.h> #include <libfdt.h> #include <tsec.h> +#include <fsl_esdhc.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -367,3 +368,16 @@ int cpu_eth_init(bd_t *bis)
return 0; } + +/* + * Initializes on-chip MMC controllers. + * to override, implement board_mmc_init() + */ +int cpu_mmc_init(bd_t *bis) +{ +#ifdef CONFIG_FSL_ESDHC + return fsl_esdhc_mmc_init(bis); +#else + return 0; +#endif +} diff --git a/include/asm-ppc/immap_83xx.h b/include/asm-ppc/immap_83xx.h index df24a6e..93421a4 100644 --- a/include/asm-ppc/immap_83xx.h +++ b/include/asm-ppc/immap_83xx.h @@ -788,4 +788,6 @@ typedef struct immap { } immap_t; #endif
+#define CONFIG_SYS_MPC83xx_ESDHC_OFFSET (0x2e000) +#define CONFIG_SYS_MPC83xx_ESDHC_ADDR (CONFIG_SYS_IMMR + CONFIG_SYS_MPC83xx_ESDHC_OFFSET) #endif /* __IMMAP_83xx__ */ diff --git a/include/configs/MPC837XEMDS.h b/include/configs/MPC837XEMDS.h index d49155f..b555012 100644 --- a/include/configs/MPC837XEMDS.h +++ b/include/configs/MPC837XEMDS.h @@ -320,6 +320,9 @@ #define CONFIG_OF_BOARD_SETUP 1 #define CONFIG_OF_STDOUT_VIA_ALIAS 1
+#define CONFIG_SYS_64BIT_STRTOUL 1 +#define CONFIG_SYS_64BIT_VSPRINTF 1 + /* I2C */ #define CONFIG_HARD_I2C /* I2C with hardware support */ #undef CONFIG_SOFT_I2C /* I2C bit-banged */ @@ -482,6 +485,18 @@ extern int board_pci_host_broken(void);
#undef CONFIG_WATCHDOG /* watchdog disabled */
+#define CONFIG_MMC 1 + +#ifdef CONFIG_MMC +#define CONFIG_FSL_ESDHC +#define CFG_FSL_ESDHC_ADDR CONFIG_SYS_MPC83xx_ESDHC_ADDR +#define CONFIG_CMD_MMC +#define CONFIG_GENERIC_MMC +#define CONFIG_CMD_EXT2 +#define CONFIG_CMD_FAT +#define CONFIG_DOS_PARTITION +#endif + /* * Miscellaneous configurable options */

Signed-off-by: Andy Fleming afleming@freescale.com --- board/freescale/mpc8536ds/mpc8536ds.c | 12 ++++++++++++ cpu/mpc85xx/cpu.c | 16 +++++++++++++++- include/configs/MPC8536DS.h | 14 ++++++++++++++ 3 files changed, 41 insertions(+), 1 deletions(-)
diff --git a/board/freescale/mpc8536ds/mpc8536ds.c b/board/freescale/mpc8536ds/mpc8536ds.c index 4944288..42f5c70 100644 --- a/board/freescale/mpc8536ds/mpc8536ds.c +++ b/board/freescale/mpc8536ds/mpc8536ds.c @@ -43,6 +43,18 @@
phys_size_t fixed_sdram(void);
+int board_early_init_f (void) +{ + volatile u32 *pmuxcr = (u32 *)(CONFIG_SYS_IMMR + 0xe0060); + u32 val; + + val = *pmuxcr; + val |= 0x60000000; + *pmuxcr = val; + + return 0; +} + int checkboard (void) { printf ("Board: MPC8536DS, System ID: 0x%02x, " diff --git a/cpu/mpc85xx/cpu.c b/cpu/mpc85xx/cpu.c index c780687..910c804 100644 --- a/cpu/mpc85xx/cpu.c +++ b/cpu/mpc85xx/cpu.c @@ -30,6 +30,7 @@ #include <watchdog.h> #include <command.h> #include <tsec.h> +#include <fsl_esdhc.h> #include <asm/cache.h> #include <asm/io.h>
@@ -384,8 +385,21 @@ void upmconfig (uint upm, uint * table, uint size) int cpu_eth_init(bd_t *bis) { #if defined(CONFIG_TSEC_ENET) || defined(CONFIG_MPC85xx_FEC) - tsec_standard_init(bis); + return tsec_standard_init(bis); +#else + return 0; #endif +}
+/* + * Initializes on-chip MMC controllers. + * to override, implement board_mmc_init() + */ +int cpu_mmc_init(bd_t *bis) +{ +#ifdef CONFIG_FSL_ESDHC + return fsl_esdhc_mmc_init(bis); +#else return 0; +#endif } diff --git a/include/configs/MPC8536DS.h b/include/configs/MPC8536DS.h index 1038790..f7354d0 100644 --- a/include/configs/MPC8536DS.h +++ b/include/configs/MPC8536DS.h @@ -72,6 +72,8 @@ extern unsigned long get_board_ddr_clk(unsigned long dummy); #define CONFIG_BTB /* toggle branch predition */ #define CONFIG_ADDR_STREAMING /* toggle addr streaming */
+#define CONFIG_BOARD_EARLY_INIT_F 1 /* Call board_pre_init */ + #define CONFIG_ENABLE_36BIT_PHYS 1
#define CONFIG_SYS_MEMTEST_START 0x00000000 /* memtest works on */ @@ -469,6 +471,18 @@ extern unsigned long get_board_ddr_clk(unsigned long dummy);
#undef CONFIG_WATCHDOG /* watchdog disabled */
+#define CONFIG_MMC 1 + +#ifdef CONFIG_MMC +#define CONFIG_FSL_ESDHC +#define CFG_FSL_ESDHC_ADDR CONFIG_SYS_MPC85xx_ESDHC_ADDR +#define CONFIG_CMD_MMC +#define CONFIG_GENERIC_MMC +#define CONFIG_CMD_EXT2 +#define CONFIG_CMD_FAT +#define CONFIG_DOS_PARTITION +#endif + /* * Miscellaneous configurable options */

From: Dave Liu daveliu@freescale.com
Current fat.c have three 64KB static array, it makes the BSS section larger. Change the static to dynamic allocation.
Signed-off-by: Dave Liu daveliu@freescale.com --- fs/fat/fat.c | 38 +++++++++++++++++++++++++++++++++++--- 1 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 2f0bd8c..e3563aa 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -28,6 +28,7 @@ #include <common.h> #include <config.h> #include <fat.h> +#include <malloc.h> #include <asm/byteorder.h> #include <part.h>
@@ -63,6 +64,37 @@ int disk_read (__u32 startblock, __u32 getsize, __u8 * bufptr) return -1; }
+__u8 *get_vfatname_block; +__u8 *get_dentfromdir_block; +__u8 *do_fat_read_block; +static int fat_mem_done = 0; +static int fat_memory_alloc(void) +{ + if (fat_mem_done) + return 0; + + get_vfatname_block = (__u8 *)malloc(MAX_CLUSTSIZE); + if (!get_vfatname_block) { + printf("alloc get_vfatname_block failed\n\r"); + return -1; + } + + get_dentfromdir_block = (__u8 *)malloc(MAX_CLUSTSIZE); + if (!get_dentfromdir_block) { + printf("alloc get_dentfromdir_block failed\n\r"); + return -1; + } + + do_fat_read_block = (__u8 *)malloc(MAX_CLUSTSIZE); + if (!do_fat_read_block) { + printf("alloc do_fat_read_block failed\n\r"); + return -1; + } + + fat_mem_done = 1; + + return 0; +}
int fat_register_device(block_dev_desc_t *dev_desc, int part_no) @@ -70,6 +102,9 @@ fat_register_device(block_dev_desc_t *dev_desc, int part_no) unsigned char buffer[SECTOR_SIZE]; disk_partition_t info;
+ if (fat_memory_alloc()) + printf("fat memory alloc failed\n\r"); + if (!dev_desc->block_read) return -1; cur_dev = dev_desc; @@ -433,7 +468,6 @@ slot2str(dir_slot *slotptr, char *l_name, int *idx) * into 'retdent' * Return 0 on success, -1 otherwise. */ -__u8 get_vfatname_block[MAX_CLUSTSIZE]; static int get_vfatname(fsdata *mydata, int curclust, __u8 *cluster, dir_entry *retdent, char *l_name) @@ -519,7 +553,6 @@ mkcksum(const char *str) * Get the directory entry associated with 'filename' from the directory * starting at 'startsect' */ -__u8 get_dentfromdir_block[MAX_CLUSTSIZE]; static dir_entry *get_dentfromdir (fsdata * mydata, int startsect, char *filename, dir_entry * retdent, int dols) @@ -725,7 +758,6 @@ read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize) }
-__u8 do_fat_read_block[MAX_CLUSTSIZE]; /* Block buffer */ long do_fat_read (const char *filename, void *buffer, unsigned long maxsize, int dols)

+int board_early_init_f (void) +{
- volatile u32 *pmuxcr = (u32 *)(CONFIG_SYS_IMMR + 0xe0060);
- u32 val;
- val = *pmuxcr;
- val |= 0x60000000;
- *pmuxcr = val;
- return 0;
+}
Andy, How about using the in/out_be32 for this?
Thanks, Dave

diff --git a/board/freescale/mpc837xemds/mpc837xemds.c b/board/freescale/mpc837xemds/mpc837xemds.c index acf8ada..57747be 100644 --- a/board/freescale/mpc837xemds/mpc837xemds.c +++ b/board/freescale/mpc837xemds/mpc837xemds.c @@ -22,6 +22,7 @@
int board_early_init_f(void) {
- struct immap __iomem *im = (struct immap __iomem
*)CONFIG_SYS_IMMR; u8 *bcsr = (u8 *)CONFIG_SYS_BCSR;
/* Enable flash write */ @@ -29,6 +30,24 @@ int board_early_init_f(void) /* Clear all of the interrupt of BCSR */ bcsr[0xe] = 0xff;
+#ifdef CONFIG_MMC
bcsr[0xc] |= 0x4c;
+#define SICRL_USB_B_MASK 0x30000000 +#define SICRL_USB_B_SD 0x20000000 +#define SICRH_GPIO2_E_MASK 0x00000c00 +#define SICRH_GPIO2_E_SD 0x00000800 +#define SICRH_SPI_MASK 0x00000003 +#define SICRH_SPI_SD 0x00000001
im->sysconf.sicrl &= ~SICRL_USB_B_MASK;
im->sysconf.sicrl |= SICRL_USB_B_SD;
im->sysconf.sicrh &= ~(SICRH_GPIO2_E_MASK | SICRH_SPI_MASK);
im->sysconf.sicrh |= SICRH_GPIO2_E_SD | SICRH_SPI_SD;
+#endif
Andy, Could you put the SICRH/L to CONFIG_SYS_SICRH/L of MPC837XEMDS.h? cpu init has these stuff initialization.
Thanks, Dave

-----Original Message----- From: u-boot-bounces@lists.denx.de [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Fleming Andy-AFLEMING Sent: Friday, October 31, 2008 6:36 AM To: wd@denx.de Cc: u-boot@lists.denx.de; Fleming Andy-AFLEMING Subject: [U-Boot] [PATCH 07/10] Add support for the Freescale eSDHC found on8379 and 8536 SoCs
This uses the new MMC framework
Some contributions by Dave Liu daveliu@freescale.com
Signed-off-by: Andy Fleming afleming@freescale.com
Acked-by: Dave Liu daveliu@freescale.com

-----Original Message----- From: u-boot-bounces@lists.denx.de [mailto:u-boot-bounces@lists.denx.de] On Behalf Of Fleming Andy-AFLEMING Sent: Friday, October 31, 2008 6:36 AM To: wd@denx.de Cc: u-boot@lists.denx.de; Fleming Andy-AFLEMING Subject: [U-Boot] [PATCH 06/10] Add MMC Framework
Here's a new framework (based roughly off the linux one) for managing MMC controllers. It handles all of the standard SD/MMC transactions, leaving the host drivers to implement only what is necessary to deal with their specific hardware.
This also hooks the infrastructure into the PowerPC board code (similar to how the ethernet infrastructure now hooks in)
Some of this code was contributed by Dave Liu daveliu@freescale.com
Signed-off-by: Andy Fleming afleming@freescale.com
Acked-by: Dave Liu daveliu@freescale.com

Andy Fleming afleming@freescale.com wrote:
Here's a new framework (based roughly off the linux one) for managing MMC controllers. It handles all of the standard SD/MMC transactions, leaving the host drivers to implement only what is necessary to deal with their specific hardware.
This also hooks the infrastructure into the PowerPC board code (similar to how the ethernet infrastructure now hooks in)
Some of this code was contributed by Dave Liu daveliu@freescale.com
Signed-off-by: Andy Fleming afleming@freescale.com
Nice. I have a few comments, please see below.
common/cmd_mmc.c | 122 +++++++ drivers/mmc/Makefile | 1 + drivers/mmc/mmc.c | 926 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 174 +++++++++- lib_ppc/board.c | 9 + 5 files changed, 1223 insertions(+), 9 deletions(-) create mode 100644 drivers/mmc/mmc.c
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index 21873d6..f27d7ef 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -25,6 +25,7 @@ #include <command.h> #include <mmc.h>
+#ifndef CONFIG_GENERIC_MMC int do_mmc (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { if (mmc_legacy_init (1) != 0) { @@ -39,3 +40,124 @@ U_BOOT_CMD( "mmcinit - init mmc card\n", NULL ); +#endif /* !CONFIG_GENERIC_MMC */
+static void print_mmcinfo(struct mmc *mmc) +{
- printf("Device: %s\n", mmc->name);
- printf("Manufacturer ID: %x\n", mmc->cid[0] >> 24);
- printf("OEM: %x\n", (mmc->cid[0] >> 8) & 0xffff);
- printf("Name: %c%c%c%c%c \n", mmc->cid[0] & 0xff,
(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff);
- printf("Tran Speed: %d\n", mmc->tran_speed);
- printf("Rd Block Len: %d\n", mmc->read_bl_len);
- printf("%s version %d.%d\n", IS_SD(mmc) ? "SD" : "MMC",
(mmc->version >> 4) & 0xf, mmc->version & 0xf);
- printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No");
- printf("Capacity: %lld\n", mmc->capacity);
- printf("Bus Width: %d-bit\n", mmc->bus_width);
+}
+int do_mmcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{
- struct mmc *mmc;
- int dev_num;
- if (argc < 2)
dev_num = 0;
- else
dev_num = simple_strtoul(argv[1], NULL, 0);
- mmc = find_mmc_device(dev_num);
- if (mmc) {
mmc_init(mmc);
print_mmcinfo(mmc);
- }
- return 0;
+}
+U_BOOT_CMD(mmcinfo, 2, 0, do_mmcinfo, "mmcinfo <dev num>-- display MMC info\n",
NULL);
+int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{
- int rc = 0;
- switch (argc) {
- case 0:
- case 1:
- case 3:
- case 4:
printf("Usage:\n%s\n", cmdtp->usage);
return 1;
- case 2:
if (!strcmp(argv[1], "list")) {
print_mmc_devices('\n');
return 0;
}
return 1;
- default: /* at least 5 args */
if (strcmp(argv[1], "read") == 0) {
int dev = simple_strtoul(argv[2], NULL, 10);
void *addr = (void *)simple_strtoul(argv[3], NULL, 16);
u32 cnt = simple_strtoul(argv[5], NULL, 16);
u32 n;
u32 blk = simple_strtoul(argv[4], NULL, 16);
struct mmc *mmc = find_mmc_device(dev);
printf("\nMMC read: dev # %d, block # %d, count %d ... ",
dev, blk, cnt);
mmc_init(mmc);
n = mmc->block_dev.block_read(dev, blk, cnt, addr);
/* flush cache after read */
flush_cache((ulong)addr, cnt * 512); //FIXME
printf("%d blocks read: %s\n",
n, (n==cnt) ? "OK" : "ERROR");
return (n == cnt) ? 0 : 1;
} else if (strcmp(argv[1], "write") == 0) {
int dev = simple_strtoul(argv[2], NULL, 10);
void *addr = (void *)simple_strtoul(argv[3], NULL, 16);
u32 cnt = simple_strtoul(argv[5], NULL, 16);
u32 n;
struct mmc *mmc = find_mmc_device(dev);
int blk = simple_strtoul(argv[4], NULL, 16);
printf("\nMMC write: dev # %d, block # %d, count %d ... ",
dev, blk, cnt);
mmc_init(mmc);
n = mmc->block_dev.block_write(dev, blk, cnt, addr);
printf("%d blocks written: %s\n",
n, (n == cnt) ? "OK" : "ERROR");
return (n == cnt) ? 0 : 1;
} else {
printf("Usage:\n%s\n", cmdtp->usage);
rc = 1;
}
Is there any way to rescan the bus like the old "mmcinit" command did? This can be useful if you need to change the card.
return rc;
- }
+}
+U_BOOT_CMD(
- mmc, 6, 1, do_mmcops,
- "mmc - MMC sub system\n",
- "mmc read <device num> addr blk# cnt\n"
- "mmc write <device num> addr blk# cnt\n"
- "mmc list - lists available devices\n");
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 3dc031b..d5a969c 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -25,6 +25,7 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libmmc.a
+COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
COBJS := $(COBJS-y) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c new file mode 100644 index 0000000..cd4eb77 --- /dev/null +++ b/drivers/mmc/mmc.c @@ -0,0 +1,926 @@ +/*
- Copyright 2008, Freescale Semiconductor, Inc
- Andy Fleming
- Based vaguely on the Linux code
- 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 <config.h> +#include <common.h> +#include <command.h> +#include <mmc.h> +#include <part.h> +#include <malloc.h> +#include <mmc.h>
+static struct mmc *mmc_devices;
Maybe a struct list_head would be more appropriate here?
+static int cur_dev_num = -1;
+int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{
- return mmc->send_cmd(mmc, cmd, data);
+}
+int mmc_set_blocklen(struct mmc *mmc, int len) +{
- struct mmc_cmd cmd;
- cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
- cmd.resp_type = MMC_CMD_R1;
- cmd.cmdarg = len;
- cmd.flags = 0;
- return mmc_send_cmd(mmc, &cmd, NULL);
+}
+struct mmc *find_mmc_device(int dev_num) +{
- struct mmc *m = mmc_devices;
- if (!m) {
printf("No MMC devices registered!\n");
return NULL;
- }
- do {
if (m->block_dev.dev == dev_num)
return m;
m = m->next;
- } while (m->next != mmc_devices);
- printf("MMC Device %d not found\n", dev_num);
- return NULL;
+}
+static ulong +mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src) +{
- struct mmc_cmd cmd;
- struct mmc_data data;
- int err;
- int stoperr = 0;
- struct mmc *mmc = find_mmc_device(dev_num);
- int blklen;
- if (!mmc)
return -1;
- blklen = mmc->write_bl_len;
- err = mmc_set_blocklen(mmc, mmc->write_bl_len);
- if (err) {
printf("set write bl len failed\n\r");
return err;
- }
- if (blkcnt > 1)
cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
- else
cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK;
- if (mmc->high_capacity)
cmd.cmdarg = start;
- else
cmd.cmdarg = start * blklen;
- cmd.resp_type = MMC_CMD_R1;
- cmd.flags = 0;
- data.src = src;
- data.blocks = blkcnt;
- data.blocksize = blklen;
- data.flags = MMC_DATA_WRITE;
- err = mmc_send_cmd(mmc, &cmd, &data);
- if (err) {
printf("mmc write failed\n\r");
return err;
- }
- if (blkcnt > 1) {
cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
cmd.cmdarg = 0;
cmd.resp_type = MMC_CMD_R1b;
cmd.flags = 0;
stoperr = mmc_send_cmd(mmc, &cmd, NULL);
- }
- return blkcnt;
+}
+int mmc_read_block(struct mmc *mmc, void *dst, uint blocknum) +{
- struct mmc_cmd cmd;
- struct mmc_data data;
- cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK;
- if (mmc->high_capacity)
cmd.cmdarg = blocknum;
- else
cmd.cmdarg = blocknum * mmc->read_bl_len;
- cmd.resp_type = MMC_CMD_R1;
- cmd.flags = 0;
- data.dest = dst;
- data.blocks = 1;
- data.blocksize = mmc->read_bl_len;
- data.flags = MMC_DATA_READ;
- return mmc_send_cmd(mmc, &cmd, &data);
+}
+int mmc_read(struct mmc *mmc, ulong src, uchar *dst, int size) +{
- char *buffer;
- int i;
- int blklen = mmc->read_bl_len;
- int startblock = src / blklen;
- int endblock = (src + size - 1) / blklen;
- int err = 0;
- /* Make a buffer big enough to hold all the blocks we might read */
- buffer = malloc(blklen);
- if (!buffer) {
printf("Could not allocate buffer for MMC read!\n");
return -1;
- }
- /* We always do full block reads from the card */
- err = mmc_set_blocklen(mmc, mmc->read_bl_len);
- if (err)
return err;
- for (i = startblock; i <= endblock; i++) {
int segment_size;
int offset;
err = mmc_read_block(mmc, buffer, i);
if (err)
goto free_buffer;
/*
* The first block may not be aligned, so we
* copy from the desired point in the block
*/
offset = (src & (blklen - 1));
segment_size = MIN(blklen - offset, size);
memcpy(dst, buffer + offset, segment_size);
dst += segment_size;
src += segment_size;
size -= segment_size;
- }
+free_buffer:
- free(buffer);
- return err;
+}
+static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) +{
- int err;
- ulong src;
- struct mmc *mmc = find_mmc_device(dev_num);
- if (!mmc)
return 0;
- src = start * mmc->read_bl_len;
- err = mmc_read(mmc, src, dst, blkcnt * mmc->read_bl_len);
Hmm...here you multiply to get a byte offset, then mmc_read does a division to get back to the block numbers. Wouldn't it be better to do it the other way around and let mmc_read() call mmc_bread()? It would also avoid the bounce buffer allocation and unaligned block handling.
I also think this will overflow on SDHC since src is a 32-bit quantity on most platforms, and SDHC cards can be larger than 4 GiB.
- if (err) {
printf("block read failed: %d\n", err);
return 0;
- }
- return blkcnt;
+}
+int mmc_go_idle(struct mmc* mmc) +{
- struct mmc_cmd cmd;
- int err;
- udelay(1000);
- cmd.cmdidx = MMC_CMD_GO_IDLE_STATE;
- cmd.cmdarg = 0;
- cmd.resp_type = MMC_CMD_RSP_NONE;
- cmd.flags = 0;
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
return err;
- udelay(2000);
- return 0;
+}
+int +sd_send_op_cond(struct mmc *mmc) +{
- int timeout = 1000;
- int err;
- struct mmc_cmd cmd;
- do {
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_CMD_R1;
cmd.cmdarg = 0;
cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
cmd.cmdidx = SD_CMD_APP_SEND_OP_COND;
cmd.resp_type = MMC_CMD_R3;
cmd.cmdarg = 0xff8000;
Hmm...hardcoded voltages? Better let the board define that...
if (mmc->version == SD_VERSION_2)
cmd.cmdarg |= 0x40000000;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
udelay(1000);
- } while ((!(cmd.response[0] & OCR_BUSY)) && timeout--);
- if (timeout <= 0)
return UNUSABLE_ERR;
- if (mmc->version != SD_VERSION_2)
mmc->version = SD_VERSION_1_0;
- mmc->ocr = ((uint *)(cmd.response))[0];
- mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
- mmc->rca = 0;
- return 0;
+}
+int mmc_send_op_cond(struct mmc *mmc) +{
- int timeout = 1000;
- struct mmc_cmd cmd;
- int err;
- /* Some cards seem to need this */
- mmc_go_idle(mmc);
- do {
cmd.cmdidx = MMC_CMD_SEND_OP_COND;
cmd.resp_type = MMC_CMD_R3;
cmd.cmdarg = 0x40ff8000;
Same here. Would be nice if this could be defined in terms of a few constants too...it's hard to tell exactly what voltages you're announcing here...
cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
udelay(1000);
- } while (!(cmd.response[0] & OCR_BUSY) && timeout--);
- if (timeout <= 0)
return UNUSABLE_ERR;
- mmc->version = MMC_VERSION_UNKNOWN;
- mmc->ocr = ((uint *)(cmd.response))[0];
- mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS);
- mmc->rca = 0;
- return 0;
+}
+int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd) +{
- struct mmc_cmd cmd;
- struct mmc_data data;
- int err;
- /* Get the Card Status Register */
- cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
- cmd.resp_type = MMC_CMD_R1;
- cmd.cmdarg = 0;
- cmd.flags = 0;
- data.dest = ext_csd;
- data.blocks = 1;
- data.blocksize = 512;
- data.flags = MMC_DATA_READ;
- err = mmc_send_cmd(mmc, &cmd, &data);
- return err;
+}
+int mmc_change_freq(struct mmc *mmc) +{
- struct mmc_cmd cmd;
- char ext_csd[512];
- char cardtype;
- int err;
- mmc->caps = 0;
- /* Only version 4 supports high-speed */
- if (mmc->version < MMC_VERSION_4)
return 0;
Hmm...but mmc_change_freq() doesn't sound like it has anything to do with high-speed...?
- mmc->caps |= MMC_MODE_4BIT;
I though the host was supposed to set that. It may even depend on how the board is wired.
- err = mmc_send_ext_csd(mmc, ext_csd);
- if (err)
return err;
- if (ext_csd[212] || ext_csd[213] || ext_csd[214] || ext_csd[215])
mmc->high_capacity = 1;
- cardtype = ext_csd[196] & 0xf;
- /* Set the card to use High Speed */
- cmd.cmdidx = MMC_CMD_SWITCH;
- cmd.resp_type = MMC_CMD_R1b;
- cmd.cmdarg = 0x1b90100;
Some constant(s) would be nice...
- cmd.flags = 0;
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
return err;
- /* Now check to see that it worked */
- err = mmc_send_ext_csd(mmc, ext_csd);
- if (err)
return err;
- /* No high-speed support */
- if (!ext_csd[185])
return 0;
- /* High Speed is set, there are two types: 52MHz and 26MHz */
- if (cardtype & MMC_HS_52MHZ)
mmc->caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
- else
mmc->caps |= MMC_MODE_HS;
How do you know the host supports high-speed? How does the host driver know when to switch into high-speed mode?
- return 0;
+}
+int sd_change_freq(struct mmc *mmc) +{
- int err;
- struct mmc_cmd cmd;
- uint scr[2];
- uint switch_status[16];
- struct mmc_data data;
- int timeout;
- mmc->caps = 0;
- /* Read the SCR to find out if this card supports higher speeds */
- cmd.cmdidx = MMC_CMD_APP_CMD;
- cmd.resp_type = MMC_CMD_R1;
- cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
return err;
- cmd.cmdidx = SD_CMD_APP_SEND_SCR;
- cmd.resp_type = MMC_CMD_R1;
- cmd.cmdarg = 0;
- cmd.flags = 0;
- timeout = 3;
+retry_scr:
- data.dest = (char *)&scr;
- data.blocksize = 8;
- data.blocks = 1;
- data.flags = MMC_DATA_READ;
- err = mmc_send_cmd(mmc, &cmd, &data);
- if (err) {
if (timeout--)
goto retry_scr;
return err;
- }
- mmc->scr[0] = scr[0];
- mmc->scr[1] = scr[1];
- switch ((mmc->scr[0] >> 24) & 0xf) {
case 0:
mmc->version = SD_VERSION_1_0;
break;
case 1:
mmc->version = SD_VERSION_1_10;
break;
case 2:
mmc->version = SD_VERSION_2;
break;
default:
mmc->version = SD_VERSION_1_0;
break;
- }
- /* Version 1.0 doesn't support switching */
- if (mmc->version == SD_VERSION_1_0)
return 0;
- timeout = 4;
- while (timeout--) {
/* Switch the frequency */
cmd.cmdidx = SD_CMD_SWITCH_FUNC;
cmd.resp_type = MMC_CMD_R1;
cmd.cmdarg = 0xfffff1;
cmd.flags = 0;
data.dest = (char *)&switch_status;
data.blocksize = 64;
data.blocks = 1;
data.flags = MMC_DATA_READ;
err = mmc_send_cmd(mmc, &cmd, &data);
if (err)
return err;
/* The high-speed function is busy. Try again */
if (!switch_status[7] & SD_HIGHSPEED_BUSY)
break;
- }
- if (mmc->scr[0] & SD_DATA_4BIT)
mmc->caps |= MMC_MODE_4BIT;
Again, we don't know if the host supports this.
- /* If high-speed isn't supported, we return */
- if (!(switch_status[3] & SD_HIGHSPEED_SUPPORTED))
return 0;
- cmd.cmdidx = SD_CMD_SWITCH_FUNC;
- cmd.resp_type = MMC_CMD_R1;
- cmd.cmdarg = 0x80fffff1;
Constants please.
- cmd.flags = 0;
- data.dest = (char *)&switch_status;
- data.blocksize = 64;
- data.blocks = 1;
- data.flags = MMC_DATA_READ;
- err = mmc_send_cmd(mmc, &cmd, &data);
- if (err)
return err;
- if ((switch_status[4] & 0x0f000000) == 0x01000000)
mmc->caps |= MMC_MODE_HS;
And we overrule the host again. Note that the host may support any combination of 1-bit, 4-bit, 8-bit busses and normal/high-speed signaling, and it needs to be told when to switch between them.
- return 0;
+}
+/* frequency bases */ +/* divided by 10 to be nice to platforms without floating point */ +int fbase[] = {
- 10000,
- 100000,
- 1000000,
- 10000000,
+};
+/* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice
- to platforms without floating point.
- */
+int multipliers[] = {
- 0, /* reserved */
- 10,
- 12,
- 13,
- 15,
- 20,
- 25,
- 30,
- 35,
- 40,
- 45,
- 50,
- 55,
- 60,
- 70,
- 80,
+};
+void mmc_set_ios(struct mmc *mmc) +{
- mmc->set_ios(mmc);
+}
+void mmc_set_clock(struct mmc *mmc, uint clock) +{
- mmc->clock = clock;
- mmc_set_ios(mmc);
+}
+void mmc_set_bus_width(struct mmc *mmc, uint width) +{
- mmc->bus_width = width;
- mmc_set_ios(mmc);
+}
+int mmc_startup(struct mmc *mmc) +{
- int err;
- uint mult, freq;
- u64 cmult, csize;
- struct mmc_cmd cmd;
- /* Put the Card in Identify Mode */
- cmd.cmdidx = MMC_CMD_ALL_SEND_CID;
- cmd.resp_type = MMC_CMD_R2;
- cmd.cmdarg = 0;
- cmd.flags = 0;
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
return err;
- memcpy(mmc->cid, cmd.response, 16);
- /*
* For MMC cards, set the Relative Address.
* For SD cards, get the Relatvie Address.
* This also puts the cards into Standby State
*/
- cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR;
- cmd.cmdarg = mmc->rca << 16;
- cmd.resp_type = MMC_CMD_R6;
- cmd.flags = 0;
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
return err;
- if (IS_SD(mmc))
mmc->rca = (((uint *)(cmd.response))[0] >> 16) & 0xffff;
- /* Get the Card-Specific Data */
- cmd.cmdidx = MMC_CMD_SEND_CSD;
- cmd.resp_type = MMC_CMD_R2;
- cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
return err;
- mmc->csd[0] = ((uint *)(cmd.response))[0];
- mmc->csd[1] = ((uint *)(cmd.response))[1];
- mmc->csd[2] = ((uint *)(cmd.response))[2];
- mmc->csd[3] = ((uint *)(cmd.response))[3];
- if (mmc->version == MMC_VERSION_UNKNOWN) {
int version = (cmd.response[0] >> 2) & 0xf;
switch (version) {
case 0:
mmc->version = MMC_VERSION_1_2;
break;
case 1:
mmc->version = MMC_VERSION_1_4;
break;
case 2:
mmc->version = MMC_VERSION_2_2;
break;
case 3:
mmc->version = MMC_VERSION_3;
break;
case 4:
mmc->version = MMC_VERSION_4;
break;
default:
mmc->version = MMC_VERSION_1_2;
break;
}
- }
- /* divide frequency by 10, since the mults are 10x bigger */
- freq = fbase[(cmd.response[3] & 0x7)];
- mult = multipliers[((cmd.response[3] >> 3) & 0xf)];
- mmc->tran_speed = freq * mult;
- mmc->read_bl_len = 1 << ((((uint *)(cmd.response))[1] >> 16) & 0xf);
- if (IS_SD(mmc))
mmc->write_bl_len = mmc->read_bl_len;
- else
mmc->write_bl_len = 1 << ((((uint *)(cmd.response))[3] >> 22) & 0xf);
- if (mmc->high_capacity) {
csize = (mmc->csd[1] & 0x3f) << 16
| (mmc->csd[2] & 0xffff0000) >> 16;
cmult = 8;
- } else {
csize = (mmc->csd[1] & 0x3ff) << 2
| (mmc->csd[2] & 0xc0000000) >> 30;
cmult = (mmc->csd[2] & 0x00038000) >> 15;
- }
- mmc->capacity = (csize + 1) << (cmult + 2);
- mmc->capacity *= mmc->read_bl_len;
64-bit multiply...hopefully all platforms are fine with that.
- if (mmc->read_bl_len > 512)
mmc->read_bl_len = 512;
- if (mmc->write_bl_len > 512)
mmc->write_bl_len = 512;
- /* Select the card, and put it into Transfer Mode */
- cmd.cmdidx = MMC_CMD_SELECT_CARD;
- cmd.resp_type = MMC_CMD_R1b;
- cmd.cmdarg = mmc->rca << 16;
- cmd.flags = 0;
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
return err;
- if (IS_SD(mmc))
err = sd_change_freq(mmc);
- else
err = mmc_change_freq(mmc);
- if (err)
return err;
- if (IS_SD(mmc)) {
if (mmc->caps & MMC_MODE_4BIT) {
cmd.cmdidx = MMC_CMD_APP_CMD;
cmd.resp_type = MMC_CMD_R1;
cmd.cmdarg = mmc->rca << 16;
cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH;
cmd.resp_type = MMC_CMD_R1;
cmd.cmdarg = 2;
cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
mmc_set_bus_width(mmc, 4);
}
if (mmc->caps & MMC_MODE_HS)
mmc_set_clock(mmc, 12000000);
else
mmc_set_clock(mmc, 12000000);
Surely high-speed capable cards can do more than 12 MHz? And where are the host/board/card-specific frequency limits taken into account?
- } else {
if (mmc->caps & MMC_MODE_4BIT) {
/* Set the card to use 4 bit*/
cmd.cmdidx = MMC_CMD_SWITCH;
cmd.resp_type = MMC_CMD_R1b;
cmd.cmdarg = 0x1b70100;
cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
mmc_set_bus_width(mmc, 4);
} else if (mmc->caps & MMC_MODE_8BIT) {
/* Set the card to use 8 bit*/
cmd.cmdidx = MMC_CMD_SWITCH;
cmd.resp_type = MMC_CMD_R1b;
cmd.cmdarg = 0x1b70200;
cmd.flags = 0;
err = mmc_send_cmd(mmc, &cmd, NULL);
if (err)
return err;
mmc_set_bus_width(mmc, 8);
}
if (mmc->caps & MMC_MODE_HS) {
if (mmc->caps & MMC_MODE_HS_52MHz)
mmc_set_clock(mmc, 52000000);
else
mmc_set_clock(mmc, 26000000);
} else
mmc_set_clock(mmc, 20000000);
Same here. You calculated tran_speed above, why don't you use it?
- }
- /* fill in device description */
- mmc->block_dev.if_type = IF_TYPE_MMC;
- mmc->block_dev.part_type = PART_TYPE_DOS;
- mmc->block_dev.dev = cur_dev_num++;
- mmc->block_dev.lun = 0;
- mmc->block_dev.type = 0;
- mmc->block_dev.blksz = mmc->read_bl_len;
- mmc->block_dev.lba = mmc->capacity/mmc->read_bl_len;
- sprintf(mmc->block_dev.vendor,"Man %02x%02x%02x Snr %02x%02x%02x%02x",
mmc->cid[0], mmc->cid[1], mmc->cid[2],
mmc->cid[9], mmc->cid[10], mmc->cid[11], mmc->cid[12]);
- sprintf(mmc->block_dev.product,"%c%c%c%c%c", mmc->cid[3],
mmc->cid[4], mmc->cid[5], mmc->cid[6], mmc->cid[7]);
- sprintf(mmc->block_dev.revision,"%d.%d", mmc->cid[8] >> 4,
mmc->cid[8] & 0xf);
- mmc->block_dev.removable = 1;
- mmc->block_dev.block_read = mmc_bread;
- mmc->block_dev.block_write = mmc_bwrite;
- init_part(&mmc->block_dev);
- return 0;
+}
+int mmc_send_if_cond(struct mmc *mmc) +{
- struct mmc_cmd cmd;
- int err;
- cmd.cmdidx = SD_CMD_SEND_IF_COND;
- cmd.cmdarg = 0x1aa;
Constant(s) please. Should this be board-dependent?
- cmd.resp_type = MMC_CMD_R7;
- cmd.flags = 0;
- err = mmc_send_cmd(mmc, &cmd, NULL);
- if (err)
return err;
- if (((uint *)(cmd.response))[0] != 0x1aa)
Ditto.
return UNUSABLE_ERR;
- else
mmc->version = SD_VERSION_2;
- return 0;
+}
+int mmc_register(struct mmc *mmc) +{
- struct mmc *m;
- if (!mmc_devices)
mmc_devices = mmc;
- else {
for (m = mmc_devices; m->next != mmc_devices; m=m->next);
m->next = mmc;
- }
- mmc->next = mmc_devices;
A list_head would make this so much simpler:
list_add_tail(&mmc->node, &mmc_devices);
- return 0;
+}
+block_dev_desc_t *mmc_get_dev(int dev) +{
- struct mmc *mmc = find_mmc_device(dev);
- return &mmc->block_dev;
+}
+int mmc_init(struct mmc *mmc) +{
- int err;
- err = mmc->init(mmc);
- if (err)
return err;
- /* Reset the Card */
- err = mmc_go_idle(mmc);
- if (err)
return err;
- /* Test for SD version 2 */
- err = mmc_send_if_cond(mmc);
- /* If we got an error other than timeout, we bail */
- if (err && err != TIMEOUT)
return err;
- /* Now try to get the SD card's operating condition */
- err = sd_send_op_cond(mmc);
- /* If the command timed out, we check for an MMC card */
- if (err == TIMEOUT) {
err = mmc_send_op_cond(mmc);
if (err) {
printf("Card did not respond to voltage select!\n");
return UNUSABLE_ERR;
}
- }
- err = mmc_startup(mmc);
- if (!err)
mmc->initialized = 1;
- return err;
+}
+/*
- CPU and board-specific MMC initializations. Aliased function
- signals caller to move on
- */
+static int __def_mmc_init(bd_t *bis) +{
- return -1;
+}
+int cpu_mmc_init(bd_t *bis) __attribute((weak, alias("__def_mmc_init"))); +int board_mmc_init(bd_t *bis) __attribute((weak, alias("__def_mmc_init")));
+void print_mmc_devices(char separator) +{
- struct mmc *m = mmc_devices;
- do {
if (m != mmc_devices)
printf("%c ", separator);
printf("%s: %d", m->name, m->block_dev.dev);
m = m->next;
- } while (m->next != mmc_devices);
- printf("\n");
+}
+int mmc_initialize(bd_t *bis) +{
- mmc_devices = NULL;
- cur_dev_num = 0;
- if (board_mmc_init(bis) < 0)
cpu_mmc_init(bis);
- if (!mmc_devices)
puts ("No MMC devices found.\n");
- else
print_mmc_devices(',');
- return 0;
+} diff --git a/include/mmc.h b/include/mmc.h index 7a03ed2..aed449c 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -1,6 +1,8 @@ /*
- (C) Copyright 2000-2003
- Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- Copyright 2008, Freescale Semiconductor, Inc
- Andy Fleming
- Based (loosely) on the Linux code
- See file CREDITS for list of people who contributed to this
- project.
@@ -24,32 +26,186 @@ #ifndef _MMC_H_ #define _MMC_H_
-/* MMC command numbers */ +#define SD_VERSION_SD 0x20000 +#define SD_VERSION_2 (SD_VERSION_SD | 0x20) +#define SD_VERSION_1_0 (SD_VERSION_SD | 0x10) +#define SD_VERSION_1_10 (SD_VERSION_SD | 0x1a) +#define MMC_VERSION_MMC 0x10000 +#define MMC_VERSION_UNKNOWN (MMC_VERSION_MMC) +#define MMC_VERSION_1_2 (MMC_VERSION_MMC | 0x12) +#define MMC_VERSION_1_4 (MMC_VERSION_MMC | 0x14) +#define MMC_VERSION_2_2 (MMC_VERSION_MMC | 0x22) +#define MMC_VERSION_3 (MMC_VERSION_MMC | 0x30) +#define MMC_VERSION_4 (MMC_VERSION_MMC | 0x40)
+#define MMC_MODE_HS 0x001 +#define MMC_MODE_HS_52MHz 0x010 +#define MMC_MODE_4BIT 0x100 +#define MMC_MODE_8BIT 0x200
+#define SD_DATA_4BIT 0x00040000
+#define IS_SD(x) (mmc->version & SD_VERSION_SD)
+#define MMC_DATA_READ 1 +#define MMC_DATA_WRITE 2
+#define NO_CARD_ERR -16 /* No SD/MMC card inserted */ +#define UNUSABLE_ERR -17 /* Unusable Card */ +#define COMM_ERR -18 /* Communications Error */ +#define TIMEOUT -19
#define MMC_CMD_GO_IDLE_STATE 0 #define MMC_CMD_SEND_OP_COND 1 #define MMC_CMD_ALL_SEND_CID 2 #define MMC_CMD_SET_RELATIVE_ADDR 3 #define MMC_CMD_SET_DSR 4 +#define MMC_CMD_SWITCH 6 #define MMC_CMD_SELECT_CARD 7 +#define MMC_CMD_SEND_EXT_CSD 8 #define MMC_CMD_SEND_CSD 9 #define MMC_CMD_SEND_CID 10 +#define MMC_CMD_STOP_TRANSMISSION 12 #define MMC_CMD_SEND_STATUS 13 #define MMC_CMD_SET_BLOCKLEN 16 #define MMC_CMD_READ_SINGLE_BLOCK 17 #define MMC_CMD_READ_MULTIPLE_BLOCK 18 -#define MMC_CMD_WRITE_BLOCK 24 +#define MMC_CMD_WRITE_SINGLE_BLOCK 24 +#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25 #define MMC_CMD_APP_CMD 55
-/* SD Card command numbers */ #define SD_CMD_SEND_RELATIVE_ADDR 3 -#define SD_CMD_SWITCH 6 +#define SD_CMD_SWITCH_FUNC 6 #define SD_CMD_SEND_IF_COND 8
#define SD_CMD_APP_SET_BUS_WIDTH 6 #define SD_CMD_APP_SEND_OP_COND 41 +#define SD_CMD_APP_SEND_SCR 51
-int mmc_legacy_init(int verbose); -int mmc_read(ulong src, uchar *dst, int size); -int mmc_write(uchar *src, ulong dst, int size); +#define SD_HIGHSPEED_BUSY 0x00020000 +#define SD_HIGHSPEED_SUPPORTED 0x00020000
Aren't those identical?
+#define MMC_HS_TIMING 0x00000100 +#define MMC_HS_52MHZ 0x2
+#define OCR_BUSY 0x80 +#define OCR_HCS 0x40000000
+#define R1_ILLEGAL_COMMAND (1 << 22) +#define R1_APP_CMD (1 << 5)
+enum {
- MMC_CMD_RSP_NONE,
- MMC_CMD_R1 = 1,
- MMC_CMD_R1b,
- MMC_CMD_R2,
- MMC_CMD_R3,
- MMC_CMD_R4,
- MMC_CMD_R5,
- MMC_CMD_R5b,
- MMC_CMD_R6,
- MMC_CMD_R7
Please define these in terms of sensible constants which make it easy to deduce the length of the reply, whether the CRC is expected to be valid, whether the card may signal busy, etc. The following flags seem to do the trick on Linux:
#define MMC_RSP_PRESENT (1 << 0) #define MMC_RSP_136 (1 << 1) /* 136 bit response */ #define MMC_RSP_CRC (1 << 2) /* expect valid crc */ #define MMC_RSP_BUSY (1 << 3) /* card may send busy */ #define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */
which becomes
#define MMC_RSP_NONE (0) #define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) #define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) #define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) #define MMC_RSP_R3 (MMC_RSP_PRESENT) #define MMC_RSP_R4 (MMC_RSP_PRESENT) #define MMC_RSP_R5 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) #define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) #define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+};
+struct mmc_cid {
- unsigned long psn;
- unsigned short oid;
- unsigned char mid;
- unsigned char prv;
- unsigned char mdt;
- 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;
+};
Are these the same in all versions of the SD and MMC specs?
+struct mmc_cmd {
- ushort cmdidx;
- int resp_type;
Should be unsigned.
- uint cmdarg;
- char response[18];
Hmm...why 18 bytes? Linux only needs 4 32-bit words.
- uint flags;
+};
+struct mmc_data {
- char *dest;
- const char *src; /* src buffers don't get written to */
But you never use both at the same time. Why two pointers?
- uint flags;
- int blocks;
- int blocksize;
Do these have to be signed?
+};
+struct mmc {
- char name[32];
- void *priv;
- uint initialized;
- uint version;
- int high_capacity;
There are a few 32-bit fields which contain 1 bit of information. Maybe they can be combined into some kind of "flags" field?
- uint bus_width;
- uint clock;
- uint caps;
Caps for whom? The host or the card?
- uint ocr;
- uint scr[2];
- uint csd[4];
- char cid[16];
- ushort rca;
- uint tran_speed;
It's not entirely clear how this is different from "clock". Ok, since I just went through the code, I happen to know the difference, but I'm sure others will be confused.
- uint read_bl_len;
- uint write_bl_len;
- u64 capacity;
- block_dev_desc_t block_dev;
- int (*send_cmd)(struct mmc *mmc,
struct mmc_cmd *cmd, struct mmc_data *data);
- void (*set_ios)(struct mmc *mmc);
- int (*init)(struct mmc *mmc);
- struct mmc *next;
Some documentation would be nice. Maybe it would be a good idea to separate the host-specific bits from the card-specific bits too?
+};
+int mmc_register(struct mmc *mmc); +int mmc_initialize(bd_t *bis); +int mmc_init(struct mmc *mmc); +int mmc_read(struct mmc *mmc, ulong src, uchar *dst, int size); +struct mmc *find_mmc_device(int dev_num); +void print_mmc_devices(char separator);
These are not the only non-static functions you define:
+int do_mmcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +int mmc_set_blocklen(struct mmc *mmc, int len) +int mmc_read_block(struct mmc *mmc, void *dst, uint blocknum) +int mmc_go_idle(struct mmc* mmc) +int +sd_send_op_cond(struct mmc *mmc) +int mmc_send_op_cond(struct mmc *mmc) +int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd) +int mmc_change_freq(struct mmc *mmc) +int sd_change_freq(struct mmc *mmc) +int fbase[] = { +int multipliers[] = { +void mmc_set_ios(struct mmc *mmc) +void mmc_set_clock(struct mmc *mmc, uint clock) +void mmc_set_bus_width(struct mmc *mmc, uint width) +int mmc_startup(struct mmc *mmc)
and more. All of them are undocumented, so it's not clear how they're supposed to be used.
+#ifndef CONFIG_GENERIC_MMC +int mmc_legacy_init(int verbose); +#endif #endif /* _MMC_H_ */ diff --git a/lib_ppc/board.c b/lib_ppc/board.c index ce07c4e..e304d8c 100644 --- a/lib_ppc/board.c +++ b/lib_ppc/board.c @@ -51,6 +51,9 @@ #include <status_led.h> #endif #include <net.h> +#ifdef CONFIG_GENERIC_MMC +#include <mmc.h> +#endif #include <serial.h> #ifdef CONFIG_SYS_ALLOC_DPRAM #if !defined(CONFIG_CPM2) @@ -1083,6 +1086,12 @@ void board_init_r (gd_t *id, ulong dest_addr) scsi_init (); #endif
+#ifdef CONFIG_GENERIC_MMC
- WATCHDOG_RESET ();
- puts ("MMC: ");
- mmc_initialize (bd);
+#endif
Why can't mmc_initialize() print the "MMC: " part too?
Better yet, why not define mmc_initialize() as an empty function if CONFIG_GENERIC_MMC is not set?
Ok, that's all for now. Thanks a lot for taking the effort to do this. A common mmc layer is much needed.
Haavard

On Tue, Nov 4, 2008 at 3:37 AM, Haavard Skinnemoen haavard.skinnemoen@atmel.com wrote:
Andy Fleming afleming@freescale.com wrote:
mmc_init(mmc);
n = mmc->block_dev.block_write(dev, blk, cnt, addr);
printf("%d blocks written: %s\n",
n, (n == cnt) ? "OK" : "ERROR");
return (n == cnt) ? 0 : 1;
} else {
printf("Usage:\n%s\n", cmdtp->usage);
rc = 1;
}
Is there any way to rescan the bus like the old "mmcinit" command did? This can be useful if you need to change the card.
I think this is being done. I designed it so that it acts like eth_init(), being called every time you start a new transaction. It's a bit clunky, but I liked it better than doing it manually. Is there a command I missed, that doesn't invoke mmc_init() first?
+static struct mmc *mmc_devices;
Maybe a struct list_head would be more appropriate here?
Yeah, it would. I copied this from eth.c, but that probably predates list_head's addition to u-boot.
+static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) +{
int err;
ulong src;
struct mmc *mmc = find_mmc_device(dev_num);
if (!mmc)
return 0;
src = start * mmc->read_bl_len;
err = mmc_read(mmc, src, dst, blkcnt * mmc->read_bl_len);
Hmm...here you multiply to get a byte offset, then mmc_read does a division to get back to the block numbers. Wouldn't it be better to do it the other way around and let mmc_read() call mmc_bread()? It would also avoid the bounce buffer allocation and unaligned block handling.
Yeah, I was thinking about that, but decided not to, for some reason. I will reconsider again, as it fits better with my way of thinking about it.
I think it may have had something to do with large reads.
I also think this will overflow on SDHC since src is a 32-bit quantity on most platforms, and SDHC cards can be larger than 4 GiB.
Good catch.
cmd.cmdidx = SD_CMD_APP_SEND_OP_COND;
cmd.resp_type = MMC_CMD_R3;
cmd.cmdarg = 0xff8000;
Hmm...hardcoded voltages? Better let the board define that...
Oh. Ugh, yeah. I had a feeling I had some stuff like that lying around.
cmd.cmdidx = MMC_CMD_SEND_OP_COND;
cmd.resp_type = MMC_CMD_R3;
cmd.cmdarg = 0x40ff8000;
Same here. Would be nice if this could be defined in terms of a few constants too...it's hard to tell exactly what voltages you're announcing here...
Agreed.
/* Only version 4 supports high-speed */
if (mmc->version < MMC_VERSION_4)
return 0;
Hmm...but mmc_change_freq() doesn't sound like it has anything to do with high-speed...?
I could use a better comment there. And probably a better function name.
mmc->caps |= MMC_MODE_4BIT;
I though the host was supposed to set that. It may even depend on how the board is wired.
Ah, in this case, I was determining the card capabilities, but I do need to match those against host capabilities.
/* Set the card to use High Speed */
cmd.cmdidx = MMC_CMD_SWITCH;
cmd.resp_type = MMC_CMD_R1b;
cmd.cmdarg = 0x1b90100;
Some constant(s) would be nice...
Yeah, my bad. I was in a rush, and didn't bother to look up these constants.
/* High Speed is set, there are two types: 52MHz and 26MHz */
if (cardtype & MMC_HS_52MHZ)
mmc->caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
else
mmc->caps |= MMC_MODE_HS;
How do you know the host supports high-speed? How does the host driver know when to switch into high-speed mode?
Yeah, that's a bit sketchy and untested. It would be better if I added a way for the host to declare a max speed. I can steal from the Linux code.
if (mmc->scr[0] & SD_DATA_4BIT)
mmc->caps |= MMC_MODE_4BIT;
Again, we don't know if the host supports this.
Agreed.
cmd.cmdidx = SD_CMD_SWITCH_FUNC;
cmd.resp_type = MMC_CMD_R1;
cmd.cmdarg = 0x80fffff1;
Constants please.
*hangs head in shame*
if ((switch_status[4] & 0x0f000000) == 0x01000000)
mmc->caps |= MMC_MODE_HS;
And we overrule the host again. Note that the host may support any combination of 1-bit, 4-bit, 8-bit busses and normal/high-speed signaling, and it needs to be told when to switch between them.
Will do.
mmc->capacity = (csize + 1) << (cmult + 2);
mmc->capacity *= mmc->read_bl_len;
64-bit multiply...hopefully all platforms are fine with that.
Are there platforms that can't do a 64-bit multiply in 32-bit chunks? I tested this on an e500 core, which is only 32-bit, but the compiler handles making it work. If there's a platform that doesn't support it, they will have to come in and fix this code in whatever their standard way for handling such things is.
if (mmc->caps & MMC_MODE_HS)
mmc_set_clock(mmc, 12000000);
else
mmc_set_clock(mmc, 12000000);
Surely high-speed capable cards can do more than 12 MHz? And where are the host/board/card-specific frequency limits taken into account?
Ugh. This is an unintentional bug (rather than the earlier ones, which were results of laziness). This was put in during testing of our hardware, because there were issues with running at higher speeds. I will fix that.
if (mmc->caps & MMC_MODE_HS) {
if (mmc->caps & MMC_MODE_HS_52MHz)
mmc_set_clock(mmc, 52000000);
else
mmc_set_clock(mmc, 26000000);
} else
mmc_set_clock(mmc, 20000000);
Same here. You calculated tran_speed above, why don't you use it?
Will do.
+int mmc_send_if_cond(struct mmc *mmc) +{
struct mmc_cmd cmd;
int err;
cmd.cmdidx = SD_CMD_SEND_IF_COND;
cmd.cmdarg = 0x1aa;
Constant(s) please. Should this be board-dependent?
Yes. The "aa" is universal, but the "1" is host-specific (board-specific things will be left up to the host driver, where possible)
if (((uint *)(cmd.response))[0] != 0x1aa)
Ditto.
Yeah.
+int mmc_register(struct mmc *mmc) +{
struct mmc *m;
if (!mmc_devices)
mmc_devices = mmc;
else {
for (m = mmc_devices; m->next != mmc_devices; m=m->next);
m->next = mmc;
}
mmc->next = mmc_devices;
A list_head would make this so much simpler:
list_add_tail(&mmc->node, &mmc_devices);
Agreed.
-int mmc_legacy_init(int verbose); -int mmc_read(ulong src, uchar *dst, int size); -int mmc_write(uchar *src, ulong dst, int size); +#define SD_HIGHSPEED_BUSY 0x00020000 +#define SD_HIGHSPEED_SUPPORTED 0x00020000
Aren't those identical?
The appear to be...I'll figure out why they both exist.
+enum {
MMC_CMD_RSP_NONE,
MMC_CMD_R1 = 1,
MMC_CMD_R1b,
MMC_CMD_R2,
MMC_CMD_R3,
MMC_CMD_R4,
MMC_CMD_R5,
MMC_CMD_R5b,
MMC_CMD_R6,
MMC_CMD_R7
Please define these in terms of sensible constants which make it easy to deduce the length of the reply, whether the CRC is expected to be valid, whether the card may signal busy, etc. The following flags seem to do the trick on Linux:
#define MMC_RSP_PRESENT (1 << 0) #define MMC_RSP_136 (1 << 1) /* 136 bit response */ #define MMC_RSP_CRC (1 << 2) /* expect valid crc */ #define MMC_RSP_BUSY (1 << 3) /* card may send busy */ #define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */
which becomes
#define MMC_RSP_NONE (0) #define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) #define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) #define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) #define MMC_RSP_R3 (MMC_RSP_PRESENT) #define MMC_RSP_R4 (MMC_RSP_PRESENT) #define MMC_RSP_R5 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) #define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) #define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
...Yes, that makes much more sense.
+};
+struct mmc_cid {
unsigned long psn;
unsigned short oid;
unsigned char mid;
unsigned char prv;
unsigned char mdt;
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;
+};
Are these the same in all versions of the SD and MMC specs?
Honestly, I didn't bother to check. None of this code uses it, I just put it in there for later enhancements (and to allow the drivers which were using it to continue doing so. I suspect that there are endianness issues.
+struct mmc_cmd {
ushort cmdidx;
int resp_type;
Should be unsigned.
Well...now. Before, it was a number between 0 and 9. But I will change it when I change the constants.
uint cmdarg;
char response[18];
Hmm...why 18 bytes? Linux only needs 4 32-bit words.
...Not sure.
uint flags;
+};
+struct mmc_data {
char *dest;
const char *src; /* src buffers don't get written to */
But you never use both at the same time. Why two pointers?
This is silly, but to get rid of a warning. Maybe my brain's just not working, but I couldn't think of a way to allow the same pointer to take assignments from bread and bwrite, as bwrite uses a constant source, and bread has a writable destination. This was my questionable hack.
uint flags;
int blocks;
int blocksize;
Do these have to be signed?
I don't think they do.
+};
+struct mmc {
char name[32];
void *priv;
uint initialized;
uint version;
int high_capacity;
There are a few 32-bit fields which contain 1 bit of information. Maybe they can be combined into some kind of "flags" field?
Sure.
uint bus_width;
uint clock;
uint caps;
Caps for whom? The host or the card?
Certainly I need to define that better, and add host capabilities
uint ocr;
uint scr[2];
uint csd[4];
char cid[16];
ushort rca;
uint tran_speed;
It's not entirely clear how this is different from "clock". Ok, since I just went through the code, I happen to know the difference, but I'm sure others will be confused.
Will clarify that clock is the current operating frequency, while tran_speed is the current card's maximum operating frequency.
uint read_bl_len;
uint write_bl_len;
u64 capacity;
block_dev_desc_t block_dev;
int (*send_cmd)(struct mmc *mmc,
struct mmc_cmd *cmd, struct mmc_data *data);
void (*set_ios)(struct mmc *mmc);
int (*init)(struct mmc *mmc);
struct mmc *next;
Some documentation would be nice. Maybe it would be a good idea to separate the host-specific bits from the card-specific bits too?
More documentation: will do. I'm not convinced on separation, though. It's certainly easy to separate them visually, but I'm not sure there's any technical advantage to be gained from putting the different bits in different structures. It would involve a lot more dereferencing, and I tend to think of u-boot drivers as a little less concerned with strict encapsulation. The one thing I could think of would be to add support for multiple, concurrent cards. To be honest, that's a problem I haven't even thought about, and I'm not sure I want to at this stage.
+};
+int mmc_register(struct mmc *mmc); +int mmc_initialize(bd_t *bis); +int mmc_init(struct mmc *mmc); +int mmc_read(struct mmc *mmc, ulong src, uchar *dst, int size); +struct mmc *find_mmc_device(int dev_num); +void print_mmc_devices(char separator);
These are not the only non-static functions you define:
+int do_mmcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +int mmc_set_blocklen(struct mmc *mmc, int len) +int mmc_read_block(struct mmc *mmc, void *dst, uint blocknum) +int mmc_go_idle(struct mmc* mmc) +int +sd_send_op_cond(struct mmc *mmc) +int mmc_send_op_cond(struct mmc *mmc) +int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd) +int mmc_change_freq(struct mmc *mmc) +int sd_change_freq(struct mmc *mmc) +int fbase[] = { +int multipliers[] = { +void mmc_set_ios(struct mmc *mmc) +void mmc_set_clock(struct mmc *mmc, uint clock) +void mmc_set_bus_width(struct mmc *mmc, uint width) +int mmc_startup(struct mmc *mmc)
and more. All of them are undocumented, so it's not clear how they're supposed to be used.
I will increase documentation, and reduce the scope of any identifiers which aren't meant to be used outside of the file.
+#ifdef CONFIG_GENERIC_MMC
WATCHDOG_RESET ();
puts ("MMC: ");
mmc_initialize (bd);
+#endif
Why can't mmc_initialize() print the "MMC: " part too?
Better yet, why not define mmc_initialize() as an empty function if CONFIG_GENERIC_MMC is not set?
I'll take a look. I copied this pretty closely from eth_initialize(), so that's the why, but I don't see why I couldn't move the MMC: inside mmc_initialize. Making it an empty function is fine with me, but different from how the other functions do it.
Ok, that's all for now. Thanks a lot for taking the effort to do this. A common mmc layer is much needed.
Thank you for taking the time to review it!
Andy

"Andy Fleming" afleming@gmail.com wrote:
On Tue, Nov 4, 2008 at 3:37 AM, Haavard Skinnemoen haavard.skinnemoen@atmel.com wrote:
Andy Fleming afleming@freescale.com wrote:
mmc_init(mmc);
n = mmc->block_dev.block_write(dev, blk, cnt, addr);
printf("%d blocks written: %s\n",
n, (n == cnt) ? "OK" : "ERROR");
return (n == cnt) ? 0 : 1;
} else {
printf("Usage:\n%s\n", cmdtp->usage);
rc = 1;
}
Is there any way to rescan the bus like the old "mmcinit" command did? This can be useful if you need to change the card.
I think this is being done. I designed it so that it acts like eth_init(), being called every time you start a new transaction. It's a bit clunky, but I liked it better than doing it manually. Is there a command I missed, that doesn't invoke mmc_init() first?
I don't think the block ops do. Nor do I think they should -- doing a full enumeration on every block access sounds a bit excessive.
So, if you do
fatls mmc 0:1 <eject and re-insert card> fatls mmc 0:1
I suspect the second command will fail.
/* Only version 4 supports high-speed */
if (mmc->version < MMC_VERSION_4)
return 0;
Hmm...but mmc_change_freq() doesn't sound like it has anything to do with high-speed...?
I could use a better comment there. And probably a better function name.
mmc_set_high_speed() or something?
mmc->caps |= MMC_MODE_4BIT;
I though the host was supposed to set that. It may even depend on how the board is wired.
Ah, in this case, I was determining the card capabilities, but I do need to match those against host capabilities.
Right. How about renaming the existing "caps" field "card_caps" and adding a new "host_caps" field?
/* Set the card to use High Speed */
cmd.cmdidx = MMC_CMD_SWITCH;
cmd.resp_type = MMC_CMD_R1b;
cmd.cmdarg = 0x1b90100;
Some constant(s) would be nice...
Yeah, my bad. I was in a rush, and didn't bother to look up these constants.
/* High Speed is set, there are two types: 52MHz and 26MHz */
if (cardtype & MMC_HS_52MHZ)
mmc->caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
else
mmc->caps |= MMC_MODE_HS;
How do you know the host supports high-speed? How does the host driver know when to switch into high-speed mode?
Yeah, that's a bit sketchy and untested. It would be better if I added a way for the host to declare a max speed. I can steal from the Linux code.
I think we need both a host flag indicating high-speed support and a f_max field indicating the maximum frequency. Even if the host supports high-speed signaling, there may be internal clock limitations that prevents it from going all the way up to 52 MHz. Or board routing issues.
mmc->capacity = (csize + 1) << (cmult + 2);
mmc->capacity *= mmc->read_bl_len;
64-bit multiply...hopefully all platforms are fine with that.
Are there platforms that can't do a 64-bit multiply in 32-bit chunks? I tested this on an e500 core, which is only 32-bit, but the compiler handles making it work. If there's a platform that doesn't support it, they will have to come in and fix this code in whatever their standard way for handling such things is.
Yeah, it's probably fine. I just felt like mentioning it.
+int mmc_send_if_cond(struct mmc *mmc) +{
struct mmc_cmd cmd;
int err;
cmd.cmdidx = SD_CMD_SEND_IF_COND;
cmd.cmdarg = 0x1aa;
Constant(s) please. Should this be board-dependent?
Yes. The "aa" is universal, but the "1" is host-specific (board-specific things will be left up to the host driver, where possible)
Sounds good.
Are these the same in all versions of the SD and MMC specs?
Honestly, I didn't bother to check. None of this code uses it, I just put it in there for later enhancements (and to allow the drivers which were using it to continue doing so. I suspect that there are endianness issues.
There could be endianness issues too...but I remember someone pointing out that the whole structure didn't correspond to some version of the spec. So I think some of the fields are slightly different from one spec revision to another.
But I guess the same argument that applied back then applies now -- if no one uses those fields, no need to worry all that much. But it's something we should keep in mind.
+struct mmc_cmd {
ushort cmdidx;
int resp_type;
Should be unsigned.
Well...now. Before, it was a number between 0 and 9. But I will change it when I change the constants.
uint cmdarg;
char response[18];
Hmm...why 18 bytes? Linux only needs 4 32-bit words.
...Not sure.
I'd prefer it to be an array of u32 so that I won't have to worry about alignment when copying the response from the 32-bit hardware registers. But I don't know what others prefer.
uint flags;
+};
+struct mmc_data {
char *dest;
const char *src; /* src buffers don't get written to */
But you never use both at the same time. Why two pointers?
This is silly, but to get rid of a warning. Maybe my brain's just not working, but I couldn't think of a way to allow the same pointer to take assignments from bread and bwrite, as bwrite uses a constant source, and bread has a writable destination. This was my questionable hack.
Union?
uint tran_speed;
It's not entirely clear how this is different from "clock". Ok, since I just went through the code, I happen to know the difference, but I'm sure others will be confused.
Will clarify that clock is the current operating frequency, while tran_speed is the current card's maximum operating frequency.
Sounds good.
uint read_bl_len;
uint write_bl_len;
u64 capacity;
block_dev_desc_t block_dev;
int (*send_cmd)(struct mmc *mmc,
struct mmc_cmd *cmd, struct mmc_data *data);
void (*set_ios)(struct mmc *mmc);
int (*init)(struct mmc *mmc);
struct mmc *next;
Some documentation would be nice. Maybe it would be a good idea to separate the host-specific bits from the card-specific bits too?
More documentation: will do. I'm not convinced on separation, though. It's certainly easy to separate them visually, but I'm not sure there's any technical advantage to be gained from putting the different bits in different structures. It would involve a lot more dereferencing, and I tend to think of u-boot drivers as a little less concerned with strict encapsulation. The one thing I could think of would be to add support for multiple, concurrent cards. To be honest, that's a problem I haven't even thought about, and I'm not sure I want to at this stage.
Well, I was mostly thinking about visual separation, perhaps create two structs with card- and host-specific data respectively and embed them into the mmc_device struct so no extra dereferencing will be necessary. But just grouping them together and adding an empty line between them would probably work just as well.
Multiple cards can probably wait, I agree.
+#ifdef CONFIG_GENERIC_MMC
WATCHDOG_RESET ();
puts ("MMC: ");
mmc_initialize (bd);
+#endif
Why can't mmc_initialize() print the "MMC: " part too?
Better yet, why not define mmc_initialize() as an empty function if CONFIG_GENERIC_MMC is not set?
I'll take a look. I copied this pretty closely from eth_initialize(), so that's the why, but I don't see why I couldn't move the MMC: inside mmc_initialize. Making it an empty function is fine with me, but different from how the other functions do it.
Yeah, I know this is the way it's usually done, and it's been bothering me for a while. It looks like most of the lib_<arch>/board.c files contain more preprocessor directives than actual C code, so I just wanted to suggest a way to reduce the cpp noise a little bit.
But I do understand if you want to stick to the usual way of doing things.
Haavard

Andy Fleming afleming@freescale.com wrote:
diff --git a/include/asm-avr32/arch-at32ap700x/mmc.h b/include/asm-avr32/arch-at32ap700x/mmc.h deleted file mode 100644 index 9caba91..0000000 --- a/include/asm-avr32/arch-at32ap700x/mmc.h +++ /dev/null
-struct mmc_cid {
-struct mmc_csd
-#define R1_ILLEGAL_COMMAND (1 << 22) -#define R1_APP_CMD (1 << 5)
Where can those definitions be found now? The driver still needs them...
Haavard

Dear Andy Fleming,
In message 1225406139-21485-2-git-send-email-afleming@freescale.com you wrote:
There were several, now there is one (two if you count the lower-case versions).
Signed-off-by: Andy Fleming afleming@freescale.com
board/delta/nand.c | 2 -- board/zylonite/nand.c | 2 -- common/cmd_bedbug.c | 4 ---- common/cmd_elf.c | 4 ---- cpu/ixp/npe/include/IxEthDB_p.h | 2 -- include/common.h | 3 +++ include/usbdcore.h | 8 -------- 7 files changed, 3 insertions(+), 22 deletions(-)
Applied, thanks.
Best regards,
Wolfgang Denk

Andy Fleming afleming@freescale.com wrote:
There were several, now there is one (two if you count the lower-case versions).
Signed-off-by: Andy Fleming afleming@freescale.com
I think it would be even better if the uppercase versions were eliminated. But getting the definitions out of the .c files is of course most important.
Haavard
participants (5)
-
Andy Fleming
-
Andy Fleming
-
Haavard Skinnemoen
-
Liu Dave-R63238
-
Wolfgang Denk