All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH] mmc: add generic mmc spi driver
@ 2010-04-23  2:53 Thomas Chou
  2010-04-23  3:35 ` Mike Frysinger
                   ` (9 more replies)
  0 siblings, 10 replies; 33+ messages in thread
From: Thomas Chou @ 2010-04-23  2:53 UTC (permalink / raw)
  To: u-boot

This patch supports mmc/sd card with spi interface.
I have tested with sd and mmc cards. But there is still ocr issue
with SDHC.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
 drivers/mmc/Makefile  |    1 +
 drivers/mmc/mmc_spi.c |  252 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 253 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mmc/mmc_spi.c

diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 1b8f5bd..d03eb47 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -31,6 +31,7 @@ LIB	:= $(obj)libmmc.a
 COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
 COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
+COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
 COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
new file mode 100644
index 0000000..76c5977
--- /dev/null
+++ b/drivers/mmc/mmc_spi.c
@@ -0,0 +1,252 @@
+/*
+ * 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 <asm/errno.h>
+
+#define CTOUT 0x10
+#define RTOUT 0x10000
+#define WTOUT 0x10000
+
+static uint mmc_spi_sendcmd(struct mmc *mmc, u8 cmdidx, u32 cmdarg)
+{
+	u8 cmdo[6];
+	u8 r1;
+	int i;
+	cmdo[0] = 0x40 + cmdidx;
+	cmdo[1] = cmdarg >> 24;
+	cmdo[2] = cmdarg >> 16;
+	cmdo[3] = cmdarg >> 8;
+	cmdo[4] = cmdarg;
+	cmdo[5] = 0x95; /* crc valid only for cmd00 */
+	spi_xfer(mmc->priv, 6 * 8, cmdo, NULL, SPI_XFER_BEGIN);
+	for (i = 0; i < CTOUT; i++) {
+		spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+		if ((r1 & 0x80) == 0)
+			break;
+	}
+	debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1);
+	return r1;
+}
+
+static uint mmc_spi_readdata(struct mmc *mmc, char *buf,
+				u32 bcnt, u32 bsize)
+{
+	u8 r1;
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		for (i = 0; i < RTOUT; i++) {
+			spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+			if (r1 != 0xff)
+				break;
+		}
+		debug("%s:tok%d %x\n", __func__, i, r1);
+		if (r1 == 0xfe) {
+			spi_xfer(mmc->priv, bsize * 8, NULL, buf, 0);
+			buf += bsize;
+			spi_xfer(mmc->priv, 2 * 8, NULL, crc, 0);
+			r1 = 0;
+		} else
+			break;
+	}
+	return r1;
+}
+
+static uint mmc_spi_writedata(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	u8 r1;
+	u8 tok[2] = { 0xff, 0xfe };
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		spi_xfer(mmc->priv, 2 * 8, tok, NULL, 0);
+		spi_xfer(mmc->priv, bsize * 8, buf, NULL, 0);
+		buf += bsize;
+		spi_xfer(mmc->priv, 2 * 8, crc, NULL, 0);
+		spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+		if (r1 == 0x05) {
+			for (i = 0; i < WTOUT; i++) {
+				spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+				if (r1 == 0xff) {
+					r1 = 0;
+					break;
+				}
+			}
+			if (i == WTOUT) {
+				debug("%s:wtout %x\n", __func__, r1);
+				r1 = 0x04;
+				break;
+			}
+		} else
+			break;
+	}
+	return r1;
+}
+
+static uint mmc_spi_writeblock(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	u8 r1;
+	u8 tok[2] = { 0xff, 0xfc };
+	u8 stop[2] = { 0xff, 0xfd };
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		spi_xfer(mmc->priv, 2 * 8, tok, NULL, 0);
+		spi_xfer(mmc->priv, bsize * 8, buf, NULL, 0);
+		buf += bsize;
+		spi_xfer(mmc->priv, 2 * 8, crc, NULL, 0);
+		spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+		if (r1 == 0x05) {
+			for (i = 0; i < WTOUT; i++) {
+				spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+				if (r1 == 0xff) {
+					r1 = 0;
+					break;
+				}
+			}
+			if (i == WTOUT) {
+				debug("%s:wtout %x\n", __func__, r1);
+				r1 = 0x04;
+				break;
+			}
+		}
+	}
+	if (bcnt == 0 && r1 == 0) {
+		spi_xfer(mmc->priv, 2 * 8, stop, NULL, 0);
+		for (i = 0; i < WTOUT; i++) {
+			spi_xfer(mmc->priv, 1 * 8, NULL, &r1, 0);
+			if (r1 == 0xff) {
+				r1 = 0;
+				break;
+			}
+		}
+		if (i == WTOUT) {
+			debug("%s:wtout %x\n", __func__, r1);
+			r1 = 0x04;
+		}
+	}
+	return r1;
+}
+
+static inline uint rswab(u8 *d)
+{
+	return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
+		struct mmc_data *data)
+{
+	u8 r1;
+	u8 resp[16];
+	int i;
+	debug("%s:cmd%d %x %x %x\n", __func__,
+	      cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags);
+	if (cmd->cmdidx == MMC_CMD_ALL_SEND_CID)
+		cmd->cmdidx = MMC_CMD_SEND_CID;
+	r1 = mmc_spi_sendcmd(mmc, cmd->cmdidx, cmd->cmdarg);
+	if (cmd->cmdidx == 55 && (r1 & 0x04))
+		return TIMEOUT;
+	if ((cmd->cmdidx == MMC_CMD_SEND_OP_COND ||
+	     cmd->cmdidx == SD_CMD_APP_SEND_OP_COND) &&
+	    cmd->resp_type == MMC_RSP_R3) {
+		r1 = mmc_spi_sendcmd(mmc, 58, 0);
+		spi_xfer(mmc->priv, 4 * 8, NULL, resp, 0);
+		cmd->response[0] = rswab(resp);
+		debug("ocr %x\n", cmd->response[0]);
+	} else if (cmd->resp_type == MMC_RSP_R2) {
+		r1 = mmc_spi_readdata(mmc, resp, 1, 16);
+		for (i = 0; i < 4; i++)
+			cmd->response[i] = rswab(resp + i * 4);
+		debug("r136 %x %x %x %x\n", cmd->response[0], cmd->response[1],
+		      cmd->response[2], cmd->response[3]);
+	} else	if (cmd->resp_type == MMC_RSP_R1) {
+		cmd->response[0] = 0;
+		if (r1 & 0x7e)
+			cmd->response[0] |= (1 << 19);
+		if (r1 & 0x40)
+			cmd->response[0] |= (1 << 31);
+		if (r1 & 0x20)
+			cmd->response[0] |= (1 << 30);
+		if (r1 & 0x10)
+			cmd->response[0] |= (1 << 28);
+		if (r1 & 0x08)
+			cmd->response[0] |= (1 << 23);
+		if (r1 & 0x04)
+			cmd->response[0] |= (1 << 22);
+		if (r1 & 0x02)
+			cmd->response[0] |= (1 << 13);
+	}
+	if (data) {
+		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) {
+			if (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)
+				r1 = mmc_spi_writeblock(mmc, data->src,
+					data->blocks, data->blocksize);
+			else
+				r1 = mmc_spi_writedata(mmc, data->src,
+					data->blocks, data->blocksize);
+		}
+	}
+	return 0;
+}
+
+static void mmc_spi_set_ios(struct mmc *mmc)
+{
+	debug("%s:\n", __func__);
+}
+
+static int mmc_spi_init_p(struct mmc *mmc)
+{
+	u8 d = 0xff;
+	int i;
+	debug("%s:\n", __func__);
+	spi_release_bus(mmc->priv);
+	for (i = 0; i < 16; i++) /* power on initialization */
+		spi_xfer(mmc->priv, 1 * 8, &d, NULL, 0);
+	spi_claim_bus(mmc->priv);
+	return 0;
+}
+
+int mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
+{
+	struct mmc *mmc = NULL;
+
+	mmc = malloc(sizeof(struct mmc));
+	if (!mmc)
+		return -ENOMEM;
+	sprintf(mmc->name, "mmc_spi");
+	mmc->priv = spi_setup_slave(bus, cs, speed, mode);
+	if (!mmc->priv) {
+		free(mmc);
+		return -ENOMEM;
+	}
+	mmc->send_cmd = mmc_spi_request;
+	mmc->set_ios = mmc_spi_set_ios;
+	mmc->init = mmc_spi_init_p;
+	mmc->host_caps = 0;
+
+	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->f_max = speed;
+	mmc->f_min = mmc->f_max >> 9;
+	mmc->block_dev.part_type = PART_TYPE_DOS;
+
+	mmc_register(mmc);
+
+	return 0;
+}
-- 
1.6.6.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH] mmc: add generic mmc spi driver
  2010-04-23  2:53 [U-Boot] [PATCH] mmc: add generic mmc spi driver Thomas Chou
@ 2010-04-23  3:35 ` Mike Frysinger
  2010-04-23  4:04   ` Thomas Chou
  2010-04-27  1:51 ` [U-Boot] [PATCH v2] " Thomas Chou
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Mike Frysinger @ 2010-04-23  3:35 UTC (permalink / raw)
  To: u-boot

On Thursday 22 April 2010 22:53:34 Thomas Chou wrote:
> This patch supports mmc/sd card with spi interface.
> I have tested with sd and mmc cards. But there is still ocr issue
> with SDHC.

now the semi-obvious question ... how does this differ from the old spi_mmc.c 
driver ?  i see this doesnt rely on the legacy mmc framework, but what about 
device/commandset support parity ?

it's too bad the new framework doesnt allow for dynamic probing like the spi 
layer.  makes it a pain to work with a SPI/MMC card that can have the CS 
changed on the fly.
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20100422/4587a788/attachment.pgp 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH] mmc: add generic mmc spi driver
  2010-04-23  3:35 ` Mike Frysinger
@ 2010-04-23  4:04   ` Thomas Chou
  2010-04-23  5:55     ` Thomas Chou
  2010-04-25  6:51     ` Mike Frysinger
  0 siblings, 2 replies; 33+ messages in thread
From: Thomas Chou @ 2010-04-23  4:04 UTC (permalink / raw)
  To: u-boot

On 04/23/2010 11:35 AM, Mike Frysinger wrote:
> On Thursday 22 April 2010 22:53:34 Thomas Chou wrote:
>    
>> This patch supports mmc/sd card with spi interface.
>> I have tested with sd and mmc cards. But there is still ocr issue
>> with SDHC.
>>      
> now the semi-obvious question ... how does this differ from the old spi_mmc.c
> driver ?  i see this doesnt rely on the legacy mmc framework, but what about
> device/commandset support parity ?
>
>    
Hi Mike,

Right after playing your spi_mmc.c, I started this one based on generic 
mmc framework last night. I tried hard to recall my memory about mmc/sd, 
and hoped to catch up the merge window.

Do you mean "parity" as crc? Crc is not supported, but can be added. I 
make some command set translation when spi doesn't support.

> it's too bad the new framework doesnt allow for dynamic probing like the spi
> layer.  makes it a pain to work with a SPI/MMC card that can have the CS
> changed on the fly.
> -mike
>    
Maybe we can move spi_setup_slave to the mmc->init() and  do probing. Or 
shall we add a subcommand?

Cheers,
Thomas

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH] mmc: add generic mmc spi driver
  2010-04-23  4:04   ` Thomas Chou
@ 2010-04-23  5:55     ` Thomas Chou
  2010-04-25  6:56       ` Mike Frysinger
  2010-04-25  6:51     ` Mike Frysinger
  1 sibling, 1 reply; 33+ messages in thread
From: Thomas Chou @ 2010-04-23  5:55 UTC (permalink / raw)
  To: u-boot

On 04/23/2010 12:04 PM, Thomas Chou wrote:
> it's too bad the new framework doesnt allow for dynamic probing like 
> the spi
>> layer.  makes it a pain to work with a SPI/MMC card that can have the CS
>> changed on the fly.
>> -mike
>>
>>      
>
Second thought.
With generic mmc framework, we can instance multiple mmc devices. Then 
we can probe one of them when we want.

#ifdef CONFIG_GENERIC_MMC
int board_mmc_init(bd_t *bis)
{
     int rc = 0;
#ifdef CONFIG_MMC_SPI
     extern int mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
     mmc_spi_init(CONFIG_MMC_SPI_BUS,
               CONFIG_MMC_SPI_CS_0,
               CONFIG_MMC_SPI_SPEED,
               CONFIG_MMC_SPI_MODE);
     mmc_spi_init(CONFIG_MMC_SPI_BUS,
               CONFIG_MMC_SPI_CS_1,
               CONFIG_MMC_SPI_SPEED,
               CONFIG_MMC_SPI_MODE);
#endif
     return rc;
}
#endif

Cheers,
Thomas

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH] mmc: add generic mmc spi driver
  2010-04-23  4:04   ` Thomas Chou
  2010-04-23  5:55     ` Thomas Chou
@ 2010-04-25  6:51     ` Mike Frysinger
  1 sibling, 0 replies; 33+ messages in thread
From: Mike Frysinger @ 2010-04-25  6:51 UTC (permalink / raw)
  To: u-boot

On Friday 23 April 2010 00:04:51 Thomas Chou wrote:
> Do you mean "parity" as crc? Crc is not supported, but can be added. I
> make some command set translation when spi doesn't support.

i mean functionality wrt card/spec support
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20100425/6445f782/attachment.pgp 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH] mmc: add generic mmc spi driver
  2010-04-23  5:55     ` Thomas Chou
@ 2010-04-25  6:56       ` Mike Frysinger
  2010-04-26 14:37         ` Thomas Chou
  0 siblings, 1 reply; 33+ messages in thread
From: Mike Frysinger @ 2010-04-25  6:56 UTC (permalink / raw)
  To: u-boot

On Friday 23 April 2010 01:55:11 Thomas Chou wrote:
> On 04/23/2010 12:04 PM, Thomas Chou wrote:
> >> it's too bad the new framework doesnt allow for dynamic probing like
> >> the spi layer.  makes it a pain to work with a SPI/MMC card that can have
> >> the CS changed on the fly.
> 
> Second thought.
> With generic mmc framework, we can instance multiple mmc devices. Then
> we can probe one of them when we want.

i dont think that's a scalable solution.  what if you have multiple spi busses 
or 10's of GPIO CS's ?  only way to scale is to have a new subcommand so 
people can do it themselves.  a simple CONFIG_CMD_MMC_SPI command which 
forwards along calls to mmc_spi_init() on the fly would work.
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20100425/c4072e98/attachment.pgp 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH] mmc: add generic mmc spi driver
  2010-04-25  6:56       ` Mike Frysinger
@ 2010-04-26 14:37         ` Thomas Chou
  2010-04-26 15:59           ` Mike Frysinger
  0 siblings, 1 reply; 33+ messages in thread
From: Thomas Chou @ 2010-04-26 14:37 UTC (permalink / raw)
  To: u-boot

On 04/25/2010 02:56 PM, Mike Frysinger wrote:
> On Friday 23 April 2010 01:55:11 Thomas Chou wrote:
>    
>> On 04/23/2010 12:04 PM, Thomas Chou wrote:
>>      
>>>> it's too bad the new framework doesnt allow for dynamic probing like
>>>> the spi layer.  makes it a pain to work with a SPI/MMC card that can have
>>>> the CS changed on the fly.
>>>>          
>> Second thought.
>> With generic mmc framework, we can instance multiple mmc devices. Then
>> we can probe one of them when we want.
>>      
> i dont think that's a scalable solution.  what if you have multiple spi busses
> or 10's of GPIO CS's ?  only way to scale is to have a new subcommand so
> people can do it themselves.  a simple CONFIG_CMD_MMC_SPI command which
> forwards along calls to mmc_spi_init() on the fly would work.
> -mike
>    
Hi Mike,

I have resolved the SDHC issue and have tested various MMC/SD cards, 
including writing. I will submit the patch tomorrow.

I have moved spi slave setup so that we can change the cs on the fly. We 
could add a subcommand, like,

mmc_spi [bus:]cs

Where should I place this command? in a new file in common dir or inside 
the mmc_spi driver?

Cheers,
Thomas

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH] mmc: add generic mmc spi driver
  2010-04-26 14:37         ` Thomas Chou
@ 2010-04-26 15:59           ` Mike Frysinger
  0 siblings, 0 replies; 33+ messages in thread
From: Mike Frysinger @ 2010-04-26 15:59 UTC (permalink / raw)
  To: u-boot

On Monday 26 April 2010 10:37:07 Thomas Chou wrote:
> I have moved spi slave setup so that we can change the cs on the fly. We
> could add a subcommand, like,
> 
> mmc_spi [bus:]cs
> 
> Where should I place this command? in a new file in common dir or inside
> the mmc_spi driver?

i would keep the current board init hook you have currently so that the boards 
which do have static settings can leverage that, and have the mmc_spi command 
simply call that.  i'd have the syntax be:
	mmc_spi [bus:]cs [hz [mode]]

i'd have it be a dedicated command in common/ so that people can disable it if 
they dont want it.
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20100426/05a65066/attachment.pgp 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH v2] mmc: add generic mmc spi driver
  2010-04-23  2:53 [U-Boot] [PATCH] mmc: add generic mmc spi driver Thomas Chou
  2010-04-23  3:35 ` Mike Frysinger
@ 2010-04-27  1:51 ` Thomas Chou
  2010-04-27  3:27 ` [U-Boot] [PATCH v3] " Thomas Chou
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 33+ messages in thread
From: Thomas Chou @ 2010-04-27  1:51 UTC (permalink / raw)
  To: u-boot

This patch supports mmc/sd card with spi interface. It is based on
the generic mmc framework. It works with SDHC and supports write.

The crc7 lib func is merged from linux and used to compute mmc
command checksum.

There is a subcomamnd "mmc_spi" to setup spi bus and cs at run time.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
v2 add crc7, use cmd58 to read ocr, add subcommand mmc_spi.

 common/Makefile       |    1 +
 common/cmd_mmc_spi.c  |   92 ++++++++++++++
 drivers/mmc/Makefile  |    1 +
 drivers/mmc/mmc_spi.c |  316 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/crc7.h  |   14 ++
 include/mmc_spi.h     |   19 +++
 lib/Makefile          |    1 +
 lib/crc7.c            |   62 ++++++++++
 8 files changed, 506 insertions(+), 0 deletions(-)
 create mode 100644 common/cmd_mmc_spi.c
 create mode 100644 drivers/mmc/mmc_spi.c
 create mode 100644 include/linux/crc7.h
 create mode 100644 include/mmc_spi.h
 create mode 100644 lib/crc7.c

diff --git a/common/Makefile b/common/Makefile
index dbf7a05..ee23e2f 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..578d7a7
--- /dev/null
+++ b/common/cmd_mmc_spi.c
@@ -0,0 +1,92 @@
+/*
+ * 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 <malloc.h>
+#include <mmc.h>
+#include <spi.h>
+#include <mmc_spi.h>
+
+static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	int dev_num;
+	uint bus;
+	uint cs;
+	uint speed;
+	uint mode;
+	char *endp;
+	struct mmc *mmc = NULL;
+	struct mmc_spi_priv *priv;
+
+	if (argc < 2)
+		goto usage;
+
+	dev_num = simple_strtoul(argv[1], &endp, 0);
+	if (*endp != 0)
+			goto usage;
+	mmc = find_mmc_device(dev_num);
+	if (!mmc || strcmp(mmc->name, "MMC_SPI"))
+			goto usage;
+	priv = mmc->priv;
+	bus = priv->bus;
+	cs = priv->cs;
+	speed = priv->speed;
+	mode = priv->mode;
+
+	if (argc < 3)
+		goto info;
+
+	cs = simple_strtoul(argv[2], &endp, 0);
+	if (*argv[2] == 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 >= 4) {
+		speed = simple_strtoul(argv[3], &endp, 0);
+		if (*argv[3] == 0 || *endp != 0)
+			goto usage;
+	}
+	if (argc >= 5) {
+		mode = simple_strtoul(argv[4], &endp, 16);
+		if (*argv[4] == 0 || *endp != 0)
+			goto usage;
+	}
+
+	if (bus != priv->bus || cs != priv->cs ||
+	    speed != priv->speed || mode != priv->speed) {
+		priv->bus = bus;
+		priv->cs = cs;
+		priv->speed = speed;
+		priv->mode = mode;
+		if (priv->slave) {
+			free(priv->slave);
+			priv->slave = NULL;
+		}
+	}
+info:
+	printf("%s:%d at %u:%u %u %u\n", mmc->name, dev_num,
+	       bus, cs, speed, mode);
+	return 0;
+
+usage:
+	cmd_usage(cmdtp);
+	return 1;
+}
+
+U_BOOT_CMD(
+	mmc_spi,	5,	0,	do_mmc_spi,
+	"mmc_spi setup",
+	"dev_num [bus:][cs] [hz] [mode]	- setup mmc_spi device on given\n"
+	"				   SPI bus and chip select\n"
+);
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 1b8f5bd..d03eb47 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -31,6 +31,7 @@ LIB	:= $(obj)libmmc.a
 COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
 COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
+COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
 COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
new file mode 100644
index 0000000..3ff171f
--- /dev/null
+++ b/drivers/mmc/mmc_spi.c
@@ -0,0 +1,316 @@
+/*
+ * 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 <mmc_spi.h>
+#include <linux/crc7.h>
+#include <asm/errno.h>
+
+#define CTOUT 0x10
+#define RTOUT 0x10000
+#define WTOUT 0x10000
+
+static uint mmc_spi_sendcmd(struct mmc *mmc, u8 cmdidx, u32 cmdarg)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 cmdo[7];
+	u8 r1;
+	int i;
+	cmdo[0] = 0xff;
+	cmdo[1] = 0x40 + 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(priv->slave, 7 * 8, cmdo, NULL, SPI_XFER_BEGIN);
+	for (i = 0; i < CTOUT; i++) {
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if ((r1 & 0x80) == 0)
+			break;
+	}
+	debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1);
+	return r1;
+}
+
+static uint mmc_spi_readdata(struct mmc *mmc, char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		for (i = 0; i < RTOUT; i++) {
+			spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+			if (r1 != 0xff)
+				break;
+		}
+		debug("%s:tok%d %x\n", __func__, i, r1);
+		if (r1 == 0xfe) {
+			spi_xfer(priv->slave, bsize * 8, NULL, buf, 0);
+			buf += bsize;
+			spi_xfer(priv->slave, 2 * 8, NULL, crc, 0);
+			r1 = 0;
+		} else
+			break;
+	}
+	return r1;
+}
+
+static uint mmc_spi_writedata(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 tok[2] = { 0xff, 0xfe };
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+		spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+		buf += bsize;
+		spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if (r1 == 0x05) {
+			for (i = 0; i < WTOUT; i++) {
+				spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+				if (r1 == 0xff) {
+					r1 = 0;
+					break;
+				}
+			}
+			if (i == WTOUT) {
+				debug("%s:wtout %x\n", __func__, r1);
+				r1 = 0x04;
+				break;
+			}
+		} else
+			break;
+	}
+	return r1;
+}
+
+static uint mmc_spi_writeblock(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 tok[2] = { 0xff, 0xfc };
+	u8 stop[2] = { 0xff, 0xfd };
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+		spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+		buf += bsize;
+		spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if (r1 == 0x05) {
+			for (i = 0; i < WTOUT; i++) {
+				spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+				if (r1 == 0xff) {
+					r1 = 0;
+					break;
+				}
+			}
+			if (i == WTOUT) {
+				debug("%s:wtout %x\n", __func__, r1);
+				r1 = 0x04;
+				break;
+			}
+		}
+	}
+	if (bcnt == 0 && r1 == 0) {
+		spi_xfer(priv->slave, 2 * 8, stop, NULL, 0);
+		for (i = 0; i < WTOUT; i++) {
+			spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+			if (r1 == 0xff) {
+				r1 = 0;
+				break;
+			}
+		}
+		if (i == WTOUT) {
+			debug("%s:wtout %x\n", __func__, r1);
+			r1 = 0x04;
+		}
+	}
+	return r1;
+}
+
+static inline uint rswab(u8 *d)
+{
+	return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static inline void mmc_spi_deactivate(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	spi_xfer(priv->slave, 1 * 8, NULL, NULL, SPI_XFER_END);
+	spi_xfer(priv->slave, 1 * 8, NULL, NULL, 0);
+}
+
+static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
+		struct mmc_data *data)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	ushort cmdidx = cmd->cmdidx;
+	uint cmdarg = cmd->cmdarg;
+	u8 resp[16];
+	int i;
+	int ret = 0;
+	debug("%s:cmd%d %x %x %x\n", __func__,
+	      cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags);
+	if (cmdidx == MMC_CMD_ALL_SEND_CID)
+		cmdidx = MMC_CMD_SEND_CID;
+	r1 = mmc_spi_sendcmd(mmc, cmdidx, cmdarg);
+	if (r1 == 0xff) {
+		ret = NO_CARD_ERR;
+		goto done;
+	} else if (cmd->resp_type == MMC_RSP_R2) {
+		r1 = mmc_spi_readdata(mmc, resp, 1, 16);
+		for (i = 0; i < 4; i++)
+			cmd->response[i] = rswab(resp + i * 4);
+		debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1],
+		      cmd->response[2], cmd->response[3]);
+	} else {
+		if (!data) {
+			spi_xfer(priv->slave, 4 * 8, NULL, resp, 0);
+			cmd->response[0] = rswab(resp);
+			debug("r32 %x\n", cmd->response[0]);
+		}
+		if (cmdidx == MMC_CMD_GO_IDLE_STATE) {
+			mmc_spi_deactivate(mmc);
+			r1 = mmc_spi_sendcmd(mmc, 58, 0);
+			spi_xfer(priv->slave, 4 * 8,
+				 NULL, resp, 0);
+			priv->ocr58 = rswab(resp) & ~OCR_BUSY;
+			debug("ocr58 %x\n", priv->ocr58);
+		} else if (cmdidx == SD_CMD_SEND_IF_COND) {
+			if ((r1 & 0x7e) == 0)
+				priv->sd_if_cond = 1;
+		} else if (cmdidx == SD_CMD_APP_SEND_OP_COND ||
+			   cmdidx == MMC_CMD_SEND_OP_COND) {
+			if (r1 & 0x04)
+				ret = TIMEOUT;
+			else {
+				if (r1 & 0x01)
+					cmd->response[0] = priv->ocr58;
+				else {
+					mmc_spi_deactivate(mmc);
+					r1 = mmc_spi_sendcmd(mmc, 58, 0);
+					spi_xfer(priv->slave, 4 * 8,
+						 NULL, resp, 0);
+					priv->ocr58 = rswab(resp);
+					debug("ocr58 %x\n", priv->ocr58);
+					cmd->response[0] = priv->ocr58;
+				}
+			}
+		} else {
+			cmd->response[0] = 0;
+			if (r1 & 0x7e)
+				cmd->response[0] |= (1 << 19);
+			if (r1 & 0x40)
+				cmd->response[0] |= (1 << 31);
+			if (r1 & 0x20)
+				cmd->response[0] |= (1 << 30);
+			if (r1 & 0x10)
+				cmd->response[0] |= (1 << 28);
+			if (r1 & 0x08)
+				cmd->response[0] |= (1 << 23);
+			if (r1 & 0x04)
+				cmd->response[0] |= (1 << 22);
+			if (r1 & 0x02)
+				cmd->response[0] |= (1 << 13);
+		}
+	}
+	if (data) {
+		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) {
+			if (cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)
+				r1 = mmc_spi_writeblock(mmc, data->src,
+					data->blocks, data->blocksize);
+			else
+				r1 = mmc_spi_writedata(mmc, data->src,
+					data->blocks, data->blocksize);
+		}
+	}
+done:
+	mmc_spi_deactivate(mmc);
+	return ret;
+}
+
+static void mmc_spi_set_ios(struct mmc *mmc)
+{
+	debug("%s:\n", __func__);
+}
+
+static int mmc_spi_init_p(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	debug("%s:\n", __func__);
+	if (!priv->slave) {
+		debug("%s: setup %u:%u %u %u\n", __func__,
+		      priv->bus, priv->cs,
+		      priv->speed, priv->mode);
+		priv->slave = spi_setup_slave(priv->bus, priv->cs,
+					      priv->speed, priv->mode);
+		if (!priv->slave)
+			return -ENOMEM;
+	}
+	priv->sd_if_cond = 0;
+	priv->ocr58 = 0;
+	spi_claim_bus(priv->slave);
+	/* power on initialization */
+	spi_xfer(priv->slave, 39 * 8, NULL, NULL,
+		 SPI_XFER_BEGIN | SPI_XFER_END);
+	spi_xfer(priv->slave, 18 * 8, NULL, NULL, 0);
+	return 0;
+}
+
+int mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
+{
+	struct mmc *mmc;
+	struct mmc_spi_priv *priv;
+
+	mmc = malloc(sizeof(*mmc));
+	if (!mmc)
+		return -ENOMEM;
+	priv = malloc(sizeof(*priv));
+	if (!priv) {
+		free(mmc);
+		return -ENOMEM;
+	}
+	mmc->priv = priv;
+	priv->bus = bus;
+	priv->cs = cs;
+	priv->speed = speed;
+	priv->mode = mode;
+	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 = 0;
+
+	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->f_max = speed;
+	mmc->f_min = mmc->f_max >> 9;
+	mmc->block_dev.part_type = PART_TYPE_DOS;
+
+	mmc_register(mmc);
+
+	return 0;
+}
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/include/mmc_spi.h b/include/mmc_spi.h
new file mode 100644
index 0000000..66aa6aa
--- /dev/null
+++ b/include/mmc_spi.h
@@ -0,0 +1,19 @@
+/*
+ * generic mmc spi driver
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _MMC_SPI_H_
+#define _MMC_SPI_H_
+struct mmc_spi_priv {
+	struct spi_slave *slave;
+	uint bus;
+	uint cs;
+	uint speed;
+	uint mode;
+	uint ocr58;
+	uint sd_if_cond:1;
+};
+#endif
diff --git a/lib/Makefile b/lib/Makefile
index c45f07c..aef2893 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;
+}
-- 
1.6.6.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH v3] mmc: add generic mmc spi driver
  2010-04-23  2:53 [U-Boot] [PATCH] mmc: add generic mmc spi driver Thomas Chou
  2010-04-23  3:35 ` Mike Frysinger
  2010-04-27  1:51 ` [U-Boot] [PATCH v2] " Thomas Chou
@ 2010-04-27  3:27 ` Thomas Chou
  2010-04-27 16:49   ` Mike Frysinger
  2010-04-28  2:50 ` [U-Boot] [PATCH v4] " Thomas Chou
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Thomas Chou @ 2010-04-27  3:27 UTC (permalink / raw)
  To: u-boot

This patch supports mmc/sd card with spi interface. It is based on
the generic mmc framework. It works with SDHC and supports write.

The crc7 lib func is merged from linux and used to compute mmc
command checksum.

There is a subcomamnd "mmc_spi" to setup spi bus and cs at run time.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
v3: add mmc_spi_init() proto to mmc_spi.h.
v2: add crc7, use cmd58 to read ocr, add subcommand mmc_spi.

 common/Makefile       |    1 +
 common/cmd_mmc_spi.c  |   92 ++++++++++++++
 drivers/mmc/Makefile  |    1 +
 drivers/mmc/mmc_spi.c |  316 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/crc7.h  |   14 ++
 include/mmc_spi.h     |   26 ++++
 lib/Makefile          |    1 +
 lib/crc7.c            |   62 ++++++++++
 8 files changed, 513 insertions(+), 0 deletions(-)
 create mode 100644 common/cmd_mmc_spi.c
 create mode 100644 drivers/mmc/mmc_spi.c
 create mode 100644 include/linux/crc7.h
 create mode 100644 include/mmc_spi.h
 create mode 100644 lib/crc7.c

diff --git a/common/Makefile b/common/Makefile
index dbf7a05..ee23e2f 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..578d7a7
--- /dev/null
+++ b/common/cmd_mmc_spi.c
@@ -0,0 +1,92 @@
+/*
+ * 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 <malloc.h>
+#include <mmc.h>
+#include <spi.h>
+#include <mmc_spi.h>
+
+static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	int dev_num;
+	uint bus;
+	uint cs;
+	uint speed;
+	uint mode;
+	char *endp;
+	struct mmc *mmc = NULL;
+	struct mmc_spi_priv *priv;
+
+	if (argc < 2)
+		goto usage;
+
+	dev_num = simple_strtoul(argv[1], &endp, 0);
+	if (*endp != 0)
+			goto usage;
+	mmc = find_mmc_device(dev_num);
+	if (!mmc || strcmp(mmc->name, "MMC_SPI"))
+			goto usage;
+	priv = mmc->priv;
+	bus = priv->bus;
+	cs = priv->cs;
+	speed = priv->speed;
+	mode = priv->mode;
+
+	if (argc < 3)
+		goto info;
+
+	cs = simple_strtoul(argv[2], &endp, 0);
+	if (*argv[2] == 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 >= 4) {
+		speed = simple_strtoul(argv[3], &endp, 0);
+		if (*argv[3] == 0 || *endp != 0)
+			goto usage;
+	}
+	if (argc >= 5) {
+		mode = simple_strtoul(argv[4], &endp, 16);
+		if (*argv[4] == 0 || *endp != 0)
+			goto usage;
+	}
+
+	if (bus != priv->bus || cs != priv->cs ||
+	    speed != priv->speed || mode != priv->speed) {
+		priv->bus = bus;
+		priv->cs = cs;
+		priv->speed = speed;
+		priv->mode = mode;
+		if (priv->slave) {
+			free(priv->slave);
+			priv->slave = NULL;
+		}
+	}
+info:
+	printf("%s:%d at %u:%u %u %u\n", mmc->name, dev_num,
+	       bus, cs, speed, mode);
+	return 0;
+
+usage:
+	cmd_usage(cmdtp);
+	return 1;
+}
+
+U_BOOT_CMD(
+	mmc_spi,	5,	0,	do_mmc_spi,
+	"mmc_spi setup",
+	"dev_num [bus:][cs] [hz] [mode]	- setup mmc_spi device on given\n"
+	"				   SPI bus and chip select\n"
+);
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6fa04b8..02ed329 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -28,6 +28,7 @@ LIB	:= $(obj)libmmc.a
 COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
 COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
+COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
 COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
new file mode 100644
index 0000000..3ff171f
--- /dev/null
+++ b/drivers/mmc/mmc_spi.c
@@ -0,0 +1,316 @@
+/*
+ * 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 <mmc_spi.h>
+#include <linux/crc7.h>
+#include <asm/errno.h>
+
+#define CTOUT 0x10
+#define RTOUT 0x10000
+#define WTOUT 0x10000
+
+static uint mmc_spi_sendcmd(struct mmc *mmc, u8 cmdidx, u32 cmdarg)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 cmdo[7];
+	u8 r1;
+	int i;
+	cmdo[0] = 0xff;
+	cmdo[1] = 0x40 + 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(priv->slave, 7 * 8, cmdo, NULL, SPI_XFER_BEGIN);
+	for (i = 0; i < CTOUT; i++) {
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if ((r1 & 0x80) == 0)
+			break;
+	}
+	debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1);
+	return r1;
+}
+
+static uint mmc_spi_readdata(struct mmc *mmc, char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		for (i = 0; i < RTOUT; i++) {
+			spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+			if (r1 != 0xff)
+				break;
+		}
+		debug("%s:tok%d %x\n", __func__, i, r1);
+		if (r1 == 0xfe) {
+			spi_xfer(priv->slave, bsize * 8, NULL, buf, 0);
+			buf += bsize;
+			spi_xfer(priv->slave, 2 * 8, NULL, crc, 0);
+			r1 = 0;
+		} else
+			break;
+	}
+	return r1;
+}
+
+static uint mmc_spi_writedata(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 tok[2] = { 0xff, 0xfe };
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+		spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+		buf += bsize;
+		spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if (r1 == 0x05) {
+			for (i = 0; i < WTOUT; i++) {
+				spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+				if (r1 == 0xff) {
+					r1 = 0;
+					break;
+				}
+			}
+			if (i == WTOUT) {
+				debug("%s:wtout %x\n", __func__, r1);
+				r1 = 0x04;
+				break;
+			}
+		} else
+			break;
+	}
+	return r1;
+}
+
+static uint mmc_spi_writeblock(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 tok[2] = { 0xff, 0xfc };
+	u8 stop[2] = { 0xff, 0xfd };
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+		spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+		buf += bsize;
+		spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if (r1 == 0x05) {
+			for (i = 0; i < WTOUT; i++) {
+				spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+				if (r1 == 0xff) {
+					r1 = 0;
+					break;
+				}
+			}
+			if (i == WTOUT) {
+				debug("%s:wtout %x\n", __func__, r1);
+				r1 = 0x04;
+				break;
+			}
+		}
+	}
+	if (bcnt == 0 && r1 == 0) {
+		spi_xfer(priv->slave, 2 * 8, stop, NULL, 0);
+		for (i = 0; i < WTOUT; i++) {
+			spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+			if (r1 == 0xff) {
+				r1 = 0;
+				break;
+			}
+		}
+		if (i == WTOUT) {
+			debug("%s:wtout %x\n", __func__, r1);
+			r1 = 0x04;
+		}
+	}
+	return r1;
+}
+
+static inline uint rswab(u8 *d)
+{
+	return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static inline void mmc_spi_deactivate(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	spi_xfer(priv->slave, 1 * 8, NULL, NULL, SPI_XFER_END);
+	spi_xfer(priv->slave, 1 * 8, NULL, NULL, 0);
+}
+
+static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
+		struct mmc_data *data)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	ushort cmdidx = cmd->cmdidx;
+	uint cmdarg = cmd->cmdarg;
+	u8 resp[16];
+	int i;
+	int ret = 0;
+	debug("%s:cmd%d %x %x %x\n", __func__,
+	      cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags);
+	if (cmdidx == MMC_CMD_ALL_SEND_CID)
+		cmdidx = MMC_CMD_SEND_CID;
+	r1 = mmc_spi_sendcmd(mmc, cmdidx, cmdarg);
+	if (r1 == 0xff) {
+		ret = NO_CARD_ERR;
+		goto done;
+	} else if (cmd->resp_type == MMC_RSP_R2) {
+		r1 = mmc_spi_readdata(mmc, resp, 1, 16);
+		for (i = 0; i < 4; i++)
+			cmd->response[i] = rswab(resp + i * 4);
+		debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1],
+		      cmd->response[2], cmd->response[3]);
+	} else {
+		if (!data) {
+			spi_xfer(priv->slave, 4 * 8, NULL, resp, 0);
+			cmd->response[0] = rswab(resp);
+			debug("r32 %x\n", cmd->response[0]);
+		}
+		if (cmdidx == MMC_CMD_GO_IDLE_STATE) {
+			mmc_spi_deactivate(mmc);
+			r1 = mmc_spi_sendcmd(mmc, 58, 0);
+			spi_xfer(priv->slave, 4 * 8,
+				 NULL, resp, 0);
+			priv->ocr58 = rswab(resp) & ~OCR_BUSY;
+			debug("ocr58 %x\n", priv->ocr58);
+		} else if (cmdidx == SD_CMD_SEND_IF_COND) {
+			if ((r1 & 0x7e) == 0)
+				priv->sd_if_cond = 1;
+		} else if (cmdidx == SD_CMD_APP_SEND_OP_COND ||
+			   cmdidx == MMC_CMD_SEND_OP_COND) {
+			if (r1 & 0x04)
+				ret = TIMEOUT;
+			else {
+				if (r1 & 0x01)
+					cmd->response[0] = priv->ocr58;
+				else {
+					mmc_spi_deactivate(mmc);
+					r1 = mmc_spi_sendcmd(mmc, 58, 0);
+					spi_xfer(priv->slave, 4 * 8,
+						 NULL, resp, 0);
+					priv->ocr58 = rswab(resp);
+					debug("ocr58 %x\n", priv->ocr58);
+					cmd->response[0] = priv->ocr58;
+				}
+			}
+		} else {
+			cmd->response[0] = 0;
+			if (r1 & 0x7e)
+				cmd->response[0] |= (1 << 19);
+			if (r1 & 0x40)
+				cmd->response[0] |= (1 << 31);
+			if (r1 & 0x20)
+				cmd->response[0] |= (1 << 30);
+			if (r1 & 0x10)
+				cmd->response[0] |= (1 << 28);
+			if (r1 & 0x08)
+				cmd->response[0] |= (1 << 23);
+			if (r1 & 0x04)
+				cmd->response[0] |= (1 << 22);
+			if (r1 & 0x02)
+				cmd->response[0] |= (1 << 13);
+		}
+	}
+	if (data) {
+		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) {
+			if (cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)
+				r1 = mmc_spi_writeblock(mmc, data->src,
+					data->blocks, data->blocksize);
+			else
+				r1 = mmc_spi_writedata(mmc, data->src,
+					data->blocks, data->blocksize);
+		}
+	}
+done:
+	mmc_spi_deactivate(mmc);
+	return ret;
+}
+
+static void mmc_spi_set_ios(struct mmc *mmc)
+{
+	debug("%s:\n", __func__);
+}
+
+static int mmc_spi_init_p(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	debug("%s:\n", __func__);
+	if (!priv->slave) {
+		debug("%s: setup %u:%u %u %u\n", __func__,
+		      priv->bus, priv->cs,
+		      priv->speed, priv->mode);
+		priv->slave = spi_setup_slave(priv->bus, priv->cs,
+					      priv->speed, priv->mode);
+		if (!priv->slave)
+			return -ENOMEM;
+	}
+	priv->sd_if_cond = 0;
+	priv->ocr58 = 0;
+	spi_claim_bus(priv->slave);
+	/* power on initialization */
+	spi_xfer(priv->slave, 39 * 8, NULL, NULL,
+		 SPI_XFER_BEGIN | SPI_XFER_END);
+	spi_xfer(priv->slave, 18 * 8, NULL, NULL, 0);
+	return 0;
+}
+
+int mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
+{
+	struct mmc *mmc;
+	struct mmc_spi_priv *priv;
+
+	mmc = malloc(sizeof(*mmc));
+	if (!mmc)
+		return -ENOMEM;
+	priv = malloc(sizeof(*priv));
+	if (!priv) {
+		free(mmc);
+		return -ENOMEM;
+	}
+	mmc->priv = priv;
+	priv->bus = bus;
+	priv->cs = cs;
+	priv->speed = speed;
+	priv->mode = mode;
+	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 = 0;
+
+	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->f_max = speed;
+	mmc->f_min = mmc->f_max >> 9;
+	mmc->block_dev.part_type = PART_TYPE_DOS;
+
+	mmc_register(mmc);
+
+	return 0;
+}
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/include/mmc_spi.h b/include/mmc_spi.h
new file mode 100644
index 0000000..6c72f65
--- /dev/null
+++ b/include/mmc_spi.h
@@ -0,0 +1,26 @@
+/*
+ * generic mmc spi driver
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _MMC_SPI_H_
+#define _MMC_SPI_H_
+
+#include <spi.h>
+#include <linux/types.h>
+
+struct mmc_spi_priv {
+	struct spi_slave *slave;
+	uint bus;
+	uint cs;
+	uint speed;
+	uint mode;
+	uint ocr58;
+	uint sd_if_cond:1;
+};
+
+int mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
+
+#endif
diff --git a/lib/Makefile b/lib/Makefile
index c45f07c..aef2893 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;
+}
-- 
1.6.6.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH v3] mmc: add generic mmc spi driver
  2010-04-27  3:27 ` [U-Boot] [PATCH v3] " Thomas Chou
@ 2010-04-27 16:49   ` Mike Frysinger
  2010-04-28  3:14     ` Thomas Chou
  0 siblings, 1 reply; 33+ messages in thread
From: Mike Frysinger @ 2010-04-27 16:49 UTC (permalink / raw)
  To: u-boot

On Monday 26 April 2010 23:27:16 Thomas Chou wrote:
> --- /dev/null
> +++ b/common/cmd_mmc_spi.c
> +	struct mmc *mmc = NULL;

useless assignment since mmc gets set shortly after

> +	struct mmc_spi_priv *priv;
> +
> +	if (argc < 2)
> +		goto usage;
> +
> +	dev_num = simple_strtoul(argv[1], &endp, 0);
> +	if (*endp != 0)
> +			goto usage;
> +	mmc = find_mmc_device(dev_num);
> +	if (!mmc || strcmp(mmc->name, "MMC_SPI"))
> +			goto usage;

too many indents on the goto

also, this looks like it bails if no existing mmc spi device is found ?  i 
thought the point of this command was to create new instances on the fly.  
this looks like it just reprograms the settings for an existing device.

> +	if (bus != priv->bus || cs != priv->cs ||
> +	    speed != priv->speed || mode != priv->speed) {
> +		priv->bus = bus;
> +		priv->cs = cs;
> +		priv->speed = speed;
> +		priv->mode = mode;
> +		if (priv->slave) {
> +			free(priv->slave);
> +			priv->slave = NULL;
> +		}

you shouldnt be poking the spi internals like this.  use the common spi funcs 
to free/alloc the slave device.
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20100427/961b2b68/attachment.pgp 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH v4] mmc: add generic mmc spi driver
  2010-04-23  2:53 [U-Boot] [PATCH] mmc: add generic mmc spi driver Thomas Chou
                   ` (2 preceding siblings ...)
  2010-04-27  3:27 ` [U-Boot] [PATCH v3] " Thomas Chou
@ 2010-04-28  2:50 ` Thomas Chou
  2010-04-28  6:00 ` [U-Boot] [PATCH v5] " Thomas Chou
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 33+ messages in thread
From: Thomas Chou @ 2010-04-28  2:50 UTC (permalink / raw)
  To: u-boot

This patch supports mmc/sd card with spi interface. It is based on
the generic mmc framework. It works with SDHC and supports write.

The crc7 lib func is merged from linux and used to compute mmc
command checksum.

There is a subcomamnd "mmc_spi" to setup spi bus and cs at run time.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
v4: change mmc_spi subcommand to search and create new mmc dev.
v3: add mmc_spi_init() proto to mmc_spi.h.
v2: add crc7, use cmd58 to read ocr, add subcommand mmc_spi.

 common/Makefile       |    1 +
 common/cmd_mmc_spi.c  |  117 ++++++++++++++++++
 drivers/mmc/Makefile  |    1 +
 drivers/mmc/mmc_spi.c |  318 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/crc7.h  |   14 ++
 include/mmc_spi.h     |   27 ++++
 lib/Makefile          |    1 +
 lib/crc7.c            |   62 ++++++++++
 8 files changed, 541 insertions(+), 0 deletions(-)
 create mode 100644 common/cmd_mmc_spi.c
 create mode 100644 drivers/mmc/mmc_spi.c
 create mode 100644 include/linux/crc7.h
 create mode 100644 include/mmc_spi.h
 create mode 100644 lib/crc7.c

diff --git a/common/Makefile b/common/Makefile
index dbf7a05..ee23e2f 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..d883837
--- /dev/null
+++ b/common/cmd_mmc_spi.c
@@ -0,0 +1,117 @@
+/*
+ * 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 <malloc.h>
+#include <mmc.h>
+#include <spi.h>
+#include <mmc_spi.h>
+
+#define MMC_SPI_DEV_NUM_MAX 16 /* number of MMC devs to query */
+#ifndef CONFIG_MMC_SPI_BUS
+# define CONFIG_MMC_SPI_BUS 0
+#endif
+#ifndef CONFIG_MMC_SPI_CS
+# define CONFIG_MMC_SPI_CS 1
+#endif
+#ifndef CONFIG_MMC_SPI_SPEED
+# define CONFIG_MMC_SPI_SPEED 30000000
+#endif
+#ifndef CONFIG_MMC_SPI_MODE
+# define CONFIG_MMC_SPI_MODE SPI_MODE_3
+#endif
+
+static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	int dev_num;
+	uint bus;
+	uint cs;
+	uint speed;
+	uint mode;
+	char *endp;
+	struct mmc *mmc;
+	struct mmc_spi_priv *priv;
+
+	for (dev_num = 0; dev_num < MMC_SPI_DEV_NUM_MAX; dev_num++) {
+		mmc = find_mmc_device(dev_num);
+		if (!mmc || strcmp(mmc->name, "MMC_SPI") == 0)
+			break;
+	}
+	if (!mmc || dev_num == MMC_SPI_DEV_NUM_MAX) {
+		printf("Create MMC Device\n");
+		mmc = mmc_spi_init(CONFIG_MMC_SPI_BUS,
+				   CONFIG_MMC_SPI_CS,
+				   CONFIG_MMC_SPI_SPEED,
+				   CONFIG_MMC_SPI_MODE);
+		if (!mmc) {
+			printf("Failed to create MMC Device\n");
+			return 1;
+		}
+		dev_num = mmc->block_dev.dev;
+	}
+
+	priv = mmc->priv;
+	bus = priv->bus;
+	cs = priv->cs;
+	speed = priv->speed;
+	mode = priv->mode;
+
+	if (argc < 2)
+		goto info;
+	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;
+	}
+
+	if (bus != priv->bus || cs != priv->cs ||
+	    speed != priv->speed || mode != priv->mode) {
+		priv->bus = bus;
+		priv->cs = cs;
+		priv->speed = speed;
+		priv->mode = mode;
+		if (priv->slave) {
+			spi_free_slave(priv->slave);
+			priv->slave = NULL;
+		}
+	}
+info:
+	printf("%s: %d at %u:%u %u %u\n", mmc->name, dev_num,
+	       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 on given\n"
+	"				   SPI bus and chip select\n"
+);
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6fa04b8..02ed329 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -28,6 +28,7 @@ LIB	:= $(obj)libmmc.a
 COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
 COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
+COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
 COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
new file mode 100644
index 0000000..dedcef7
--- /dev/null
+++ b/drivers/mmc/mmc_spi.c
@@ -0,0 +1,318 @@
+/*
+ * 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 <mmc_spi.h>
+#include <linux/crc7.h>
+#include <asm/errno.h>
+
+#define CTOUT 0x10
+#define RTOUT 0x10000
+#define WTOUT 0x10000
+
+static uint mmc_spi_sendcmd(struct mmc *mmc, u8 cmdidx, u32 cmdarg)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 cmdo[7];
+	u8 r1;
+	int i;
+	cmdo[0] = 0xff;
+	cmdo[1] = 0x40 + 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(priv->slave, 7 * 8, cmdo, NULL, SPI_XFER_BEGIN);
+	for (i = 0; i < CTOUT; i++) {
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if ((r1 & 0x80) == 0)
+			break;
+	}
+	debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1);
+	return r1;
+}
+
+static uint mmc_spi_readdata(struct mmc *mmc, char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		for (i = 0; i < RTOUT; i++) {
+			spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+			if (r1 != 0xff)
+				break;
+		}
+		debug("%s:tok%d %x\n", __func__, i, r1);
+		if (r1 == 0xfe) {
+			spi_xfer(priv->slave, bsize * 8, NULL, buf, 0);
+			buf += bsize;
+			spi_xfer(priv->slave, 2 * 8, NULL, crc, 0);
+			r1 = 0;
+		} else
+			break;
+	}
+	return r1;
+}
+
+static uint mmc_spi_writedata(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 tok[2] = { 0xff, 0xfe };
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+		spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+		buf += bsize;
+		spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if (r1 == 0x05) {
+			for (i = 0; i < WTOUT; i++) {
+				spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+				if (r1 == 0xff) {
+					r1 = 0;
+					break;
+				}
+			}
+			if (i == WTOUT) {
+				debug("%s:wtout %x\n", __func__, r1);
+				r1 = 0x04;
+				break;
+			}
+		} else
+			break;
+	}
+	return r1;
+}
+
+static uint mmc_spi_writeblock(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 tok[2] = { 0xff, 0xfc };
+	u8 stop[2] = { 0xff, 0xfd };
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+		spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+		buf += bsize;
+		spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if (r1 == 0x05) {
+			for (i = 0; i < WTOUT; i++) {
+				spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+				if (r1 == 0xff) {
+					r1 = 0;
+					break;
+				}
+			}
+			if (i == WTOUT) {
+				debug("%s:wtout %x\n", __func__, r1);
+				r1 = 0x04;
+				break;
+			}
+		}
+	}
+	if (bcnt == 0 && r1 == 0) {
+		spi_xfer(priv->slave, 2 * 8, stop, NULL, 0);
+		for (i = 0; i < WTOUT; i++) {
+			spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+			if (r1 == 0xff) {
+				r1 = 0;
+				break;
+			}
+		}
+		if (i == WTOUT) {
+			debug("%s:wtout %x\n", __func__, r1);
+			r1 = 0x04;
+		}
+	}
+	return r1;
+}
+
+static inline uint rswab(u8 *d)
+{
+	return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static inline void mmc_spi_deactivate(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	spi_xfer(priv->slave, 1 * 8, NULL, NULL, SPI_XFER_END);
+	spi_xfer(priv->slave, 1 * 8, NULL, NULL, 0);
+}
+
+static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
+		struct mmc_data *data)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	ushort cmdidx = cmd->cmdidx;
+	uint cmdarg = cmd->cmdarg;
+	u8 resp[16];
+	int i;
+	int ret = 0;
+	debug("%s:cmd%d %x %x %x\n", __func__,
+	      cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags);
+	if (cmdidx == MMC_CMD_ALL_SEND_CID)
+		cmdidx = MMC_CMD_SEND_CID;
+	r1 = mmc_spi_sendcmd(mmc, cmdidx, cmdarg);
+	if (r1 == 0xff) {
+		ret = NO_CARD_ERR;
+		goto done;
+	} else if (cmd->resp_type == MMC_RSP_R2) {
+		r1 = mmc_spi_readdata(mmc, resp, 1, 16);
+		for (i = 0; i < 4; i++)
+			cmd->response[i] = rswab(resp + i * 4);
+		debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1],
+		      cmd->response[2], cmd->response[3]);
+	} else {
+		if (!data) {
+			spi_xfer(priv->slave, 4 * 8, NULL, resp, 0);
+			cmd->response[0] = rswab(resp);
+			debug("r32 %x\n", cmd->response[0]);
+		}
+		if (cmdidx == MMC_CMD_GO_IDLE_STATE) {
+			mmc_spi_deactivate(mmc);
+			r1 = mmc_spi_sendcmd(mmc, 58, 0);
+			spi_xfer(priv->slave, 4 * 8,
+				 NULL, resp, 0);
+			priv->ocr58 = rswab(resp) & ~OCR_BUSY;
+			debug("ocr58 %x\n", priv->ocr58);
+		} else if (cmdidx == SD_CMD_SEND_IF_COND) {
+			if ((r1 & 0x7e) == 0)
+				priv->sd_if_cond = 1;
+		} else if (cmdidx == SD_CMD_APP_SEND_OP_COND ||
+			   cmdidx == MMC_CMD_SEND_OP_COND) {
+			if (r1 & 0x04)
+				ret = TIMEOUT;
+			else {
+				if (r1 & 0x01)
+					cmd->response[0] = priv->ocr58;
+				else {
+					mmc_spi_deactivate(mmc);
+					r1 = mmc_spi_sendcmd(mmc, 58, 0);
+					spi_xfer(priv->slave, 4 * 8,
+						 NULL, resp, 0);
+					priv->ocr58 = rswab(resp);
+					debug("ocr58 %x\n", priv->ocr58);
+					cmd->response[0] = priv->ocr58;
+				}
+			}
+		} else {
+			cmd->response[0] = 0;
+			if (r1 & 0x7e)
+				cmd->response[0] |= (1 << 19);
+			if (r1 & 0x40)
+				cmd->response[0] |= (1 << 31);
+			if (r1 & 0x20)
+				cmd->response[0] |= (1 << 30);
+			if (r1 & 0x10)
+				cmd->response[0] |= (1 << 28);
+			if (r1 & 0x08)
+				cmd->response[0] |= (1 << 23);
+			if (r1 & 0x04)
+				cmd->response[0] |= (1 << 22);
+			if (r1 & 0x02)
+				cmd->response[0] |= (1 << 13);
+		}
+	}
+	if (data) {
+		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) {
+			if (cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)
+				r1 = mmc_spi_writeblock(mmc, data->src,
+					data->blocks, data->blocksize);
+			else
+				r1 = mmc_spi_writedata(mmc, data->src,
+					data->blocks, data->blocksize);
+		}
+	}
+done:
+	mmc_spi_deactivate(mmc);
+	return ret;
+}
+
+static void mmc_spi_set_ios(struct mmc *mmc)
+{
+	debug("%s:\n", __func__);
+}
+
+static int mmc_spi_init_p(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	debug("%s:\n", __func__);
+	if (!priv->slave) {
+		debug("%s: setup %u:%u %u %u\n", __func__,
+		      priv->bus, priv->cs,
+		      priv->speed, priv->mode);
+		priv->slave = spi_setup_slave(priv->bus, priv->cs,
+					      priv->speed, priv->mode);
+		if (!priv->slave) {
+			debug("%s: failed to setup spi slave\n", __func__);
+			return -ENOMEM;
+		}
+	}
+	priv->sd_if_cond = 0;
+	priv->ocr58 = 0;
+	spi_claim_bus(priv->slave);
+	/* power on initialization */
+	spi_xfer(priv->slave, 39 * 8, NULL, NULL,
+		 SPI_XFER_BEGIN | SPI_XFER_END);
+	spi_xfer(priv->slave, 18 * 8, NULL, NULL, 0);
+	return 0;
+}
+
+struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
+{
+	struct mmc *mmc;
+	struct mmc_spi_priv *priv;
+
+	mmc = malloc(sizeof(*mmc));
+	if (!mmc)
+		return NULL;
+	priv = malloc(sizeof(*priv));
+	if (!priv) {
+		free(mmc);
+		return NULL;
+	}
+	mmc->priv = priv;
+	priv->bus = bus;
+	priv->cs = cs;
+	priv->speed = speed;
+	priv->mode = mode;
+	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 = 0;
+
+	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->f_max = speed;
+	mmc->f_min = mmc->f_max >> 9;
+	mmc->block_dev.part_type = PART_TYPE_DOS;
+
+	mmc_register(mmc);
+
+	return mmc;
+}
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/include/mmc_spi.h b/include/mmc_spi.h
new file mode 100644
index 0000000..fbfa1ce
--- /dev/null
+++ b/include/mmc_spi.h
@@ -0,0 +1,27 @@
+/*
+ * generic mmc spi driver
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _MMC_SPI_H_
+#define _MMC_SPI_H_
+
+#include <spi.h>
+#include <mmc.h>
+#include <linux/types.h>
+
+struct mmc_spi_priv {
+	struct spi_slave *slave;
+	uint bus;
+	uint cs;
+	uint speed;
+	uint mode;
+	uint ocr58;
+	uint sd_if_cond:1;
+};
+
+struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
+
+#endif
diff --git a/lib/Makefile b/lib/Makefile
index c45f07c..aef2893 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;
+}
-- 
1.6.6.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH v3] mmc: add generic mmc spi driver
  2010-04-27 16:49   ` Mike Frysinger
@ 2010-04-28  3:14     ` Thomas Chou
  0 siblings, 0 replies; 33+ messages in thread
From: Thomas Chou @ 2010-04-28  3:14 UTC (permalink / raw)
  To: u-boot

Hi Mike,

On 04/28/2010 12:49 AM, Mike Frysinger wrote:
> On Monday 26 April 2010 23:27:16 Thomas Chou wrote:
>    
>> --- /dev/null
>> +++ b/common/cmd_mmc_spi.c
>> +	struct mmc *mmc = NULL;
>>      
> useless assignment since mmc gets set shortly after
>    
OK.
>    
>> +	struct mmc_spi_priv *priv;
>> +
>> +	if (argc<  2)
>> +		goto usage;
>> +
>> +	dev_num = simple_strtoul(argv[1],&endp, 0);
>> +	if (*endp != 0)
>> +			goto usage;
>> +	mmc = find_mmc_device(dev_num);
>> +	if (!mmc || strcmp(mmc->name, "MMC_SPI"))
>> +			goto usage;
>>      
> too many indents on the goto
>    
The tabs were interfered by the '+' . It is correct when applied.
> also, this looks like it bails if no existing mmc spi device is found ?  i
> thought the point of this command was to create new instances on the fly.
> this looks like it just reprograms the settings for an existing device.
>
>    
OK. Now v4 patch creates a MMC dev on the fly.

>> +	if (bus != priv->bus || cs != priv->cs ||
>> +	    speed != priv->speed || mode != priv->speed) {
>> +		priv->bus = bus;
>> +		priv->cs = cs;
>> +		priv->speed = speed;
>> +		priv->mode = mode;
>> +		if (priv->slave) {
>> +			free(priv->slave);
>> +			priv->slave = NULL;
>> +		}
>>      
> you shouldnt be poking the spi internals like this.  use the common spi funcs
> to free/alloc the slave device.
>    
OK.

Best regards,
Thomas

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH v5] mmc: add generic mmc spi driver
  2010-04-23  2:53 [U-Boot] [PATCH] mmc: add generic mmc spi driver Thomas Chou
                   ` (3 preceding siblings ...)
  2010-04-28  2:50 ` [U-Boot] [PATCH v4] " Thomas Chou
@ 2010-04-28  6:00 ` Thomas Chou
  2010-04-28 15:21   ` Andy Fleming
  2010-05-03  0:54 ` [U-Boot] [PATCH 0/3] mmc: add mmc_spi driver Thomas Chou
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 33+ messages in thread
From: Thomas Chou @ 2010-04-28  6:00 UTC (permalink / raw)
  To: u-boot

This patch supports mmc/sd card with spi interface. It is based on
the generic mmc framework. It works with SDHC and supports write.

The crc7 lib func is merged from linux and used to compute mmc
command checksum.

There is a subcomamnd "mmc_spi" to setup spi bus and cs at run time.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
v5: remove dev_num limit to search.
v4: change mmc_spi subcommand to search and create new mmc dev.
v3: add mmc_spi_init() proto to mmc_spi.h.
v2: add crc7, use cmd58 to read ocr, add subcommand mmc_spi.

 common/Makefile       |    1 +
 common/cmd_mmc_spi.c  |  114 ++++++++++++++++++
 drivers/mmc/Makefile  |    1 +
 drivers/mmc/mmc_spi.c |  318 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/crc7.h  |   14 ++
 include/mmc_spi.h     |   27 ++++
 lib/Makefile          |    1 +
 lib/crc7.c            |   62 ++++++++++
 8 files changed, 538 insertions(+), 0 deletions(-)
 create mode 100644 common/cmd_mmc_spi.c
 create mode 100644 drivers/mmc/mmc_spi.c
 create mode 100644 include/linux/crc7.h
 create mode 100644 include/mmc_spi.h
 create mode 100644 lib/crc7.c

diff --git a/common/Makefile b/common/Makefile
index dbf7a05..ee23e2f 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..0cbb449
--- /dev/null
+++ b/common/cmd_mmc_spi.c
@@ -0,0 +1,114 @@
+/*
+ * 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 <malloc.h>
+#include <mmc.h>
+#include <spi.h>
+#include <mmc_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
+#ifndef CONFIG_MMC_SPI_SPEED
+# define CONFIG_MMC_SPI_SPEED 30000000
+#endif
+#ifndef CONFIG_MMC_SPI_MODE
+# define CONFIG_MMC_SPI_MODE SPI_MODE_3
+#endif
+
+static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	int dev_num = -1;
+	uint bus;
+	uint cs;
+	uint speed;
+	uint mode;
+	char *endp;
+	struct mmc *mmc;
+	struct mmc_spi_priv *priv;
+
+	do {
+		mmc = find_mmc_device(++dev_num);
+	} while (mmc && strcmp(mmc->name, "MMC_SPI"));
+	if (!mmc) {
+		printf("Create MMC Device\n");
+		mmc = mmc_spi_init(CONFIG_MMC_SPI_BUS,
+				   CONFIG_MMC_SPI_CS,
+				   CONFIG_MMC_SPI_SPEED,
+				   CONFIG_MMC_SPI_MODE);
+		if (!mmc) {
+			printf("Failed to create MMC Device\n");
+			return 1;
+		}
+		dev_num = mmc->block_dev.dev;
+	}
+
+	priv = mmc->priv;
+	bus = priv->bus;
+	cs = priv->cs;
+	speed = priv->speed;
+	mode = priv->mode;
+
+	if (argc < 2)
+		goto info;
+	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;
+	}
+
+	if (bus != priv->bus || cs != priv->cs ||
+	    speed != priv->speed || mode != priv->mode) {
+		priv->bus = bus;
+		priv->cs = cs;
+		priv->speed = speed;
+		priv->mode = mode;
+		if (priv->slave) {
+			spi_free_slave(priv->slave);
+			priv->slave = NULL;
+		}
+	}
+info:
+	printf("%s: %d at %u:%u %u %u\n", mmc->name, dev_num,
+	       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 on given\n"
+	"				   SPI bus and chip select\n"
+);
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6fa04b8..02ed329 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -28,6 +28,7 @@ LIB	:= $(obj)libmmc.a
 COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
 COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
+COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
 COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
new file mode 100644
index 0000000..dedcef7
--- /dev/null
+++ b/drivers/mmc/mmc_spi.c
@@ -0,0 +1,318 @@
+/*
+ * 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 <mmc_spi.h>
+#include <linux/crc7.h>
+#include <asm/errno.h>
+
+#define CTOUT 0x10
+#define RTOUT 0x10000
+#define WTOUT 0x10000
+
+static uint mmc_spi_sendcmd(struct mmc *mmc, u8 cmdidx, u32 cmdarg)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 cmdo[7];
+	u8 r1;
+	int i;
+	cmdo[0] = 0xff;
+	cmdo[1] = 0x40 + 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(priv->slave, 7 * 8, cmdo, NULL, SPI_XFER_BEGIN);
+	for (i = 0; i < CTOUT; i++) {
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if ((r1 & 0x80) == 0)
+			break;
+	}
+	debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1);
+	return r1;
+}
+
+static uint mmc_spi_readdata(struct mmc *mmc, char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		for (i = 0; i < RTOUT; i++) {
+			spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+			if (r1 != 0xff)
+				break;
+		}
+		debug("%s:tok%d %x\n", __func__, i, r1);
+		if (r1 == 0xfe) {
+			spi_xfer(priv->slave, bsize * 8, NULL, buf, 0);
+			buf += bsize;
+			spi_xfer(priv->slave, 2 * 8, NULL, crc, 0);
+			r1 = 0;
+		} else
+			break;
+	}
+	return r1;
+}
+
+static uint mmc_spi_writedata(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 tok[2] = { 0xff, 0xfe };
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+		spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+		buf += bsize;
+		spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if (r1 == 0x05) {
+			for (i = 0; i < WTOUT; i++) {
+				spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+				if (r1 == 0xff) {
+					r1 = 0;
+					break;
+				}
+			}
+			if (i == WTOUT) {
+				debug("%s:wtout %x\n", __func__, r1);
+				r1 = 0x04;
+				break;
+			}
+		} else
+			break;
+	}
+	return r1;
+}
+
+static uint mmc_spi_writeblock(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 tok[2] = { 0xff, 0xfc };
+	u8 stop[2] = { 0xff, 0xfd };
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+		spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+		buf += bsize;
+		spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if (r1 == 0x05) {
+			for (i = 0; i < WTOUT; i++) {
+				spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+				if (r1 == 0xff) {
+					r1 = 0;
+					break;
+				}
+			}
+			if (i == WTOUT) {
+				debug("%s:wtout %x\n", __func__, r1);
+				r1 = 0x04;
+				break;
+			}
+		}
+	}
+	if (bcnt == 0 && r1 == 0) {
+		spi_xfer(priv->slave, 2 * 8, stop, NULL, 0);
+		for (i = 0; i < WTOUT; i++) {
+			spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+			if (r1 == 0xff) {
+				r1 = 0;
+				break;
+			}
+		}
+		if (i == WTOUT) {
+			debug("%s:wtout %x\n", __func__, r1);
+			r1 = 0x04;
+		}
+	}
+	return r1;
+}
+
+static inline uint rswab(u8 *d)
+{
+	return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static inline void mmc_spi_deactivate(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	spi_xfer(priv->slave, 1 * 8, NULL, NULL, SPI_XFER_END);
+	spi_xfer(priv->slave, 1 * 8, NULL, NULL, 0);
+}
+
+static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
+		struct mmc_data *data)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	ushort cmdidx = cmd->cmdidx;
+	uint cmdarg = cmd->cmdarg;
+	u8 resp[16];
+	int i;
+	int ret = 0;
+	debug("%s:cmd%d %x %x %x\n", __func__,
+	      cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags);
+	if (cmdidx == MMC_CMD_ALL_SEND_CID)
+		cmdidx = MMC_CMD_SEND_CID;
+	r1 = mmc_spi_sendcmd(mmc, cmdidx, cmdarg);
+	if (r1 == 0xff) {
+		ret = NO_CARD_ERR;
+		goto done;
+	} else if (cmd->resp_type == MMC_RSP_R2) {
+		r1 = mmc_spi_readdata(mmc, resp, 1, 16);
+		for (i = 0; i < 4; i++)
+			cmd->response[i] = rswab(resp + i * 4);
+		debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1],
+		      cmd->response[2], cmd->response[3]);
+	} else {
+		if (!data) {
+			spi_xfer(priv->slave, 4 * 8, NULL, resp, 0);
+			cmd->response[0] = rswab(resp);
+			debug("r32 %x\n", cmd->response[0]);
+		}
+		if (cmdidx == MMC_CMD_GO_IDLE_STATE) {
+			mmc_spi_deactivate(mmc);
+			r1 = mmc_spi_sendcmd(mmc, 58, 0);
+			spi_xfer(priv->slave, 4 * 8,
+				 NULL, resp, 0);
+			priv->ocr58 = rswab(resp) & ~OCR_BUSY;
+			debug("ocr58 %x\n", priv->ocr58);
+		} else if (cmdidx == SD_CMD_SEND_IF_COND) {
+			if ((r1 & 0x7e) == 0)
+				priv->sd_if_cond = 1;
+		} else if (cmdidx == SD_CMD_APP_SEND_OP_COND ||
+			   cmdidx == MMC_CMD_SEND_OP_COND) {
+			if (r1 & 0x04)
+				ret = TIMEOUT;
+			else {
+				if (r1 & 0x01)
+					cmd->response[0] = priv->ocr58;
+				else {
+					mmc_spi_deactivate(mmc);
+					r1 = mmc_spi_sendcmd(mmc, 58, 0);
+					spi_xfer(priv->slave, 4 * 8,
+						 NULL, resp, 0);
+					priv->ocr58 = rswab(resp);
+					debug("ocr58 %x\n", priv->ocr58);
+					cmd->response[0] = priv->ocr58;
+				}
+			}
+		} else {
+			cmd->response[0] = 0;
+			if (r1 & 0x7e)
+				cmd->response[0] |= (1 << 19);
+			if (r1 & 0x40)
+				cmd->response[0] |= (1 << 31);
+			if (r1 & 0x20)
+				cmd->response[0] |= (1 << 30);
+			if (r1 & 0x10)
+				cmd->response[0] |= (1 << 28);
+			if (r1 & 0x08)
+				cmd->response[0] |= (1 << 23);
+			if (r1 & 0x04)
+				cmd->response[0] |= (1 << 22);
+			if (r1 & 0x02)
+				cmd->response[0] |= (1 << 13);
+		}
+	}
+	if (data) {
+		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) {
+			if (cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)
+				r1 = mmc_spi_writeblock(mmc, data->src,
+					data->blocks, data->blocksize);
+			else
+				r1 = mmc_spi_writedata(mmc, data->src,
+					data->blocks, data->blocksize);
+		}
+	}
+done:
+	mmc_spi_deactivate(mmc);
+	return ret;
+}
+
+static void mmc_spi_set_ios(struct mmc *mmc)
+{
+	debug("%s:\n", __func__);
+}
+
+static int mmc_spi_init_p(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	debug("%s:\n", __func__);
+	if (!priv->slave) {
+		debug("%s: setup %u:%u %u %u\n", __func__,
+		      priv->bus, priv->cs,
+		      priv->speed, priv->mode);
+		priv->slave = spi_setup_slave(priv->bus, priv->cs,
+					      priv->speed, priv->mode);
+		if (!priv->slave) {
+			debug("%s: failed to setup spi slave\n", __func__);
+			return -ENOMEM;
+		}
+	}
+	priv->sd_if_cond = 0;
+	priv->ocr58 = 0;
+	spi_claim_bus(priv->slave);
+	/* power on initialization */
+	spi_xfer(priv->slave, 39 * 8, NULL, NULL,
+		 SPI_XFER_BEGIN | SPI_XFER_END);
+	spi_xfer(priv->slave, 18 * 8, NULL, NULL, 0);
+	return 0;
+}
+
+struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
+{
+	struct mmc *mmc;
+	struct mmc_spi_priv *priv;
+
+	mmc = malloc(sizeof(*mmc));
+	if (!mmc)
+		return NULL;
+	priv = malloc(sizeof(*priv));
+	if (!priv) {
+		free(mmc);
+		return NULL;
+	}
+	mmc->priv = priv;
+	priv->bus = bus;
+	priv->cs = cs;
+	priv->speed = speed;
+	priv->mode = mode;
+	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 = 0;
+
+	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+	mmc->f_max = speed;
+	mmc->f_min = mmc->f_max >> 9;
+	mmc->block_dev.part_type = PART_TYPE_DOS;
+
+	mmc_register(mmc);
+
+	return mmc;
+}
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/include/mmc_spi.h b/include/mmc_spi.h
new file mode 100644
index 0000000..fbfa1ce
--- /dev/null
+++ b/include/mmc_spi.h
@@ -0,0 +1,27 @@
+/*
+ * generic mmc spi driver
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _MMC_SPI_H_
+#define _MMC_SPI_H_
+
+#include <spi.h>
+#include <mmc.h>
+#include <linux/types.h>
+
+struct mmc_spi_priv {
+	struct spi_slave *slave;
+	uint bus;
+	uint cs;
+	uint speed;
+	uint mode;
+	uint ocr58;
+	uint sd_if_cond:1;
+};
+
+struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
+
+#endif
diff --git a/lib/Makefile b/lib/Makefile
index c45f07c..aef2893 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;
+}
-- 
1.6.6.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH v5] mmc: add generic mmc spi driver
  2010-04-28  6:00 ` [U-Boot] [PATCH v5] " Thomas Chou
@ 2010-04-28 15:21   ` Andy Fleming
  2010-04-29  5:52     ` Thomas Chou
  2010-04-29 14:51     ` Thomas Chou
  0 siblings, 2 replies; 33+ messages in thread
From: Andy Fleming @ 2010-04-28 15:21 UTC (permalink / raw)
  To: u-boot

On Wed, Apr 28, 2010 at 1:00 AM, Thomas Chou <thomas@wytron.com.tw> wrote:
> This patch supports mmc/sd card with spi interface. It is based on
> the generic mmc framework. It works with SDHC and supports write.
>
> The crc7 lib func is merged from linux and used to compute mmc
> command checksum.

This should probably be a separate patch.

>
> There is a subcomamnd "mmc_spi" to setup spi bus and cs at run time.
>
> Signed-off-by: Thomas Chou <thomas@wytron.com.tw>

> +
> +static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
> +{
> + ? ? ? int dev_num = -1;
> + ? ? ? uint bus;
> + ? ? ? uint cs;
> + ? ? ? uint speed;
> + ? ? ? uint mode;
> + ? ? ? char *endp;
> + ? ? ? struct mmc *mmc;
> + ? ? ? struct mmc_spi_priv *priv;
> +
> + ? ? ? do {
> + ? ? ? ? ? ? ? mmc = find_mmc_device(++dev_num);
> + ? ? ? } while (mmc && strcmp(mmc->name, "MMC_SPI"));
> + ? ? ? if (!mmc) {
> + ? ? ? ? ? ? ? printf("Create MMC Device\n");
> + ? ? ? ? ? ? ? mmc = mmc_spi_init(CONFIG_MMC_SPI_BUS,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?CONFIG_MMC_SPI_CS,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?CONFIG_MMC_SPI_SPEED,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?CONFIG_MMC_SPI_MODE);
> + ? ? ? ? ? ? ? if (!mmc) {
> + ? ? ? ? ? ? ? ? ? ? ? printf("Failed to create MMC Device\n");
> + ? ? ? ? ? ? ? ? ? ? ? return 1;
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? dev_num = mmc->block_dev.dev;
> + ? ? ? }


I'm not sure I understand the logic behind this code.  The arguments
to the command should be used to either find the already-existing bus,
or to create a new one.  Unless I'm misunderstanding, this searches
for the first MMC_SPI bus, and if it finds it, uses that, otherwise it
creates a new one with the values specified in the config file.  Then
it parses the command and overwrites the old parameters for the bus
with new ones?  Why?  My instinct would be to create a separate
instance of an MMC_SPI bus for each bus and chip select.  My SPI is
rusty, so maybe chip-select should be configurable on a use-by-use
basis.

Certainly the current code will only use at most one MMC_SPI bus even
if more are created, which seems wrong.


> +
> + ? ? ? priv = mmc->priv;
> + ? ? ? bus = priv->bus;
> + ? ? ? cs = priv->cs;
> + ? ? ? speed = priv->speed;
> + ? ? ? mode = priv->mode;
> +
> + ? ? ? if (argc < 2)
> + ? ? ? ? ? ? ? goto info;
> + ? ? ? 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;
> + ? ? ? }
> +
> + ? ? ? if (bus != priv->bus || cs != priv->cs ||
> + ? ? ? ? ? speed != priv->speed || mode != priv->mode) {
> + ? ? ? ? ? ? ? priv->bus = bus;
> + ? ? ? ? ? ? ? priv->cs = cs;
> + ? ? ? ? ? ? ? priv->speed = speed;
> + ? ? ? ? ? ? ? priv->mode = mode;
> + ? ? ? ? ? ? ? if (priv->slave) {
> + ? ? ? ? ? ? ? ? ? ? ? spi_free_slave(priv->slave);
> + ? ? ? ? ? ? ? ? ? ? ? priv->slave = NULL;
> + ? ? ? ? ? ? ? }
> + ? ? ? }

> diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
> new file mode 100644
> index 0000000..dedcef7
> --- /dev/null
> +++ b/drivers/mmc/mmc_spi.c
> @@ -0,0 +1,318 @@
> +/*
> + * generic mmc spi driver
> + *

> +static uint mmc_spi_sendcmd(struct mmc *mmc, u8 cmdidx, u32 cmdarg)
> +{
> + ? ? ? struct mmc_spi_priv *priv = mmc->priv;
> + ? ? ? u8 cmdo[7];
> + ? ? ? u8 r1;
> + ? ? ? int i;
> + ? ? ? cmdo[0] = 0xff;
> + ? ? ? cmdo[1] = 0x40 + cmdidx;

Use a #define'd constant for 0x40.  Maybe do this:

/* MMC SPI commands start with a start bit "0" and a transmit bit "1" */
#define MMC_SPI_CMD(x) (0x40 | (x & 0x3f))

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(priv->slave, 7 * 8, cmdo, NULL, SPI_XFER_BEGIN);
> + ? ? ? for (i = 0; i < CTOUT; i++) {
> + ? ? ? ? ? ? ? spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
> + ? ? ? ? ? ? ? if ((r1 & 0x80) == 0)

define a constant

> + ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? }
> + ? ? ? debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1);
> + ? ? ? return r1;
> +}
> +
> +static uint mmc_spi_readdata(struct mmc *mmc, char *buf,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u32 bcnt, u32 bsize)
> +{
> + ? ? ? struct mmc_spi_priv *priv = mmc->priv;
> + ? ? ? u8 r1;
> + ? ? ? u8 crc[2];
> + ? ? ? int i;
> + ? ? ? while (bcnt--) {
> + ? ? ? ? ? ? ? for (i = 0; i < RTOUT; i++) {
> + ? ? ? ? ? ? ? ? ? ? ? spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
> + ? ? ? ? ? ? ? ? ? ? ? if (r1 != 0xff)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? debug("%s:tok%d %x\n", __func__, i, r1);
> + ? ? ? ? ? ? ? if (r1 == 0xfe) {
> + ? ? ? ? ? ? ? ? ? ? ? spi_xfer(priv->slave, bsize * 8, NULL, buf, 0);
> + ? ? ? ? ? ? ? ? ? ? ? buf += bsize;
> + ? ? ? ? ? ? ? ? ? ? ? spi_xfer(priv->slave, 2 * 8, NULL, crc, 0);
> + ? ? ? ? ? ? ? ? ? ? ? r1 = 0;
> + ? ? ? ? ? ? ? } else
> + ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? }
> + ? ? ? return r1;


Why not check the CRC to make sure your data is correct?


> +}
> +
> +static uint mmc_spi_writedata(struct mmc *mmc, const char *buf,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u32 bcnt, u32 bsize)
> +{
> + ? ? ? struct mmc_spi_priv *priv = mmc->priv;
> + ? ? ? u8 r1;
> + ? ? ? u8 tok[2] = { 0xff, 0xfe };
> + ? ? ? u8 crc[2];
> + ? ? ? int i;
> + ? ? ? while (bcnt--) {
> + ? ? ? ? ? ? ? spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
> + ? ? ? ? ? ? ? spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
> + ? ? ? ? ? ? ? buf += bsize;
> + ? ? ? ? ? ? ? spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
> + ? ? ? ? ? ? ? spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
> + ? ? ? ? ? ? ? if (r1 == 0x05) {

Either define a named constant for 0x05, or put a comment describing
what's happening.  Isn't this technically a data response, rather than
r1?  Also,

> + ? ? ? ? ? ? ? ? ? ? ? for (i = 0; i < WTOUT; i++) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (r1 == 0xff) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? r1 = 0;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? ? ? ? ? if (i == WTOUT) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? debug("%s:wtout %x\n", __func__, r1);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? r1 = 0x04;

A constant for this would be good, too.

> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? } else
> + ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? }
> + ? ? ? return r1;
> +}
> +
> +static uint mmc_spi_writeblock(struct mmc *mmc, const char *buf,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? u32 bcnt, u32 bsize)
> +{
> + ? ? ? struct mmc_spi_priv *priv = mmc->priv;
> + ? ? ? u8 r1;
> + ? ? ? u8 tok[2] = { 0xff, 0xfc };
> + ? ? ? u8 stop[2] = { 0xff, 0xfd };


Constants for 0xfc and 0xfd, please.


> + ? ? ? u8 crc[2];
> + ? ? ? int i;
> + ? ? ? while (bcnt--) {
> + ? ? ? ? ? ? ? spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
> + ? ? ? ? ? ? ? spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
> + ? ? ? ? ? ? ? buf += bsize;
> + ? ? ? ? ? ? ? spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
> + ? ? ? ? ? ? ? spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
> + ? ? ? ? ? ? ? if (r1 == 0x05) {
> + ? ? ? ? ? ? ? ? ? ? ? for (i = 0; i < WTOUT; i++) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (r1 == 0xff) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? r1 = 0;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? ? ? ? ? if (i == WTOUT) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? debug("%s:wtout %x\n", __func__, r1);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? r1 = 0x04;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> + ? ? ? if (bcnt == 0 && r1 == 0) {
> + ? ? ? ? ? ? ? spi_xfer(priv->slave, 2 * 8, stop, NULL, 0);
> + ? ? ? ? ? ? ? for (i = 0; i < WTOUT; i++) {
> + ? ? ? ? ? ? ? ? ? ? ? spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
> + ? ? ? ? ? ? ? ? ? ? ? if (r1 == 0xff) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? r1 = 0;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? if (i == WTOUT) {
> + ? ? ? ? ? ? ? ? ? ? ? debug("%s:wtout %x\n", __func__, r1);
> + ? ? ? ? ? ? ? ? ? ? ? r1 = 0x04;
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> + ? ? ? return r1;
> +}
> +
> +static inline uint rswab(u8 *d)

Did you mean "swap", here?

> +{
> + ? ? ? return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
> +}
> +
> +static inline void mmc_spi_deactivate(struct mmc *mmc)
> +{
> + ? ? ? struct mmc_spi_priv *priv = mmc->priv;
> + ? ? ? spi_xfer(priv->slave, 1 * 8, NULL, NULL, SPI_XFER_END);
> + ? ? ? spi_xfer(priv->slave, 1 * 8, NULL, NULL, 0);
> +}
> +
> +static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
> + ? ? ? ? ? ? ? struct mmc_data *data)
> +{
> + ? ? ? struct mmc_spi_priv *priv = mmc->priv;
> + ? ? ? u8 r1;
> + ? ? ? ushort cmdidx = cmd->cmdidx;
> + ? ? ? uint cmdarg = cmd->cmdarg;
> + ? ? ? u8 resp[16];
> + ? ? ? int i;
> + ? ? ? int ret = 0;
> + ? ? ? debug("%s:cmd%d %x %x %x\n", __func__,
> + ? ? ? ? ? ? cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags);
> + ? ? ? if (cmdidx == MMC_CMD_ALL_SEND_CID)
> + ? ? ? ? ? ? ? cmdidx = MMC_CMD_SEND_CID;
> + ? ? ? r1 = mmc_spi_sendcmd(mmc, cmdidx, cmdarg);
> + ? ? ? if (r1 == 0xff) {
> + ? ? ? ? ? ? ? ret = NO_CARD_ERR;
> + ? ? ? ? ? ? ? goto done;
> + ? ? ? } else if (cmd->resp_type == MMC_RSP_R2) {
> + ? ? ? ? ? ? ? r1 = mmc_spi_readdata(mmc, resp, 1, 16);
> + ? ? ? ? ? ? ? for (i = 0; i < 4; i++)
> + ? ? ? ? ? ? ? ? ? ? ? cmd->response[i] = rswab(resp + i * 4);
> + ? ? ? ? ? ? ? debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1],
> + ? ? ? ? ? ? ? ? ? ? cmd->response[2], cmd->response[3]);
> + ? ? ? } else {
> + ? ? ? ? ? ? ? if (!data) {
> + ? ? ? ? ? ? ? ? ? ? ? spi_xfer(priv->slave, 4 * 8, NULL, resp, 0);
> + ? ? ? ? ? ? ? ? ? ? ? cmd->response[0] = rswab(resp);
> + ? ? ? ? ? ? ? ? ? ? ? debug("r32 %x\n", cmd->response[0]);
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? if (cmdidx == MMC_CMD_GO_IDLE_STATE) {
> + ? ? ? ? ? ? ? ? ? ? ? mmc_spi_deactivate(mmc);
> + ? ? ? ? ? ? ? ? ? ? ? r1 = mmc_spi_sendcmd(mmc, 58, 0);

Name "58", please.

> + ? ? ? ? ? ? ? ? ? ? ? spi_xfer(priv->slave, 4 * 8,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?NULL, resp, 0);
> + ? ? ? ? ? ? ? ? ? ? ? priv->ocr58 = rswab(resp) & ~OCR_BUSY;
> + ? ? ? ? ? ? ? ? ? ? ? debug("ocr58 %x\n", priv->ocr58);
> + ? ? ? ? ? ? ? } else if (cmdidx == SD_CMD_SEND_IF_COND) {
> + ? ? ? ? ? ? ? ? ? ? ? if ((r1 & 0x7e) == 0)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? priv->sd_if_cond = 1;
> + ? ? ? ? ? ? ? } else if (cmdidx == SD_CMD_APP_SEND_OP_COND ||
> + ? ? ? ? ? ? ? ? ? ? ? ? ?cmdidx == MMC_CMD_SEND_OP_COND) {
> + ? ? ? ? ? ? ? ? ? ? ? if (r1 & 0x04)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ret = TIMEOUT;
> + ? ? ? ? ? ? ? ? ? ? ? else {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (r1 & 0x01)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cmd->response[0] = priv->ocr58;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? else {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mmc_spi_deactivate(mmc);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? r1 = mmc_spi_sendcmd(mmc, 58, 0);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? spi_xfer(priv->slave, 4 * 8,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?NULL, resp, 0);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? priv->ocr58 = rswab(resp);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? debug("ocr58 %x\n", priv->ocr58);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cmd->response[0] = priv->ocr58;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? } else {
> + ? ? ? ? ? ? ? ? ? ? ? cmd->response[0] = 0;
> + ? ? ? ? ? ? ? ? ? ? ? if (r1 & 0x7e)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cmd->response[0] |= (1 << 19);
> + ? ? ? ? ? ? ? ? ? ? ? if (r1 & 0x40)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cmd->response[0] |= (1 << 31);
> + ? ? ? ? ? ? ? ? ? ? ? if (r1 & 0x20)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cmd->response[0] |= (1 << 30);
> + ? ? ? ? ? ? ? ? ? ? ? if (r1 & 0x10)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cmd->response[0] |= (1 << 28);
> + ? ? ? ? ? ? ? ? ? ? ? if (r1 & 0x08)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cmd->response[0] |= (1 << 23);
> + ? ? ? ? ? ? ? ? ? ? ? if (r1 & 0x04)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cmd->response[0] |= (1 << 22);
> + ? ? ? ? ? ? ? ? ? ? ? if (r1 & 0x02)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cmd->response[0] |= (1 << 13);
> + ? ? ? ? ? ? ? }


Hmmm....  I'm not entirely sure this is the way to go.  If I'm reading
this correctly, this function is converting between the standard SD
protocol, and the SPI version of the protocol.  It might be better to
make mmc.c aware of which protocol it is using, so it a) doesn't issue
commands that the SPI card doesn't understand, and b) can properly
interpret responses.



> + ? ? ? }
> + ? ? ? if (data) {
> + ? ? ? ? ? ? ? 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) {
> + ? ? ? ? ? ? ? ? ? ? ? if (cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? r1 = mmc_spi_writeblock(mmc, data->src,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? data->blocks, data->blocksize);
> + ? ? ? ? ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? r1 = mmc_spi_writedata(mmc, data->src,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? data->blocks, data->blocksize);
> + ? ? ? ? ? ? ? }


Why not check r1 to make sure your writes actually succeeded?


> +struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
> +{
> + ? ? ? struct mmc *mmc;
> + ? ? ? struct mmc_spi_priv *priv;
> +
> + ? ? ? mmc = malloc(sizeof(*mmc));
> + ? ? ? if (!mmc)
> + ? ? ? ? ? ? ? return NULL;
> + ? ? ? priv = malloc(sizeof(*priv));
> + ? ? ? if (!priv) {
> + ? ? ? ? ? ? ? free(mmc);
> + ? ? ? ? ? ? ? return NULL;
> + ? ? ? }
> + ? ? ? mmc->priv = priv;
> + ? ? ? priv->bus = bus;
> + ? ? ? priv->cs = cs;
> + ? ? ? priv->speed = speed;
> + ? ? ? priv->mode = mode;
> + ? ? ? 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 = 0;
> +
> + ? ? ? mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;

Is this part of the SPI spec?  If so, this is fine, otherwise, we need
to get the voltage information from the SPI bus, somehow.

> + ? ? ? mmc->f_max = speed;
> + ? ? ? mmc->f_min = mmc->f_max >> 9;


What's the logic behind f_min being f_max/512?


> + ? ? ? mmc->block_dev.part_type = PART_TYPE_DOS;
> +
> + ? ? ? mmc_register(mmc);
> +
> + ? ? ? return mmc;
> +}

Andy

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH v5] mmc: add generic mmc spi driver
  2010-04-28 15:21   ` Andy Fleming
@ 2010-04-29  5:52     ` Thomas Chou
  2010-04-29 14:51     ` Thomas Chou
  1 sibling, 0 replies; 33+ messages in thread
From: Thomas Chou @ 2010-04-29  5:52 UTC (permalink / raw)
  To: u-boot

Hi Andy,

Thanks for you review.

On 04/28/2010 11:21 PM, Andy Fleming wrote:
>
>> The crc7 lib func is merged from linux and used to compute mmc
>> command checksum.
>>      
> This should probably be a separate patch.
>
>    
OK.
>
>> +
>> +       do {
>> +               mmc = find_mmc_device(++dev_num);
>> +       } while (mmc&&  strcmp(mmc->name, "MMC_SPI"));
>> +       if (!mmc) {
>> +               printf("Create MMC Device\n");
>> +               mmc = mmc_spi_init(CONFIG_MMC_SPI_BUS,
>> +                                  CONFIG_MMC_SPI_CS,
>> +                                  CONFIG_MMC_SPI_SPEED,
>> +                                  CONFIG_MMC_SPI_MODE);
>> +               if (!mmc) {
>> +                       printf("Failed to create MMC Device\n");
>> +                       return 1;
>> +               }
>> +               dev_num = mmc->block_dev.dev;
>> +       }
>>      
> I'm not sure I understand the logic behind this code.  The arguments
> to the command should be used to either find the already-existing bus,
> or to create a new one.  Unless I'm misunderstanding, this searches
> for the first MMC_SPI bus, and if it finds it, uses that, otherwise it
> creates a new one with the values specified in the config file.  Then
> it parses the command and overwrites the old parameters for the bus
> with new ones?  Why?  My instinct would be to create a separate
> instance of an MMC_SPI bus for each bus and chip select.  My SPI is
> rusty, so maybe chip-select should be configurable on a use-by-use
> basis.
>
> Certainly the current code will only use at most one MMC_SPI bus even
> if more are created, which seems wrong.
>    
Though more than one MMC_SPI device can be created for specific bus and 
cs by calling mmc_spi_init() within board_mmc_init() or cpu_mmc_init(). 
This command is used to change the spi bus and chip select on a 
use-by-use basis at run time, so one changeable mmc_spi device (the 
first one if we found) should be enough.


>
>> +       cmdo[1] = 0x40 + cmdidx;
>>      
> Use a #define'd constant for 0x40.  Maybe do this:
>
> /* MMC SPI commands start with a start bit "0" and a transmit bit "1" */
> #define MMC_SPI_CMD(x) (0x40 | (x&  0x3f))
>
> cmdo[1] = MMC_SPI_CMD(cmdidx);
>    
OK. Will define macros for all the cmd, token and response.

> Why not check the CRC to make sure your data is correct?
>    
OK. Will check CRC on data packet.


>> +
>> +static inline uint rswab(u8 *d)
>>      
> Did you mean "swap", here?
>    
It should be swap.

> Hmmm....  I'm not entirely sure this is the way to go.  If I'm reading
> this correctly, this function is converting between the standard SD
> protocol, and the SPI version of the protocol.  It might be better to
> make mmc.c aware of which protocol it is using, so it a) doesn't issue
> commands that the SPI card doesn't understand, and b) can properly
> interpret responses.
>
>    
I have avoided to touch the core mmc.c, so that I won't break other SD 
hosts by accident. Only minor translation of initialization commands and 
status mapping is enough to support SPI protocol.
>    
>> +       }
>> +       if (data) {
>> +               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) {
>> +                       if (cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)
>> +                               r1 = mmc_spi_writeblock(mmc, data->src,
>> +                                       data->blocks, data->blocksize);
>> +                       else
>> +                               r1 = mmc_spi_writedata(mmc, data->src,
>> +                                       data->blocks, data->blocksize);
>> +               }
>>      
> Why not check r1 to make sure your writes actually succeeded?
>
>    
OK. Will check data response.
>    
>> +
>> +       mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
>>      
> Is this part of the SPI spec?  If so, this is fine, otherwise, we need
> to get the voltage information from the SPI bus, somehow.
>
>    
There is no voltage capability from SPI bus. This assumes 3.3V 
interface. Should I include other voltage?

>    
>> +       mmc->f_max = speed;
>> +       mmc->f_min = mmc->f_max>>  9;
>>      
> What's the logic behind f_min being f_max/512?
>
>    
It yields f_min lower than 400KHz to meet the requirement for MMC.

Best regards,
Thomas

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH v5] mmc: add generic mmc spi driver
  2010-04-28 15:21   ` Andy Fleming
  2010-04-29  5:52     ` Thomas Chou
@ 2010-04-29 14:51     ` Thomas Chou
  2010-04-29 19:07       ` Mike Frysinger
  1 sibling, 1 reply; 33+ messages in thread
From: Thomas Chou @ 2010-04-29 14:51 UTC (permalink / raw)
  To: u-boot

On 04/28/2010 11:21 PM, Andy Fleming wrote:
>
>> +static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
>> +{
>> +       int dev_num = -1;
>> +       uint bus;
>> +       uint cs;
>> +       uint speed;
>> +       uint mode;
>> +       char *endp;
>> +       struct mmc *mmc;
>> +       struct mmc_spi_priv *priv;
>> +
>> +       do {
>> +               mmc = find_mmc_device(++dev_num);
>> +       } while (mmc&&  strcmp(mmc->name, "MMC_SPI"));
>> +       if (!mmc) {
>> +               printf("Create MMC Device\n");
>> +               mmc = mmc_spi_init(CONFIG_MMC_SPI_BUS,
>> +                                  CONFIG_MMC_SPI_CS,
>> +                                  CONFIG_MMC_SPI_SPEED,
>> +                                  CONFIG_MMC_SPI_MODE);
>> +               if (!mmc) {
>> +                       printf("Failed to create MMC Device\n");
>> +                       return 1;
>> +               }
>> +               dev_num = mmc->block_dev.dev;
>> +       }
>>      
>
> I'm not sure I understand the logic behind this code.  The arguments
> to the command should be used to either find the already-existing bus,
> or to create a new one.  Unless I'm misunderstanding, this searches
> for the first MMC_SPI bus, and if it finds it, uses that, otherwise it
> creates a new one with the values specified in the config file.  Then
> it parses the command and overwrites the old parameters for the bus
> with new ones?  Why?  My instinct would be to create a separate
> instance of an MMC_SPI bus for each bus and chip select.  My SPI is
> rusty, so maybe chip-select should be configurable on a use-by-use
> basis.
>
> Certainly the current code will only use at most one MMC_SPI bus even
> if more are created, which seems wrong.
>    
Hi Mike,

Could you please give me some suggestion on the mmc_spi subcommand?

1. In v5 patch, I assumed a single changeable mmc_spi device is enough.

2. Andy suggested to create a new mmc_spi device for each bus and cs.

Either way is fine to me. Which one do you prefer?

Best regards,
Thomas

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH v5] mmc: add generic mmc spi driver
  2010-04-29 14:51     ` Thomas Chou
@ 2010-04-29 19:07       ` Mike Frysinger
  2010-04-30  0:16         ` Thomas Chou
  0 siblings, 1 reply; 33+ messages in thread
From: Mike Frysinger @ 2010-04-29 19:07 UTC (permalink / raw)
  To: u-boot

On Thursday 29 April 2010 10:51:03 Thomas Chou wrote:
> 2. Andy suggested to create a new mmc_spi device for each bus and cs.

this is what i was suggesting from the start ... sorry if i wasnt clear
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20100429/448e715f/attachment.pgp 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH v5] mmc: add generic mmc spi driver
  2010-04-29 19:07       ` Mike Frysinger
@ 2010-04-30  0:16         ` Thomas Chou
  0 siblings, 0 replies; 33+ messages in thread
From: Thomas Chou @ 2010-04-30  0:16 UTC (permalink / raw)
  To: u-boot

On 04/30/2010 03:07 AM, Mike Frysinger wrote:
> On Thursday 29 April 2010 10:51:03 Thomas Chou wrote:
>    
>> 2. Andy suggested to create a new mmc_spi device for each bus and cs.
>>      
> this is what i was suggesting from the start ... sorry if i wasnt clear
> -mike
>    
Hi Mike,

Thank you anyway.

Hi Andy,

I will change the command like this,

1. if no args given, show the bus and cs of existing mmc_spi devices.
2. with args given, check for duplicated bus and cs from existing 
devices, then create a new device.

I will need to move the spurious printing of "MMC Device %d not found\n" 
out from find_mmc_device() of mmc.c .

Best regards,
Thomas

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 0/3] mmc: add mmc_spi driver
  2010-04-23  2:53 [U-Boot] [PATCH] mmc: add generic mmc spi driver Thomas Chou
                   ` (4 preceding siblings ...)
  2010-04-28  6:00 ` [U-Boot] [PATCH v5] " Thomas Chou
@ 2010-05-03  0:54 ` Thomas Chou
  2010-05-03  0:54 ` [U-Boot] [PATCH 1/3] lib: add crc7 from Linux Thomas Chou
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 33+ messages in thread
From: Thomas Chou @ 2010-05-03  0:54 UTC (permalink / raw)
  To: u-boot

This patch series adds a mmc_spi driver.

Thomas Chou (3):
  lib: add crc7 from Linux
  mmc: add find_mmc_device_quiet that doesnt print not found message
  mmc: add generic mmc spi driver

 common/Makefile       |    1 +
 common/cmd_mmc_spi.c  |  115 ++++++++++++++
 drivers/mmc/Makefile  |    1 +
 drivers/mmc/mmc.c     |   14 ++-
 drivers/mmc/mmc_spi.c |  393 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/crc7.h  |   14 ++
 include/mmc.h         |    1 +
 include/mmc_spi.h     |   27 ++++
 lib/Makefile          |    1 +
 lib/crc7.c            |   62 ++++++++
 10 files changed, 626 insertions(+), 3 deletions(-)
 create mode 100644 common/cmd_mmc_spi.c
 create mode 100644 drivers/mmc/mmc_spi.c
 create mode 100644 include/linux/crc7.h
 create mode 100644 include/mmc_spi.h
 create mode 100644 lib/crc7.c

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 1/3] lib: add crc7 from Linux
  2010-04-23  2:53 [U-Boot] [PATCH] mmc: add generic mmc spi driver Thomas Chou
                   ` (5 preceding siblings ...)
  2010-05-03  0:54 ` [U-Boot] [PATCH 0/3] mmc: add mmc_spi driver Thomas Chou
@ 2010-05-03  0:54 ` Thomas Chou
  2010-05-03  0:54 ` [U-Boot] [PATCH 2/3] mmc: add find_mmc_device_quiet that doesnt print not found message Thomas Chou
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 33+ messages in thread
From: Thomas Chou @ 2010-05-03  0:54 UTC (permalink / raw)
  To: u-boot

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 c45f07c..aef2893 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;
+}
-- 
1.6.6.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 2/3] mmc: add find_mmc_device_quiet that doesnt print not found message
  2010-04-23  2:53 [U-Boot] [PATCH] mmc: add generic mmc spi driver Thomas Chou
                   ` (6 preceding siblings ...)
  2010-05-03  0:54 ` [U-Boot] [PATCH 1/3] lib: add crc7 from Linux Thomas Chou
@ 2010-05-03  0:54 ` Thomas Chou
  2010-05-06 22:14   ` Wolfgang Denk
  2010-05-03  0:54 ` [U-Boot] [PATCH 3/3 v6] " Thomas Chou
  2010-05-19  4:37 ` [U-Boot] [PATCH 3/3 v8] " Thomas Chou
  9 siblings, 1 reply; 33+ messages in thread
From: Thomas Chou @ 2010-05-03  0:54 UTC (permalink / raw)
  To: u-boot

We need to query mmc devices in mmc_spi subcommand and don't want
the "Device not found" message.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
 drivers/mmc/mmc.c |   14 +++++++++++---
 include/mmc.h     |    1 +
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index cf4ea16..55ab13e 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -60,7 +60,7 @@ int mmc_set_blocklen(struct mmc *mmc, int len)
 	return mmc_send_cmd(mmc, &cmd, NULL);
 }
 
-struct mmc *find_mmc_device(int dev_num)
+struct mmc *find_mmc_device_quiet(int dev_num)
 {
 	struct mmc *m;
 	struct list_head *entry;
@@ -72,11 +72,19 @@ struct mmc *find_mmc_device(int dev_num)
 			return m;
 	}
 
-	printf("MMC Device %d not found\n", dev_num);
-
 	return NULL;
 }
 
+struct mmc *find_mmc_device(int dev_num)
+{
+	struct mmc *m = find_mmc_device_quiet(dev_num);
+
+	if (!m)
+		printf("MMC Device %d not found\n", dev_num);
+
+	return m;
+}
+
 static ulong
 mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src)
 {
diff --git a/include/mmc.h b/include/mmc.h
index 8973bc7..b24b596 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -270,6 +270,7 @@ int mmc_register(struct mmc *mmc);
 int mmc_initialize(bd_t *bis);
 int mmc_init(struct mmc *mmc);
 int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size);
+struct mmc *find_mmc_device_quiet(int dev_num);
 struct mmc *find_mmc_device(int dev_num);
 void print_mmc_devices(char separator);
 int board_mmc_getcd(u8 *cd, struct mmc *mmc);
-- 
1.6.6.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 3/3 v6] mmc: add generic mmc spi driver
  2010-04-23  2:53 [U-Boot] [PATCH] mmc: add generic mmc spi driver Thomas Chou
                   ` (7 preceding siblings ...)
  2010-05-03  0:54 ` [U-Boot] [PATCH 2/3] mmc: add find_mmc_device_quiet that doesnt print not found message Thomas Chou
@ 2010-05-03  0:54 ` Thomas Chou
  2010-05-19  4:37 ` [U-Boot] [PATCH 3/3 v8] " Thomas Chou
  9 siblings, 0 replies; 33+ messages in thread
From: Thomas Chou @ 2010-05-03  0:54 UTC (permalink / raw)
  To: u-boot

This patch supports mmc/sd card with spi interface. It is based on
the generic mmc framework. It works with SDHC and supports write.

The crc checksum on data packet is enabled with the def,
#define CONFIG_MMC_SPI_CRC_ON

There is a subcomamnd "mmc_spi" to setup spi bus and cs at run time.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
v6: add constant macros, crc check on data, per Andy.
v5: remove dev_num limit to search.
v4: change mmc_spi subcommand to search and create new mmc dev.
v3: add mmc_spi_init() proto to mmc_spi.h.
v2: add crc7, use cmd58 to read ocr, add subcommand mmc_spi.

 common/Makefile       |    1 +
 common/cmd_mmc_spi.c  |  115 ++++++++++++++
 drivers/mmc/Makefile  |    1 +
 drivers/mmc/mmc_spi.c |  393 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/mmc_spi.h     |   27 ++++
 5 files changed, 537 insertions(+), 0 deletions(-)
 create mode 100644 common/cmd_mmc_spi.c
 create mode 100644 drivers/mmc/mmc_spi.c
 create mode 100644 include/mmc_spi.h

diff --git a/common/Makefile b/common/Makefile
index dbf7a05..ee23e2f 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..8affa02
--- /dev/null
+++ b/common/cmd_mmc_spi.c
@@ -0,0 +1,115 @@
+/*
+ * 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 <malloc.h>
+#include <mmc.h>
+#include <spi.h>
+#include <mmc_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
+#ifndef CONFIG_MMC_SPI_SPEED
+# define CONFIG_MMC_SPI_SPEED 30000000
+#endif
+#ifndef CONFIG_MMC_SPI_MODE
+# define CONFIG_MMC_SPI_MODE SPI_MODE_3
+#endif
+
+static void print_mmc_spi(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	printf("%s: %d at %u:%u %u %u\n", mmc->name, mmc->block_dev.dev,
+	       priv->bus, priv->cs, priv->speed, priv->mode);
+}
+
+static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	int dev_num = -1;
+	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;
+	struct mmc_spi_priv *priv;
+
+	if (argc < 2) {
+		while (1) {
+			mmc = find_mmc_device_quiet(++dev_num);
+			if (!mmc)
+				break;
+			if (strcmp(mmc->name, "MMC_SPI") == 0)
+				print_mmc_spi(mmc);
+		}
+		return 0;
+	}
+
+	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;
+	}
+
+	while (1) {
+		mmc = find_mmc_device_quiet(++dev_num);
+		if (!mmc)
+			break;
+		if (strcmp(mmc->name, "MMC_SPI") == 0) {
+			priv = mmc->priv;
+			if (priv->bus == bus && priv->cs == cs) {
+				printf("SPI bus and cs in use\n");
+				print_mmc_spi(mmc);
+				return 1;
+			}
+		}
+	}
+
+	printf("Create MMC Device\n");
+	mmc = mmc_spi_init(bus, cs, speed, mode);
+	if (!mmc) {
+		printf("Failed to create MMC Device\n");
+		return 1;
+	}
+	print_mmc_spi(mmc);
+	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 on given\n"
+	"				   SPI bus and chip select\n"
+);
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6fa04b8..02ed329 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -28,6 +28,7 @@ LIB	:= $(obj)libmmc.a
 COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
 COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
+COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
 COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
new file mode 100644
index 0000000..b601941
--- /dev/null
+++ b/drivers/mmc/mmc_spi.c
@@ -0,0 +1,393 @@
+/*
+ * 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 <mmc_spi.h>
+#include <crc.h>
+#include <linux/crc7.h>
+#include <asm/errno.h>
+
+/* Standard MMC commands (4.1)           type  argument     response */
+#define MMC_SPI_READ_OCR         58   /* spi                  spi_R3 */
+#define MMC_SPI_CRC_ON_OFF       59   /* spi  [0:0] flag      spi_R1 */
+
+/*
+ * 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 */
+#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 0x10
+#define RTOUT 0x10000
+#define WTOUT 0x10000
+
+static uint mmc_spi_sendcmd(struct mmc *mmc, u8 cmdidx, u32 cmdarg)
+{
+	struct mmc_spi_priv *priv = 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(priv->slave, 7 * 8, cmdo, NULL, SPI_XFER_BEGIN);
+	for (i = 0; i < CTOUT; i++) {
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if ((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, char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 tok;
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		for (i = 0; i < RTOUT; i++) {
+			spi_xfer(priv->slave, 1 * 8, NULL, &tok, 0);
+			if (tok != 0xff)
+				break;
+		}
+		debug("%s:tok%d %x\n", __func__, i, tok);
+		if (tok == SPI_TOKEN_SINGLE) {
+			spi_xfer(priv->slave, bsize * 8, NULL, buf, 0);
+			spi_xfer(priv->slave, 2 * 8, NULL, crc, 0);
+#ifdef CONFIG_MMC_SPI_CRC_ON
+			if (cyg_crc16((unsigned char *)buf, bsize) !=
+			    ((crc[0] << 8) | crc[1])) {
+				printf("%s: Readdata CRC error\n", mmc->name);
+				tok = R1_SPI_COM_CRC;
+				break;
+			}
+#endif
+			tok = 0;
+		} else {
+			tok = R1_SPI_ERROR;
+			break;
+		}
+		buf += bsize;
+	}
+	return tok;
+}
+
+static uint mmc_spi_writedata(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 tok[2] = {0xff, SPI_TOKEN_SINGLE};
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+#ifdef CONFIG_MMC_SPI_CRC_ON
+		u16 cks;
+		cks = cyg_crc16((unsigned char *)buf, bsize);
+		crc[0] = cks >> 8;
+		crc[1] = cks;
+#endif
+		spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+		spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+		spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) {
+			for (i = 0; i < WTOUT; i++) { /* wait busy */
+				spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+				if (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;
+	}
+	return r1;
+}
+
+static uint mmc_spi_writeblock(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 tok[2] = {0xff, SPI_TOKEN_MULTI_WRITE};
+	u8 stop[2] = {0xff, SPI_TOKEN_STOP_TRAN};
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+#ifdef CONFIG_MMC_SPI_CRC_ON
+		u16 cks;
+		cks = cyg_crc16((unsigned char *)buf, bsize);
+		crc[0] = cks >> 8;
+		crc[1] = cks;
+#endif
+		spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+		spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+		spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) {
+			for (i = 0; i < WTOUT; i++) {
+				spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+				if (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 (bcnt == 0 && r1 == 0) {
+		spi_xfer(priv->slave, 2 * 8, stop, NULL, 0);
+		for (i = 0; i < WTOUT; i++) {
+			spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+			if (r1 == 0xff) {
+				r1 = 0;
+				break;
+			}
+		}
+		if (i == WTOUT) {
+			debug("%s:wtout %x\n", __func__, r1);
+			r1 = R1_SPI_ERROR;
+		}
+	}
+	return r1;
+}
+
+static inline uint rswap(u8 *d)
+{
+	return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static inline void mmc_spi_deactivate(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	spi_xfer(priv->slave, 1 * 8, NULL, NULL, SPI_XFER_END);
+	spi_xfer(priv->slave, 1 * 8, NULL, NULL, 0);
+}
+
+static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
+		struct mmc_data *data)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	ushort cmdidx = cmd->cmdidx;
+	uint cmdarg = cmd->cmdarg;
+	u8 resp[16];
+	int i;
+	int ret = 0;
+	debug("%s:cmd%d %x %x %x\n", __func__,
+	      cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags);
+	if (cmdidx == MMC_CMD_ALL_SEND_CID)
+		cmdidx = MMC_CMD_SEND_CID;
+	r1 = mmc_spi_sendcmd(mmc, cmdidx, cmdarg);
+	if (r1 == 0xff) {
+		ret = NO_CARD_ERR;
+		goto done;
+	} else if (cmd->resp_type == MMC_RSP_R2) {
+		r1 = mmc_spi_readdata(mmc, resp, 1, 16);
+		for (i = 0; i < 4; i++)
+			cmd->response[i] = rswap(resp + i * 4);
+		debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1],
+		      cmd->response[2], cmd->response[3]);
+	} else {
+		if (!data) {
+			spi_xfer(priv->slave, 4 * 8, NULL, resp, 0);
+			cmd->response[0] = rswap(resp);
+			debug("r32 %x\n", cmd->response[0]);
+		}
+		if (cmdidx == MMC_CMD_GO_IDLE_STATE) {
+			mmc_spi_deactivate(mmc);
+			r1 = mmc_spi_sendcmd(mmc, MMC_SPI_READ_OCR, 0);
+			spi_xfer(priv->slave, 4 * 8,
+				 NULL, resp, 0);
+			priv->ocr58 = rswap(resp) & ~OCR_BUSY;
+			debug("ocr58 %x\n", priv->ocr58);
+		} else if (cmdidx == SD_CMD_SEND_IF_COND) {
+			if ((r1 & R1_SPI_ERROR) == 0)
+				priv->sd_if_cond = 1;
+		} else if (cmdidx == SD_CMD_APP_SEND_OP_COND ||
+			   cmdidx == MMC_CMD_SEND_OP_COND) {
+			if (r1 & R1_SPI_ILLEGAL_COMMAND)
+				ret = TIMEOUT;
+			else {
+				if (r1 & R1_SPI_IDLE)
+					cmd->response[0] = priv->ocr58;
+				else {
+					mmc_spi_deactivate(mmc);
+					r1 = mmc_spi_sendcmd(mmc,
+						MMC_SPI_READ_OCR, 0);
+					spi_xfer(priv->slave, 4 * 8,
+						 NULL, resp, 0);
+					priv->ocr58 = rswap(resp);
+					debug("ocr58 %x\n", priv->ocr58);
+					cmd->response[0] = priv->ocr58;
+#ifdef CONFIG_MMC_SPI_CRC_ON
+					mmc_spi_deactivate(mmc);
+					r1 = mmc_spi_sendcmd(mmc,
+						MMC_SPI_CRC_ON_OFF, 1);
+#endif
+				}
+			}
+		} else {
+			cmd->response[0] = 0;
+			if (r1 & R1_SPI_COM_CRC)
+				ret = COMM_ERR;
+		}
+	}
+	if (data) {
+		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) {
+			if (cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)
+				r1 = mmc_spi_writeblock(mmc, data->src,
+					data->blocks, data->blocksize);
+			else
+				r1 = mmc_spi_writedata(mmc, data->src,
+					data->blocks, data->blocksize);
+		}
+		if (r1 & R1_SPI_COM_CRC)
+			ret = COMM_ERR;
+		else if (r1)
+			ret = TIMEOUT;
+	}
+done:
+	mmc_spi_deactivate(mmc);
+	return ret;
+}
+
+static void mmc_spi_set_ios(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	debug("%s: clock %u\n", __func__, mmc->clock);
+	if (mmc->clock && mmc->clock != priv->speed) {
+		/* change clock rate */
+		priv->speed = mmc->clock;
+		spi_claim_bus(priv->slave);
+	}
+}
+
+static int mmc_spi_init_p(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	debug("%s: clock %u\n", __func__, mmc->clock);
+	if (!priv->slave) {
+		debug("%s: setup %u:%u %u %u\n", __func__,
+		      priv->bus, priv->cs,
+		      priv->speed, priv->mode);
+		priv->slave = spi_setup_slave(priv->bus, priv->cs,
+					      priv->speed, priv->mode);
+		if (!priv->slave) {
+			debug("%s: failed to setup spi slave\n", __func__);
+			return -ENOMEM;
+		}
+	}
+	priv->sd_if_cond = 0;
+	priv->ocr58 = 0;
+	/* power on initialization with low speed clock */
+	priv->speed = mmc->f_min;
+	spi_claim_bus(priv->slave);
+	/* finish any pending transfer */
+	spi_xfer(priv->slave, 39 * 8, NULL, NULL,
+		 SPI_XFER_BEGIN | SPI_XFER_END);
+	/* cs deactivated for 100+ clock */
+	spi_xfer(priv->slave, 18 * 8, NULL, NULL, 0);
+	return 0;
+}
+
+struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
+{
+	struct mmc *mmc;
+	struct mmc_spi_priv *priv;
+
+	mmc = malloc(sizeof(*mmc));
+	if (!mmc)
+		return NULL;
+	priv = malloc(sizeof(*priv));
+	if (!priv) {
+		free(mmc);
+		return NULL;
+	}
+	mmc->priv = priv;
+	priv->bus = bus;
+	priv->cs = cs;
+	priv->speed = speed;
+	priv->mode = mode;
+	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 = 0;
+
+	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_spi.h b/include/mmc_spi.h
new file mode 100644
index 0000000..fbfa1ce
--- /dev/null
+++ b/include/mmc_spi.h
@@ -0,0 +1,27 @@
+/*
+ * generic mmc spi driver
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _MMC_SPI_H_
+#define _MMC_SPI_H_
+
+#include <spi.h>
+#include <mmc.h>
+#include <linux/types.h>
+
+struct mmc_spi_priv {
+	struct spi_slave *slave;
+	uint bus;
+	uint cs;
+	uint speed;
+	uint mode;
+	uint ocr58;
+	uint sd_if_cond:1;
+};
+
+struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
+
+#endif
-- 
1.6.6.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 2/3] mmc: add find_mmc_device_quiet that doesnt print not found message
  2010-05-03  0:54 ` [U-Boot] [PATCH 2/3] mmc: add find_mmc_device_quiet that doesnt print not found message Thomas Chou
@ 2010-05-06 22:14   ` Wolfgang Denk
  2010-05-07  0:19     ` Thomas Chou
                       ` (2 more replies)
  0 siblings, 3 replies; 33+ messages in thread
From: Wolfgang Denk @ 2010-05-06 22:14 UTC (permalink / raw)
  To: u-boot

Dear Thomas Chou,

In message <1272848085-10698-3-git-send-email-thomas@wytron.com.tw> you wrote:
> We need to query mmc devices in mmc_spi subcommand and don't want
> the "Device not found" message.

Your subject line is way too long.

> Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
> ---
>  drivers/mmc/mmc.c |   14 +++++++++++---
>  include/mmc.h     |    1 +
>  2 files changed, 12 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
> index cf4ea16..55ab13e 100644
> --- a/drivers/mmc/mmc.c
> +++ b/drivers/mmc/mmc.c
> @@ -60,7 +60,7 @@ int mmc_set_blocklen(struct mmc *mmc, int len)
>  	return mmc_send_cmd(mmc, &cmd, NULL);
>  }
>  
> -struct mmc *find_mmc_device(int dev_num)
> +struct mmc *find_mmc_device_quiet(int dev_num)
>  {
>  	struct mmc *m;
>  	struct list_head *entry;
> @@ -72,11 +72,19 @@ struct mmc *find_mmc_device(int dev_num)
>  			return m;
>  	}
>  
> -	printf("MMC Device %d not found\n", dev_num);
> -
>  	return NULL;
>  }
>  
> +struct mmc *find_mmc_device(int dev_num)
> +{
> +	struct mmc *m = find_mmc_device_quiet(dev_num);
> +
> +	if (!m)
> +		printf("MMC Device %d not found\n", dev_num);
> +
> +	return m;
> +}
> +

Instead of creating a new function please consider using an argument
instead?

Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
While money can't buy happiness, it certainly lets  you  choose  your
own form of misery.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 2/3] mmc: add find_mmc_device_quiet that doesnt print not found message
  2010-05-06 22:14   ` Wolfgang Denk
@ 2010-05-07  0:19     ` Thomas Chou
  2010-05-07  0:51     ` [U-Boot] [PATCH 2/3 v2] mmc: control message print in find_mmc_device Thomas Chou
  2010-05-07  0:51     ` [U-Boot] [PATCH 3/3 v7] mmc: add generic mmc spi driver Thomas Chou
  2 siblings, 0 replies; 33+ messages in thread
From: Thomas Chou @ 2010-05-07  0:19 UTC (permalink / raw)
  To: u-boot

Dear Wolfgang,

On 05/07/2010 06:14 AM, Wolfgang Denk wrote:
> Dear Thomas Chou,
>
> In message<1272848085-10698-3-git-send-email-thomas@wytron.com.tw>  you wrote:
>    
>> We need to query mmc devices in mmc_spi subcommand and don't want
>> the "Device not found" message.
>>      
> Your subject line is way too long.
>    
I will resubmit with a shorter subject.
> Instead of creating a new function please consider using an argument
> instead?
>
>
>    
I will add an argument, int verbose.

Thanks.

Best regards,
Thomas

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 2/3 v2] mmc: control message print in find_mmc_device
  2010-05-06 22:14   ` Wolfgang Denk
  2010-05-07  0:19     ` Thomas Chou
@ 2010-05-07  0:51     ` Thomas Chou
  2010-05-19  4:31       ` Thomas Chou
  2010-05-07  0:51     ` [U-Boot] [PATCH 3/3 v7] mmc: add generic mmc spi driver Thomas Chou
  2 siblings, 1 reply; 33+ messages in thread
From: Thomas Chou @ 2010-05-07  0:51 UTC (permalink / raw)
  To: u-boot

An argument, verbose, is added to enable/disable the "Device not
found" message. Because we need to query mmc devices in mmc_spi
subcommand and don't want the message.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
v2: add an argument instead of add a new func, per Wolfgang.

 common/cmd_mmc.c  |    8 ++++----
 drivers/mmc/mmc.c |   11 ++++++-----
 include/mmc.h     |    2 +-
 3 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
index c67c9cf..9736e5a 100644
--- a/common/cmd_mmc.c
+++ b/common/cmd_mmc.c
@@ -124,7 +124,7 @@ int do_mmcinfo (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
 	else
 		dev_num = simple_strtoul(argv[1], NULL, 0);
 
-	mmc = find_mmc_device(dev_num);
+	mmc = find_mmc_device(dev_num, 1);
 
 	if (mmc) {
 		mmc_init(mmc);
@@ -148,7 +148,7 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
 	case 3:
 		if (strcmp(argv[1], "rescan") == 0) {
 			int dev = simple_strtoul(argv[2], NULL, 10);
-			struct mmc *mmc = find_mmc_device(dev);
+			struct mmc *mmc = find_mmc_device(dev, 1);
 
 			if (!mmc)
 				return 1;
@@ -177,7 +177,7 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
 			u32 cnt = simple_strtoul(argv[5], NULL, 16);
 			u32 n;
 			u32 blk = simple_strtoul(argv[4], NULL, 16);
-			struct mmc *mmc = find_mmc_device(dev);
+			struct mmc *mmc = find_mmc_device(dev, 1);
 
 			if (!mmc)
 				return 1;
@@ -200,7 +200,7 @@ int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
 			void *addr = (void *)simple_strtoul(argv[3], NULL, 16);
 			u32 cnt = simple_strtoul(argv[5], NULL, 16);
 			u32 n;
-			struct mmc *mmc = find_mmc_device(dev);
+			struct mmc *mmc = find_mmc_device(dev, 1);
 
 			int blk = simple_strtoul(argv[4], NULL, 16);
 
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index cf4ea16..42e7937 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -60,7 +60,7 @@ int mmc_set_blocklen(struct mmc *mmc, int len)
 	return mmc_send_cmd(mmc, &cmd, NULL);
 }
 
-struct mmc *find_mmc_device(int dev_num)
+struct mmc *find_mmc_device(int dev_num, int verbose)
 {
 	struct mmc *m;
 	struct list_head *entry;
@@ -72,7 +72,8 @@ struct mmc *find_mmc_device(int dev_num)
 			return m;
 	}
 
-	printf("MMC Device %d not found\n", dev_num);
+	if (verbose)
+		printf("MMC Device %d not found\n", dev_num);
 
 	return NULL;
 }
@@ -84,7 +85,7 @@ mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src)
 	struct mmc_data data;
 	int err;
 	int stoperr = 0;
-	struct mmc *mmc = find_mmc_device(dev_num);
+	struct mmc *mmc = find_mmc_device(dev_num, 1);
 	int blklen;
 
 	if (!mmc)
@@ -214,7 +215,7 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst)
 {
 	int err;
 	int i;
-	struct mmc *mmc = find_mmc_device(dev_num);
+	struct mmc *mmc = find_mmc_device(dev_num, 1);
 
 	if (!mmc)
 		return 0;
@@ -860,7 +861,7 @@ int mmc_register(struct mmc *mmc)
 
 block_dev_desc_t *mmc_get_dev(int dev)
 {
-	struct mmc *mmc = find_mmc_device(dev);
+	struct mmc *mmc = find_mmc_device(dev, 1);
 
 	return mmc ? &mmc->block_dev : NULL;
 }
diff --git a/include/mmc.h b/include/mmc.h
index 8973bc7..0903491 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -270,7 +270,7 @@ int mmc_register(struct mmc *mmc);
 int mmc_initialize(bd_t *bis);
 int mmc_init(struct mmc *mmc);
 int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size);
-struct mmc *find_mmc_device(int dev_num);
+struct mmc *find_mmc_device(int dev_num, int verbose);
 void print_mmc_devices(char separator);
 int board_mmc_getcd(u8 *cd, struct mmc *mmc);
 
-- 
1.6.6.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 3/3 v7] mmc: add generic mmc spi driver
  2010-05-06 22:14   ` Wolfgang Denk
  2010-05-07  0:19     ` Thomas Chou
  2010-05-07  0:51     ` [U-Boot] [PATCH 2/3 v2] mmc: control message print in find_mmc_device Thomas Chou
@ 2010-05-07  0:51     ` Thomas Chou
  2 siblings, 0 replies; 33+ messages in thread
From: Thomas Chou @ 2010-05-07  0:51 UTC (permalink / raw)
  To: u-boot

This patch supports mmc/sd card with spi interface. It is based on
the generic mmc framework. It works with SDHC and supports write.

The crc checksum on data packet is enabled with the def,
#define CONFIG_MMC_SPI_CRC_ON

There is a subcomamnd "mmc_spi" to setup spi bus and cs at run time.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
v7: use find_mmc_device(dev, verbose), per Wolfgang.
v6: add constant macros, crc check on data, per Andy.
v5: remove dev_num limit to search.
v4: change mmc_spi subcommand to search and create new mmc dev.
v3: add mmc_spi_init() proto to mmc_spi.h.
v2: add crc7, use cmd58 to read ocr, add subcommand mmc_spi.

 common/Makefile       |    1 +
 common/cmd_mmc_spi.c  |  115 ++++++++++++++
 drivers/mmc/Makefile  |    1 +
 drivers/mmc/mmc_spi.c |  393 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/mmc_spi.h     |   27 ++++
 5 files changed, 537 insertions(+), 0 deletions(-)
 create mode 100644 common/cmd_mmc_spi.c
 create mode 100644 drivers/mmc/mmc_spi.c
 create mode 100644 include/mmc_spi.h

diff --git a/common/Makefile b/common/Makefile
index dbf7a05..ee23e2f 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..cb33457
--- /dev/null
+++ b/common/cmd_mmc_spi.c
@@ -0,0 +1,115 @@
+/*
+ * 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 <malloc.h>
+#include <mmc.h>
+#include <spi.h>
+#include <mmc_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
+#ifndef CONFIG_MMC_SPI_SPEED
+# define CONFIG_MMC_SPI_SPEED 30000000
+#endif
+#ifndef CONFIG_MMC_SPI_MODE
+# define CONFIG_MMC_SPI_MODE SPI_MODE_3
+#endif
+
+static void print_mmc_spi(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	printf("%s: %d at %u:%u %u %u\n", mmc->name, mmc->block_dev.dev,
+	       priv->bus, priv->cs, priv->speed, priv->mode);
+}
+
+static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+	int dev_num = -1;
+	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;
+	struct mmc_spi_priv *priv;
+
+	if (argc < 2) {
+		while (1) {
+			mmc = find_mmc_device(++dev_num, 0);
+			if (!mmc)
+				break;
+			if (strcmp(mmc->name, "MMC_SPI") == 0)
+				print_mmc_spi(mmc);
+		}
+		return 0;
+	}
+
+	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;
+	}
+
+	while (1) {
+		mmc = find_mmc_device(++dev_num, 0);
+		if (!mmc)
+			break;
+		if (strcmp(mmc->name, "MMC_SPI") == 0) {
+			priv = mmc->priv;
+			if (priv->bus == bus && priv->cs == cs) {
+				printf("SPI bus and cs in use\n");
+				print_mmc_spi(mmc);
+				return 1;
+			}
+		}
+	}
+
+	printf("Create MMC Device\n");
+	mmc = mmc_spi_init(bus, cs, speed, mode);
+	if (!mmc) {
+		printf("Failed to create MMC Device\n");
+		return 1;
+	}
+	print_mmc_spi(mmc);
+	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 on given\n"
+	"				   SPI bus and chip select\n"
+);
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6fa04b8..02ed329 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -28,6 +28,7 @@ LIB	:= $(obj)libmmc.a
 COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
 COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
+COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
 COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
new file mode 100644
index 0000000..27d9269
--- /dev/null
+++ b/drivers/mmc/mmc_spi.c
@@ -0,0 +1,393 @@
+/*
+ * 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 <mmc_spi.h>
+#include <crc.h>
+#include <linux/crc7.h>
+#include <asm/errno.h>
+
+/* Standard MMC commands (4.1)           type  argument     response */
+#define MMC_SPI_READ_OCR         58   /* spi                  spi_R3 */
+#define MMC_SPI_CRC_ON_OFF       59   /* spi  [0:0] flag      spi_R1 */
+
+/*
+ * 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 */
+#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 0x10
+#define RTOUT 0x10000
+#define WTOUT 0x10000
+
+static uint mmc_spi_sendcmd(struct mmc *mmc, u8 cmdidx, u32 cmdarg)
+{
+	struct mmc_spi_priv *priv = 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(priv->slave, 7 * 8, cmdo, NULL, SPI_XFER_BEGIN);
+	for (i = 0; i < CTOUT; i++) {
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if ((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, char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 tok;
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		for (i = 0; i < RTOUT; i++) {
+			spi_xfer(priv->slave, 1 * 8, NULL, &tok, 0);
+			if (tok != 0xff)
+				break;
+		}
+		debug("%s:tok%d %x\n", __func__, i, tok);
+		if (tok == SPI_TOKEN_SINGLE) {
+			spi_xfer(priv->slave, bsize * 8, NULL, buf, 0);
+			spi_xfer(priv->slave, 2 * 8, NULL, crc, 0);
+#ifdef CONFIG_MMC_SPI_CRC_ON
+			if (cyg_crc16((unsigned char *)buf, bsize) !=
+			    ((crc[0] << 8) | crc[1])) {
+				printf("%s: Readdata CRC error\n", mmc->name);
+				tok = R1_SPI_COM_CRC;
+				break;
+			}
+#endif
+			tok = 0;
+		} else {
+			tok = R1_SPI_ERROR;
+			break;
+		}
+		buf += bsize;
+	}
+	return tok;
+}
+
+static uint mmc_spi_writedata(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 tok[2] = {0xff, SPI_TOKEN_SINGLE};
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+#ifdef CONFIG_MMC_SPI_CRC_ON
+		u16 cks;
+		cks = cyg_crc16((unsigned char *)buf, bsize);
+		crc[0] = cks >> 8;
+		crc[1] = cks;
+#endif
+		spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+		spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+		spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) {
+			for (i = 0; i < WTOUT; i++) { /* wait busy */
+				spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+				if (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;
+	}
+	return r1;
+}
+
+static uint mmc_spi_writeblock(struct mmc *mmc, const char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	u8 tok[2] = {0xff, SPI_TOKEN_MULTI_WRITE};
+	u8 stop[2] = {0xff, SPI_TOKEN_STOP_TRAN};
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+#ifdef CONFIG_MMC_SPI_CRC_ON
+		u16 cks;
+		cks = cyg_crc16((unsigned char *)buf, bsize);
+		crc[0] = cks >> 8;
+		crc[1] = cks;
+#endif
+		spi_xfer(priv->slave, 2 * 8, tok, NULL, 0);
+		spi_xfer(priv->slave, bsize * 8, buf, NULL, 0);
+		spi_xfer(priv->slave, 2 * 8, crc, NULL, 0);
+		spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+		if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) {
+			for (i = 0; i < WTOUT; i++) {
+				spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+				if (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 (bcnt == 0 && r1 == 0) {
+		spi_xfer(priv->slave, 2 * 8, stop, NULL, 0);
+		for (i = 0; i < WTOUT; i++) {
+			spi_xfer(priv->slave, 1 * 8, NULL, &r1, 0);
+			if (r1 == 0xff) {
+				r1 = 0;
+				break;
+			}
+		}
+		if (i == WTOUT) {
+			debug("%s:wtout %x\n", __func__, r1);
+			r1 = R1_SPI_ERROR;
+		}
+	}
+	return r1;
+}
+
+static inline uint rswap(u8 *d)
+{
+	return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static inline void mmc_spi_deactivate(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	spi_xfer(priv->slave, 1 * 8, NULL, NULL, SPI_XFER_END);
+	spi_xfer(priv->slave, 1 * 8, NULL, NULL, 0);
+}
+
+static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
+		struct mmc_data *data)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	u8 r1;
+	ushort cmdidx = cmd->cmdidx;
+	uint cmdarg = cmd->cmdarg;
+	u8 resp[16];
+	int i;
+	int ret = 0;
+	debug("%s:cmd%d %x %x %x\n", __func__,
+	      cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags);
+	if (cmdidx == MMC_CMD_ALL_SEND_CID)
+		cmdidx = MMC_CMD_SEND_CID;
+	r1 = mmc_spi_sendcmd(mmc, cmdidx, cmdarg);
+	if (r1 == 0xff) {
+		ret = NO_CARD_ERR;
+		goto done;
+	} else if (cmd->resp_type == MMC_RSP_R2) {
+		r1 = mmc_spi_readdata(mmc, resp, 1, 16);
+		for (i = 0; i < 4; i++)
+			cmd->response[i] = rswap(resp + i * 4);
+		debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1],
+		      cmd->response[2], cmd->response[3]);
+	} else {
+		if (!data) {
+			spi_xfer(priv->slave, 4 * 8, NULL, resp, 0);
+			cmd->response[0] = rswap(resp);
+			debug("r32 %x\n", cmd->response[0]);
+		}
+		if (cmdidx == MMC_CMD_GO_IDLE_STATE) {
+			mmc_spi_deactivate(mmc);
+			r1 = mmc_spi_sendcmd(mmc, MMC_SPI_READ_OCR, 0);
+			spi_xfer(priv->slave, 4 * 8,
+				 NULL, resp, 0);
+			priv->ocr58 = rswap(resp) & ~OCR_BUSY;
+			debug("ocr58 %x\n", priv->ocr58);
+		} else if (cmdidx == SD_CMD_SEND_IF_COND) {
+			if ((r1 & R1_SPI_ERROR) == 0)
+				priv->sd_if_cond = 1;
+		} else if (cmdidx == SD_CMD_APP_SEND_OP_COND ||
+			   cmdidx == MMC_CMD_SEND_OP_COND) {
+			if (r1 & R1_SPI_ILLEGAL_COMMAND)
+				ret = TIMEOUT;
+			else {
+				if (r1 & R1_SPI_IDLE)
+					cmd->response[0] = priv->ocr58;
+				else {
+					mmc_spi_deactivate(mmc);
+					r1 = mmc_spi_sendcmd(mmc,
+						MMC_SPI_READ_OCR, 0);
+					spi_xfer(priv->slave, 4 * 8,
+						 NULL, resp, 0);
+					priv->ocr58 = rswap(resp);
+					debug("ocr58 %x\n", priv->ocr58);
+					cmd->response[0] = priv->ocr58;
+#ifdef CONFIG_MMC_SPI_CRC_ON
+					mmc_spi_deactivate(mmc);
+					r1 = mmc_spi_sendcmd(mmc,
+						MMC_SPI_CRC_ON_OFF, 1);
+#endif
+				}
+			}
+		} else {
+			cmd->response[0] = 0;
+			if (r1 & R1_SPI_COM_CRC)
+				ret = COMM_ERR;
+		}
+	}
+	if (data) {
+		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) {
+			if (cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)
+				r1 = mmc_spi_writeblock(mmc, data->src,
+					data->blocks, data->blocksize);
+			else
+				r1 = mmc_spi_writedata(mmc, data->src,
+					data->blocks, data->blocksize);
+		}
+		if (r1 & R1_SPI_COM_CRC)
+			ret = COMM_ERR;
+		else if (r1)
+			ret = TIMEOUT;
+	}
+done:
+	mmc_spi_deactivate(mmc);
+	return ret;
+}
+
+static void mmc_spi_set_ios(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	debug("%s: clock %u\n", __func__, mmc->clock);
+	if (mmc->clock && mmc->clock != priv->speed) {
+		/* change clock rate */
+		priv->speed = mmc->clock;
+		spi_claim_bus(priv->slave);
+	}
+}
+
+static int mmc_spi_init_p(struct mmc *mmc)
+{
+	struct mmc_spi_priv *priv = mmc->priv;
+	debug("%s: clock %u\n", __func__, mmc->clock);
+	if (!priv->slave) {
+		debug("%s: setup %u:%u %u %u\n", __func__,
+		      priv->bus, priv->cs,
+		      priv->speed, priv->mode);
+		priv->slave = spi_setup_slave(priv->bus, priv->cs,
+					      priv->speed, priv->mode);
+		if (!priv->slave) {
+			debug("%s: failed to setup spi slave\n", __func__);
+			return -ENOMEM;
+		}
+	}
+	priv->sd_if_cond = 0;
+	priv->ocr58 = 0;
+	/* power on initialization with low speed clock */
+	priv->speed = mmc->f_min;
+	spi_claim_bus(priv->slave);
+	/* finish any pending transfer */
+	spi_xfer(priv->slave, 39 * 8, NULL, NULL,
+		 SPI_XFER_BEGIN | SPI_XFER_END);
+	/* cs deactivated for 100+ clock */
+	spi_xfer(priv->slave, 18 * 8, NULL, NULL, 0);
+	return 0;
+}
+
+struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
+{
+	struct mmc *mmc;
+	struct mmc_spi_priv *priv;
+
+	mmc = malloc(sizeof(*mmc));
+	if (!mmc)
+		return NULL;
+	priv = malloc(sizeof(*priv));
+	if (!priv) {
+		free(mmc);
+		return NULL;
+	}
+	mmc->priv = priv;
+	priv->bus = bus;
+	priv->cs = cs;
+	priv->speed = speed;
+	priv->mode = mode;
+	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 = 0;
+
+	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_spi.h b/include/mmc_spi.h
new file mode 100644
index 0000000..fbfa1ce
--- /dev/null
+++ b/include/mmc_spi.h
@@ -0,0 +1,27 @@
+/*
+ * generic mmc spi driver
+ *
+ * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _MMC_SPI_H_
+#define _MMC_SPI_H_
+
+#include <spi.h>
+#include <mmc.h>
+#include <linux/types.h>
+
+struct mmc_spi_priv {
+	struct spi_slave *slave;
+	uint bus;
+	uint cs;
+	uint speed;
+	uint mode;
+	uint ocr58;
+	uint sd_if_cond:1;
+};
+
+struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
+
+#endif
-- 
1.6.6.1

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 2/3 v2] mmc: control message print in find_mmc_device
  2010-05-07  0:51     ` [U-Boot] [PATCH 2/3 v2] mmc: control message print in find_mmc_device Thomas Chou
@ 2010-05-19  4:31       ` Thomas Chou
  0 siblings, 0 replies; 33+ messages in thread
From: Thomas Chou @ 2010-05-19  4:31 UTC (permalink / raw)
  To: u-boot

Thomas Chou wrote:
> An argument, verbose, is added to enable/disable the "Device not
> found" message. Because we need to query mmc devices in mmc_spi
> subcommand and don't want the message.
> 

Please drop this one. Because I reduced the cmd_mmc_spi.c, so that the 
query is not needed.

- Thomas

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 3/3 v8] mmc: add generic mmc spi driver
  2010-04-23  2:53 [U-Boot] [PATCH] mmc: add generic mmc spi driver Thomas Chou
                   ` (8 preceding siblings ...)
  2010-05-03  0:54 ` [U-Boot] [PATCH 3/3 v6] " Thomas Chou
@ 2010-05-19  4:37 ` Thomas Chou
  2010-05-28 22:44   ` Thomas Chou
  2010-07-05  7:40   ` Mike Frysinger
  9 siblings, 2 replies; 33+ messages in thread
From: Thomas Chou @ 2010-05-19  4:37 UTC (permalink / raw)
  To: u-boot

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,
#define CONFIG_MMC_SPI_CRC_ON

There is a subcomamnd "mmc_spi" to setup spi bus and cs at run time.

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
---
v8: make mmc.c core aware of spi protocol, per Andy.
    work with multi blocks read/write.
    reduce cmd_mmc_spi.c, doesnt query to mmc dev list.
v7: use find_mmc_device(dev, verbose), per Wolfgang.
v6: add constant macros, crc check on data, per Andy.
v5: remove dev_num limit to search.
v4: change mmc_spi subcommand to search and create new mmc dev.
v3: add mmc_spi_init() proto to mmc_spi.h.
v2: add crc7, use cmd58 to read ocr, add subcommand mmc_spi.

 common/Makefile       |    1 +
 common/cmd_mmc_spi.c  |   82 ++++++++++++++
 drivers/mmc/Makefile  |    1 +
 drivers/mmc/mmc.c     |   81 +++++++++++---
 drivers/mmc/mmc_spi.c |  296 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/mmc.h         |    8 ++
 6 files changed, 451 insertions(+), 18 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 2c37073..55beac5 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -119,6 +119,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..13933b0
--- /dev/null
+++ b/common/cmd_mmc_spi.c
@@ -0,0 +1,82 @@
+/*
+ * 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
+#ifndef CONFIG_MMC_SPI_SPEED
+# define CONFIG_MMC_SPI_SPEED 25000000
+#endif
+#ifndef CONFIG_MMC_SPI_MODE
+# define CONFIG_MMC_SPI_MODE SPI_MODE_3
+#endif
+
+static int do_mmc_spi(cmd_tbl_t *cmdtp, int flag, int argc, char *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 %u %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 on given\n"
+	"				   SPI bus and chip select\n"
+);
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6fa04b8..02ed329 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -28,6 +28,7 @@ LIB	:= $(obj)libmmc.a
 COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
 COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
+COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
 COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index aefd721..0e819f8 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -283,7 +283,10 @@ static int mmc_write_blocks(struct mmc *mmc, const char *src, uint start,
 		return err;
 	}
 
-	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;
@@ -458,6 +461,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);
@@ -492,6 +507,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];
 
@@ -776,8 +803,22 @@ int mmc_startup(struct mmc *mmc)
 	u64 cmult, csize;
 	struct mmc_cmd cmd;
 
+#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;
@@ -794,18 +835,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;
@@ -881,14 +924,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))
 		err = sd_change_freq(mmc);
diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c
new file mode 100644
index 0000000..d675c7f
--- /dev/null
+++ b/drivers/mmc/mmc_spi.c
@@ -0,0 +1,296 @@
+/*
+ * 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>
+
+/*
+ * 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 */
+#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 */
+#define MMC_SPI_MAX_BLOCKS 65536
+
+/* timeout value */
+#define CTOUT 8
+#define RTOUT 3000000 /* 1 sec */
+#define WTOUT 3000000 /* 1 sec */
+
+static uint mmc_spi_sendcmd(struct mmc *mmc, u8 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, 7 * 8, cmdo, NULL, SPI_XFER_BEGIN);
+	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, char *buf,
+				u32 bcnt, u32 bsize)
+{
+	struct spi_slave *spi = mmc->priv;
+	u8 tok;
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+		for (i = 0; i < RTOUT; i++) {
+			spi_xfer(spi, 1 * 8, NULL, &tok, 0);
+			if (tok != 0xff) /* data token */
+				break;
+		}
+		debug("%s:tok%d %x\n", __func__, i, tok);
+		if (tok == 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 (cyg_crc16((unsigned char *)buf, bsize) !=
+			    ((crc[0] << 8) | crc[1])) {
+				debug("%s: CRC error\n", mmc->name);
+				tok = R1_SPI_COM_CRC;
+				break;
+			}
+#endif
+			tok = 0;
+		} else {
+			tok = R1_SPI_ERROR;
+			break;
+		}
+		buf += bsize;
+	}
+	return tok;
+}
+
+static uint mmc_spi_writedata(struct mmc *mmc, const char *buf,
+			      u32 bcnt, u32 bsize, int multi)
+{
+	struct spi_slave *spi = mmc->priv;
+	u8 r1;
+	const u8 toks[2] = {0xff, SPI_TOKEN_SINGLE};
+	const u8 tokm[2] = {0xff, SPI_TOKEN_MULTI_WRITE};
+	const u8 stop[2] = {0xff, SPI_TOKEN_STOP_TRAN};
+	u8 crc[2];
+	int i;
+	while (bcnt--) {
+#ifdef CONFIG_MMC_SPI_CRC_ON
+		u16 cks;
+		cks = cyg_crc16((unsigned char *)buf, bsize);
+		crc[0] = cks >> 8;
+		crc[1] = cks;
+#endif
+		spi_xfer(spi, 2 * 8, multi ? tokm : toks, 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 */
+		spi_xfer(spi, 2 * 8, stop, 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;
+}
+
+/* rswap: swap spi byte stream to mmc response */
+static inline uint rswap(u8 *d)
+{
+	return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+static inline void mmc_spi_deactivate(struct mmc *mmc)
+{
+	struct spi_slave *spi = mmc->priv;
+	spi_xfer(spi, 1 * 8, NULL, NULL, SPI_XFER_END);
+	spi_xfer(spi, 1 * 8, NULL, NULL, 0);
+}
+
+static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd,
+		struct mmc_data *data)
+{
+	struct spi_slave *spi = mmc->priv;
+	u8 r1;
+	ushort cmdidx = cmd->cmdidx;
+	uint cmdarg = cmd->cmdarg;
+	u8 resp[16];
+	int i;
+	int ret = 0;
+	debug("%s:cmd%d %x %x %x\n", __func__,
+	      cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags);
+	r1 = mmc_spi_sendcmd(mmc, cmdidx, 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, (char *)resp, 1, 16);
+		for (i = 0; i < 4; i++)
+			cmd->response[i] = rswap(resp + i * 4);
+		debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1],
+		      cmd->response[2], cmd->response[3]);
+	} else if (!data) {
+		switch (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, resp, 0);
+			cmd->response[0] = rswap(resp);
+			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,
+				(cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK));
+		if (r1 & R1_SPI_COM_CRC)
+			ret = COMM_ERR;
+		else if (r1) /* other errors */
+			ret = TIMEOUT;
+	}
+done:
+	mmc_spi_deactivate(mmc);
+	return ret;
+}
+
+static void mmc_spi_set_ios(struct mmc *mmc)
+{
+	debug("%s: clock %u\n", __func__, mmc->clock);
+	/* the current spi framework does not support change clock freq */
+}
+
+static int mmc_spi_init_p(struct mmc *mmc)
+{
+	struct spi_slave *spi = mmc->priv;
+	debug("%s: clock %u\n", __func__, mmc->clock);
+	spi_claim_bus(spi);
+	/* finish any pending transfer */
+	spi_xfer(spi, 39 * 8, NULL, NULL,
+		 SPI_XFER_BEGIN | SPI_XFER_END);
+	/* cs deactivated for 100+ clock */
+	spi_xfer(spi, 18 * 8, NULL, NULL, 0);
+	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;
+	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->b_max = MMC_SPI_MAX_BLOCKS;
+
+	mmc_register(mmc);
+
+	return mmc;
+}
diff --git a/include/mmc.h b/include/mmc.h
index 04c7eaf..0422e22 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -44,6 +44,9 @@
 #define MMC_MODE_HS_52MHz	0x010
 #define MMC_MODE_4BIT		0x100
 #define MMC_MODE_8BIT		0x200
+#define MMC_MODE_SPI		0x400
+
+#define mmc_host_is_spi(mmc)	((mmc)->host_caps & MMC_MODE_SPI)
 
 #define SD_DATA_4BIT	0x00040000
 
@@ -75,6 +78,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
@@ -277,6 +282,9 @@ struct mmc *find_mmc_device(int dev_num);
 void print_mmc_devices(char separator);
 int board_mmc_getcd(u8 *cd, struct mmc *mmc);
 
+/* Driver initialization prototypes */
+struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode);
+
 #ifndef CONFIG_GENERIC_MMC
 int mmc_legacy_init(int verbose);
 #endif
-- 
1.7.1.86.g0e460

^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 3/3 v8] mmc: add generic mmc spi driver
  2010-05-19  4:37 ` [U-Boot] [PATCH 3/3 v8] " Thomas Chou
@ 2010-05-28 22:44   ` Thomas Chou
  2010-07-05  7:40   ` Mike Frysinger
  1 sibling, 0 replies; 33+ messages in thread
From: Thomas Chou @ 2010-05-28 22:44 UTC (permalink / raw)
  To: u-boot

Hi Andy,

Would you please review these patches?

05/03 [PATCH 1/3] lib: add crc7 from Linux
05/19 [PATCH 3/3 v8] mmc: add generic mmc spi driver

Best regards,
Thomas

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 3/3 v8] mmc: add generic mmc spi driver
  2010-05-19  4:37 ` [U-Boot] [PATCH 3/3 v8] " Thomas Chou
  2010-05-28 22:44   ` Thomas Chou
@ 2010-07-05  7:40   ` Mike Frysinger
  2010-07-05 14:22     ` Thomas Chou
  1 sibling, 1 reply; 33+ messages in thread
From: Mike Frysinger @ 2010-07-05  7:40 UTC (permalink / raw)
  To: u-boot

On Wednesday, May 19, 2010 00:37:47 Thomas Chou wrote:
> --- /dev/null
> +++ b/common/cmd_mmc_spi.c
> +	printf("%s: %d at %u:%u %u %u\n", mmc->name, mmc->block_dev.dev,
> +	       bus, cs, speed, mode);

this is a bit terse.  how about prefixing the hz output with like "hz:" and 
the mode with like "mode:" ?

> +U_BOOT_CMD(
> +	mmc_spi,	4,	0,	do_mmc_spi,
> +	"mmc_spi setup",
> +	"[bus:]cs [hz] [mode]	- setup mmc_spi device on given\n"
> +	"				   SPI bus and chip select\n"
> +);

there should be no newline at the end of the help string

> --- /dev/null
> +++ b/drivers/mmc/mmc_spi.c
> +struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
> +{
> +	struct mmc *mmc;
> +	mmc->b_max = MMC_SPI_MAX_BLOCKS;

do you have some local modification ?  i dont see b_max anywhere in 
include/mmc.h ...

unfortunately though, i tried this on my system and it doesnt seem to work.  
using a simple SPI<->MMC card, the old mmc_spi driver works on my board, but 
booting the new u-boot and running the same things shows:

bfin> mmc_spi 4 30000000 3
MMC_SPI: 0 at 0:4 30000000 3

bfin> mmc list
MMC_SPI: 0

bfin> mmcinfo
Card did not respond to voltage select!
Device: MMC_SPI
Manufacturer ID: 0
OEM: 0
Name: Tran Speed: 0
Rd Block Len: 0
MMC version 0.0
High Capacity: No
Capacity: 0
Bus Width: 1-bit

enabling debugging in the driver shows this:
bfin> mmcinfo
mmc_spi_init_p: clock 0
mmc_spi_set_ios: clock 0
mmc_spi_set_ios: clock 400000
mmc_spi_request:cmd0 0 0 0
mmc_spi_sendcmd:cmd0 resp6 1
mmc_spi_request:cmd8 15 1aa 0
mmc_spi_sendcmd:cmd8 resp8 ff
mmc_spi_request:cmd55 15 0 0
mmc_spi_sendcmd:cmd55 resp6 5
mmc_spi_request:cmd0 0 0 0
mmc_spi_sendcmd:cmd0 resp6 1
mmc_spi_request:cmd1 1 40300000 0
mmc_spi_sendcmd:cmd1 resp6 1
mmc_spi_request:cmd1 1 40300000 0
mmc_spi_sendcmd:cmd1 resp6 1
mmc_spi_request:cmd1 1 40300000 0
mmc_spi_sendcmd:cmd1 resp4 1
mmc_spi_request:cmd1 1 40300000 0
mmc_spi_sendcmd:cmd1 resp4 1
<these last 2 lines repeat for a while>
Card did not respond to voltage select!
...
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20100705/d2acad0b/attachment.pgp 

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 3/3 v8] mmc: add generic mmc spi driver
  2010-07-05  7:40   ` Mike Frysinger
@ 2010-07-05 14:22     ` Thomas Chou
  2010-07-05 20:11       ` Mike Frysinger
  0 siblings, 1 reply; 33+ messages in thread
From: Thomas Chou @ 2010-07-05 14:22 UTC (permalink / raw)
  To: u-boot

Hi Mike,

Thank you very much for the review and testing.


On 07/05/2010 03:40 PM, Mike Frysinger wrote:
> On Wednesday, May 19, 2010 00:37:47 Thomas Chou wrote:
>> --- /dev/null
>> +++ b/common/cmd_mmc_spi.c
>> +	printf("%s: %d at %u:%u %u %u\n", mmc->name, mmc->block_dev.dev,
>> +	       bus, cs, speed, mode);
>
> this is a bit terse.  how about prefixing the hz output with like "hz:" and
> the mode with like "mode:" ?

Yes, I will add them.

>
>> +U_BOOT_CMD(
>> +	mmc_spi,	4,	0,	do_mmc_spi,
>> +	"mmc_spi setup",
>> +	"[bus:]cs [hz] [mode]	- setup mmc_spi device on given\n"
>> +	"				   SPI bus and chip select\n"
>> +);
>
> there should be no newline at the end of the help string

OK. I will remove the last newline.

>
>> --- /dev/null
>> +++ b/drivers/mmc/mmc_spi.c
>> +struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
>> +{
>> +	struct mmc *mmc;
>> +	mmc->b_max = MMC_SPI_MAX_BLOCKS;
>
> do you have some local modification ?  i dont see b_max anywhere in
> include/mmc.h ...

Please apply the multi-blocks patches (1,2,3/4) from Alagu on 05/12, as 
Andy said he had applied them.


>
> unfortunately though, i tried this on my system and it doesnt seem to work.
> using a simple SPI<->MMC card, the old mmc_spi driver works on my board, but
> booting the new u-boot and running the same things shows:
>

> mmc_spi_request:cmd1 1 40300000 0
> mmc_spi_sendcmd:cmd1 resp4 1
> <these last 2 lines repeat for a while>
> Card did not respond to voltage select!

It seems the mmc card was not initialized and timed out. Please try 
remove the OCR_HCS in mmc_send_op_cond() of mmc.c temporarily.

	cmd.cmdarg = OCR_HCS | mmc->voltages;
---------------------^^^^^^^^^

Best regards,
Thomas

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [U-Boot] [PATCH 3/3 v8] mmc: add generic mmc spi driver
  2010-07-05 14:22     ` Thomas Chou
@ 2010-07-05 20:11       ` Mike Frysinger
  0 siblings, 0 replies; 33+ messages in thread
From: Mike Frysinger @ 2010-07-05 20:11 UTC (permalink / raw)
  To: u-boot

On Monday, July 05, 2010 10:22:45 Thomas Chou wrote:
> On 07/05/2010 03:40 PM, Mike Frysinger wrote:
> > On Wednesday, May 19, 2010 00:37:47 Thomas Chou wrote:
> >> --- /dev/null
> >> +++ b/drivers/mmc/mmc_spi.c
> >> +struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode)
> >> +{
> >> +	struct mmc *mmc;
> >> +	mmc->b_max = MMC_SPI_MAX_BLOCKS;
> > 
> > do you have some local modification ?  i dont see b_max anywhere in
> > include/mmc.h ...
> 
> Please apply the multi-blocks patches (1,2,3/4) from Alagu on 05/12, as
> Andy said he had applied them.

those arent required for basic probing functionality, right ?

> > unfortunately though, i tried this on my system and it doesnt seem to
> > work. using a simple SPI<->MMC card, the old mmc_spi driver works on my
> > board, but booting the new u-boot and running the same things shows:
> > 
> > 
> > mmc_spi_request:cmd1 1 40300000 0
> > mmc_spi_sendcmd:cmd1 resp4 1
> > <these last 2 lines repeat for a while>
> > Card did not respond to voltage select!
> 
> It seems the mmc card was not initialized and timed out. Please try
> remove the OCR_HCS in mmc_send_op_cond() of mmc.c temporarily.
> 
> 	cmd.cmdarg = OCR_HCS | mmc->voltages;
> ---------------------^^^^^^^^^

that does fix the timeout/warning, but the card doesnt probe yet:
bfin> mmcinfo
mmc_spi_init_p: clock 0
mmc_spi_set_ios: clock 0
mmc_spi_set_ios: clock 400000
mmc_spi_request:cmd0 0 0 0
mmc_spi_sendcmd:cmd0 resp8 ff
Device: MMC_SPI
Manufacturer ID: 0
OEM: 0
Name: Tran Speed: 0
Rd Block Len: 0
MMC version 0.0
High Capacity: No
Capacity: 0
Bus Width: 1-bit

if i boot up the old u-boot and probe the card there, then load up the new u-
boot and try again, things get further:
bfin> mmcinfo
mmc_spi_init_p: clock 0
mmc_spi_set_ios: clock 0
mmc_spi_set_ios: clock 400000
mmc_spi_request:cmd0 0 0 0
mmc_spi_sendcmd:cmd0 resp6 1
mmc_spi_request:cmd8 15 1aa 0
mmc_spi_sendcmd:cmd8 resp8 ff
mmc_spi_request:cmd55 15 0 0
mmc_spi_sendcmd:cmd55 resp6 5
mmc_spi_request:cmd0 0 0 0
mmc_spi_sendcmd:cmd0 resp6 1
mmc_spi_request:cmd1 1 300000 0
mmc_spi_sendcmd:cmd1 resp6 1
mmc_spi_request:cmd1 1 300000 0
mmc_spi_sendcmd:cmd1 resp6 1
mmc_spi_request:cmd1 1 300000 0
mmc_spi_sendcmd:cmd1 resp6 1
mmc_spi_request:cmd1 1 300000 0
mmc_spi_sendcmd:cmd1 resp6 1
mmc_spi_request:cmd1 1 300000 0
mmc_spi_sendcmd:cmd1 resp6 1
mmc_spi_request:cmd1 1 300000 0
mmc_spi_sendcmd:cmd1 resp6 1
mmc_spi_request:cmd1 1 300000 0
mmc_spi_sendcmd:cmd1 resp6 0
mmc_spi_request:cmd58 1 0 0
mmc_spi_sendcmd:cmd58 resp6 0
r32 ffffffff
mmc_spi_request:cmd10 7 0 0
mmc_spi_sendcmd:cmd10 resp6 0
mmc_spi_readdata:tok0 80
r128 0 ff7a0000 fdff 3831f903
mmc_spi_request:cmd9 7 0 0
mmc_spi_sendcmd:cmd9 resp6 0
mmc_spi_readdata:tok1 fe
r128 ff4900 263 61726420 1659810
mmc_spi_set_ios: clock 20000000
mmc_spi_request:cmd16 15 1 0
mmc_spi_sendcmd:cmd16 resp6 0
mmc_spi_request:cmd17 15 0 0
mmc_spi_sendcmd:cmd17 resp8 ff
block read failed: -16
Device: MMC_SPI
Manufacturer ID: 0
OEM: 0
Name: Tran Speed: 0
Rd Block Len: 1
MMC version 1.2
High Capacity: Yes
Capacity: 2374355968
Bus Width: 1-bit

however, in poking the code, i see your mmc_spi_init_p() function calls 
spi_claim_bus(), but nowhere do i see spi_release_bus().
-mike
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part.
Url : http://lists.denx.de/pipermail/u-boot/attachments/20100705/5bf8f0e8/attachment.pgp 

^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2010-07-05 20:11 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-04-23  2:53 [U-Boot] [PATCH] mmc: add generic mmc spi driver Thomas Chou
2010-04-23  3:35 ` Mike Frysinger
2010-04-23  4:04   ` Thomas Chou
2010-04-23  5:55     ` Thomas Chou
2010-04-25  6:56       ` Mike Frysinger
2010-04-26 14:37         ` Thomas Chou
2010-04-26 15:59           ` Mike Frysinger
2010-04-25  6:51     ` Mike Frysinger
2010-04-27  1:51 ` [U-Boot] [PATCH v2] " Thomas Chou
2010-04-27  3:27 ` [U-Boot] [PATCH v3] " Thomas Chou
2010-04-27 16:49   ` Mike Frysinger
2010-04-28  3:14     ` Thomas Chou
2010-04-28  2:50 ` [U-Boot] [PATCH v4] " Thomas Chou
2010-04-28  6:00 ` [U-Boot] [PATCH v5] " Thomas Chou
2010-04-28 15:21   ` Andy Fleming
2010-04-29  5:52     ` Thomas Chou
2010-04-29 14:51     ` Thomas Chou
2010-04-29 19:07       ` Mike Frysinger
2010-04-30  0:16         ` Thomas Chou
2010-05-03  0:54 ` [U-Boot] [PATCH 0/3] mmc: add mmc_spi driver Thomas Chou
2010-05-03  0:54 ` [U-Boot] [PATCH 1/3] lib: add crc7 from Linux Thomas Chou
2010-05-03  0:54 ` [U-Boot] [PATCH 2/3] mmc: add find_mmc_device_quiet that doesnt print not found message Thomas Chou
2010-05-06 22:14   ` Wolfgang Denk
2010-05-07  0:19     ` Thomas Chou
2010-05-07  0:51     ` [U-Boot] [PATCH 2/3 v2] mmc: control message print in find_mmc_device Thomas Chou
2010-05-19  4:31       ` Thomas Chou
2010-05-07  0:51     ` [U-Boot] [PATCH 3/3 v7] mmc: add generic mmc spi driver Thomas Chou
2010-05-03  0:54 ` [U-Boot] [PATCH 3/3 v6] " Thomas Chou
2010-05-19  4:37 ` [U-Boot] [PATCH 3/3 v8] " Thomas Chou
2010-05-28 22:44   ` Thomas Chou
2010-07-05  7:40   ` Mike Frysinger
2010-07-05 14:22     ` Thomas Chou
2010-07-05 20:11       ` Mike Frysinger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.