[U-Boot] [PATCH 0/4] generic mmc_spi driver

This is the v9 update of the mmc_spi driver. Please enable DEBUG on the top of mmc_spi.c and help me perform the tests.
A new spi_set_speed() is added to meet the 400KHz clock requirement during mmc card initialization. An example update to bfin_spi is included. The altera_spi core does not support speed change, so I am going to post a new opencore ip for this.
The driver now claims and releases the spi bus for each mmc command, as Mike suggested.
Thomas Chou (4): lib: add crc7 from Linux spi: add spi_set_speed func bfin_spi: add spi_set_speed mmc: add generic mmc spi driver
common/Makefile | 1 + common/cmd_mmc_spi.c | 88 +++++++++++++++ drivers/mmc/Makefile | 1 + drivers/mmc/mmc.c | 93 +++++++++++++---- drivers/mmc/mmc_spi.c | 278 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/Makefile | 1 + drivers/spi/bfin_spi.c | 32 ++++--- drivers/spi/spi.c | 9 ++ include/linux/crc7.h | 14 +++ include/mmc.h | 5 + include/spi.h | 8 ++ lib/Makefile | 1 + lib/crc7.c | 62 +++++++++++ 13 files changed, 560 insertions(+), 33 deletions(-) create mode 100644 common/cmd_mmc_spi.c create mode 100644 drivers/mmc/mmc_spi.c create mode 100644 drivers/spi/spi.c create mode 100644 include/linux/crc7.h create mode 100644 lib/crc7.c

Crc7 is used to compute mmc spi comamnd packet checksum.
Signed-off-by: Thomas Chou thomas@wytron.com.tw --- include/linux/crc7.h | 14 +++++++++++ lib/Makefile | 1 + lib/crc7.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 0 deletions(-) create mode 100644 include/linux/crc7.h create mode 100644 lib/crc7.c
diff --git a/include/linux/crc7.h b/include/linux/crc7.h new file mode 100644 index 0000000..1786e77 --- /dev/null +++ b/include/linux/crc7.h @@ -0,0 +1,14 @@ +#ifndef _LINUX_CRC7_H +#define _LINUX_CRC7_H +#include <linux/types.h> + +extern const u8 crc7_syndrome_table[256]; + +static inline u8 crc7_byte(u8 crc, u8 data) +{ + return crc7_syndrome_table[(crc << 1) ^ data]; +} + +extern u8 crc7(u8 crc, const u8 *buffer, size_t len); + +#endif diff --git a/lib/Makefile b/lib/Makefile index ffdee7d..fcfe351 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -32,6 +32,7 @@ COBJS-$(CONFIG_BZIP2) += bzlib_decompress.o COBJS-$(CONFIG_BZIP2) += bzlib_randtable.o COBJS-$(CONFIG_BZIP2) += bzlib_huffman.o COBJS-$(CONFIG_USB_TTY) += circbuf.o +COBJS-y += crc7.o COBJS-y += crc16.o COBJS-y += crc32.o COBJS-y += ctype.o diff --git a/lib/crc7.c b/lib/crc7.c new file mode 100644 index 0000000..e635c9c --- /dev/null +++ b/lib/crc7.c @@ -0,0 +1,62 @@ +/* + * crc7.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/types.h> +#include <linux/crc7.h> + + +/* Table for CRC-7 (polynomial x^7 + x^3 + 1) */ +const u8 crc7_syndrome_table[256] = { + 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, + 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, + 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26, + 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, + 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d, + 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45, + 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, + 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c, + 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b, + 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, + 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42, + 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a, + 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, + 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21, + 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70, + 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, + 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e, + 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36, + 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, + 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f, + 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, + 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, + 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55, + 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d, + 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, + 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52, + 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03, + 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, + 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28, + 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60, + 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, + 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79 +}; + +/** + * crc7 - update the CRC7 for the data buffer + * @crc: previous CRC7 value + * @buffer: data pointer + * @len: number of bytes in the buffer + * Context: any + * + * Returns the updated CRC7 value. + */ +u8 crc7(u8 crc, const u8 *buffer, size_t len) +{ + while (len--) + crc = crc7_byte(crc, *buffer++); + return crc; +}

Dear Thomas Chou,
In message 1293174969-18653-2-git-send-email-thomas@wytron.com.tw you wrote:
Crc7 is used to compute mmc spi comamnd packet checksum.
s/comamnd/sommand/
Signed-off-by: Thomas Chou thomas@wytron.com.tw
Please fix the typos, and provide proper attribution where this code is coming from - see http://www.denx.de/wiki/view/U-Boot/Patches#Attributing_Code_Copyrights_Sign for details.
Thanks.
Best regards,
Wolfgang Denk

Crc7 is used to compute mmc spi command packet checksum.
Copy from linux-2.6 lib/crc7.c include/linux/crc7.h commit ad241528c4919505afccb022acbab3eeb0db4d80
Signed-off-by: Thomas Chou thomas@wytron.com.tw --- v2 update attribution as Wolfgang suggested.
include/linux/crc7.h | 14 +++++++++++ lib/Makefile | 1 + lib/crc7.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 0 deletions(-) create mode 100644 include/linux/crc7.h create mode 100644 lib/crc7.c
diff --git a/include/linux/crc7.h b/include/linux/crc7.h new file mode 100644 index 0000000..1786e77 --- /dev/null +++ b/include/linux/crc7.h @@ -0,0 +1,14 @@ +#ifndef _LINUX_CRC7_H +#define _LINUX_CRC7_H +#include <linux/types.h> + +extern const u8 crc7_syndrome_table[256]; + +static inline u8 crc7_byte(u8 crc, u8 data) +{ + return crc7_syndrome_table[(crc << 1) ^ data]; +} + +extern u8 crc7(u8 crc, const u8 *buffer, size_t len); + +#endif diff --git a/lib/Makefile b/lib/Makefile index ffdee7d..fcfe351 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -32,6 +32,7 @@ COBJS-$(CONFIG_BZIP2) += bzlib_decompress.o COBJS-$(CONFIG_BZIP2) += bzlib_randtable.o COBJS-$(CONFIG_BZIP2) += bzlib_huffman.o COBJS-$(CONFIG_USB_TTY) += circbuf.o +COBJS-y += crc7.o COBJS-y += crc16.o COBJS-y += crc32.o COBJS-y += ctype.o diff --git a/lib/crc7.c b/lib/crc7.c new file mode 100644 index 0000000..e635c9c --- /dev/null +++ b/lib/crc7.c @@ -0,0 +1,62 @@ +/* + * crc7.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/types.h> +#include <linux/crc7.h> + + +/* Table for CRC-7 (polynomial x^7 + x^3 + 1) */ +const u8 crc7_syndrome_table[256] = { + 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, + 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, + 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26, + 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, + 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d, + 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45, + 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, + 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c, + 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b, + 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, + 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42, + 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a, + 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, + 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21, + 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70, + 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, + 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e, + 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36, + 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, + 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f, + 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, + 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, + 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55, + 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d, + 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, + 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52, + 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03, + 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, + 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28, + 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60, + 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, + 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79 +}; + +/** + * crc7 - update the CRC7 for the data buffer + * @crc: previous CRC7 value + * @buffer: data pointer + * @len: number of bytes in the buffer + * Context: any + * + * Returns the updated CRC7 value. + */ +u8 crc7(u8 crc, const u8 *buffer, size_t len) +{ + while (len--) + crc = crc7_byte(crc, *buffer++); + return crc; +}

Dear Thomas Chou,
In message 1294709900-2764-1-git-send-email-thomas@wytron.com.tw you wrote:
Crc7 is used to compute mmc spi command packet checksum.
Copy from linux-2.6 lib/crc7.c include/linux/crc7.h commit ad241528c4919505afccb022acbab3eeb0db4d80
Signed-off-by: Thomas Chou thomas@wytron.com.tw
v2 update attribution as Wolfgang suggested.
include/linux/crc7.h | 14 +++++++++++ lib/Makefile | 1 + lib/crc7.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 0 deletions(-) create mode 100644 include/linux/crc7.h create mode 100644 lib/crc7.c
Applied, thanks.
Best regards,
Wolfgang Denk

This func helps mmc_spi driver set correct speed for mmc/sd, as mmc card needs 400KHz clock for spi mode initialization.
Signed-off-by: Thomas Chou thomas@wytron.com.tw --- drivers/spi/Makefile | 1 + drivers/spi/spi.c | 9 +++++++++ include/spi.h | 8 ++++++++ 3 files changed, 18 insertions(+), 0 deletions(-) create mode 100644 drivers/spi/spi.c
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e34a124..c4eb627 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -25,6 +25,7 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libspi.o
+COBJS-$(CONFIG_MMC_SPI) += spi.o COBJS-$(CONFIG_ALTERA_SPI) += altera_spi.o COBJS-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c new file mode 100644 index 0000000..944466a --- /dev/null +++ b/drivers/spi/spi.c @@ -0,0 +1,9 @@ +#include <common.h> +#include <spi.h> + +/* default func for SPI driver without set speed func */ +static void __spi_set_speed(struct spi_slave *slave, uint hz) +{ +} +void spi_set_speed(struct spi_slave *slave, uint hz) + __attribute__((weak, alias("__spi_set_speed"))); diff --git a/include/spi.h b/include/spi.h index 320e50e..7887d0f 100644 --- a/include/spi.h +++ b/include/spi.h @@ -176,6 +176,14 @@ void spi_cs_activate(struct spi_slave *slave); void spi_cs_deactivate(struct spi_slave *slave);
/*----------------------------------------------------------------------- + * Set transfer speed. + * This sets a new speed to be applied for next spi_xfer(). + * slave: The SPI slave + * hz: The transfer speed + */ +void spi_set_speed(struct spi_slave *slave, uint hz); + +/*----------------------------------------------------------------------- * Write 8 bits, then read 8 bits. * slave: The SPI slave we're communicating with * byte: Byte to be written

On Friday, December 24, 2010 02:16:07 Thomas Chou wrote:
--- /dev/null +++ b/drivers/spi/spi.c @@ -0,0 +1,9 @@ +#include <common.h> +#include <spi.h>
+/* default func for SPI driver without set speed func */ +static void __spi_set_speed(struct spi_slave *slave, uint hz) +{ +} +void spi_set_speed(struct spi_slave *slave, uint hz)
- __attribute__((weak, alias("__spi_set_speed")));
let's not go this route. just add the prototype to the header and when the respective spi bus maintainer wants to support this new func, they can implement it. i'd rather people know about the problem up front rather than have things randomly not work. -mike

This func helps mmc_spi driver set correct speed for mmc/sd, as mmc card needs 400KHz clock for spi mode initialization.
Signed-off-by: Thomas Chou thomas@wytron.com.tw --- v2 remove weak func as Mike suggested.
include/spi.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/include/spi.h b/include/spi.h index 320e50e..7887d0f 100644 --- a/include/spi.h +++ b/include/spi.h @@ -176,6 +176,14 @@ void spi_cs_activate(struct spi_slave *slave); void spi_cs_deactivate(struct spi_slave *slave);
/*----------------------------------------------------------------------- + * Set transfer speed. + * This sets a new speed to be applied for next spi_xfer(). + * slave: The SPI slave + * hz: The transfer speed + */ +void spi_set_speed(struct spi_slave *slave, uint hz); + +/*----------------------------------------------------------------------- * Write 8 bits, then read 8 bits. * slave: The SPI slave we're communicating with * byte: Byte to be written

Dear Thomas Chou,
In message 1293232433-2985-1-git-send-email-thomas@wytron.com.tw you wrote:
This func helps mmc_spi driver set correct speed for mmc/sd, as mmc card needs 400KHz clock for spi mode initialization.
Signed-off-by: Thomas Chou thomas@wytron.com.tw
v2 remove weak func as Mike suggested.
include/spi.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/include/spi.h b/include/spi.h index 320e50e..7887d0f 100644 --- a/include/spi.h +++ b/include/spi.h @@ -176,6 +176,14 @@ void spi_cs_activate(struct spi_slave *slave); void spi_cs_deactivate(struct spi_slave *slave);
/*-----------------------------------------------------------------------
- Set transfer speed.
- This sets a new speed to be applied for next spi_xfer().
- slave: The SPI slave
- hz: The transfer speed
- */
Incorrect multiline comment style.
Best regards,
Wolfgang Denk

This func helps mmc_spi driver set correct speed for mmc/sd, as mmc card needs 400KHz clock for spi mode initialization.
Signed-off-by: Thomas Chou thomas@wytron.com.tw --- v2 remove weak func as Mike suggested. v3 comment style fix as Wolfgang suggested.
include/spi.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/include/spi.h b/include/spi.h index 320e50e..e364d2d 100644 --- a/include/spi.h +++ b/include/spi.h @@ -175,6 +175,14 @@ void spi_cs_activate(struct spi_slave *slave); */ void spi_cs_deactivate(struct spi_slave *slave);
+/* + * Set transfer speed. + * This sets a new speed to be applied for next spi_xfer(). + * slave: The SPI slave + * hz: The transfer speed + */ +void spi_set_speed(struct spi_slave *slave, uint hz); + /*----------------------------------------------------------------------- * Write 8 bits, then read 8 bits. * slave: The SPI slave we're communicating with

The new speed will be applied by spi_claim_bus.
Signed-off-by: Thomas Chou thomas@wytron.com.tw --- drivers/spi/bfin_spi.c | 32 +++++++++++++++++++------------- 1 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/drivers/spi/bfin_spi.c b/drivers/spi/bfin_spi.c index d7e1474..bfecdaf 100644 --- a/drivers/spi/bfin_spi.c +++ b/drivers/spi/bfin_spi.c @@ -138,13 +138,29 @@ static const unsigned short cs_pins[][7] = { #endif };
+void spi_set_speed(struct spi_slave *slave, uint hz) +{ + struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + ulong sclk; + u32 baud; + + sclk = get_sclk(); + baud = sclk / (2 * hz); + /* baud should be rounded up */ + if (sclk % (2 * hz)) + baud += 1; + if (baud < 2) + baud = 2; + else if (baud > (u16)-1) + baud = -1; + bss->baud = baud; +} + struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int mode) { struct bfin_spi_slave *bss; - ulong sclk; u32 mmr_base; - u32 baud;
if (!spi_cs_is_valid(bus, cs)) return NULL; @@ -166,16 +182,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, default: return NULL; }
- sclk = get_sclk(); - baud = sclk / (2 * max_hz); - /* baud should be rounded up */ - if (sclk % (2 * max_hz)) - baud += 1; - if (baud < 2) - baud = 2; - else if (baud > (u16)-1) - baud = -1; - bss = malloc(sizeof(*bss)); if (!bss) return NULL; @@ -187,8 +193,8 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, if (mode & SPI_CPHA) bss->ctl |= CPHA; if (mode & SPI_CPOL) bss->ctl |= CPOL; if (mode & SPI_LSB_FIRST) bss->ctl |= LSBF; - bss->baud = baud; bss->flg = mode & SPI_CS_HIGH ? 1 : 0; + spi_set_speed(&bss->slave, max_hz);
debug("%s: bus:%i cs:%i mmr:%x ctl:%x baud:%i flg:%i\n", __func__, bus, cs, mmr_base, bss->ctl, baud, bss->flg);

On Friday, December 24, 2010 02:16:08 Thomas Chou wrote:
The new speed will be applied by spi_claim_bus.
Signed-off-by: Mike Frysinger vapier@gentoo.org -mike

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 thomas@wytron.com.tw --- common/Makefile | 1 + common/cmd_mmc_spi.c | 88 ++++++++++++++++ drivers/mmc/Makefile | 1 + drivers/mmc/mmc.c | 93 +++++++++++++---- drivers/mmc/mmc_spi.c | 278 +++++++++++++++++++++++++++++++++++++++++++++++++ include/mmc.h | 5 + 6 files changed, 446 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 abea91c..381ac38 100644 --- a/common/Makefile +++ b/common/Makefile @@ -117,6 +117,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 thomas@wytron.com.tw + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <mmc.h> +#include <spi.h> + +#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 68afd30..599105f 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -30,6 +30,7 @@ COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.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 6805b33..7218a36 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -112,7 +112,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; @@ -280,7 +283,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; @@ -299,6 +303,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); @@ -319,7 +335,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); @@ -333,6 +350,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];
@@ -388,6 +417,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; @@ -461,6 +493,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; @@ -611,8 +646,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; @@ -629,18 +678,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; @@ -716,14 +767,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..6882fbd --- /dev/null +++ b/drivers/mmc/mmc_spi.c @@ -0,0 +1,278 @@ +/* + * generic mmc spi driver + * + * Copyright (C) 2010 Thomas Chou thomas@wytron.com.tw + * Licensed under the GPL-2 or later. + */ +#include <common.h> +#include <malloc.h> +#include <part.h> +#include <mmc.h> +#include <spi.h> +#include <crc.h> +#include <linux/crc7.h> +#include <linux/byteorder/swab.h> + +/* 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; + 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 74c0b1d..0b1e064 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 @@ -288,6 +291,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

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 thomas@wytron.com.tw --- v10 set speed to 400KHz for deactived 144 clock, 18 bytes, initialization.
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 abea91c..381ac38 100644 --- a/common/Makefile +++ b/common/Makefile @@ -117,6 +117,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 thomas@wytron.com.tw + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <mmc.h> +#include <spi.h> + +#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 68afd30..599105f 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -30,6 +30,7 @@ COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.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 6805b33..7218a36 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -112,7 +112,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; @@ -280,7 +283,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; @@ -299,6 +303,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); @@ -319,7 +335,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); @@ -333,6 +350,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];
@@ -388,6 +417,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; @@ -461,6 +493,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; @@ -611,8 +646,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; @@ -629,18 +678,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; @@ -716,14 +767,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 thomas@wytron.com.tw + * Licensed under the GPL-2 or later. + */ +#include <common.h> +#include <malloc.h> +#include <part.h> +#include <mmc.h> +#include <spi.h> +#include <crc.h> +#include <linux/crc7.h> +#include <linux/byteorder/swab.h> + +/* 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 74c0b1d..0b1e064 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 @@ -288,6 +291,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

On Friday, December 24, 2010 02:16:05 Thomas Chou wrote:
This is the v9 update of the mmc_spi driver. Please enable DEBUG on the top of mmc_spi.c and help me perform the tests.
A new spi_set_speed() is added to meet the 400KHz clock requirement during mmc card initialization. An example update to bfin_spi is included. The altera_spi core does not support speed change, so I am going to post a new opencore ip for this.
this seems to work much nicer for me. only odd thing is that 'fatls' does not work if i dont 'mmcinfo' first. do you see the same ? maybe this is (currently) expected behavior ?
from cold boot:
bfin> mmc_spi 4 MMC_SPI: 0 at 0:4 hz 25000000 mode 0
bfin> fatls mmc 0:1 MMC: block number 0x1 exceeds max(0x0) ** Can't read from device 0 **
** Unable to use mmc 0:1 for fatls **
bfin> mmcinfo Device: MMC_SPI Manufacturer ID: 0 OEM: 2 Name: card Tran Speed: 25000000 Rd Block Len: 512 SD version 1.0 High Capacity: No Capacity: 31326208 Bus Width: 1-bit
bfin> fatls mmc 0:1 private/ bootmii/ 3041 installer.log apps/ 5213 readme-bootmii.txt 7273 readme-hbc.txt config/
3 file(s), 4 dir(s)
-mike

Dear Mike Frysinger,
this seems to work much nicer for me. only odd thing is that 'fatls' does not work if i dont 'mmcinfo' first. do you see the same ? maybe this is (currently) expected behavior ?
Currently it is like that with the common MMC framework. Same behavior with gen_atmel_mci.
I'd think its normal that you have to "init" such a block device first, isn't it the same with USB?
Otherwise you'd have to implement automated init on first block read, and detection of removal and insertion of a new device to re-read the info.
Season's Greetings, Reinhard

On Friday, December 24, 2010 14:06:34 Reinhard Meyer wrote:
Dear Mike Frysinger,
this seems to work much nicer for me. only odd thing is that 'fatls' does not work if i dont 'mmcinfo' first. do you see the same ? maybe this is (currently) expected behavior ?
Currently it is like that with the common MMC framework. Same behavior with gen_atmel_mci.
I'd think its normal that you have to "init" such a block device first, isn't it the same with USB?
Otherwise you'd have to implement automated init on first block read, and detection of removal and insertion of a new device to re-read the info.
my gripe is that it's called "mmcinfo" and not "mmc init". if it were the latter, i wouldnt be surprised at all. but it seems odd that i have to "query the device and print its extended info" rather than "initialize the mmc card". perhaps i'm just whining semantics though.
if this is currently the standard/expected behavior for the generic mmc framework, then that's OK from the mmc_spi perspective. i'll merge this into the Blackfin tree and let our testers validate it with our regression tests. -mike

On 12/25/2010 03:45 AM, Mike Frysinger wrote:
if this is currently the standard/expected behavior for the generic mmc framework, then that's OK from the mmc_spi perspective. i'll merge this into the Blackfin tree and let our testers validate it with our regression tests. -mike
Yes. It is the current behaviour. I just added an update to run the initialization clock at 400KHz which should improve compatibility. Please try out. Thank you very much.
Merry Xmas!
- Thomas

On Friday, December 24, 2010 02:16:05 Thomas Chou wrote:
This is the v9 update of the mmc_spi driver. Please enable DEBUG on the top of mmc_spi.c and help me perform the tests.
A new spi_set_speed() is added to meet the 400KHz clock requirement during mmc card initialization. An example update to bfin_spi is included. The altera_spi core does not support speed change, so I am going to post a new opencore ip for this.
The driver now claims and releases the spi bus for each mmc command, as Mike suggested.
Thomas Chou (4): lib: add crc7 from Linux spi: add spi_set_speed func bfin_spi: add spi_set_speed mmc: add generic mmc spi driver
Andy: i think we've hit a fairly stable point. could you merge this now ? -mike

On Tue, Apr 12, 2011 at 1:58 AM, Mike Frysinger vapier@gentoo.org wrote:
On Friday, December 24, 2010 02:16:05 Thomas Chou wrote:
This is the v9 update of the mmc_spi driver. Please enable DEBUG on the top of mmc_spi.c and help me perform the tests.
A new spi_set_speed() is added to meet the 400KHz clock requirement during mmc card initialization. An example update to bfin_spi is included. The altera_spi core does not support speed change, so I am going to post a new opencore ip for this.
The driver now claims and releases the spi bus for each mmc command, as Mike suggested.
Thomas Chou (4): lib: add crc7 from Linux spi: add spi_set_speed func bfin_spi: add spi_set_speed mmc: add generic mmc spi driver
Andy: i think we've hit a fairly stable point. could you merge this now ?
I'm going to merge this, but I'll note that the reason you had to run mmcinfo before this will work is because this code never invoks mmc_init(). If you look at the mmc command in common/cmd_mmc.c, and see how the various sub-commands operate (under GENERIC_MMC), all of them call mmc_init() before running the command. It's suboptimal in terms of performance, but has the advantage of being robust and actually detecting if you put a card in, or took one out. If someone could find the appropriate places to call mmc_init(mmc), I'll be happy to also merge in the follow-up patch.
Also, I'm assuming, at the moment, that this supersedes the legacy mmc_spi patch you submitted earlier? If not, I'll put that in, now, too.
Andy

On Wednesday, April 13, 2011 06:17:43 Andy Fleming wrote:
On Tue, Apr 12, 2011 at 1:58 AM, Mike Frysinger vapier@gentoo.org wrote:
On Friday, December 24, 2010 02:16:05 Thomas Chou wrote:
This is the v9 update of the mmc_spi driver. Please enable DEBUG on the top of mmc_spi.c and help me perform the tests.
A new spi_set_speed() is added to meet the 400KHz clock requirement during mmc card initialization. An example update to bfin_spi is included. The altera_spi core does not support speed change, so I am going to post a new opencore ip for this.
The driver now claims and releases the spi bus for each mmc command, as Mike suggested.
Thomas Chou (4): lib: add crc7 from Linux spi: add spi_set_speed func bfin_spi: add spi_set_speed mmc: add generic mmc spi driver
Andy: i think we've hit a fairly stable point. could you merge this now ?
I'm going to merge this, but I'll note that the reason you had to run mmcinfo before this will work is because this code never invoks mmc_init(). If you look at the mmc command in common/cmd_mmc.c, and see how the various sub-commands operate (under GENERIC_MMC), all of them call mmc_init() before running the command. It's suboptimal in terms of performance, but has the advantage of being robust and actually detecting if you put a card in, or took one out. If someone could find the appropriate places to call mmc_init(mmc), I'll be happy to also merge in the follow-up patch.
i hope Thomas can do that :)
Also, I'm assuming, at the moment, that this supersedes the legacy mmc_spi patch you submitted earlier? If not, I'll put that in, now, too.
correct, the legacy mmc_spi code is hopefully no longer needed -mike

As Andy Fleming suggested, we can call mmc_init() in mmc_spi command. So that we don't need to run mmcinfo command next.
Signed-off-by: Thomas Chou thomas@wytron.com.tw --- for u-boot.
common/cmd_mmc_spi.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/common/cmd_mmc_spi.c b/common/cmd_mmc_spi.c index 63fe313..cfd0fb1 100644 --- a/common/cmd_mmc_spi.c +++ b/common/cmd_mmc_spi.c @@ -74,6 +74,7 @@ static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } printf("%s: %d at %u:%u hz %u mode %u\n", mmc->name, mmc->block_dev.dev, bus, cs, speed, mode); + mmc_init(mmc); return 0;
usage:
participants (5)
-
Andy Fleming
-
Mike Frysinger
-
Reinhard Meyer
-
Thomas Chou
-
Wolfgang Denk