From b44c70837a87bcd92b76a94e49b85d7656cb2a11 Mon Sep 17 00:00:00 2001 From: Alagu Sankar Date: Wed, 12 May 2010 15:08:24 +0530 Subject: SD1.00 wide-bus fix Fixed a bug wherein SD version 1.0 cards were not configured for 4-bit mode Signed-off-by: Alagu Sankar --- drivers/mmc/mmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 6805b33..9988cb1 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -512,6 +512,9 @@ retry_scr: break; } + if (mmc->scr[0] & SD_DATA_4BIT) + mmc->card_caps |= MMC_MODE_4BIT; + /* Version 1.0 doesn't support switching */ if (mmc->version == SD_VERSION_1_0) return 0; @@ -529,9 +532,6 @@ retry_scr: break; } - if (mmc->scr[0] & SD_DATA_4BIT) - mmc->card_caps |= MMC_MODE_4BIT; - /* If high-speed isn't supported, we return */ if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)) return 0; -- cgit v1.1 From ce0fbcd2e1bd8c7b0975589b9853f3e8914aa406 Mon Sep 17 00:00:00 2001 From: Matt Waddel Date: Thu, 24 Feb 2011 16:35:23 +0000 Subject: MMC: Max blocks value adjustable The maximum blocks value was hardcoded to 65535 due to a 16 bit register length. The value can change for different platforms. This patch makes the default the current value of 65535, but it is configurable for other platforms. Signed-off-by: Matt Waddel Signed-off-by: Andy Fleming --- drivers/mmc/mmc.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 9988cb1..1355735 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -33,6 +33,11 @@ #include #include +/* Set block count limit because of 16 bit register limit on some hardware*/ +#ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT +#define CONFIG_SYS_MMC_MAX_BLK_COUNT 65535 +#endif + static struct list_head mmc_devices; static int cur_dev_num = -1; @@ -139,11 +144,8 @@ mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src) return 0; do { - /* - * The 65535 constraint comes from some hardware has - * only 16 bit width block number counter - */ - cur = (blocks_todo > 65535) ? 65535 : blocks_todo; + cur = (blocks_todo > CONFIG_SYS_MMC_MAX_BLK_COUNT) ? + CONFIG_SYS_MMC_MAX_BLK_COUNT : blocks_todo; if(mmc_write_blocks(mmc, start, cur, src) != cur) return 0; blocks_todo -= cur; @@ -215,11 +217,8 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) return 0; do { - /* - * The 65535 constraint comes from some hardware has - * only 16 bit width block number counter - */ - cur = (blocks_todo > 65535) ? 65535 : blocks_todo; + cur = (blocks_todo > CONFIG_SYS_MMC_MAX_BLK_COUNT) ? + CONFIG_SYS_MMC_MAX_BLK_COUNT : blocks_todo; if(mmc_read_blocks(mmc, dst, start, cur) != cur) return 0; blocks_todo -= cur; -- cgit v1.1 From b97e0cd78837180e122cd9209b9e71e2c2d3f911 Mon Sep 17 00:00:00 2001 From: Wolfgang Wegner Date: Fri, 23 Apr 2010 05:43:12 +0000 Subject: add CONFIG_SPI_IDLE_VAL for cf_spi.c to allow use of spi_mmc This patch adds CONFIG_SPI_IDLE_VAL to cf_spi.c The default setting is 0x0 to behave same as current version, in case CONFIG_SPI_MMC is set, the value is set to 0xFFFF (all ones). In either case, the value can be overwritten by board configuration. Signed-off-by: Wolfgang Wegner Signed-off-by: Andy Fleming --- drivers/spi/cf_spi.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/spi/cf_spi.c b/drivers/spi/cf_spi.c index 722aafc..a883da9 100644 --- a/drivers/spi/cf_spi.c +++ b/drivers/spi/cf_spi.c @@ -49,6 +49,14 @@ extern void cfspi_release_bus(uint bus, uint cs); DECLARE_GLOBAL_DATA_PTR; +#ifndef CONFIG_SPI_IDLE_VAL +#if defined(CONFIG_SPI_MMC) +#define CONFIG_SPI_IDLE_VAL 0xFFFF +#else +#define CONFIG_SPI_IDLE_VAL 0x0 +#endif +#endif + #if defined(CONFIG_CF_DSPI) /* DSPI specific mode */ #define SPI_MODE_MOD 0x00200000 @@ -145,7 +153,7 @@ int cfspi_xfer(struct spi_slave *slave, uint bitlen, const void *dout, } if (din != NULL) { - cfspi_tx(ctrl, 0); + cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL); if (cfslave->charbit == 16) *spi_rd16++ = cfspi_rx(); else @@ -169,7 +177,7 @@ int cfspi_xfer(struct spi_slave *slave, uint bitlen, const void *dout, } if (din != NULL) { - cfspi_tx(ctrl, 0); + cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL); if (cfslave->charbit == 16) *spi_rd16 = cfspi_rx(); else @@ -177,7 +185,7 @@ int cfspi_xfer(struct spi_slave *slave, uint bitlen, const void *dout, } } else { /* dummy read */ - cfspi_tx(ctrl, 0); + cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL); cfspi_rx(); } -- cgit v1.1 From 63fb5a7e4b0ada0bf77dd89120a61d2bd14e50af Mon Sep 17 00:00:00 2001 From: Frans Meulenbroeks Date: Sat, 31 Jul 2010 04:45:18 +0000 Subject: drivers/mmc/fsl_esdhc.c: reordered tests As DATA_ERROR includes the value IRQSTAT_DTOE, a timeout error would yield the first error return instead of TIMEOUT. By swapping the test TIMEOUTs are reported as such An alternate solution would be to remove the IRQSTAT_DTOE from the DATA_ERROR define but as that one might be less desired I've opted for the simplest solution Signed-off-by: Frans Meulenbroeks Signed-off-by: Andy Fleming --- drivers/mmc/fsl_esdhc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 4f1b515..2838795 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -332,11 +332,11 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) do { irqstat = esdhc_read32(®s->irqstat); - if (irqstat & DATA_ERR) - return COMM_ERR; - if (irqstat & IRQSTAT_DTOE) return TIMEOUT; + + if (irqstat & DATA_ERR) + return COMM_ERR; } while (!(irqstat & IRQSTAT_TC) && (esdhc_read32(®s->prsstat) & PRSSTAT_DLA)); #endif -- cgit v1.1 From 5f837c2c0ebda8f22474d26f85857993fb81ad7c Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 20 Oct 2010 01:15:53 +0000 Subject: mmc: constify & localize data These local vars need not be writable nor exported. Signed-off-by: Mike Frysinger Signed-off-by: Andy Fleming --- drivers/mmc/mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 1355735..e8bc4b8 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -548,7 +548,7 @@ retry_scr: /* frequency bases */ /* divided by 10 to be nice to platforms without floating point */ -int fbase[] = { +static const int fbase[] = { 10000, 100000, 1000000, @@ -558,7 +558,7 @@ int fbase[] = { /* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice * to platforms without floating point. */ -int multipliers[] = { +static const int multipliers[] = { 0, /* reserved */ 10, 12, -- cgit v1.1 From d52ebf102209cc1ad460c79b9498b2c8936ba413 Mon Sep 17 00:00:00 2001 From: Thomas Chou Date: Fri, 24 Dec 2010 13:12:21 +0000 Subject: mmc: add generic mmc spi driver This patch supports mmc/sd card with spi interface. It is based on the generic mmc framework. It works with SDHC and supports multi blocks read/write. The crc checksum on data packet is enabled with the def, There is a subcomamnd "mmc_spi" to setup spi bus and cs at run time. Signed-off-by: Thomas Chou Signed-off-by: Andy Fleming --- common/Makefile | 1 + common/cmd_mmc_spi.c | 88 ++++++++++++++++ drivers/mmc/Makefile | 1 + drivers/mmc/mmc.c | 93 +++++++++++++---- drivers/mmc/mmc_spi.c | 280 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 5 + 6 files changed, 448 insertions(+), 20 deletions(-) create mode 100644 common/cmd_mmc_spi.c create mode 100644 drivers/mmc/mmc_spi.c diff --git a/common/Makefile b/common/Makefile index 4555716..6f41ce4 100644 --- a/common/Makefile +++ b/common/Makefile @@ -118,6 +118,7 @@ COBJS-$(CONFIG_CMD_MII) += miiphyutil.o COBJS-$(CONFIG_CMD_MII) += cmd_mii.o COBJS-$(CONFIG_CMD_MISC) += cmd_misc.o COBJS-$(CONFIG_CMD_MMC) += cmd_mmc.o +COBJS-$(CONFIG_CMD_MMC_SPI) += cmd_mmc_spi.o COBJS-$(CONFIG_MP) += cmd_mp.o COBJS-$(CONFIG_CMD_MTDPARTS) += cmd_mtdparts.o COBJS-$(CONFIG_CMD_NAND) += cmd_nand.o diff --git a/common/cmd_mmc_spi.c b/common/cmd_mmc_spi.c new file mode 100644 index 0000000..63fe313 --- /dev/null +++ b/common/cmd_mmc_spi.c @@ -0,0 +1,88 @@ +/* + * Command for mmc_spi setup. + * + * Copyright (C) 2010 Thomas Chou + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include + +#ifndef CONFIG_MMC_SPI_BUS +# define CONFIG_MMC_SPI_BUS 0 +#endif +#ifndef CONFIG_MMC_SPI_CS +# define CONFIG_MMC_SPI_CS 1 +#endif +/* in SPI mode, MMC speed limit is 20MHz, while SD speed limit is 25MHz */ +#ifndef CONFIG_MMC_SPI_SPEED +# define CONFIG_MMC_SPI_SPEED 25000000 +#endif +/* MMC and SD specs only seem to care that sampling is on the + * rising edge ... meaning SPI modes 0 or 3. So either SPI mode + * should be legit. We'll use mode 0 since the steady state is 0, + * which is appropriate for hotplugging, unless the platform data + * specify mode 3 (if hardware is not compatible to mode 0). + */ +#ifndef CONFIG_MMC_SPI_MODE +# define CONFIG_MMC_SPI_MODE SPI_MODE_0 +#endif + +static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + uint bus = CONFIG_MMC_SPI_BUS; + uint cs = CONFIG_MMC_SPI_CS; + uint speed = CONFIG_MMC_SPI_SPEED; + uint mode = CONFIG_MMC_SPI_MODE; + char *endp; + struct mmc *mmc; + + if (argc < 2) + goto usage; + + cs = simple_strtoul(argv[1], &endp, 0); + if (*argv[1] == 0 || (*endp != 0 && *endp != ':')) + goto usage; + if (*endp == ':') { + if (endp[1] == 0) + goto usage; + bus = cs; + cs = simple_strtoul(endp + 1, &endp, 0); + if (*endp != 0) + goto usage; + } + if (argc >= 3) { + speed = simple_strtoul(argv[2], &endp, 0); + if (*argv[2] == 0 || *endp != 0) + goto usage; + } + if (argc >= 4) { + mode = simple_strtoul(argv[3], &endp, 16); + if (*argv[3] == 0 || *endp != 0) + goto usage; + } + if (!spi_cs_is_valid(bus, cs)) { + printf("Invalid SPI bus %u cs %u\n", bus, cs); + return 1; + } + + mmc = mmc_spi_init(bus, cs, speed, mode); + if (!mmc) { + printf("Failed to create MMC Device\n"); + return 1; + } + printf("%s: %d at %u:%u hz %u mode %u\n", mmc->name, mmc->block_dev.dev, + bus, cs, speed, mode); + return 0; + +usage: + cmd_usage(cmdtp); + return 1; +} + +U_BOOT_CMD( + mmc_spi, 4, 0, do_mmc_spi, + "mmc_spi setup", + "[bus:]cs [hz] [mode] - setup mmc_spi device" +); diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 3496f0a..9aca3a2 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -31,6 +31,7 @@ COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o +COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index e8bc4b8..e8d61c3 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -117,7 +117,10 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) return 0; } - if (blkcnt > 1) { + /* SPI multiblock writes terminate using a special + * token, not a STOP_TRANSMISSION request. + */ + if (!mmc_host_is_spi(mmc) && blkcnt > 1) { cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_R1b; @@ -279,7 +282,8 @@ sd_send_op_cond(struct mmc *mmc) * how to manage low voltages SD card is not yet * specified. */ - cmd.cmdarg = mmc->voltages & 0xff8000; + cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 : + (mmc->voltages & 0xff8000); if (mmc->version == SD_VERSION_2) cmd.cmdarg |= OCR_HCS; @@ -298,6 +302,18 @@ sd_send_op_cond(struct mmc *mmc) if (mmc->version != SD_VERSION_2) mmc->version = SD_VERSION_1_0; + if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ + cmd.cmdidx = MMC_CMD_SPI_READ_OCR; + cmd.resp_type = MMC_RSP_R3; + cmd.cmdarg = 0; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + } + mmc->ocr = cmd.response[0]; mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); @@ -318,7 +334,8 @@ int mmc_send_op_cond(struct mmc *mmc) do { cmd.cmdidx = MMC_CMD_SEND_OP_COND; cmd.resp_type = MMC_RSP_R3; - cmd.cmdarg = OCR_HCS | mmc->voltages; + cmd.cmdarg = OCR_HCS | (mmc_host_is_spi(mmc) ? 0 : + mmc->voltages); cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); @@ -332,6 +349,18 @@ int mmc_send_op_cond(struct mmc *mmc) if (timeout <= 0) return UNUSABLE_ERR; + if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ + cmd.cmdidx = MMC_CMD_SPI_READ_OCR; + cmd.resp_type = MMC_RSP_R3; + cmd.cmdarg = 0; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + } + mmc->version = MMC_VERSION_UNKNOWN; mmc->ocr = cmd.response[0]; @@ -387,6 +416,9 @@ int mmc_change_freq(struct mmc *mmc) mmc->card_caps = 0; + if (mmc_host_is_spi(mmc)) + return 0; + /* Only version 4 supports high-speed */ if (mmc->version < MMC_VERSION_4) return 0; @@ -460,6 +492,9 @@ int sd_change_freq(struct mmc *mmc) mmc->card_caps = 0; + if (mmc_host_is_spi(mmc)) + return 0; + /* Read the SCR to find out if this card supports higher speeds */ cmd.cmdidx = MMC_CMD_APP_CMD; cmd.resp_type = MMC_RSP_R1; @@ -610,8 +645,22 @@ int mmc_startup(struct mmc *mmc) struct mmc_cmd cmd; char ext_csd[512]; +#ifdef CONFIG_MMC_SPI_CRC_ON + if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */ + cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 1; + cmd.flags = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + } +#endif + /* Put the Card in Identify Mode */ - cmd.cmdidx = MMC_CMD_ALL_SEND_CID; + cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID : + MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */ cmd.resp_type = MMC_RSP_R2; cmd.cmdarg = 0; cmd.flags = 0; @@ -628,18 +677,20 @@ int mmc_startup(struct mmc *mmc) * 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_RSP_R6; - cmd.flags = 0; + if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ + cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR; + cmd.cmdarg = mmc->rca << 16; + cmd.resp_type = MMC_RSP_R6; + cmd.flags = 0; - err = mmc_send_cmd(mmc, &cmd, NULL); + err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - return err; + if (err) + return err; - if (IS_SD(mmc)) - mmc->rca = (cmd.response[0] >> 16) & 0xffff; + if (IS_SD(mmc)) + mmc->rca = (cmd.response[0] >> 16) & 0xffff; + } /* Get the Card-Specific Data */ cmd.cmdidx = MMC_CMD_SEND_CSD; @@ -715,14 +766,16 @@ int mmc_startup(struct mmc *mmc) mmc->write_bl_len = 512; /* Select the card, and put it into Transfer Mode */ - cmd.cmdidx = MMC_CMD_SELECT_CARD; - cmd.resp_type = MMC_RSP_R1b; - cmd.cmdarg = mmc->rca << 16; - cmd.flags = 0; - err = mmc_send_cmd(mmc, &cmd, NULL); + if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ + cmd.cmdidx = MMC_CMD_SELECT_CARD; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = mmc->rca << 16; + cmd.flags = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - return err; + if (err) + return err; + } if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { /* check ext_csd version and capacity */ diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c new file mode 100644 index 0000000..dc7574c --- /dev/null +++ b/drivers/mmc/mmc_spi.c @@ -0,0 +1,280 @@ +/* + * generic mmc spi driver + * + * Copyright (C) 2010 Thomas Chou + * Licensed under the GPL-2 or later. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* MMC/SD in SPI mode reports R1 status always */ +#define R1_SPI_IDLE (1 << 0) +#define R1_SPI_ERASE_RESET (1 << 1) +#define R1_SPI_ILLEGAL_COMMAND (1 << 2) +#define R1_SPI_COM_CRC (1 << 3) +#define R1_SPI_ERASE_SEQ (1 << 4) +#define R1_SPI_ADDRESS (1 << 5) +#define R1_SPI_PARAMETER (1 << 6) +/* R1 bit 7 is always zero, reuse this bit for error */ +#define R1_SPI_ERROR (1 << 7) + +/* Response tokens used to ack each block written: */ +#define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f) +#define SPI_RESPONSE_ACCEPTED ((2 << 1)|1) +#define SPI_RESPONSE_CRC_ERR ((5 << 1)|1) +#define SPI_RESPONSE_WRITE_ERR ((6 << 1)|1) + +/* Read and write blocks start with these tokens and end with crc; + * on error, read tokens act like a subset of R2_SPI_* values. + */ +#define SPI_TOKEN_SINGLE 0xfe /* single block r/w, multiblock read */ +#define SPI_TOKEN_MULTI_WRITE 0xfc /* multiblock write */ +#define SPI_TOKEN_STOP_TRAN 0xfd /* terminate multiblock write */ + +/* MMC SPI commands start with a start bit "0" and a transmit bit "1" */ +#define MMC_SPI_CMD(x) (0x40 | (x & 0x3f)) + +/* bus capability */ +#define MMC_SPI_VOLTAGE (MMC_VDD_32_33 | MMC_VDD_33_34) +#define MMC_SPI_MIN_CLOCK 400000 /* 400KHz to meet MMC spec */ + +/* timeout value */ +#define CTOUT 8 +#define RTOUT 3000000 /* 1 sec */ +#define WTOUT 3000000 /* 1 sec */ + +static uint mmc_spi_sendcmd(struct mmc *mmc, ushort cmdidx, u32 cmdarg) +{ + struct spi_slave *spi = mmc->priv; + u8 cmdo[7]; + u8 r1; + int i; + cmdo[0] = 0xff; + cmdo[1] = MMC_SPI_CMD(cmdidx); + cmdo[2] = cmdarg >> 24; + cmdo[3] = cmdarg >> 16; + cmdo[4] = cmdarg >> 8; + cmdo[5] = cmdarg; + cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01; + spi_xfer(spi, sizeof(cmdo) * 8, cmdo, NULL, 0); + for (i = 0; i < CTOUT; i++) { + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if (i && (r1 & 0x80) == 0) /* r1 response */ + break; + } + debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1); + return r1; +} + +static uint mmc_spi_readdata(struct mmc *mmc, void *xbuf, + u32 bcnt, u32 bsize) +{ + struct spi_slave *spi = mmc->priv; + u8 *buf = xbuf; + u8 r1; + u16 crc; + int i; + while (bcnt--) { + for (i = 0; i < RTOUT; i++) { + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if (r1 != 0xff) /* data token */ + break; + } + debug("%s:tok%d %x\n", __func__, i, r1); + if (r1 == SPI_TOKEN_SINGLE) { + spi_xfer(spi, bsize * 8, NULL, buf, 0); + spi_xfer(spi, 2 * 8, NULL, &crc, 0); +#ifdef CONFIG_MMC_SPI_CRC_ON + if (swab16(cyg_crc16(buf, bsize)) != crc) { + debug("%s: CRC error\n", mmc->name); + r1 = R1_SPI_COM_CRC; + break; + } +#endif + r1 = 0; + } else { + r1 = R1_SPI_ERROR; + break; + } + buf += bsize; + } + return r1; +} + +static uint mmc_spi_writedata(struct mmc *mmc, const void *xbuf, + u32 bcnt, u32 bsize, int multi) +{ + struct spi_slave *spi = mmc->priv; + const u8 *buf = xbuf; + u8 r1; + u16 crc; + u8 tok[2]; + int i; + tok[0] = 0xff; + tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE; + while (bcnt--) { +#ifdef CONFIG_MMC_SPI_CRC_ON + crc = swab16(cyg_crc16((u8 *)buf, bsize)); +#endif + spi_xfer(spi, 2 * 8, tok, NULL, 0); + spi_xfer(spi, bsize * 8, buf, NULL, 0); + spi_xfer(spi, 2 * 8, &crc, NULL, 0); + for (i = 0; i < CTOUT; i++) { + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if ((r1 & 0x10) == 0) /* response token */ + break; + } + debug("%s:tok%d %x\n", __func__, i, r1); + if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) { + for (i = 0; i < WTOUT; i++) { /* wait busy */ + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if (i && r1 == 0xff) { + r1 = 0; + break; + } + } + if (i == WTOUT) { + debug("%s:wtout %x\n", __func__, r1); + r1 = R1_SPI_ERROR; + break; + } + } else { + debug("%s: err %x\n", __func__, r1); + r1 = R1_SPI_COM_CRC; + break; + } + buf += bsize; + } + if (multi && bcnt == -1) { /* stop multi write */ + tok[1] = SPI_TOKEN_STOP_TRAN; + spi_xfer(spi, 2 * 8, tok, NULL, 0); + for (i = 0; i < WTOUT; i++) { /* wait busy */ + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if (i && r1 == 0xff) { + r1 = 0; + break; + } + } + if (i == WTOUT) { + debug("%s:wstop %x\n", __func__, r1); + r1 = R1_SPI_ERROR; + } + } + return r1; +} + +static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct spi_slave *spi = mmc->priv; + u8 r1; + int i; + int ret = 0; + debug("%s:cmd%d %x %x %x\n", __func__, + cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags); + spi_claim_bus(spi); + spi_cs_activate(spi); + r1 = mmc_spi_sendcmd(mmc, cmd->cmdidx, cmd->cmdarg); + if (r1 == 0xff) { /* no response */ + ret = NO_CARD_ERR; + goto done; + } else if (r1 & R1_SPI_COM_CRC) { + ret = COMM_ERR; + goto done; + } else if (r1 & ~R1_SPI_IDLE) { /* other errors */ + ret = TIMEOUT; + goto done; + } else if (cmd->resp_type == MMC_RSP_R2) { + r1 = mmc_spi_readdata(mmc, cmd->response, 1, 16); + for (i = 0; i < 4; i++) + cmd->response[i] = swab32(cmd->response[i]); + debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1], + cmd->response[2], cmd->response[3]); + } else if (!data) { + switch (cmd->cmdidx) { + case SD_CMD_APP_SEND_OP_COND: + case MMC_CMD_SEND_OP_COND: + cmd->response[0] = (r1 & R1_SPI_IDLE) ? 0 : OCR_BUSY; + break; + case SD_CMD_SEND_IF_COND: + case MMC_CMD_SPI_READ_OCR: + spi_xfer(spi, 4 * 8, NULL, cmd->response, 0); + cmd->response[0] = swab32(cmd->response[0]); + debug("r32 %x\n", cmd->response[0]); + break; + } + } else { + debug("%s:data %x %x %x\n", __func__, + data->flags, data->blocks, data->blocksize); + if (data->flags == MMC_DATA_READ) + r1 = mmc_spi_readdata(mmc, data->dest, + data->blocks, data->blocksize); + else if (data->flags == MMC_DATA_WRITE) + r1 = mmc_spi_writedata(mmc, data->src, + data->blocks, data->blocksize, + (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)); + if (r1 & R1_SPI_COM_CRC) + ret = COMM_ERR; + else if (r1) /* other errors */ + ret = TIMEOUT; + } +done: + spi_cs_deactivate(spi); + spi_release_bus(spi); + return ret; +} + +static void mmc_spi_set_ios(struct mmc *mmc) +{ + struct spi_slave *spi = mmc->priv; + debug("%s: clock %u\n", __func__, mmc->clock); + if (mmc->clock) + spi_set_speed(spi, mmc->clock); +} + +static int mmc_spi_init_p(struct mmc *mmc) +{ + struct spi_slave *spi = mmc->priv; + mmc->clock = 0; + spi_set_speed(spi, MMC_SPI_MIN_CLOCK); + spi_claim_bus(spi); + /* cs deactivated for 100+ clock */ + spi_xfer(spi, 18 * 8, NULL, NULL, 0); + spi_release_bus(spi); + return 0; +} + +struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode) +{ + struct mmc *mmc; + + mmc = malloc(sizeof(*mmc)); + if (!mmc) + return NULL; + memset(mmc, 0, sizeof(*mmc)); + mmc->priv = spi_setup_slave(bus, cs, speed, mode); + if (!mmc->priv) { + free(mmc); + return NULL; + } + sprintf(mmc->name, "MMC_SPI"); + mmc->send_cmd = mmc_spi_request; + mmc->set_ios = mmc_spi_set_ios; + mmc->init = mmc_spi_init_p; + mmc->host_caps = MMC_MODE_SPI; + + mmc->voltages = MMC_SPI_VOLTAGE; + mmc->f_max = speed; + mmc->f_min = MMC_SPI_MIN_CLOCK; + mmc->block_dev.part_type = PART_TYPE_DOS; + + mmc_register(mmc); + + return mmc; +} diff --git a/include/mmc.h b/include/mmc.h index fcd0fd1..2cf489f 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -44,6 +44,7 @@ #define MMC_MODE_HS_52MHz 0x010 #define MMC_MODE_4BIT 0x100 #define MMC_MODE_8BIT 0x200 +#define MMC_MODE_SPI 0x400 #define SD_DATA_4BIT 0x00040000 @@ -75,6 +76,8 @@ #define MMC_CMD_WRITE_SINGLE_BLOCK 24 #define MMC_CMD_WRITE_MULTIPLE_BLOCK 25 #define MMC_CMD_APP_CMD 55 +#define MMC_CMD_SPI_READ_OCR 58 +#define MMC_CMD_SPI_CRC_ON_OFF 59 #define SD_CMD_SEND_RELATIVE_ADDR 3 #define SD_CMD_SWITCH_FUNC 6 @@ -291,6 +294,8 @@ int board_mmc_getcd(u8 *cd, struct mmc *mmc); #ifdef CONFIG_GENERIC_MMC int atmel_mci_init(void *regs); +#define mmc_host_is_spi(mmc) ((mmc)->host_caps & MMC_MODE_SPI) +struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode); #else int mmc_legacy_init(int verbose); #endif -- cgit v1.1 From 940e078297fc5f8707fe11003c9fda4a275fad5b Mon Sep 17 00:00:00 2001 From: Minkyu Kang Date: Tue, 4 Jan 2011 01:04:19 +0000 Subject: mmc: show mmc capacity using print_size Signed-off-by: Minkyu Kang Signed-off-by: Andy Fleming --- common/cmd_mmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c index 4323f76..6166749 100644 --- a/common/cmd_mmc.c +++ b/common/cmd_mmc.c @@ -104,7 +104,8 @@ static void print_mmcinfo(struct mmc *mmc) (mmc->version >> 4) & 0xf, mmc->version & 0xf); printf("High Capacity: %s\n", mmc->high_capacity ? "Yes" : "No"); - printf("Capacity: %lld\n", mmc->capacity); + puts("Capacity: "); + print_size(mmc->capacity, "\n"); printf("Bus Width: %d-bit\n", mmc->bus_width); } -- cgit v1.1 From 8baf939c2c5e0eb1f3d0cba1067e069e199cbf7f Mon Sep 17 00:00:00 2001 From: Minkyu Kang Date: Tue, 4 Jan 2011 01:04:56 +0000 Subject: mmc: remove duplicated header file Signed-off-by: Minkyu Kang Signed-off-by: Andy Fleming --- drivers/mmc/mmc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index e8d61c3..41c28d4 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -30,7 +30,6 @@ #include #include #include -#include #include /* Set block count limit because of 16 bit register limit on some hardware*/ -- cgit v1.1 From 5d4fc8d907ed12844b9c9190601fef2919f3ec25 Mon Sep 17 00:00:00 2001 From: Raffaele Recalcati Date: Fri, 11 Mar 2011 02:01:12 +0000 Subject: mmc: checking status after commands with R1b response It is recommended to check card status after these kind of commands. This is done using CMD13 (SEND_STATUS) JEDEC command until the card is ready. In case of error the card status field is displayed. Signed-off-by: Raffaele Recalcati Signed-off-by: Andy Fleming --- drivers/mmc/mmc.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- include/mmc.h | 4 ++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 41c28d4..3d445c0 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -52,6 +52,42 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) return mmc->send_cmd(mmc, cmd, data); } +int mmc_send_status(struct mmc *mmc, int timeout) +{ + struct mmc_cmd cmd; + int err; +#ifdef CONFIG_MMC_TRACE + int status; +#endif + + cmd.cmdidx = MMC_CMD_SEND_STATUS; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + cmd.flags = 0; + + do { + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + else if (cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) + break; + + udelay(1000); + + if (cmd.response[0] & MMC_STATUS_MASK) { + printf("Status Error: 0x%08X\n", cmd.response[0]); + return COMM_ERR; + } + } while (timeout--); + + if (!timeout) { + printf("Timeout waiting card ready\n"); + return TIMEOUT; + } + + return 0; +} + int mmc_set_blocklen(struct mmc *mmc, int len) { struct mmc_cmd cmd; @@ -86,6 +122,7 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) { struct mmc_cmd cmd; struct mmc_data data; + int timeout = 1000; if ((start + blkcnt) > mmc->block_dev.lba) { printf("MMC: block number 0x%lx exceeds max(0x%lx)\n", @@ -128,6 +165,9 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) printf("mmc fail to send stop cmd\n"); return 0; } + + /* Waiting for the ready status */ + mmc_send_status(mmc, timeout); } return blkcnt; @@ -162,6 +202,7 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt) { struct mmc_cmd cmd; struct mmc_data data; + int timeout = 1000; if (blkcnt > 1) cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK; @@ -193,6 +234,9 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt) printf("mmc fail to send stop cmd\n"); return 0; } + + /* Waiting for the ready status */ + mmc_send_status(mmc, timeout); } return blkcnt; @@ -396,15 +440,23 @@ int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd) int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) { struct mmc_cmd cmd; + int timeout = 1000; + int ret; cmd.cmdidx = MMC_CMD_SWITCH; cmd.resp_type = MMC_RSP_R1b; cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (index << 16) | - (value << 8); + (index << 16) | + (value << 8); cmd.flags = 0; - return mmc_send_cmd(mmc, &cmd, NULL); + ret = mmc_send_cmd(mmc, &cmd, NULL); + + /* Waiting for the ready status */ + mmc_send_status(mmc, timeout); + + return ret; + } int mmc_change_freq(struct mmc *mmc) @@ -643,6 +695,7 @@ int mmc_startup(struct mmc *mmc) u64 cmult, csize; struct mmc_cmd cmd; char ext_csd[512]; + int timeout = 1000; #ifdef CONFIG_MMC_SPI_CRC_ON if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */ @@ -699,6 +752,9 @@ int mmc_startup(struct mmc *mmc) err = mmc_send_cmd(mmc, &cmd, NULL); + /* Waiting for the ready status */ + mmc_send_status(mmc, timeout); + if (err) return err; diff --git a/include/mmc.h b/include/mmc.h index 2cf489f..ed084c8 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -97,6 +97,10 @@ #define OCR_BUSY 0x80000000 #define OCR_HCS 0x40000000 +#define MMC_STATUS_MASK (~0x0206BF7F) +#define MMC_STATUS_RDY_FOR_DATA (1<<8) +#define MMC_STATUS_CURR_STATE (0xf<<9) + #define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */ #define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */ #define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */ -- cgit v1.1 From 31cacbabf07ce00f5250b9826d5e48d4bbee1f94 Mon Sep 17 00:00:00 2001 From: Raffaele Recalcati Date: Fri, 11 Mar 2011 02:01:13 +0000 Subject: mmc: SEND_OP_COND considers card capabilities (voltage) The first SEND_OP_COND (CMD1) command added is used to ask card capabilities. After it an AND operation is done between card capabilities and host capabilities (at the moment only for the voltage field). Finally the correct value is sent to the MMC, waiting that the card exits from busy state. Signed-off-by: Raffaele Recalcati Signed-off-by: Andy Fleming --- drivers/mmc/mmc.c | 21 ++++++++++++++++++--- include/mmc.h | 2 ++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 3d445c0..64c8d56 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -367,18 +367,33 @@ sd_send_op_cond(struct mmc *mmc) int mmc_send_op_cond(struct mmc *mmc) { - int timeout = 1000; + int timeout = 10000; struct mmc_cmd cmd; int err; /* Some cards seem to need this */ mmc_go_idle(mmc); + /* Asking to the card its capabilities */ + cmd.cmdidx = MMC_CMD_SEND_OP_COND; + cmd.resp_type = MMC_RSP_R3; + cmd.cmdarg = 0; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + udelay(1000); + do { cmd.cmdidx = MMC_CMD_SEND_OP_COND; cmd.resp_type = MMC_RSP_R3; - cmd.cmdarg = OCR_HCS | (mmc_host_is_spi(mmc) ? 0 : - mmc->voltages); + cmd.cmdarg = (mmc_host_is_spi(mmc) ? 0 : + (mmc->voltages & + (cmd.response[0] & OCR_VOLTAGE_MASK)) | + (cmd.response[0] & OCR_ACCESS_MODE)); cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); diff --git a/include/mmc.h b/include/mmc.h index ed084c8..e0a56d9 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -96,6 +96,8 @@ #define OCR_BUSY 0x80000000 #define OCR_HCS 0x40000000 +#define OCR_VOLTAGE_MASK 0x007FFF80 +#define OCR_ACCESS_MODE 0x60000000 #define MMC_STATUS_MASK (~0x0206BF7F) #define MMC_STATUS_RDY_FOR_DATA (1<<8) -- cgit v1.1 From 5db2fe3ad9bdf4f32d72dbf2d3e3042bcd137a52 Mon Sep 17 00:00:00 2001 From: Raffaele Recalcati Date: Fri, 11 Mar 2011 02:01:14 +0000 Subject: mmc: trace added Defining CONFIG_MMC_TRACE in the include board file it is possible to activate a tracing support. This code helps in case of eMMC hw failure or to investigate possible eMMC initialization issues. Signed-off-by: Raffaele Recalcati Signed-off-by: Andy Fleming --- drivers/mmc/mmc.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 64c8d56..36f80fa 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -49,7 +49,60 @@ int board_mmc_getcd(u8 *cd, struct mmc *mmc)__attribute__((weak, int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { +#ifdef CONFIG_MMC_TRACE + int ret; + int i; + u8 *ptr; + + printf("CMD_SEND:%d\n", cmd->cmdidx); + printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg); + printf("\t\tFLAG\t\t\t %d\n", cmd->flags); + ret = mmc->send_cmd(mmc, cmd, data); + switch (cmd->resp_type) { + case MMC_RSP_NONE: + printf("\t\tMMC_RSP_NONE\n"); + break; + case MMC_RSP_R1: + printf("\t\tMMC_RSP_R1,5,6,7 \t 0x%08X \n", + cmd->response[0]); + break; + case MMC_RSP_R1b: + printf("\t\tMMC_RSP_R1b\t\t 0x%08X \n", + cmd->response[0]); + break; + case MMC_RSP_R2: + printf("\t\tMMC_RSP_R2\t\t 0x%08X \n", + cmd->response[0]); + printf("\t\t \t\t 0x%08X \n", + cmd->response[1]); + printf("\t\t \t\t 0x%08X \n", + cmd->response[2]); + printf("\t\t \t\t 0x%08X \n", + cmd->response[3]); + printf("\n"); + printf("\t\t\t\t\tDUMPING DATA\n"); + for (i = 0; i < 4; i++) { + int j; + printf("\t\t\t\t\t%03d - ", i*4); + ptr = &cmd->response[i]; + ptr += 3; + for (j = 0; j < 4; j++) + printf("%02X ", *ptr--); + printf("\n"); + } + break; + case MMC_RSP_R3: + printf("\t\tMMC_RSP_R3,4\t\t 0x%08X \n", + cmd->response[0]); + break; + default: + printf("\t\tERROR MMC rsp not supported\n"); + break; + } + return ret; +#else return mmc->send_cmd(mmc, cmd, data); +#endif } int mmc_send_status(struct mmc *mmc, int timeout) @@ -80,6 +133,10 @@ int mmc_send_status(struct mmc *mmc, int timeout) } } while (timeout--); +#ifdef CONFIG_MMC_TRACE + status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9; + printf("CURR STATE:%d\n", status); +#endif if (!timeout) { printf("Timeout waiting card ready\n"); return TIMEOUT; -- cgit v1.1 From 8511cd84ab2749c8edf41d123f365a1d2353528e Mon Sep 17 00:00:00 2001 From: Raffaele Recalcati Date: Wed, 23 Mar 2011 04:06:50 +0000 Subject: MMC may wrongly regconize 2GB eMMC as high capacity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hi Terry, > So I guess: > mmc_init calls mmc_send_op_cond  that set  high_capacity, > than it calls mmc_startup, that, with MMC_CMD_SEND_CSD  command, set > the capacity, using values in CSD register. > So I guess that mmc_change_freq should not recalculate high_capacity. > > It seems better, isn't it? > > Regards, > Raffaele > Finally I think that it is enough to apply the following patch in order to fix the issue. Regards, Raffaele Signed-off-by: Andy Fleming --- drivers/mmc/mmc.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 36f80fa..f27b7c7 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -553,9 +553,6 @@ int mmc_change_freq(struct mmc *mmc) 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; err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); -- cgit v1.1