linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch 2.6.22-git5 0/4] MMC-over-SPI
@ 2007-07-14 22:04 David Brownell
       [not found] ` <200707141504.51950.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-14 22:04 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Anton Vorontsov, Hans-Peter Nilsson, Mikael Starvik,
	Mike Lavender, Pierre Ossman

Here's an updated version of the MMC-over-SPI patches, reworked to
address most of Pierre's issues (moving even more code into the MMC
core) and to clean up the I/O paths.

To build/run these, you'll need the CRC7 patch from recent MM trees.

 - headers learn about SPI

 - mmc_block learns about SPI

 - mmc core updates 

 - mmc_spi host driver

The bits related to card lock/unlock aren't included here, since
those didn't go upstream yet.



-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* [patch 2.6.22-git5 1/4] MMC headers learn about SPI
       [not found] ` <200707141504.51950.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-14 22:05   ` David Brownell
       [not found]     ` <200707141506.00262.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-07-14 22:06   ` [patch 2.6.22-git5 2/4] MMC block learns " David Brownell
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-14 22:05 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Anton Vorontsov, Hans-Peter Nilsson, Mikael Starvik,
	Mike Lavender, Pierre Ossman

Teach the MMC/SD/SDIO system headers that some hosts use SPI mode

 - New host capabilities and status bits
    * MMC_CAP_SPI, with mmc_host_is_spi() test
    * mmc_host.use_spi-crc flag
 
 - SPI-specific declarations:
    * Response types, MMC_RSP_SPI_R*
    * Two SPI-only commands 
    * Status bits used native to SPI:  R1_SPI_*, R2_SPI_*

 - Fix a few (unrelated) whitespace bugs in the headers.

 - Reorder a few mmc_host fields, removing several bytes of padding

None of these changes affect current code.

Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
---
 include/linux/mmc/core.h |   23 ++++++++++++++++++++--
 include/linux/mmc/host.h |    9 ++++++--
 include/linux/mmc/mmc.h  |   48 ++++++++++++++++++++++++++++++++++++++++-------
 3 files changed, 69 insertions(+), 11 deletions(-)

--- g26.orig/include/linux/mmc/host.h	2007-07-14 14:47:13.000000000 -0700
+++ g26/include/linux/mmc/host.h	2007-07-14 14:47:51.000000000 -0700
@@ -90,6 +90,7 @@ struct mmc_host {
 #define MMC_CAP_BYTEBLOCK	(1 << 2)	/* Can do non-log2 block sizes */
 #define MMC_CAP_MMC_HIGHSPEED	(1 << 3)	/* Can do MMC high-speed timing */
 #define MMC_CAP_SD_HIGHSPEED	(1 << 4)	/* Can do SD high-speed timing */
+#define MMC_CAP_SPI		(1 << 5)	/* Talks only SPI protocols */
 
 	/* host specific block data */
 	unsigned int		max_seg_size;	/* see blk_queue_max_segment_size */
@@ -115,14 +116,16 @@ struct mmc_host {
 	wait_queue_head_t	wq;
 	unsigned int		claimed:1;	/* host exclusively claimed */
 
-	struct delayed_work	detect;
+	unsigned int		use_spi_crc:1;
+
 #ifdef CONFIG_MMC_DEBUG
 	unsigned int		removed:1;	/* host is being removed */
 #endif
+	unsigned int		bus_dead:1;	/* bus has been released */
+	struct delayed_work	detect;
 
 	const struct mmc_bus_ops *bus_ops;	/* current bus driver */
 	unsigned int		bus_refs;	/* reference counter */
-	unsigned int		bus_dead:1;	/* bus has been released */
 
 	unsigned long		private[0] ____cacheline_aligned;
 };
@@ -137,6 +140,8 @@ static inline void *mmc_priv(struct mmc_
 	return (void *)host->private;
 }
 
+#define mmc_host_is_spi(host)	((host)->caps & MMC_CAP_SPI)
+
 #define mmc_dev(x)	((x)->parent)
 #define mmc_classdev(x)	(&(x)->class_dev)
 #define mmc_hostname(x)	((x)->class_dev.bus_id)
--- g26.orig/include/linux/mmc/core.h	2007-07-14 14:47:13.000000000 -0700
+++ g26/include/linux/mmc/core.h	2007-07-14 14:47:51.000000000 -0700
@@ -25,14 +25,19 @@ struct mmc_command {
 #define MMC_RSP_CRC	(1 << 2)		/* expect valid crc */
 #define MMC_RSP_BUSY	(1 << 3)		/* card may send busy */
 #define MMC_RSP_OPCODE	(1 << 4)		/* response contains opcode */
-#define MMC_CMD_MASK	(3 << 5)		/* command type */
+
+#define MMC_CMD_MASK	(3 << 5)		/* non-SPI command type */
 #define MMC_CMD_AC	(0 << 5)
 #define MMC_CMD_ADTC	(1 << 5)
 #define MMC_CMD_BC	(2 << 5)
 #define MMC_CMD_BCR	(3 << 5)
 
+#define MMC_RSP_SPI_S1	(1 << 7)		/* one status byte */
+#define MMC_RSP_SPI_S2	(1 << 8)		/* second status byte */
+#define MMC_RSP_SPI_B4	(1 << 9)		/* four data bytes */
+
 /*
- * These are the response types, and correspond to valid bit
+ * These are the native response types, and correspond to valid bit
  * patterns of the above flags.  One additional valid pattern
  * is all zeros, which means we don't expect a response.
  */
@@ -47,6 +52,20 @@ struct mmc_command {
 #define mmc_resp_type(cmd)	((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
 
 /*
+ * These are the SPI response types for MMC and SD cards (not SDIO).
+ * Commands return R1, with maybe more info.  Zero is an error type;
+ * callers must always provide the appropriate MMC_RSP_SPI_Rx flags.
+ */
+#define MMC_RSP_SPI_R1	(MMC_RSP_SPI_S1)
+#define MMC_RSP_SPI_R1B	(MMC_RSP_SPI_S1|MMC_RSP_BUSY)
+#define MMC_RSP_SPI_R2	(MMC_RSP_SPI_S1|MMC_RSP_SPI_S2)
+#define MMC_RSP_SPI_R3	(MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
+#define MMC_RSP_SPI_R7	(MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
+
+#define mmc_spi_resp_type(cmd)	((cmd)->flags & \
+		(MMC_RSP_SPI_S1|MMC_RSP_BUSY|MMC_RSP_SPI_S2|MMC_RSP_SPI_B4))
+
+/*
  * These are the command types.
  */
 #define mmc_cmd_type(cmd)	((cmd)->flags & MMC_CMD_MASK)
--- g26.orig/include/linux/mmc/mmc.h	2007-07-14 14:47:12.000000000 -0700
+++ g26/include/linux/mmc/mmc.h	2007-07-14 14:47:51.000000000 -0700
@@ -27,7 +27,7 @@
 
 /* Standard MMC commands (4.1)           type  argument     response */
    /* class 1 */
-#define	MMC_GO_IDLE_STATE         0   /* bc                          */
+#define MMC_GO_IDLE_STATE         0   /* bc                          */
 #define MMC_SEND_OP_COND          1   /* bcr  [31:0] OCR         R3  */
 #define MMC_ALL_SEND_CID          2   /* bcr                     R2  */
 #define MMC_SET_RELATIVE_ADDR     3   /* ac   [31:16] RCA        R1  */
@@ -39,8 +39,10 @@
 #define MMC_SEND_CID             10   /* ac   [31:16] RCA        R2  */
 #define MMC_READ_DAT_UNTIL_STOP  11   /* adtc [31:0] dadr        R1  */
 #define MMC_STOP_TRANSMISSION    12   /* ac                      R1b */
-#define MMC_SEND_STATUS	         13   /* ac   [31:16] RCA        R1  */
+#define MMC_SEND_STATUS          13   /* ac   [31:16] RCA        R1  */
 #define MMC_GO_INACTIVE_STATE    15   /* ac   [31:16] RCA            */
+#define MMC_SPI_READ_OCR         58   /* spi                  spi_R3 */
+#define MMC_SPI_CRC_ON_OFF       59   /* spi  [0:0] flag      spi_R1 */
 
   /* class 2 */
 #define MMC_SET_BLOCKLEN         16   /* ac   [31:0] block len   R1  */
@@ -90,15 +92,15 @@
  */
 
 /*
-  MMC status in R1
+  MMC status in R1, for native mode (SPI bits are different)
   Type
-  	e : error bit
+	e : error bit
 	s : status bit
 	r : detected and set for the actual command response
 	x : detected and set during command execution. the host must poll
             the card by sending status command in order to read these bits.
   Clear condition
-  	a : according to the card state
+	a : according to the card state
 	b : always related to the previous command. Reception of
             a valid command will clear it (with a delay of one command)
 	c : clear by read
@@ -124,10 +126,42 @@
 #define R1_CARD_ECC_DISABLED	(1 << 14)	/* sx, a */
 #define R1_ERASE_RESET		(1 << 13)	/* sr, c */
 #define R1_STATUS(x)            (x & 0xFFFFE000)
-#define R1_CURRENT_STATE(x)    	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
+#define R1_CURRENT_STATE(x)	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
 #define R1_READY_FOR_DATA	(1 << 8)	/* sx, a */
 #define R1_APP_CMD		(1 << 5)	/* sr, c */
 
+/*
+ * MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS
+ * R1 is the low order byte; R2 is the next highest byte, when present.
+ */
+#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 R2_SPI_CARD_LOCKED	(1 << 8)
+#define R2_SPI_WP_ERASE_SKIP	(1 << 9)	/* or lock/unlock fail */
+#define R2_SPI_LOCK_UNLOCK_FAIL	R2_SPI_WP_ERASE_SKIP
+#define R2_SPI_ERROR		(1 << 10)
+#define R2_SPI_CC_ERROR		(1 << 11)
+#define R2_SPI_CARD_ECC_ERROR	(1 << 12)
+#define R2_SPI_WP_VIOLATION	(1 << 13)
+#define R2_SPI_ERASE_PARAM	(1 << 14)
+#define R2_SPI_OUT_OF_RANGE	(1 << 15)	/* or CSD overwrite */
+#define R2_SPI_CSD_OVERWRITE	R2_SPI_OUT_OF_RANGE
+
+static inline int mmc_status_card_is_locked(struct mmc_host *host, u32 status)
+{
+	if (mmc_host_is_spi(host))
+		return status & R2_SPI_CARD_LOCKED;
+	else
+		return status & R1_CARD_IS_LOCKED;
+}
+
+
 /* These are unpacked versions of the actual responses */
 
 struct _mmc_csd {
@@ -182,6 +216,7 @@ struct _mmc_csd {
  */
 #define CCC_BASIC		(1<<0)	/* (0) Basic protocol functions */
 					/* (CMD0,1,2,3,4,7,9,10,12,13,15) */
+					/* (and for SPI, CMD58,59) */
 #define CCC_STREAM_READ		(1<<1)	/* (1) Stream read commands */
 					/* (CMD11) */
 #define CCC_BLOCK_READ		(1<<2)	/* (2) Block read commands */

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* [patch 2.6.22-git5 2/4] MMC block learns about SPI
       [not found] ` <200707141504.51950.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-07-14 22:05   ` [patch 2.6.22-git5 1/4] MMC headers learn about SPI David Brownell
@ 2007-07-14 22:06   ` David Brownell
       [not found]     ` <200707141506.42880.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-07-14 22:07   ` [patch 2.6.22-git5 3/4] MMC core " David Brownell
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-14 22:06 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Anton Vorontsov, Hans-Peter Nilsson, Mikael Starvik,
	Mike Lavender, Pierre Ossman

Teaching the MMC/SD block card driver about SPI.

 - Provide the SPI response type flags with each request issued.  The
   model is that if no such flags are provided, it will be rejected 
   by the MMC-over-SPI host.

 - Understand that multiblock SPI writes don't use STOP_TRANSMISSION.

 - Correct check for APP_CMD failure.

Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
---
 drivers/mmc/card/block.c |   26 +++++++++++++++++---------
 1 files changed, 17 insertions(+), 9 deletions(-)

--- g26.orig/drivers/mmc/card/block.c	2007-07-14 14:47:12.000000000 -0700
+++ g26/drivers/mmc/card/block.c	2007-07-14 14:47:55.000000000 -0700
@@ -151,17 +151,19 @@ static u32 mmc_sd_num_wr_blocks(struct m
 
 	cmd.opcode = MMC_APP_CMD;
 	cmd.arg = card->rca << 16;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
 
 	err = mmc_wait_for_cmd(card->host, &cmd, 0);
-	if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD))
+	if (err != MMC_ERR_NONE)
+		return (u32)-1;
+	if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD))
 		return (u32)-1;
 
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
 	cmd.opcode = SD_APP_SEND_NUM_WR_BLKS;
 	cmd.arg = 0;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 
 	memset(&data, 0, sizeof(struct mmc_data));
 
@@ -220,11 +222,11 @@ static int mmc_blk_issue_rq(struct mmc_q
 		brq.cmd.arg = req->sector;
 		if (!mmc_card_blockaddr(card))
 			brq.cmd.arg <<= 9;
-		brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+		brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 		brq.data.blksz = 1 << md->block_bits;
 		brq.stop.opcode = MMC_STOP_TRANSMISSION;
 		brq.stop.arg = 0;
-		brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
+		brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
 		brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);
 		if (brq.data.blocks > card->host->max_blk_count)
 			brq.data.blocks = card->host->max_blk_count;
@@ -244,7 +246,12 @@ static int mmc_blk_issue_rq(struct mmc_q
 
 		if (brq.data.blocks > 1) {
 			brq.data.flags |= MMC_DATA_MULTI;
-			brq.mrq.stop = &brq.stop;
+			/* SPI multiblock writes terminate using a special
+			 * token, not a STOP_TRANSMISSION request.
+			 */
+			if (!mmc_host_is_spi(card->host)
+					|| rq_data_dir(req) == READ)
+				brq.mrq.stop = &brq.stop;
 			readcmd = MMC_READ_MULTIPLE_BLOCK;
 			writecmd = MMC_WRITE_MULTIPLE_BLOCK;
 		} else {
@@ -302,13 +309,14 @@ static int mmc_blk_issue_rq(struct mmc_q
 			goto cmd_err;
 		}
 
-		if (rq_data_dir(req) != READ) {
+		if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
 			do {
 				int err;
 
 				cmd.opcode = MMC_SEND_STATUS;
 				cmd.arg = card->rca << 16;
-				cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+				cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1
+						| MMC_CMD_AC;
 				err = mmc_wait_for_cmd(card->host, &cmd, 5);
 				if (err) {
 					printk(KERN_ERR "%s: error %d requesting status\n",
@@ -511,7 +519,7 @@ mmc_blk_set_blksize(struct mmc_blk_data 
 	mmc_claim_host(card->host);
 	cmd.opcode = MMC_SET_BLOCKLEN;
 	cmd.arg = 1 << md->block_bits;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
 	err = mmc_wait_for_cmd(card->host, &cmd, 5);
 	mmc_release_host(card->host);
 

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* [patch 2.6.22-git5 3/4] MMC core learns about SPI
       [not found] ` <200707141504.51950.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-07-14 22:05   ` [patch 2.6.22-git5 1/4] MMC headers learn about SPI David Brownell
  2007-07-14 22:06   ` [patch 2.6.22-git5 2/4] MMC block learns " David Brownell
@ 2007-07-14 22:07   ` David Brownell
       [not found]     ` <200707141507.17484.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-07-14 22:08   ` [patch 2.6.22-git5 4/4] mmc_spi host driver David Brownell
  2007-07-16 16:48   ` [patch 2.6.22-git5 0/4] MMC-over-SPI Anton Vorontsov
  4 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-14 22:07 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Anton Vorontsov, Hans-Peter Nilsson, Mikael Starvik,
	Mike Lavender, Pierre Ossman

Teach the MMC/SD/SDIO core about using SPI mode.

 - Use mmc_host_is_spi() so enumeration works through SPI signaling,
   and protocols, not just the native versions.

 - Provide the SPI response type flags with each request issued,
   including requests from the new lock/unlock code.

 - Understand that some commands act a bit differently ... notably:
     * OP_COND command reports busy status differently
     * OP_COND command doesn't return the OCR
     * APP_CMD status doesn't have an R1_APP_CMD analogue

 - Understand that cmd->resp[0] and mmc_get_status() results for SPI
   return different values than for "native" MMC/SD protocol; this
   affects checking card lock status, and some others.

Those changes required some new and updated primitives:

 - Provide utilities to access two SPI-only requests, and one
   request that wasn't previously needed:
     * mmc_spi_read_ocr() ... SPI only
     * mmc_spi_set_crc() ... SPI only (override by module parm)
     * mmc_spi_send_cid() ... works without broadcast mode

 - Updated internal routines:
     * Previous mmc_send_csd() modified into mmc_send_cxd_native();
       it uses native "R2" responses, which include 16 bytes of data.
     * Previous mmc_send_ext_csd() becomes new mmc_send_cxd_data()
       helper for command-and-data access
     * Bugfix to that mmc_send_cxd_data() code:  dma-to-stack is
       unsafe/nonportable, so kmalloc a bounce buffer instead.

 - Modified mmc_send_ext_csd() now uses mmc_send_cxd_data() helper

 - Modified mmc_send_csd(), and new mmc_spi_send_cid(), routines use
   those helper routines based on whether they're native or SPI

The SPI support hasn't been tested on the new MMC4 cards (they're not
widely available); likewise SD cards with 4GB and up may have surprises
lurking.

Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
---
 drivers/mmc/core/core.c    |   24 +++++-
 drivers/mmc/core/mmc.c     |   33 ++++++---
 drivers/mmc/core/mmc_ops.c |  158 ++++++++++++++++++++++++++++++++++++---------
 drivers/mmc/core/mmc_ops.h |    4 +
 drivers/mmc/core/sd.c      |   31 +++++---
 drivers/mmc/core/sd_ops.c  |   32 ++++++---
 6 files changed, 215 insertions(+), 67 deletions(-)

--- g26.orig/drivers/mmc/core/mmc_ops.h	2007-07-14 14:47:12.000000000 -0700
+++ g26/drivers/mmc/core/mmc_ops.h	2007-07-14 14:47:54.000000000 -0700
@@ -23,5 +23,9 @@ int mmc_send_ext_csd(struct mmc_card *ca
 int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
 int mmc_send_status(struct mmc_card *card, u32 *status);
 
+int mmc_spi_send_cid(struct mmc_host *host, u32 *cid);
+int mmc_spi_read_ocr(struct mmc_host *host, u32 *ocrp);
+int mmc_spi_set_crc(struct mmc_host *host);
+
 #endif
 
--- g26.orig/drivers/mmc/core/core.c	2007-07-14 14:47:12.000000000 -0700
+++ g26/drivers/mmc/core/core.c	2007-07-14 14:47:54.000000000 -0700
@@ -404,8 +404,13 @@ static void mmc_power_up(struct mmc_host
 	int bit = fls(host->ocr_avail) - 1;
 
 	host->ios.vdd = bit;
-	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
-	host->ios.chip_select = MMC_CS_DONTCARE;
+	if (mmc_host_is_spi(host)) {
+		host->ios.chip_select = MMC_CS_HIGH;
+		host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+	} else {
+		host->ios.chip_select = MMC_CS_DONTCARE;
+		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+	}
 	host->ios.power_mode = MMC_POWER_UP;
 	host->ios.bus_width = MMC_BUS_WIDTH_1;
 	host->ios.timing = MMC_TIMING_LEGACY;
@@ -424,8 +429,10 @@ static void mmc_power_off(struct mmc_hos
 {
 	host->ios.clock = 0;
 	host->ios.vdd = 0;
-	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
-	host->ios.chip_select = MMC_CS_DONTCARE;
+	if (!mmc_host_is_spi(host)) {
+		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+		host->ios.chip_select = MMC_CS_DONTCARE;
+	}
 	host->ios.power_mode = MMC_POWER_OFF;
 	host->ios.bus_width = MMC_BUS_WIDTH_1;
 	host->ios.timing = MMC_TIMING_LEGACY;
@@ -541,7 +548,9 @@ void mmc_rescan(struct work_struct *work
 
 		err = mmc_send_app_op_cond(host, 0, &ocr);
 		if (err == MMC_ERR_NONE) {
-			if (mmc_attach_sd(host, ocr))
+			if (mmc_host_is_spi(host))
+				err = mmc_spi_read_ocr(host, &ocr);
+			if (err != MMC_ERR_NONE || mmc_attach_sd(host, ocr))
 				mmc_power_off(host);
 		} else {
 			/*
@@ -550,7 +559,10 @@ void mmc_rescan(struct work_struct *work
 			 */
 			err = mmc_send_op_cond(host, 0, &ocr);
 			if (err == MMC_ERR_NONE) {
-				if (mmc_attach_mmc(host, ocr))
+				if (mmc_host_is_spi(host))
+					err = mmc_spi_read_ocr(host, &ocr);
+				if (err != MMC_ERR_NONE
+						|| mmc_attach_mmc(host, ocr))
 					mmc_power_off(host);
 			} else {
 				mmc_power_off(host);
--- g26.orig/drivers/mmc/core/mmc_ops.c	2007-07-14 14:47:12.000000000 -0700
+++ g26/drivers/mmc/core/mmc_ops.c	2007-07-14 14:47:54.000000000 -0700
@@ -63,23 +63,34 @@ int mmc_go_idle(struct mmc_host *host)
 	int err;
 	struct mmc_command cmd;
 
-	mmc_set_chip_select(host, MMC_CS_HIGH);
-
-	mmc_delay(1);
+	/*
+	 * Non-SPI hosts need to prevent chipselect going active during
+	 * GO_IDLE; that would put chips into SPI mode.  Remind them of
+	 * that in case of hardware that won't pull up DAT3/nCS otherwise.
+	 *
+	 * SPI hosts ignore ios.chip_select; it's managed according to
+	 * rules that must accomodate non-MMC slaves which this layer
+	 * won't even know about.
+	 */
+	if (!mmc_host_is_spi(host)) {
+		mmc_set_chip_select(host, MMC_CS_HIGH);
+		mmc_delay(1);
+	}
 
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
 	cmd.opcode = MMC_GO_IDLE_STATE;
 	cmd.arg = 0;
-	cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
 
 	err = mmc_wait_for_cmd(host, &cmd, 0);
 
 	mmc_delay(1);
 
-	mmc_set_chip_select(host, MMC_CS_DONTCARE);
-
-	mmc_delay(1);
+	if (!mmc_host_is_spi(host)) {
+		mmc_set_chip_select(host, MMC_CS_DONTCARE);
+		mmc_delay(1);
+	}
 
 	return err;
 }
@@ -95,14 +106,18 @@ int mmc_send_op_cond(struct mmc_host *ho
 
 	cmd.opcode = MMC_SEND_OP_COND;
 	cmd.arg = ocr;
-	cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
 
 	for (i = 100; i; i--) {
 		err = mmc_wait_for_cmd(host, &cmd, 0);
 		if (err != MMC_ERR_NONE)
 			break;
 
-		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+		if (mmc_host_is_spi(host)) {
+			/* wait until reset completes */
+			if (!(cmd.resp[0] & R1_SPI_IDLE))
+				break;
+		} else if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
 			break;
 
 		err = MMC_ERR_TIMEOUT;
@@ -110,7 +125,7 @@ int mmc_send_op_cond(struct mmc_host *ho
 		mmc_delay(10);
 	}
 
-	if (rocr)
+	if (rocr && !mmc_host_is_spi(host))
 		*rocr = cmd.resp[0];
 
 	return err;
@@ -160,40 +175,45 @@ int mmc_set_relative_addr(struct mmc_car
 	return MMC_ERR_NONE;
 }
 
-int mmc_send_csd(struct mmc_card *card, u32 *csd)
+static int
+mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
 {
 	int err;
 	struct mmc_command cmd;
 
-	BUG_ON(!card);
-	BUG_ON(!card->host);
-	BUG_ON(!csd);
+	BUG_ON(!host);
+	BUG_ON(!cxd);
 
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
-	cmd.opcode = MMC_SEND_CSD;
-	cmd.arg = card->rca << 16;
+	cmd.opcode = opcode;
+	cmd.arg = arg;
 	cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
 
-	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
 	if (err != MMC_ERR_NONE)
 		return err;
 
-	memcpy(csd, cmd.resp, sizeof(u32) * 4);
+	memcpy(cxd, cmd.resp, sizeof(u32) * 4);
 
 	return MMC_ERR_NONE;
 }
 
-int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
+static int
+mmc_send_cxd_data(struct mmc_host *host, u32 opcode, void *buf, unsigned len)
 {
 	struct mmc_request mrq;
 	struct mmc_command cmd;
 	struct mmc_data data;
 	struct scatterlist sg;
+	void *data_buf;
 
-	BUG_ON(!card);
-	BUG_ON(!card->host);
-	BUG_ON(!ext_csd);
+	/* dma onto stack is unsafe/nonportable, but callers here
+	 * normally provide temporary on-stack buffers.
+	 */
+	data_buf = kmalloc(len, GFP_KERNEL);
+	if (data_buf == NULL)
+		return -ENOMEM;
 
 	memset(&mrq, 0, sizeof(struct mmc_request));
 	memset(&cmd, 0, sizeof(struct mmc_command));
@@ -202,21 +222,32 @@ int mmc_send_ext_csd(struct mmc_card *ca
 	mrq.cmd = &cmd;
 	mrq.data = &data;
 
-	cmd.opcode = MMC_SEND_EXT_CSD;
+	cmd.opcode = opcode;
 	cmd.arg = 0;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
 
-	data.blksz = 512;
+	/* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
+	 * rely on callers to never use this with "native" calls for reading
+	 * CSD or CID.  Native versions of those commands use the R2 type,
+	 * not R1 plus a data block.
+	 */
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	data.blksz = len;
 	data.blocks = 1;
 	data.flags = MMC_DATA_READ;
 	data.sg = &sg;
 	data.sg_len = 1;
 
-	sg_init_one(&sg, ext_csd, 512);
+	sg_init_one(&sg, data_buf, len);
 
-	mmc_set_data_timeout(&data, card, 0);
+	/* Note that for MMC_SEND_EXT_CSD we could set the timeout; but
+	 * not for the other requests. But host->card isn't set yet!
+	 */
 
-	mmc_wait_for_req(card->host, &mrq);
+	mmc_wait_for_req(host, &mrq);
+
+	memcpy(buf, data_buf, len);
+	kfree(data_buf);
 
 	if (cmd.error != MMC_ERR_NONE)
 		return cmd.error;
@@ -226,6 +257,67 @@ int mmc_send_ext_csd(struct mmc_card *ca
 	return MMC_ERR_NONE;
 }
 
+int mmc_send_csd(struct mmc_card *card, u32 *csd)
+{
+	if (!mmc_host_is_spi(card->host))
+		return mmc_send_cxd_native(card->host, card->rca << 16,
+				csd, MMC_SEND_CSD);
+
+	return mmc_send_cxd_data(card->host, MMC_SEND_CSD, csd, 16);
+}
+
+int mmc_spi_send_cid(struct mmc_host *host, u32 *cid)
+{
+	if (!mmc_host_is_spi(host))
+		return mmc_send_cxd_native(host, 0, cid, MMC_SEND_CID);
+
+	return mmc_send_cxd_data(host, MMC_SEND_CID, cid, 16);
+}
+
+int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
+{
+	return mmc_send_cxd_data(card->host, MMC_SEND_EXT_CSD, ext_csd, 512);
+}
+
+int mmc_spi_read_ocr(struct mmc_host *host, u32 *ocrp)
+{
+	struct mmc_command cmd;
+	int err;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = MMC_SPI_READ_OCR;
+	cmd.flags = MMC_RSP_SPI_R3;
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+
+	*ocrp = cmd.resp[1];
+	return err;
+}
+
+/* Enabling software CRCs can be a significant (30%) performance cost,
+ * and for other reasons isn't always desired; so it can be disabled.
+ */
+static int use_spi_crc = 1;
+module_param(use_spi_crc, bool, 0);
+
+int mmc_spi_set_crc(struct mmc_host *host)
+{
+	struct mmc_command cmd;
+	int err;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = MMC_SPI_CRC_ON_OFF;
+	cmd.flags = MMC_RSP_SPI_R1;
+	cmd.arg = use_spi_crc;
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err == MMC_ERR_NONE)
+		host->use_spi_crc = use_spi_crc;
+	return err;
+}
+
 int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
 {
 	int err;
@@ -241,7 +333,7 @@ int mmc_switch(struct mmc_card *card, u8
 		  (index << 16) |
 		  (value << 8) |
 		  set;
-	cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+	cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
 
 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
 	if (err != MMC_ERR_NONE)
@@ -261,13 +353,17 @@ int mmc_send_status(struct mmc_card *car
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
 	cmd.opcode = MMC_SEND_STATUS;
-	cmd.arg = card->rca << 16;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+	if (!mmc_host_is_spi(card->host))
+		cmd.arg = card->rca << 16;
+	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
 
 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
 	if (err != MMC_ERR_NONE)
 		return err;
 
+	/* NOTE: callers are required to understand the difference
+	 * between "native" and SPI format status words!
+	 */
 	if (status)
 		*status = cmd.resp[0];
 
--- g26.orig/drivers/mmc/core/mmc.c	2007-07-14 14:47:12.000000000 -0700
+++ g26/drivers/mmc/core/mmc.c	2007-07-14 14:47:54.000000000 -0700
@@ -264,7 +264,13 @@ static int mmc_init_card(struct mmc_host
 	/*
 	 * Fetch CID from card.
 	 */
-	err = mmc_all_send_cid(host, cid);
+	if (mmc_host_is_spi(host)) {
+		err = mmc_spi_set_crc(host);
+		if (err != MMC_ERR_NONE)
+			goto err;
+		err = mmc_spi_send_cid(host, cid);
+	} else
+		err = mmc_all_send_cid(host, cid);
 	if (err != MMC_ERR_NONE)
 		goto err;
 
@@ -287,13 +293,15 @@ static int mmc_init_card(struct mmc_host
 	}
 
 	/*
-	 * Set card RCA.
+	 * For native busses:  set card RCA and leave open drain mode.
 	 */
-	err = mmc_set_relative_addr(card);
-	if (err != MMC_ERR_NONE)
-		goto free_card;
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_set_relative_addr(card);
+		if (err != MMC_ERR_NONE)
+			goto free_card;
 
-	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+	}
 
 	if (!oldcard) {
 		/*
@@ -314,13 +322,15 @@ static int mmc_init_card(struct mmc_host
 	/*
 	 * Select card, as all following commands rely on that.
 	 */
-	err = mmc_select_card(card);
-	if (err != MMC_ERR_NONE)
-		goto free_card;
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_select_card(card);
+		if (err != MMC_ERR_NONE)
+			goto free_card;
+	}
 
 	if (!oldcard) {
 		/*
-		 * Fetch and process extened CSD.
+		 * Fetch and process extended CSD.
 		 */
 		err = mmc_read_ext_csd(card);
 		if (err != MMC_ERR_NONE)
@@ -480,7 +490,8 @@ static void mmc_suspend(struct mmc_host 
 	BUG_ON(!host->card);
 
 	mmc_claim_host(host);
-	mmc_deselect_cards(host);
+	if (!mmc_host_is_spi(host))
+		mmc_deselect_cards(host);
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 	mmc_release_host(host);
 }
--- g26.orig/drivers/mmc/core/sd.c	2007-07-14 14:47:12.000000000 -0700
+++ g26/drivers/mmc/core/sd.c	2007-07-14 14:47:54.000000000 -0700
@@ -321,7 +321,13 @@ static int mmc_sd_init_card(struct mmc_h
 	/*
 	 * Fetch CID from card.
 	 */
-	err = mmc_all_send_cid(host, cid);
+	if (mmc_host_is_spi(host)) {
+		err = mmc_spi_set_crc(host);
+		if (err != MMC_ERR_NONE)
+			goto err;
+		err = mmc_spi_send_cid(host, cid);
+	} else
+		err = mmc_all_send_cid(host, cid);
 	if (err != MMC_ERR_NONE)
 		goto err;
 
@@ -343,13 +349,15 @@ static int mmc_sd_init_card(struct mmc_h
 	}
 
 	/*
-	 * Set card RCA.
+	 * For native busses:  get card RCA and leave open drain mode.
 	 */
-	err = mmc_send_relative_addr(host, &card->rca);
-	if (err != MMC_ERR_NONE)
-		goto free_card;
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_send_relative_addr(host, &card->rca);
+		if (err != MMC_ERR_NONE)
+			goto free_card;
 
-	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+	}
 
 	if (!oldcard) {
 		/*
@@ -369,9 +377,11 @@ static int mmc_sd_init_card(struct mmc_h
 	/*
 	 * Select card, as all following commands rely on that.
 	 */
-	err = mmc_select_card(card);
-	if (err != MMC_ERR_NONE)
-		goto free_card;
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_select_card(card);
+		if (err != MMC_ERR_NONE)
+			goto free_card;
+	}
 
 	if (!oldcard) {
 		/*
@@ -554,7 +564,8 @@ static void mmc_sd_suspend(struct mmc_ho
 	BUG_ON(!host->card);
 
 	mmc_claim_host(host);
-	mmc_deselect_cards(host);
+	if (!mmc_host_is_spi(host))
+		mmc_deselect_cards(host);
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 	mmc_release_host(host);
 }
--- g26.orig/drivers/mmc/core/sd_ops.c	2007-07-14 14:47:12.000000000 -0700
+++ g26/drivers/mmc/core/sd_ops.c	2007-07-14 14:47:54.000000000 -0700
@@ -70,6 +70,12 @@ int mmc_wait_for_app_cmd(struct mmc_host
 		err = cmd->error;
 		if (cmd->error == MMC_ERR_NONE)
 			break;
+
+		/* no point in retrying illegal commands! */
+		if (mmc_host_is_spi(host)) {
+			if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
+				break;
+		}
 	}
 
 	return err;
@@ -89,10 +95,10 @@ int mmc_app_cmd(struct mmc_host *host, s
 
 	if (card) {
 		cmd.arg = card->rca << 16;
-		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+		cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
 	} else {
 		cmd.arg = 0;
-		cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR;
+		cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR;
 	}
 
 	err = mmc_wait_for_cmd(host, &cmd, 0);
@@ -100,8 +106,8 @@ int mmc_app_cmd(struct mmc_host *host, s
 		return err;
 
 	/* Check that card supported application commands */
-	if (!(cmd.resp[0] & R1_APP_CMD))
-		return MMC_ERR_FAILED;
+	if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
+		return (u32)-1;
 
 	return MMC_ERR_NONE;
 }
@@ -148,14 +154,18 @@ int mmc_send_app_op_cond(struct mmc_host
 
 	cmd.opcode = SD_APP_OP_COND;
 	cmd.arg = ocr;
-	cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
 
 	for (i = 100; i; i--) {
 		err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
 		if (err != MMC_ERR_NONE)
 			break;
 
-		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+		if (mmc_host_is_spi(host)) {
+			/* wait until reset completes */
+			if (!(cmd.resp[0] & R1_SPI_IDLE))
+				break;
+		} else if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
 			break;
 
 		err = MMC_ERR_TIMEOUT;
@@ -182,7 +192,7 @@ int mmc_send_if_cond(struct mmc_host *ho
 	 */
 	cmd.opcode = SD_SEND_IF_COND;
 	cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
-	cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
+	cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;
 
 	err = mmc_wait_for_cmd(host, &cmd, 0);
 	if (err != MMC_ERR_NONE)
@@ -229,6 +239,8 @@ int mmc_app_send_scr(struct mmc_card *ca
 	BUG_ON(!card->host);
 	BUG_ON(!scr);
 
+	/* NOTE: caller guarantees scr is heap-allocated */
+
 	err = mmc_app_cmd(card->host, card);
 	if (err != MMC_ERR_NONE)
 		return err;
@@ -242,7 +254,7 @@ int mmc_app_send_scr(struct mmc_card *ca
 
 	cmd.opcode = SD_APP_SEND_SCR;
 	cmd.arg = 0;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 
 	data.blksz = 8;
 	data.blocks = 1;
@@ -278,6 +290,8 @@ int mmc_sd_switch(struct mmc_card *card,
 	BUG_ON(!card);
 	BUG_ON(!card->host);
 
+	/* NOTE: caller guarantees resp is heap-allocated */
+
 	mode = !!mode;
 	value &= 0xF;
 
@@ -292,7 +306,7 @@ int mmc_sd_switch(struct mmc_card *card,
 	cmd.arg = mode << 31 | 0x00FFFFFF;
 	cmd.arg &= ~(0xF << (group * 4));
 	cmd.arg |= value << (group * 4);
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 
 	data.blksz = 64;
 	data.blocks = 1;

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* [patch 2.6.22-git5 4/4] mmc_spi host driver
       [not found] ` <200707141504.51950.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
                     ` (2 preceding siblings ...)
  2007-07-14 22:07   ` [patch 2.6.22-git5 3/4] MMC core " David Brownell
@ 2007-07-14 22:08   ` David Brownell
       [not found]     ` <200707141508.07555.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-07-16 16:48   ` [patch 2.6.22-git5 0/4] MMC-over-SPI Anton Vorontsov
  4 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-14 22:08 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Anton Vorontsov, Hans-Peter Nilsson, Mikael Starvik,
	Mike Lavender, Pierre Ossman

This is the latest version of the MMC-over-SPI support.  It works
on 2.6.22-git5, along with the preceding patches which teach the
rest of the MMC stack about SPI so that this host driver can focus
on doing only lowlevel SPI-specific stuff.

It's been lightly tested on MMC and SD cards, reading and writing
ext3 filesystems (including fsck).  This includes CRC mode, which
is now enabled by default.

The main issue of note is that sometimes cards need to be power
cycled to recover after certain faults.  In some cases, it may be
necessary to disable CRCs.  ("modprobe mmc_core use_spi_crc=n")

Changes from previous versions include more code shrinkage and cleanup,
reducing the number of I/O requests needed per operation, and moving
even more nuances into the MMC core code.

Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
Cc: mikael.starvik-VrBV9hrLPhE@public.gmane.org,
Cc: Hans-Peter Nilsson <hp-VrBV9hrLPhE@public.gmane.org>
Cc: Jan Nikitenko <jan.nikitenko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Mike Lavender <mike-UTnDXsALFwNjMdQLN6DIHgC/G2K4zDHf@public.gmane.org>
---
 drivers/mmc/host/Kconfig    |   13 
 drivers/mmc/host/Makefile   |    1 
 drivers/mmc/host/mmc_spi.c  | 1416 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/mmc_spi.h |   33 +
 4 files changed, 1463 insertions(+)

--- g26.orig/drivers/mmc/host/Kconfig	2007-07-14 14:47:11.000000000 -0700
+++ g26/drivers/mmc/host/Kconfig	2007-07-14 14:47:57.000000000 -0700
@@ -100,3 +100,16 @@ config MMC_TIFM_SD
           To compile this driver as a module, choose M here: the
 	  module will be called tifm_sd.
 
+config MMC_SPI
+	tristate "MMC/SD over SPI"
+	depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL
+	select CRC7
+	select CRC_ITU_T
+	help
+	  Some systems accss MMC/SD cards using a SPI controller instead of
+	  using a "native" MMC/SD controller.  This has a disadvantage of
+	  being relatively high overhead, but a compensating advantage of
+	  working on many systems without dedicated MMC/SD controllers.
+
+	  If unsure, or if your system has no SPI master driver, say N.
+
--- g26.orig/drivers/mmc/host/Makefile	2007-07-14 14:47:11.000000000 -0700
+++ g26/drivers/mmc/host/Makefile	2007-07-14 14:47:57.000000000 -0700
@@ -15,4 +15,5 @@ obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)		+= omap.o
 obj-$(CONFIG_MMC_AT91)		+= at91_mci.o
 obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o
+obj-$(CONFIG_MMC_SPI)		+= mmc_spi.o
 
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ g26/include/linux/spi/mmc_spi.h	2007-07-14 14:47:57.000000000 -0700
@@ -0,0 +1,33 @@
+#ifndef __LINUX_SPI_MMC_SPI_H
+#define __LINUX_SPI_MMC_SPI_H
+
+struct device;
+struct mmc_host;
+
+/* Put this in platform_data of a device being used to manage an MMC/SD
+ * card slot.  (Modeled after PXA mmc glue; see that for usage examples.)
+ *
+ * REVISIT This is not a spi-specific notion.  Any card slot should be
+ * able to handle it.  If the MMC core doesn't adopt this kind of notion,
+ * switch the "struct device *" parameters over to "struct spi_device *".
+ */
+struct mmc_spi_platform_data {
+	/* driver activation and (optional) card detect irq hookup */
+	int (*init)(struct device *,
+		irqreturn_t (*)(int, void *),
+		void *);
+	void (*exit)(struct device *, void *);
+
+	/* sense switch on sd cards */
+	int (*get_ro)(struct device *);
+
+	/* how long to debounce card detect, in msecs */
+	u16 detect_delay;
+
+	/* power management */
+	u16 powerup_msecs;		/* delay of up to 250 msec */
+	u32 ocr_mask;			/* available voltages */
+	void (*setpower)(struct device *, unsigned int maskval);
+};
+
+#endif /* __LINUX_SPI_MMC_SPI_H */
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ g26/drivers/mmc/host/mmc_spi.c	2007-07-14 14:47:57.000000000 -0700
@@ -0,0 +1,1416 @@
+/*
+ * mmc_spi.c - Access SD/MMC cards through SPI master controllers
+ *
+ * (C) Copyright 2005, Intec Automation,
+ *		Mike Lavender (mike@steroidmicros)
+ * (C) Copyright 2006-2007, David Brownell
+ * (C) Copyright 2007, Axis Communications,
+ *		Hans-Peter Nilsson (hp-VrBV9hrLPhE@public.gmane.org)
+ * (C) Copyright 2007, ATRON electronic GmbH,
+ *		Jan Nikitenko <jan.nikitenko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/crc7.h>
+#include <linux/crc-itu-t.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>		/* for R1_SPI_* bit values */
+
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
+
+#include <asm/unaligned.h>
+
+
+/* NOTES:
+ *
+ * - For now, we won't try to interoperate with a real mmc/sd/sdio
+ *   controller, although some of them do have hardware support for
+ *   SPI protocol.  The main reason for such configs would be mmc-ish
+ *   cards like DataFlash, which don't support that "native" protocol.
+ *
+ *   We don't have a "DataFlash/MMC/SD/SDIO card slot" abstraction to
+ *   switch between driver stacks, and in any case if "native" mode
+ *   is available, it will be faster and hence preferable.
+ *
+ * - MMC depends on a different chipselect management policy than the
+ *   SPI interface currently supports for shared bus segments:  it needs
+ *   to issue multiple spi_message requests with the chipselect active,
+ *   using the results of one message to decide the next one to issue.
+ *
+ *   Pending updates to the programming interface, this driver insists
+ *   that it not share the bus with other drivers (preventing conflicts).
+ *
+ * - We tell the controller to keep the chipselect active from the
+ *   beginning of an mmc_host_ops.request until the end.  So beware
+ *   of SPI controller drivers that mis-handle the cs_change flag!
+ *
+ *   However, many cards seem OK with chipselect flapping up/down
+ *   during that time ... at least on unshared bus segments.
+ */
+
+
+/*
+ * Local protocol constants, internal to data block protocols.
+ */
+
+/* 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 */
+
+#define MMC_SPI_BLOCKSIZE	512
+
+
+/* These fixed timeouts come from the latest SD specs, which say to ignore
+ * the CSD values.  The R1B value is for card erase (e.g. the "I forgot the
+ * card's password" scenario); it's mostly applied to STOP_TRANSMISSION after
+ * reads which takes nowhere near that long.  Older cards may be able to use
+ * shorter timeouts ... but why bother?
+ */
+#define readblock_timeout	ktime_set(0, 100 * 1000 * 1000)
+#define writeblock_timeout	ktime_set(0, 250 * 1000 * 1000)
+#define r1b_timeout		ktime_set(3, 0)
+
+
+/****************************************************************************/
+
+/*
+ * Local Data Structures
+ */
+
+/* "scratch" is per-{command,block} data exchanged with the card */
+struct scratch {
+	u8			status[29];
+	u8			data_token;
+	__be16			crc_val;
+};
+
+struct mmc_spi_host {
+	struct mmc_host		*mmc;
+	struct spi_device	*spi;
+
+	unsigned char		power_mode;
+	u16			powerup_msecs;
+
+	struct mmc_spi_platform_data	*pdata;
+
+	/* for bulk data transfers */
+	struct spi_transfer	token, t, crc, early_status;
+	struct spi_message	m;
+
+	/* for status readback */
+	struct spi_transfer	status;
+	struct spi_message	readback;
+
+	/* underlying DMA-aware controller, or null */
+	struct device		*dma_dev;
+
+	/* buffer used for commands and for message "overhead" */
+	struct scratch		*data;
+	dma_addr_t		data_dma;
+
+	/* Specs say to write ones most of the time, even when the card
+	 * has no need to read its input data; and many cards won't care.
+	 * This is our source of those ones.
+	 */
+	void			*ones;
+	dma_addr_t		ones_dma;
+};
+
+
+/****************************************************************************/
+
+/*
+ * MMC-over-SPI protocol glue, used by the MMC stack interface
+ */
+
+static inline int mmc_cs_off(struct mmc_spi_host *host)
+{
+	/* chipselect will always be inactive after setup() */
+	return spi_setup(host->spi);
+}
+
+static int
+mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len)
+{
+	int status;
+
+	if (len > sizeof *host->data) {
+		WARN_ON(1);
+		return -EIO;
+	}
+
+	host->status.len = len;
+
+	if (host->dma_dev)
+		dma_sync_single_for_device(host->dma_dev,
+				host->data_dma, sizeof *host->data,
+				DMA_FROM_DEVICE);
+
+	status = spi_sync(host->spi, &host->readback);
+	if (status == 0)
+		status = host->readback.status;
+
+	if (host->dma_dev)
+		dma_sync_single_for_cpu(host->dma_dev,
+				host->data_dma, sizeof *host->data,
+				DMA_FROM_DEVICE);
+
+	return status;
+}
+
+static int
+mmc_spi_skip(struct mmc_spi_host *host, ktime_t timeout, unsigned n, u8 byte)
+{
+	u8		*cp = host->data->status;
+
+	timeout = ktime_add(timeout, ktime_get());
+
+	while (1) {
+		int		status;
+		unsigned	i;
+
+		status = mmc_spi_readbytes(host, n);
+		if (status < 0)
+			return status;
+
+		for (i = 0; i < n; i++) {
+			if (cp[i] != byte)
+				return cp[i];
+		}
+
+		/* REVISIT investigate msleep() to avoid busy-wait I/O
+		 * in at least some cases.
+		 */
+		if (ktime_to_ns(ktime_sub(ktime_get(), timeout)) > 0)
+			break;
+	}
+	return -ETIMEDOUT;
+}
+
+static inline int
+mmc_spi_wait_unbusy(struct mmc_spi_host *host, ktime_t timeout)
+{
+	return mmc_spi_skip(host, timeout, sizeof(host->data->status), 0);
+}
+
+static int mmc_spi_readtoken(struct mmc_spi_host *host)
+{
+	return mmc_spi_skip(host, readblock_timeout, 1, 0xff);
+}
+
+
+/*
+ * Note that for SPI, cmd->resp[0] is not the same data as "native" protocol
+ * hosts return!  The low byte holds R1_SPI bits.  The next byte may hold
+ * R2_SPI bits ... for SEND_STATUS, or after data read errors.
+ *
+ * cmd->resp[1] holds any four-byte response, for R3 (READ_OCR) and on
+ * newer cards R7 (IF_COND).
+ */
+
+static char *maptype(struct mmc_command *cmd)
+{
+	switch (mmc_spi_resp_type(cmd)) {
+	case MMC_RSP_SPI_R1:	return "R1";
+	case MMC_RSP_SPI_R1B:	return "R1B";
+	case MMC_RSP_SPI_R2:	return "R2";
+	case MMC_RSP_SPI_R3:	return "R3/R7";
+	default:		return "?";
+	}
+}
+
+/* return zero, else negative errno after setting cmd->error */
+static int mmc_spi_response_get(struct mmc_spi_host *host,
+		struct mmc_command *cmd, int cs_on)
+{
+	u8	*cp = host->data->status;
+	u8	*end = cp + host->t.len;
+	int	value = 0;
+	char	tag[32];
+
+	snprintf(tag, sizeof tag, "  ... CMD%d response SPI_%s",
+		cmd->opcode, maptype(cmd));
+
+	/* Except for data block reads, the whole response will already
+	 * be stored in the scratch buffer.  It's somewhere after the
+	 * command and the first byte we read after it.  We ignore that
+	 * first byte.  After STOP_TRANSMISSION command it may include
+	 * two data bits, but otherwise it's all ones.
+	 */
+	cp += 8;
+	while (cp < end && *cp == 0xff)
+		cp++;
+
+	/* Data block reads (R1 response types) may need more data... */
+	if (cp == end) {
+		unsigned	i;
+
+		cp = host->data->status;
+
+		/* Card sends N(CR) (== 1..8) bytes of all-ones then one
+		 * status byte ... and we already scanned 2 bytes.
+		 *
+		 * REVISIT block read paths use nasty byte-at-a-time I/O
+		 * so it can always DMA directly into the target buffer.
+		 * It'd probably be better to memcpy() the first chunk and
+		 * avoid extra i/o calls...
+		 */
+		for (i = 2; i < 9; i++) {
+			value = mmc_spi_readbytes(host, 1);
+			if (value < 0) {
+				cmd->error = MMC_ERR_FAILED;
+				goto done;
+			}
+			if (*cp != 0xff)
+				goto checkstatus;
+		}
+		value = -ETIMEDOUT;
+		cmd->error = MMC_ERR_TIMEOUT;
+		goto done;
+	}
+
+checkstatus:
+	if (*cp & 0x80) {
+		dev_dbg(&host->spi->dev, "%s: INVALID RESPONSE, %02x\n",
+					tag, *cp);
+		cmd->error = MMC_ERR_FAILED;
+		value = -EBADR;
+		goto done;
+	}
+
+	cmd->resp[0] = *cp++;
+	cmd->error = MMC_ERR_NONE;
+
+	/* Status byte: the entire seven-bit R1 response.  */
+	if (cmd->resp[0] != 0) {
+		if (R1_SPI_COM_CRC & cmd->resp[0]) {
+			cmd->error = MMC_ERR_BADCRC;
+			value = -EILSEQ;
+		} else if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS
+					| R1_SPI_ILLEGAL_COMMAND)
+				& cmd->resp[0]) {
+			cmd->error = MMC_ERR_INVALID;
+			value = -EINVAL;
+		} else if ((R1_SPI_ERASE_SEQ | R1_SPI_ERASE_RESET)
+				& cmd->resp[0]) {
+			cmd->error = MMC_ERR_FAILED;
+			value = -EINVAL;
+		} /* else R1_SPI_IDLE, "it's resetting" */
+
+		if (value < 0)
+			goto fail;
+	}
+
+	switch (mmc_spi_resp_type(cmd)) {
+
+	/* SPI R1B == R1 + busy; STOP_TRANSMISSION (for multiblock reads)
+	 * and less-common stuff like various erase operations.
+	 */
+	case MMC_RSP_SPI_R1B:
+		/* maybe we read all the busy tokens already */
+		while (cp < end && *cp == 0)
+			cp++;
+		if (cp == end)
+			mmc_spi_wait_unbusy(host, r1b_timeout);
+		break;
+
+	/* SPI R2 == R1 + second status byte; SEND_STATUS */
+	case MMC_RSP_SPI_R2:
+		cmd->resp[0] |= *cp << 8;
+		break;
+
+	/* SPI R3 or R7 == R1 + 4 bytes */
+	case MMC_RSP_SPI_R3:
+		cmd->resp[1] = be32_to_cpu(get_unaligned((u32 *)cp));
+		break;
+
+	/* SPI R1 == just one status byte */
+	case MMC_RSP_SPI_R1:
+		break;
+
+	default:
+		dev_dbg(&host->spi->dev, "bad response type %04x\n",
+				mmc_spi_resp_type(cmd));
+		if (value >= 0) {
+			cmd->error = MMC_ERR_INVALID;
+			value = -EINVAL;
+		}
+	}
+
+fail:
+	if (value < 0)
+		dev_dbg(&host->spi->dev, "%s: resp %04x %08x\n",
+			tag, cmd->resp[0], cmd->resp[1]);
+
+	/* disable chipselect on errors and some success cases */
+	if (value >= 0 && cs_on)
+		return value;
+done:
+	mmc_cs_off(host);
+	return value;
+}
+
+/* Issue command and read its response.
+ * Returns zero on success, negative for error.
+ *
+ * On error, caller must cope with mmc core retry mechanism.  That
+ * means immediate low-level resubmit, which affects the bus lock...
+ */
+static int
+mmc_spi_command_send(struct mmc_spi_host *host,
+		struct mmc_request *mrq,
+		struct mmc_command *cmd, int cs_on)
+{
+	struct scratch		*data = host->data;
+	u8			*cp = data->status;
+	u32			arg = cmd->arg;
+	int			status;
+	struct spi_transfer	*t;
+
+	/* We can handle most commands (except block reads) in one full
+	 * duplex I/O operation before either starting the next transfer
+	 * (data block or command) or else deselecting the card.
+	 *
+	 * First, write 7 bytes:
+	 *  - an all-ones byte to ensure the card is ready
+	 *  - opcode byte (plus start and transmission bits)
+	 *  - four bytes of big-endian argument
+	 *  - crc7 (plus end bit) ... always computed, it's cheap
+	 *
+	 * We init the whole buffer to all-ones, which is what we need
+	 * to write while we're reading (later) response data.
+	 */
+	memset(cp++, 0xff, sizeof(data->status));
+
+	*cp++ = 0x40 | cmd->opcode;
+	*cp++ = (u8)(arg >> 24);
+	*cp++ = (u8)(arg >> 16);
+	*cp++ = (u8)(arg >> 8);
+	*cp++ = (u8)arg;
+	*cp++ = (crc7(0, &data->status[1], 5) << 1) | 0x01;
+
+	/* Then, read up to 13 bytes (while writing all-ones):
+	 *  - N(CR) (== 1..8) bytes of all-ones
+	 *  - status byte (for all response types)
+	 *  - the rest of the response, either:
+	 *      + nothing, for R1 or R1B responses
+	 *	+ second status byte, for R2 responses
+	 *	+ four data bytes, for R3 and R7 responses
+	 *
+	 * Finally, read some more bytes ... in the nice cases we know in
+	 * advance how many, and reading 1 more is always OK:
+	 *  - N(EC) (== 0..N) bytes of all-ones, before deselect/finish
+	 *  - N(RC) (== 1..N) bytes of all-ones, before next command
+	 *  - N(WR) (== 1..N) bytes of all-ones, before data write
+	 *
+	 * So in those cases one full duplex I/O of at most 21 bytes will
+	 * handle the whole command, leaving the card ready to receive a
+	 * data block or new command.  We do that whenever we can, shaving
+	 * CPU and IRQ costs (especially when using DMA or FIFOs).
+	 *
+	 * There are two other cases, where it's not generally practical
+	 * to rely on a single I/O:
+	 *
+	 *  - R1B responses need at least N(EC) bytes of all-zeroes.
+	 *
+	 *    In this case we can *try* to fit it into one I/O, then
+	 *    maybe read more data later.
+	 *
+	 *  - Data block reads are more troublesome, since a variable
+	 *    number of padding bytes precede the token and data.
+	 *      + N(CX) (== 0..8) bytes of all-ones, before CSD or CID
+	 *      + N(AC) (== 1..many) bytes of all-ones
+	 *
+	 *    In this case we currently only have minimal speedups here:
+	 *    when N(CR) == 1 we can avoid I/O in response_get().
+	 */
+	if (cs_on && (mrq->data->flags & MMC_DATA_READ)) {
+		cp += 2;	/* min(N(CR)) + status */
+		/* R1 */
+	} else {
+		cp += 10;	/* max(N(CR)) + status + min(N(RC),N(WR)) */
+		if (cmd->flags & MMC_RSP_SPI_S2)	/* R2 */
+			cp++;
+		else if (cmd->flags & MMC_RSP_SPI_B4)	/* R3, R7 */
+			cp += 4;
+		else if (cmd->flags & MMC_RSP_BUSY)	/* R1B */
+			cp = data->status + sizeof(data->status);
+		/* else:  R1 (most commands) */
+	}
+
+	dev_dbg(&host->spi->dev, "  mmc_spi: CMD%d, MMC_SPI_%s\n",
+		cmd->opcode, maptype(cmd));
+
+	/* send command, leaving chipselect active */
+	spi_message_init(&host->m);
+
+	t = &host->t;
+	memset(t, 0, sizeof *t);
+	t->tx_buf = t->rx_buf = data->status;
+	t->tx_dma = t->rx_dma = host->data_dma;
+	t->len = cp - data->status;
+	t->cs_change = 1;
+	spi_message_add_tail(t, &host->m);
+
+	if (host->dma_dev) {
+		host->m.is_dma_mapped = 1;
+		dma_sync_single_for_device(host->dma_dev,
+				host->data_dma, sizeof *host->data,
+				DMA_BIDIRECTIONAL);
+	}
+	status = spi_sync(host->spi, &host->m);
+	if (status == 0)
+		status = host->m.status;
+
+	if (host->dma_dev)
+		dma_sync_single_for_cpu(host->dma_dev,
+				host->data_dma, sizeof *host->data,
+				DMA_BIDIRECTIONAL);
+	if (status < 0) {
+		dev_dbg(&host->spi->dev, "  ... write returned %d\n", status);
+		cmd->error = MMC_ERR_FAILED;
+		return status;
+	}
+
+	/* after no-data commands and STOP_TRANSMISSION, chipselect off */
+	return mmc_spi_response_get(host, cmd, cs_on);
+}
+
+/* Build data message with up to four separate transfers.  For TX, we
+ * start by writing the data token.  And in most cases, we finish with
+ * a status transfer.
+ *
+ * We always provide TX data for data and CRC.  The MMC/SD protocol
+ * requires us to write ones; but Linux defaults to writing zeroes;
+ * so we explicitly initialize it to all ones on RX paths.
+ *
+ * We also handle DMA mapping, so the underlying SPI controller does
+ * not need to (re)do it for each message.
+ */
+static void
+mmc_spi_setup_data_message(
+	struct mmc_spi_host	*host,
+	int			multiple,
+	enum dma_data_direction	direction)
+{
+	struct spi_transfer	*t;
+	struct scratch		*scratch = host->data;
+	dma_addr_t		dma = host->data_dma;
+
+	spi_message_init(&host->m);
+	if (dma)
+		host->m.is_dma_mapped = 1;
+
+	/* for reads, readblock() skips 0xff bytes before finding
+	 * the token; for writes, this transfer issues that token.
+	 */
+	if (direction == DMA_TO_DEVICE) {
+		t = &host->token;
+		memset(t, 0, sizeof *t);
+		t->len = 1;
+		if (multiple)
+			scratch->data_token = SPI_TOKEN_MULTI_WRITE;
+		else
+			scratch->data_token = SPI_TOKEN_SINGLE;
+		t->tx_buf = &scratch->data_token;
+		if (dma)
+			t->tx_dma = dma + offsetof(struct scratch, data_token);
+		spi_message_add_tail(t, &host->m);
+	}
+
+	/* Body of transfer is buffer, then CRC ...
+	 * either TX-only, or RX with TX-ones.
+	 */
+	t = &host->t;
+	memset(t, 0, sizeof *t);
+	t->tx_buf = host->ones;
+	t->tx_dma = host->ones_dma;
+	/* length and actual buffer info are written later */
+	spi_message_add_tail(t, &host->m);
+
+	t = &host->crc;
+	memset(t, 0, sizeof *t);
+	t->len = 2;
+	if (direction == DMA_TO_DEVICE) {
+		/* the actual CRC may get written later */
+		t->tx_buf = &scratch->crc_val;
+		if (dma)
+			t->tx_dma = dma + offsetof(struct scratch, crc_val);
+	} else {
+		t->tx_buf = host->ones;
+		t->tx_dma = host->ones_dma;
+		t->rx_buf = &scratch->crc_val;
+		if (dma)
+			t->rx_dma = dma + offsetof(struct scratch, crc_val);
+	}
+	spi_message_add_tail(t, &host->m);
+
+	/*
+	 * A single block read is followed by N(EC) [0+] all-ones bytes
+	 * before deselect ... don't bother.
+	 *
+	 * Multiblock reads are followed by N(AC) [1+] all-ones bytes before
+	 * the next block is read, or a STOP_TRANSMISSION is issued.  We'll
+	 * collect that single byte, so readblock() doesn't need to.
+	 *
+	 * For a write, the one-byte data response follows immediately, then
+	 * come zero or more busy bytes, then N(WR) [1+] all-ones bytes.
+	 * Then single block reads may deselect, and multiblock ones issue
+	 * the next token (next data block, or STOP_TRAN).  We can try to
+	 * minimize I/O ops by using a single read to collect end-of-busy.
+	 */
+	if (multiple || direction == DMA_TO_DEVICE) {
+		t = &host->early_status;
+		memset(t, 0, sizeof *t);
+		t->len = (direction == DMA_TO_DEVICE)
+				? sizeof(scratch->status)
+				: 1;
+		t->tx_buf = host->ones;
+		t->tx_dma = host->ones_dma;
+		t->rx_buf = scratch->status;
+		if (dma)
+			t->rx_dma = dma + offsetof(struct scratch, status);
+		t->cs_change = 1;
+		spi_message_add_tail(t, &host->m);
+	}
+}
+
+/*
+ * Write one block:
+ *  - caller handled preceding N(WR) [1+] all-ones bytes
+ *  - data block
+ *	+ token
+ *	+ data bytes
+ *	+ crc16
+ *  - an all-ones byte ... card writes a data-response byte
+ *  - followed by N(EC) [0+] all-ones bytes, card writes zero/'busy'
+ *
+ * Return negative errno, else success.
+ */
+static int
+mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t)
+{
+	struct spi_device	*spi = host->spi;
+	int			status, i;
+	struct scratch		*scratch = host->data;
+
+	if (host->mmc->use_spi_crc)
+		scratch->crc_val = cpu_to_be16(
+				crc_itu_t(0, t->tx_buf, t->len));
+	if (host->dma_dev)
+		dma_sync_single_for_device(host->dma_dev,
+				host->data_dma, sizeof *scratch,
+				DMA_BIDIRECTIONAL);
+
+	status = spi_sync(spi, &host->m);
+	if (status == 0)
+		status = host->m.status;
+
+	if (status != 0) {
+		dev_dbg(&spi->dev, "write error (%d)\n", status);
+		return status;
+	}
+
+	if (host->dma_dev)
+		dma_sync_single_for_cpu(host->dma_dev,
+				host->data_dma, sizeof *scratch,
+				DMA_BIDIRECTIONAL);
+
+	/*
+	 * Get the transmission data-response reply.  It must follow
+	 * immediately after the data block we transferred.  This reply
+	 * doesn't necessarily tell whether the write operation succeeded;
+	 * it just says if the transmission was ok and whether *earlier*
+	 * writes succeeded; see the standard.
+	 */
+	switch (SPI_MMC_RESPONSE_CODE(scratch->status[0])) {
+	case SPI_RESPONSE_ACCEPTED:
+		status = 0;
+		break;
+	case SPI_RESPONSE_CRC_ERR:
+		/* host shall then issue MMC_STOP_TRANSMISSION */
+		status = -EILSEQ;
+		break;
+	case SPI_RESPONSE_WRITE_ERR:
+		/* host shall then issue MMC_STOP_TRANSMISSION,
+		 * and should MMC_SEND_STATUS to sort it out
+		 */
+		status = -EIO;
+		break;
+	default:
+		status = -EPROTO;
+		break;
+	}
+	if (status != 0) {
+		dev_dbg(&spi->dev, "write error %02x (%d)\n",
+			scratch->status[0], status);
+		return status;
+	}
+
+	t->tx_buf += t->len;
+	if (host->dma_dev)
+		t->tx_dma += t->len;
+
+	/* Return when not busy.  If we didn't collect that status yet,
+	 * we'll need some more I/O.
+	 */
+	for (i = 1; i < sizeof scratch->status; i++) {
+		if (scratch->status[i] != 0)
+			return 0;
+	}
+	return mmc_spi_wait_unbusy(host, writeblock_timeout);
+}
+
+/*
+ * Read one block:
+ *  - skip leading all-ones bytes ... either
+ *      + N(AC) [1..f(clock,CSD)] usually, else
+ *      + N(CX) [0..8] when reading CSD or CID
+ *  - data block
+ *	+ token ... if error token, no data or crc
+ *	+ data bytes
+ *	+ crc16
+ *
+ * After single block reads, we're done; N(EC) [0+] all-ones bytes follow
+ * before dropping chipselect.
+ *
+ * For multiblock reads, caller either reads the next block or issues a
+ * STOP_TRANSMISSION command.
+ */
+static int
+mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t)
+{
+	struct spi_device	*spi = host->spi;
+	int			status;
+	struct scratch		*scratch = host->data;
+
+	/* At least one SD card sends an all-zeroes byte when N(CX)
+	 * applies, before the all-ones bytes ... just cope with that.
+	 */
+	status = mmc_spi_readbytes(host, 1);
+	if (status < 0)
+		return status;
+	status = scratch->status[0];
+	if (status == 0xff || status == 0)
+		status = mmc_spi_readtoken(host);
+
+	if (status == SPI_TOKEN_SINGLE) {
+		if (host->dma_dev)
+			dma_sync_single_for_device(host->dma_dev,
+					host->data_dma, sizeof *scratch,
+					DMA_BIDIRECTIONAL);
+
+		status = spi_sync(spi, &host->m);
+		if (status == 0)
+			status = host->m.status;
+
+		if (host->dma_dev)
+			dma_sync_single_for_cpu(host->dma_dev,
+					host->data_dma, sizeof *scratch,
+					DMA_BIDIRECTIONAL);
+	} else {
+		dev_dbg(&spi->dev, "read error %02x (%d)\n", status, status);
+
+		/* we've read extra garbage, timed out, etc */
+		if (status < 0)
+			return status;
+
+		/* low four bits are an R2 subset, fifth seems to be
+		 * vendor specific ... map them all to generic error..
+		 */
+		return -EIO;
+	}
+
+	if (host->mmc->use_spi_crc) {
+		u16 crc = crc_itu_t(0, t->rx_buf, t->len);
+
+		be16_to_cpus(&scratch->crc_val);
+		if (scratch->crc_val != crc) {
+			dev_dbg(&spi->dev, "read - crc error: crc_val=0x%04x, "
+					"computed=0x%04x len=%d\n",
+					scratch->crc_val, crc, t->len);
+			return -EILSEQ;
+		}
+	}
+
+	t->rx_buf += t->len;
+	if (host->dma_dev)
+		t->rx_dma += t->len;
+
+	return 0;
+}
+
+/*
+ * An MMC/SD data stage includes one or more blocks, optional CRCs,
+ * and inline handshaking.  That handhaking makes it unlike most
+ * other SPI protocol stacks.
+ */
+static void
+mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
+		struct mmc_data *data, u32 blk_size)
+{
+	struct spi_device	*spi = host->spi;
+	struct device		*dma_dev = host->dma_dev;
+	struct spi_transfer	*t;
+	enum dma_data_direction	direction;
+	struct scatterlist	*sg;
+	unsigned		n_sg;
+	int			multiple = (data->blocks > 1);
+
+	if (data->flags & MMC_DATA_READ)
+		direction = DMA_FROM_DEVICE;
+	else
+		direction = DMA_TO_DEVICE;
+	mmc_spi_setup_data_message(host, multiple, direction);
+	t = &host->t;
+
+	/* Handle scatterlist segments one at a time, with synch for
+	 * each 512-byte block
+	 */
+	for (sg = data->sg, n_sg = data->sg_len; n_sg; n_sg--, sg++) {
+		int			status = 0;
+		dma_addr_t		dma_addr = 0;
+		void			*kmap_addr;
+		unsigned		length = sg->length;
+
+		/* set up dma mapping for controller drivers that might
+		 * use DMA ... though they may fall back to PIO
+		 */
+		if (dma_dev) {
+			dma_addr = dma_map_page(dma_dev, sg->page, 0,
+						PAGE_SIZE, direction);
+			if (direction == DMA_TO_DEVICE)
+				t->tx_dma = dma_addr + sg->offset;
+			else
+				t->rx_dma = dma_addr + sg->offset;
+		}
+
+		/* allow pio too; we don't allow highmem */
+		kmap_addr = kmap(sg->page);
+		if (direction == DMA_TO_DEVICE)
+			t->tx_buf = kmap_addr + sg->offset;
+		else
+			t->rx_buf = kmap_addr + sg->offset;
+
+		/* transfer each block, and update request status */
+		while (length) {
+			t->len = min(length, blk_size);
+
+			dev_dbg(&host->spi->dev,
+				"    mmc_spi: %s block, %d bytes\n",
+				(direction == DMA_TO_DEVICE)
+				? "write"
+				: "read",
+				t->len);
+
+			if (direction == DMA_TO_DEVICE)
+				status = mmc_spi_writeblock(host, t);
+			else
+				status = mmc_spi_readblock(host, t);
+			if (status < 0)
+				break;
+
+			data->bytes_xfered += t->len;
+			length -= t->len;
+
+			if (!multiple)
+				break;
+		}
+
+		/* discard mappings */
+		if (direction == DMA_FROM_DEVICE)
+			flush_kernel_dcache_page(sg->page);
+		kunmap(sg->page);
+		if (dma_dev)
+			dma_unmap_page(dma_dev, dma_addr,
+					PAGE_SIZE, direction);
+
+		if (status < 0) {
+			dev_dbg(&spi->dev, "%s status %d\n",
+				(direction == DMA_TO_DEVICE)
+					? "write" : "read",
+				status);
+			if (status == -EILSEQ)
+				data->error = MMC_ERR_BADCRC;
+			else if (status == -ETIMEDOUT)
+				data->error = MMC_ERR_TIMEOUT;
+			else if (status == -EINVAL)
+				data->error = MMC_ERR_INVALID;
+			else if (data->error == MMC_ERR_NONE)
+				data->error = MMC_ERR_FAILED;
+			break;
+		}
+	}
+
+	/* NOTE some docs describe an MMC-only SET_BLOCK_COUNT (CMD23) that
+	 * can be issued before multiblock writes.  Unlike its more widely
+	 * documented analogue for SD cards (SET_WR_BLK_ERASE_COUNT, ACMD23),
+	 * that can affect the STOP_TRAN logic.   Complete (and current)
+	 * MMC specs should sort that out before Linux starts using CMD23.
+	 */
+	if (direction == DMA_TO_DEVICE && multiple) {
+		struct scratch	*scratch = host->data;
+		int		tmp;
+		const unsigned	statlen = sizeof(scratch->status);
+
+		dev_dbg(&spi->dev, "    mmc_spi: STOP_TRAN\n");
+
+		/* Tweak the per-block message we set up earlier by morphing
+		 * it to hold single buffer with the token followed by some
+		 * all-ones bytes ... skip N(BR) (0..1), scan the rest for
+		 * "not busy any longer" status, and leave chip selected.
+		 */
+		INIT_LIST_HEAD(&host->m.transfers);
+		list_add(&host->early_status.transfer_list,
+				&host->m.transfers);
+
+		scratch->status[0] = SPI_TOKEN_STOP_TRAN;
+		memset(scratch->status + 1, 0xff, statlen - 1);
+		host->early_status.tx_buf = host->early_status.rx_buf;
+		host->early_status.tx_dma = host->early_status.rx_dma;
+		host->early_status.len = 1 + statlen;
+
+		if (host->dma_dev)
+			dma_sync_single_for_device(host->dma_dev,
+					host->data_dma, sizeof *scratch,
+					DMA_BIDIRECTIONAL);
+
+		tmp = spi_sync(spi, &host->m);
+		if (tmp == 0)
+			tmp = host->m.status;
+
+		if (host->dma_dev)
+			dma_sync_single_for_cpu(host->dma_dev,
+					host->data_dma, sizeof *scratch,
+					DMA_BIDIRECTIONAL);
+
+		if (tmp < 0) {
+			if (data->error == MMC_ERR_NONE)
+				data->error = MMC_ERR_FAILED;
+			return;
+		}
+
+		/* Ideally we collected "not busy" status with one I/O,
+		 * avoiding wasteful byte-at-a-time scanning... but more
+		 * I/O is often needed.
+		 */
+		for (tmp = 2; tmp < statlen; tmp++) {
+			if (scratch->status[tmp] != 0)
+				return;
+		}
+		tmp = mmc_spi_wait_unbusy(host, writeblock_timeout);
+		if (tmp < 0 && data->error == MMC_ERR_NONE) {
+			if (tmp == -ETIMEDOUT)
+				data->error = MMC_ERR_TIMEOUT;
+			else
+				data->error = MMC_ERR_FAILED;
+		}
+	}
+}
+
+/****************************************************************************/
+
+/*
+ * MMC driver implementation -- the interface to the MMC stack
+ */
+
+static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct mmc_spi_host	*host = mmc_priv(mmc);
+	int			status = -EINVAL;
+
+#ifdef DEBUG
+	/* MMC core and layered drivers *MUST* issue SPI-aware commands */
+	{
+		struct mmc_command	*cmd;
+		int			invalid = 0;
+
+		cmd = mrq->cmd;
+		if (!mmc_spi_resp_type(cmd)) {
+			dev_dbg(&host->spi->dev, "bogus command\n");
+			cmd->error = MMC_ERR_INVALID;
+			invalid = 1;
+		}
+
+		cmd = mrq->stop;
+		if (cmd && !mmc_spi_resp_type(cmd)) {
+			dev_dbg(&host->spi->dev, "bogus STOP command\n");
+			cmd->error = MMC_ERR_INVALID;
+			invalid = 1;
+		}
+
+		if (invalid) {
+			dump_stack();
+			mmc_request_done(host->mmc, mrq);
+			return;
+		}
+	}
+#endif
+
+	/* issue command; then optionally data and stop */
+	status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL);
+	if (status == 0 && mrq->data) {
+		mmc_spi_data_do(host, mrq->cmd, mrq->data, mrq->data->blksz);
+		if (mrq->stop)
+			status = mmc_spi_command_send(host, mrq, mrq->stop, 0);
+		else
+			mmc_cs_off(host);
+	}
+
+	mmc_request_done(host->mmc, mrq);
+}
+
+/* See Section 6.4.1, in SD "Simplified Physical Layer Specification 2.0"
+ *
+ * NOTE that here we can't know that the card has just been powered up;
+ * not all MMC/SD sockets support power switching.
+ *
+ * FIXME when the card is still in SPI mode, e.g. from a previous kernel,
+ * this doesn't seem to do the right thing at all...
+ */
+static void mmc_spi_initsequence(struct mmc_spi_host *host)
+{
+	/* Try to be very sure any previous command has completed;
+	 * wait till not-busy, skip debris from any old commands.
+	 */
+	mmc_spi_wait_unbusy(host, r1b_timeout);
+	mmc_spi_readbytes(host, 10);
+
+	/*
+	 * Do a burst with chipselect active-high.  We need to do this to
+	 * meet the requirement of 74 clock cycles with both chipselect
+	 * and CMD (MOSI) high before CMD0 ... after the card has been
+	 * powered up to Vdd(min), and so is ready to take commands.
+	 *
+	 * Some cards are particularly needy of this (e.g. Viking "SD256")
+	 * while most others don't seem to care.
+	 *
+	 * Note that this is one of the places MMC/SD plays games with the
+	 * SPI protocol.  Another is that when chipselect is released while
+	 * the card returns BUSY status, the clock must issue several cycles
+	 * with chipselect high before the card will stop driving its output.
+	 */
+	host->spi->mode |= SPI_CS_HIGH;
+	if (spi_setup(host->spi) != 0) {
+		/* Just warn; most cards work without it. */
+		dev_warn(&host->spi->dev,
+				"can't change chip-select polarity\n");
+		host->spi->mode &= ~SPI_CS_HIGH;
+	} else {
+		mmc_spi_readbytes(host, 18);
+
+		host->spi->mode &= ~SPI_CS_HIGH;
+		if (spi_setup(host->spi) != 0) {
+			/* Wot, we can't get the same setup we had before? */
+			dev_err(&host->spi->dev,
+					"can't restore chip-select polarity\n");
+		}
+	}
+}
+
+static char *mmc_powerstring(u8 power_mode)
+{
+	switch (power_mode) {
+	case MMC_POWER_OFF: return "off";
+	case MMC_POWER_UP:  return "up";
+	case MMC_POWER_ON:  return "on";
+	}
+	return "?";
+}
+
+static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct mmc_spi_host *host = mmc_priv(mmc);
+
+	if (host->power_mode != ios->power_mode) {
+		int		canpower;
+
+		canpower = host->pdata && host->pdata->setpower;
+
+		dev_dbg(&host->spi->dev, "mmc_spi: power %s (%d)%s\n",
+				mmc_powerstring(ios->power_mode),
+				ios->vdd,
+				canpower ? ", can switch" : "");
+
+		/* switch power on/off if possible, accounting for
+		 * max 250msec powerup time if needed.
+		 */
+		if (canpower) {
+			switch (ios->power_mode) {
+			case MMC_POWER_OFF:
+			case MMC_POWER_UP:
+				host->pdata->setpower(&host->spi->dev,
+						ios->vdd);
+				if (ios->power_mode == MMC_POWER_UP)
+					msleep(host->powerup_msecs);
+			}
+		}
+
+		/* See 6.4.1 in the simplified SD card physical spec 2.0 */
+		if (ios->power_mode == MMC_POWER_ON)
+			mmc_spi_initsequence(host);
+
+		/* If powering down, ground all card inputs to avoid power
+		 * delivery from data lines!  On a shared SPI bus, this
+		 * will probably be temporary; 6.4.2 of the simplified SD
+		 * spec says this must last at least 1msec.
+		 *
+		 *   - Clock low means CPOL 0, e.g. mode 0
+		 *   - MOSI low comes from writing zero
+		 *   - Chipselect is usually active low...
+		 */
+		if (canpower && ios->power_mode == MMC_POWER_OFF) {
+			int mres;
+
+			host->spi->mode &= ~(SPI_CPOL|SPI_CPHA);
+			mres = spi_setup(host->spi);
+			if (mres < 0)
+				dev_dbg(&host->spi->dev,
+					"switch to SPI mode 0 failed\n");
+
+			if (spi_w8r8(host->spi, 0x00) < 0)
+				dev_dbg(&host->spi->dev,
+					"put spi signals to low failed\n");
+
+			/*
+			 * Now clock should be low due to spi mode 0;
+			 * MOSI should be low because of written 0x00;
+			 * chipselect should be low (it is active low)
+			 * power supply is off, so now MMC is off too!
+			 *
+			 * FIXME no, chipselect can be high since the
+			 * device is inactive and SPI_CS_HIGH is clear...
+			 */
+			msleep(10);
+			if (mres == 0) {
+				host->spi->mode |= (SPI_CPOL|SPI_CPHA);
+				mres = spi_setup(host->spi);
+				if (mres < 0)
+					dev_dbg(&host->spi->dev,
+						"switch back to SPI mode 3"
+						" failed\n");
+			}
+		}
+
+		host->power_mode = ios->power_mode;
+	}
+
+	if (host->spi->max_speed_hz != ios->clock && ios->clock != 0) {
+		int		status;
+
+		host->spi->max_speed_hz = ios->clock;
+		status = spi_setup(host->spi);
+		dev_dbg(&host->spi->dev,
+			"mmc_spi:  clock to %d Hz, %d\n",
+			host->spi->max_speed_hz, status);
+	}
+}
+
+static int mmc_spi_get_ro(struct mmc_host *mmc)
+{
+	struct mmc_spi_host *host = mmc_priv(mmc);
+
+	if (host->pdata && host->pdata->get_ro)
+		return host->pdata->get_ro(mmc->parent);
+	/* board doesn't support read only detection; assume writeable */
+	return 0;
+}
+
+
+static const struct mmc_host_ops mmc_spi_ops = {
+	.request	= mmc_spi_request,
+	.set_ios	= mmc_spi_set_ios,
+	.get_ro		= mmc_spi_get_ro,
+};
+
+
+/****************************************************************************/
+
+/*
+ * SPI driver implementation
+ */
+
+static irqreturn_t
+mmc_spi_detect_irq(int irq, void *mmc)
+{
+	struct mmc_spi_host *host = mmc_priv(mmc);
+	u16 delay_msec = max(host->pdata->detect_delay, (u16)100);
+
+	mmc_detect_change(mmc, msecs_to_jiffies(delay_msec));
+	return IRQ_HANDLED;
+}
+
+static int mmc_spi_probe(struct spi_device *spi)
+{
+	void			*ones;
+	struct mmc_host		*mmc;
+	struct mmc_spi_host	*host;
+	int			status;
+
+	/* SanDisk and Hitachi MMC docs both show clock timing diagrams
+	 * with clock starting low (CPOL=0) and sampling on leading edge
+	 * (CPHA=0); clock is measured between rising edges.  Sandisk SD
+	 * docs show clock starting high (CPOL=1) and sampling on trailing
+	 * edge (CPHA=1), measuring between falling edges.
+	 *
+	 * Docs are very explicit that sampling is on the rising edge, so
+	 * SPI_MODE_0 and SPI_MODE_3 having different CPOL may not matter.
+	 */
+	spi->mode |= SPI_CPOL | SPI_CPHA;
+	spi->bits_per_word = 8;
+
+	status = spi_setup(spi);
+	if (status < 0) {
+		dev_dbg(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n",
+				spi->mode, spi->max_speed_hz / 1000,
+				status);
+		return status;
+	}
+
+	/* We can use the bus safely iff nobody else will interfere with
+	 * us.  That is, either we have the experimental exclusive access
+	 * primitives ... or else there's nobody to share it with.
+	 */
+	if (spi->master->num_chipselect > 1) {
+		struct device	*parent = spi->dev.parent;
+
+		/* If there are multiple devices on this bus, we
+		 * can't proceed.
+		 */
+		spin_lock(&parent->klist_children.k_lock);
+		if (parent->klist_children.k_list.next
+				!= parent->klist_children.k_list.prev)
+			status = -EMLINK;
+		else
+			status = 0;
+		spin_unlock(&parent->klist_children.k_lock);
+		if (status < 0) {
+			dev_err(&spi->dev, "can't share SPI bus\n");
+			return status;
+		}
+
+		/* REVISIT we can't guarantee another device won't
+		 * be added later.  It's uncommon though ... for now,
+		 * work as if this is safe.
+		 */
+		dev_warn(&spi->dev, "ASSUMING unshared SPI bus!\n");
+	}
+
+	/* We need a supply of ones to transmit.  This is the only time
+	 * the CPU touches these, so cache coherency isn't a concern.
+	 *
+	 * NOTE if many systems use more than one MMC-over-SPI connector
+	 * it'd save some memory to share this.  That's evidently rare.
+	 */
+	status = -ENOMEM;
+	ones = kmalloc(MMC_SPI_BLOCKSIZE, GFP_KERNEL);
+	if (!ones)
+		goto nomem;
+	memset(ones, 0xff, MMC_SPI_BLOCKSIZE);
+
+	mmc = mmc_alloc_host(sizeof *host, &spi->dev);
+	if (!mmc)
+		goto nomem;
+
+	mmc->ops = &mmc_spi_ops;
+	mmc->max_blk_size = MMC_SPI_BLOCKSIZE;
+
+	/* As long as we keep track of the number of successfully
+	 * transmitted blocks, we're good for multiwrite.
+	 */
+	mmc->caps = MMC_CAP_SPI | MMC_CAP_MULTIWRITE;
+
+	/* SPI doesn't need the lowspeed device identification thing for
+	 * MMC or SD cards, since it never comes up in open drain mode.
+	 * That's good; some SPI masters can't handle very low speeds!
+	 *
+	 * However, low speed SDIO cards need not handle over 400 KHz;
+	 * that's the only reason not to use a few MHz for f_min (until
+	 * the upper layer reads the target frequency from the CSD).
+	 */
+	mmc->f_min = 400000;
+	mmc->f_max = spi->max_speed_hz;
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->spi = spi;
+
+	host->ones = ones;
+
+	/* Platform data is used to hook up things like card sensing
+	 * and power switching gpios.
+	 */
+	host->pdata = spi->dev.platform_data;
+	if (host->pdata)
+		mmc->ocr_avail = host->pdata->ocr_mask;
+	if (!mmc->ocr_avail) {
+		dev_warn(&spi->dev, "ASSUMING 3.2-3.4 V slot power\n");
+		mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+	}
+	if (host->pdata && host->pdata->setpower) {
+		host->powerup_msecs = host->pdata->powerup_msecs;
+		if (!host->powerup_msecs || host->powerup_msecs > 250)
+			host->powerup_msecs = 250;
+	}
+
+	dev_set_drvdata(&spi->dev, mmc);
+
+	/* preallocate dma buffers */
+	host->data = kmalloc(sizeof *host->data, GFP_KERNEL);
+	if (!host->data)
+		goto fail_nobuf1;
+
+	if (spi->master->cdev.dev->dma_mask) {
+		struct device	*dev = spi->master->cdev.dev;
+
+		host->dma_dev = dev;
+		host->ones_dma = dma_map_single(dev, ones,
+				MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
+		host->data_dma = dma_map_single(dev, host->data,
+				sizeof *host->data, DMA_BIDIRECTIONAL);
+
+		/* REVISIT in theory those map operations can fail... */
+
+		dma_sync_single_for_cpu(host->dma_dev,
+				host->data_dma, sizeof *host->data,
+				DMA_BIDIRECTIONAL);
+	}
+
+	/* setup message for status/busy readback */
+	spi_message_init(&host->readback);
+	host->readback.is_dma_mapped = (host->dma_dev != NULL);
+
+	spi_message_add_tail(&host->status, &host->readback);
+	host->status.tx_buf = host->ones;
+	host->status.tx_dma = host->ones_dma;
+	host->status.rx_buf = &host->data->status;
+	host->status.rx_dma = host->data_dma + offsetof(struct scratch, status);
+	host->status.cs_change = 1;
+
+	/* register card detect irq */
+	if (host->pdata && host->pdata->init) {
+		status = host->pdata->init(&spi->dev, mmc_spi_detect_irq, mmc);
+		if (status != 0)
+			goto fail_glue_init;
+	}
+
+	status = mmc_add_host(mmc);
+	if (status != 0)
+		goto fail_add_host;
+
+	dev_info(&spi->dev, "SD/MMC host %s%s%s%s\n",
+			mmc->class_dev.bus_id,
+			host->dma_dev ? "" : ", no DMA",
+			(host->pdata && host->pdata->get_ro)
+				? "" : ", no WP",
+			(host->pdata && host->pdata->setpower)
+				? "" : ", no poweroff");
+	return 0;
+
+fail_add_host:
+	mmc_remove_host (mmc);
+fail_glue_init:
+	if (host->dma_dev)
+		dma_unmap_single(host->dma_dev, host->data_dma,
+				sizeof *host->data, DMA_BIDIRECTIONAL);
+	kfree(host->data);
+
+fail_nobuf1:
+	mmc_free_host(mmc);
+	dev_set_drvdata(&spi->dev, NULL);
+
+nomem:
+	kfree(ones);
+	return status;
+}
+
+
+static int __devexit mmc_spi_remove(struct spi_device *spi)
+{
+	struct mmc_host		*mmc = dev_get_drvdata(&spi->dev);
+	struct mmc_spi_host	*host;
+
+	if (mmc) {
+		host = mmc_priv(mmc);
+
+		/* prevent new mmc_detect_change() calls */
+		if (host->pdata && host->pdata->exit)
+			host->pdata->exit(&spi->dev, mmc);
+
+		mmc_remove_host(mmc);
+
+		if (host->dma_dev) {
+			dma_unmap_single(host->dma_dev, host->ones_dma,
+				MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
+			dma_unmap_single(host->dma_dev, host->data_dma,
+				sizeof *host->data, DMA_BIDIRECTIONAL);
+		}
+
+		kfree(host->data);
+		kfree(host->ones);
+
+		spi->max_speed_hz = mmc->f_max;
+		mmc_free_host(mmc);
+		dev_set_drvdata(&spi->dev, NULL);
+	}
+	return 0;
+}
+
+
+static struct spi_driver mmc_spi_driver = {
+	.driver = {
+		.name =		"mmc_spi",
+		.bus =		&spi_bus_type,
+		.owner =	THIS_MODULE,
+	},
+	.probe =	mmc_spi_probe,
+	.remove =	__devexit_p(mmc_spi_remove),
+};
+
+
+static int __init mmc_spi_init(void)
+{
+	return spi_register_driver(&mmc_spi_driver);
+}
+module_init(mmc_spi_init);
+
+
+static void __exit mmc_spi_exit(void)
+{
+	spi_unregister_driver(&mmc_spi_driver);
+}
+module_exit(mmc_spi_exit);
+
+
+MODULE_AUTHOR("Mike Lavender, David Brownell, "
+		"Hans-Peter Nilsson, Jan Nikitenko");
+MODULE_DESCRIPTION("SPI SD/MMC host driver");
+MODULE_LICENSE("GPL");

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found] ` <200707141504.51950.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
                     ` (3 preceding siblings ...)
  2007-07-14 22:08   ` [patch 2.6.22-git5 4/4] mmc_spi host driver David Brownell
@ 2007-07-16 16:48   ` Anton Vorontsov
       [not found]     ` <20070716164837.GA18707-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
  4 siblings, 1 reply; 47+ messages in thread
From: Anton Vorontsov @ 2007-07-16 16:48 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

Hello David,

On Sat, Jul 14, 2007 at 03:04:51PM -0700, David Brownell wrote:
> Here's an updated version of the MMC-over-SPI patches, reworked to
> address most of Pierre's issues (moving even more code into the MMC
> core) and to clean up the I/O paths.
> 
> To build/run these, you'll need the CRC7 patch from recent MM trees.
> 
>  - headers learn about SPI
> 
>  - mmc_block learns about SPI
> 
>  - mmc core updates 
> 
>  - mmc_spi host driver
> 
> The bits related to card lock/unlock aren't included here, since
> those didn't go upstream yet.

Much thanks for the patches!

Though, I still have some troubles with getting MMC(SD)-over-SPI to
work.

I'm using slightly patched spi_mpc83xx.c driver + these four mmc
patches.

spi_mpc83xx.c tested only in loopback mode and using oscilloscope.
It _seem_ to work fine, but as I didn't tested it on real hardware
(other than MMC/SD), I can't say if it's fault of spi_mpc83xx or
mmc_spi.

David, can you see anything helpful in these debug messages?

- - - -
mmc_spi spi1.0: ASSUMING unshared SPI bus!
mmc_spi spi1.0: ASSUMING 3.2-3.4 V slot power
mmc0: clock 0Hz busmode 0 powermode 0 cs 0 Vdd 0 width 0 timing 0
mmc_spi spi1.0: SD/MMC host mmc0, no DMA, no WP, no poweroff
mmc0: clock 0Hz busmode 2 powermode 1 cs 1 Vdd 21 width 0 timing 0
mmc_spi spi1.0: mmc_spi: power up (21)
mmc0: clock 400000Hz busmode 2 powermode 2 cs 1 Vdd 21 width 0 timing 0
mmc_spi spi1.0: mmc_spi: power on (21)
mmc_spi spi1.0: mmc_spi:  clock to 400000 Hz, 0
mmc0: starting CMD0 arg 00000000 flags 000000c0
mmc_spi spi1.0:   mmc_spi: CMD0, MMC_SPI_R1
mmc0: req done (CMD0): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD8 arg 000001aa flags 000002f5
mmc_spi spi1.0:   mmc_spi: CMD8, MMC_SPI_R3/R7
mmc_spi spi1.0:   ... CMD8 response SPI_R3/R7: resp 0005 00000000
mmc0: req done (CMD8): 5/0/0: 00000005 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00000000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000000 00000000 00000000 00000000
mmc0: starting CMD58 arg 00000000 flags 00000280
mmc_spi spi1.0:   mmc_spi: CMD58, MMC_SPI_R3/R7
mmc0: req done (CMD58): 0/0/0: 00000000 80ff8000 00000000 00000000
mmc0: clock 400000Hz busmode 2 powermode 2 cs 1 Vdd 20 width 0 timing 0
mmc0: starting CMD0 arg 00000000 flags 000000c0
mmc_spi spi1.0:   mmc_spi: CMD0, MMC_SPI_R1
mmc0: req done (CMD0): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD8 arg 000001aa flags 000002f5
mmc_spi spi1.0:   mmc_spi: CMD8, MMC_SPI_R3/R7
mmc_spi spi1.0:   ... CMD8 response SPI_R3/R7: resp 0005 00000000
mmc0: req done (CMD8): 5/0/0: 00000005 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD55 arg 00000000 flags 000000f5
mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
mmc0: starting CMD41 arg 00300000 flags 000000e1
mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
mmc0: req done (CMD41): 0/0/0: 00000000 00000000 00000000 00000000
mmc0: starting CMD59 arg 00000001 flags 00000080
mmc_spi spi1.0:   mmc_spi: CMD59, MMC_SPI_R1
mmc0: req done (CMD59): 0/0/0: 00000000 00000000 00000000 00000000
mmc0: starting CMD10 arg 00000000 flags 000000b5
mmc_spi spi1.0:   mmc_spi: CMD10, MMC_SPI_R1
mmc_spi spi1.0:     mmc_spi: read block, 16 bytes
mmc_spi spi1.0: read error f8 (248)
mmc_spi spi1.0: read status -5
mmc0: req done (CMD10): 0/4/0: 00000001 00000000 00000000 00000000
mmc0: clock 0Hz busmode 2 powermode 0 cs 1 Vdd 0 width 0 timing 0
mmc_spi spi1.0: mmc_spi: power off (0)
- - - -


SD card is Kingston 1GB, 3.3V.

Thanks!

p.s. I've sent subscription request to this list, but SF still
processing it. So please Cc me, just to be sure I'll not miss
something.

-- 
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
spi-devel-general mailing list
spi-devel-general@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]     ` <20070716164837.GA18707-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
@ 2007-07-16 18:54       ` David Brownell
       [not found]         ` <200707161154.54728.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-16 18:54 UTC (permalink / raw)
  To: avorontsov-hkdhdckH98+B+jHODAdFcQ
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

On Monday 16 July 2007, Anton Vorontsov wrote:
> Hello David,
> 
> On Sat, Jul 14, 2007 at 03:04:51PM -0700, David Brownell wrote:
> > Here's an updated version of the MMC-over-SPI patches, reworked to
> > address most of Pierre's issues (moving even more code into the MMC
> > core) and to clean up the I/O paths.
> > 
> > ...
> 
> Much thanks for the patches!
> 
> Though, I still have some troubles with getting MMC(SD)-over-SPI to
> work.
> 
> I'm using slightly patched spi_mpc83xx.c driver + these four mmc
> patches.
> 
> spi_mpc83xx.c tested only in loopback mode and using oscilloscope.
> It _seem_ to work fine, but as I didn't tested it on real hardware
> (other than MMC/SD), I can't say if it's fault of spi_mpc83xx or
> mmc_spi.

I can't say either, but my first thought is that it might be a
card-specific quirk.  I already found one in that particular place,
and the messages below look fine until the very end.


> David, can you see anything helpful in these debug messages?
> 
> - - - -
> mmc_spi spi1.0: ASSUMING unshared SPI bus!
> mmc_spi spi1.0: ASSUMING 3.2-3.4 V slot power

You should provide an ocr_mask in your platform data, saying what
voltage range you can supply ... even if it's only 3V3 !!


> mmc0: clock 0Hz busmode 0 powermode 0 cs 0 Vdd 0 width 0 timing 0
> mmc_spi spi1.0: SD/MMC host mmc0, no DMA, no WP, no poweroff
> mmc0: clock 0Hz busmode 2 powermode 1 cs 1 Vdd 21 width 0 timing 0
> mmc_spi spi1.0: mmc_spi: power up (21)
> mmc0: clock 400000Hz busmode 2 powermode 2 cs 1 Vdd 21 width 0 timing 0
> mmc_spi spi1.0: mmc_spi: power on (21)
> mmc_spi spi1.0: mmc_spi:  clock to 400000 Hz, 0
> mmc0: starting CMD0 arg 00000000 flags 000000c0
> mmc_spi spi1.0:   mmc_spi: CMD0, MMC_SPI_R1
> mmc0: req done (CMD0): 0/0/0: 00000001 00000000 00000000 00000000

Looks fine:  reset card, R1_SPI_IDLE status is set in
the status word (00000001) ... it'll be resetting for
some time.


> mmc0: starting CMD8 arg 000001aa flags 000002f5
> mmc_spi spi1.0:   mmc_spi: CMD8, MMC_SPI_R3/R7
> mmc_spi spi1.0:   ... CMD8 response SPI_R3/R7: resp 0005 00000000
> mmc0: req done (CMD8): 5/0/0: 00000005 00000000 00000000 00000000

OK, it doesn't handle SEND_EXT_CSD at all (status R1_SPI_ILLEGAL_COMMAND
in addition to the idle status), so it comes as MMC_ERR_INVALID.
(Or SEND_IF_COND, if this were an SD 2.0+ card...)


> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

ACMD41 == SD_SEND_OP_COND, it's still resetting.  Repeats..

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00000000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000000 00000000 00000000 00000000

Finally the reset completed, R1_SPI_IDLE bit clears.


> mmc0: starting CMD58 arg 00000000 flags 00000280
> mmc_spi spi1.0:   mmc_spi: CMD58, MMC_SPI_R3/R7
> mmc0: req done (CMD58): 0/0/0: 00000000 80ff8000 00000000 00000000

Reads the OCR, fine.

> mmc0: clock 400000Hz busmode 2 powermode 2 cs 1 Vdd 20 width 0 timing 0
> mmc0: starting CMD0 arg 00000000 flags 000000c0
> mmc_spi spi1.0:   mmc_spi: CMD0, MMC_SPI_R1
> mmc0: req done (CMD0): 0/0/0: 00000001 00000000 00000000 00000000

Restarts the enumeration, now knowing it's an SD card.

> mmc0: starting CMD8 arg 000001aa flags 000002f5
> mmc_spi spi1.0:   mmc_spi: CMD8, MMC_SPI_R3/R7
> mmc_spi spi1.0:   ... CMD8 response SPI_R3/R7: resp 0005 00000000
> mmc0: req done (CMD8): 5/0/0: 00000005 00000000 00000000 00000000

... so here it's trying SEND_IF_COND (still failing) ...

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000001 00000000 00000000 00000000

> mmc0: starting CMD55 arg 00000000 flags 000000f5
> mmc_spi spi1.0:   mmc_spi: CMD55, MMC_SPI_R1
> mmc0: req done (CMD55): 0/0/0: 00000001 00000000 00000000 00000000
> mmc0: starting CMD41 arg 00300000 flags 000000e1
> mmc_spi spi1.0:   mmc_spi: CMD41, MMC_SPI_R1
> mmc0: req done (CMD41): 0/0/0: 00000000 00000000 00000000 00000000

Reset finished, IDLE status is no longer set ...

> mmc0: starting CMD59 arg 00000001 flags 00000080
> mmc_spi spi1.0:   mmc_spi: CMD59, MMC_SPI_R1
> mmc0: req done (CMD59): 0/0/0: 00000000 00000000 00000000 00000000

Enables CRC mode (arg == 1).

> mmc0: starting CMD10 arg 00000000 flags 000000b5
> mmc_spi spi1.0:   mmc_spi: CMD10, MMC_SPI_R1
> mmc_spi spi1.0:     mmc_spi: read block, 16 bytes
> mmc_spi spi1.0: read error f8 (248)

Hmm, the data block in the MMC_SEND_CID command didn't start
with a valid token ... it saw 0xf8, not 0xfe, which is a
lowlevel protocol error.

Maybe your scope can show whether or not this was the actual
data sent by the card... or you can otherwise verify that the
input data (from start of CMD10) was a bunch of 0xff values
then an 0xf8...


> mmc_spi spi1.0: read status -5
> mmc0: req done (CMD10): 0/4/0: 00000001 00000000 00000000 00000000
> mmc0: clock 0Hz busmode 2 powermode 0 cs 1 Vdd 0 width 0 timing 0
> mmc_spi spi1.0: mmc_spi: power off (0)

Rather than retrying that command, everything shuts down.
Which is annoying, but seems to be the intended behavior;
if that's bug, it's not a bug in the SPI support.


> - - - -
> 
> 
> SD card is Kingston 1GB, 3.3V.

I didn't try Kingston cards.  Since everything worked fine until
that last command, maybe a different card will behave better.

The driver does handle one protocol quirk.  A SanDisk SD card
would issue an 0x00 byte right after issuing the command, in
that SEND_CID case as well as the SEND_CSD case.  That should
be an 0xff byte ... depending on exactly where that 0xf8 showed
up, maybe the mmc_spi_readblock() code should be changed to skip
not just 0x00 but *ANY* non-{0xff,0xfe} byte in that slot.

- Dave

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]         ` <200707161154.54728.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-17 15:13           ` Anton Vorontsov
       [not found]             ` <20070717151350.GA3752-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: Anton Vorontsov @ 2007-07-17 15:13 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

On Mon, Jul 16, 2007 at 11:54:54AM -0700, David Brownell wrote:
> On Monday 16 July 2007, Anton Vorontsov wrote:
> > Hello David,
> > 
> > On Sat, Jul 14, 2007 at 03:04:51PM -0700, David Brownell wrote:
> > > Here's an updated version of the MMC-over-SPI patches, reworked to
> > > address most of Pierre's issues (moving even more code into the MMC
> > > core) and to clean up the I/O paths.
> > > 
> > > ...
> > 
> > Much thanks for the patches!
> > 
> > Though, I still have some troubles with getting MMC(SD)-over-SPI to
> > work.
> > 
> > I'm using slightly patched spi_mpc83xx.c driver + these four mmc
> > patches.
> > 
> > spi_mpc83xx.c tested only in loopback mode and using oscilloscope.
> > It _seem_ to work fine, but as I didn't tested it on real hardware
> > (other than MMC/SD), I can't say if it's fault of spi_mpc83xx or
> > mmc_spi.
> 
> I can't say either, but my first thought is that it might be a
> card-specific quirk.  I already found one in that particular place,
> and the messages below look fine until the very end.

Uhh.. can you believe?

mmc_spi spi1.0: ASSUMING unshared SPI bus!
mmc_spi spi1.0: SD/MMC host mmc0, no DMA, no WP, no poweroff
mmcblk0: mmc0:0000 SD01G 1006080KiB
 mmcblk0: p1
root-/FxswC0nw/VuxbqukCiDuA@public.gmane.org:~# mount /dev/mmcblk0p1 /mnt/
EXT2-fs warning: mounting unchecked fs, running e2fsck is recommended
root-/FxswC0nw/VuxbqukCiDuA@public.gmane.org:~# ls /mnt/
bin  etc  include  lib  libexec  lost+found  man  sbin  share  var
root-/FxswC0nw/VuxbqukCiDuA@public.gmane.org:~#

Yup, I've turned debugging off, it's plainly working.


The only change I've made is:

--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -1184,7 +1184,7 @@ static int mmc_spi_probe(struct spi_device *spi)
 	 * Docs are very explicit that sampling is on the rising edge, so
 	 * SPI_MODE_0 and SPI_MODE_3 having different CPOL may not matter.
 	 */
-	spi->mode |= SPI_CPOL | SPI_CPHA;
+	spi->mode = 0;
 	spi->bits_per_word = 8;
 
 	status = spi_setup(spi);


Am I understanding correctly that SPI_CPOL is:

  ~~~| |~~| |~~~~
     | |  | |
0    |_|  |_|
(does not work for me)

While !SPI_CPOL is:

     |~|  |~|
     | |  | |
0 ___| |__| |____
(works great for me)

Is that correct? Not vice versa?

At least that is what I'm observing on oscilloscope. Maybe it's
something still messed in mpc83xx_spi.c (I've already fixed a lot),
or maybe something else. So, if mmc_spi modes are correct, then
mpc83xx_spi having inversed values, and should be fixed.

With only SPI_CPHA that thing does not work also.


Today I've tested it on bunch of cards, MMC Transcend 64MB,
MMC SanDisk 16MB, SD Kingston 128MB and 1GB. All of them work.

Thus, despite minor issues, mmc_spi works great, much thanks!

-- 
Anton Vorontsov
email: cbou-JGs/UdohzUI@public.gmane.org
backup email: ya-cbou-o+MxOtu4lMCHXe+LvDLADg@public.gmane.org
irc://irc.freenode.net/bd2

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]             ` <20070717151350.GA3752-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
@ 2007-07-17 16:11               ` David Brownell
       [not found]                 ` <200707170911.16715.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-17 16:11 UTC (permalink / raw)
  To: avorontsov-hkdhdckH98+B+jHODAdFcQ
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

On Tuesday 17 July 2007, Anton Vorontsov wrote:

> Uhh.. can you believe?
> 
> mmc_spi spi1.0: ASSUMING unshared SPI bus!
> mmc_spi spi1.0: SD/MMC host mmc0, no DMA, no WP, no poweroff
> mmcblk0: mmc0:0000 SD01G 1006080KiB
>  mmcblk0: p1
> root-/FxswC0nw/VuxbqukCiDuA@public.gmane.org:~# mount /dev/mmcblk0p1 /mnt/
> EXT2-fs warning: mounting unchecked fs, running e2fsck is recommended
> root-/FxswC0nw/VuxbqukCiDuA@public.gmane.org:~# ls /mnt/
> bin  etc  include  lib  libexec  lost+found  man  sbin  share  var
> root-/FxswC0nw/VuxbqukCiDuA@public.gmane.org:~#

Good!


> Yup, I've turned debugging off, it's plainly working.

And presumably it works with debugging enabled, too ...


> The only change I've made is:
> 
> --- a/drivers/mmc/host/mmc_spi.c
> +++ b/drivers/mmc/host/mmc_spi.c
> @@ -1184,7 +1184,7 @@ static int mmc_spi_probe(struct spi_device *spi)
>  	 * Docs are very explicit that sampling is on the rising edge, so
>  	 * SPI_MODE_0 and SPI_MODE_3 having different CPOL may not matter.
>  	 */
> -	spi->mode |= SPI_CPOL | SPI_CPHA;
> +	spi->mode = 0;
>  	spi->bits_per_word = 8;
>  
>  	status = spi_setup(spi);

Curious.  I think it was Jan Nikitenko who reported mode 0 not
working for SD, while mode 3 did work.  All my recent testing
used mode 3, but I started with mode 0...

Ideally, someone with access to full MMC and SD specs can see
what they say about the SPI clocking.  The simplified SD specs
omit the timing diagrams.
 

> Am I understanding correctly that SPI_CPOL is:
> 
>   ~~~| |~~| |~~~~
>      | |  | |
> 0    |_|  |_|
> (does not work for me)
> 
> While !SPI_CPOL is:
> 
>      |~|  |~|
>      | |  | |
> 0 ___| |__| |____
> (works great for me)
> 
> Is that correct? Not vice versa?

That's for the clock, I take it ... yes.  There's a pretty
good timing diagram at Wikipedia, which should make the two
bits (CPOL, CPHA) clear:

  http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus

CPOL=0 means the clock starts at idle=low (vs high if CPOL=1).
CPHA=0 means capture on leading clock edge (vs trailing if CPHA=1).


> At least that is what I'm observing on oscilloscope. Maybe it's
> something still messed in mpc83xx_spi.c (I've already fixed a lot),

Could you post your changes to that driver?


> or maybe something else. So, if mmc_spi modes are correct, then
> mpc83xx_spi having inversed values, and should be fixed.
> 
> With only SPI_CPHA that thing does not work also.

Later today two patches to mpc83xx_spi should merge into the
kernel.org GIT tree but they don't look like they'd relate
to this SPI mode issue.  (And the CRC7 patch should also be
merging...)

It's not inconceivable that the SPI controller driver is a
bit confused with respect to interpreting the mode bits.
We've seen that problem before.

 
> Today I've tested it on bunch of cards, MMC Transcend 64MB,
> MMC SanDisk 16MB, SD Kingston 128MB and 1GB. All of them work.

Presumably you tested all of them with mode 0 ... which of them
work with mode 3?  Does it work better in mode 3 if you use
a lower clock speed ceiling in the spi board info for that card
slot?


> Thus, despite minor issues, mmc_spi works great, much thanks!

Glad to hear it!  Now, to sort out why mode 3 fails for you ...

- Dave

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]                 ` <200707170911.16715.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-18  7:35                   ` Jan Nikitenko
       [not found]                     ` <469DC2BC.5070305-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
  2007-07-18 10:00                   ` Anton Vorontsov
  2007-07-18 14:44                   ` Pierre Ossman
  2 siblings, 1 reply; 47+ messages in thread
From: Jan Nikitenko @ 2007-07-18  7:35 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

David Brownell wrote:
> On Tuesday 17 July 2007, Anton Vorontsov wrote:
> 
>> Uhh.. can you believe?
>>
>> mmc_spi spi1.0: ASSUMING unshared SPI bus!
>> mmc_spi spi1.0: SD/MMC host mmc0, no DMA, no WP, no poweroff
>> mmcblk0: mmc0:0000 SD01G 1006080KiB
>>  mmcblk0: p1
>> root-/FxswC0nw/VuxbqukCiDuA@public.gmane.org:~# mount /dev/mmcblk0p1 /mnt/
>> EXT2-fs warning: mounting unchecked fs, running e2fsck is recommended
>> root-/FxswC0nw/VuxbqukCiDuA@public.gmane.org:~# ls /mnt/
>> bin  etc  include  lib  libexec  lost+found  man  sbin  share  var
>> root-/FxswC0nw/VuxbqukCiDuA@public.gmane.org:~#
> 
> Good!
> 
> 
>> Yup, I've turned debugging off, it's plainly working.
> 
> And presumably it works with debugging enabled, too ...

I had the same issues with debugging on in the past, mainly if it went
directly to serial console - could it be the changed timing?

> 
> 
>> The only change I've made is:
>>
>> --- a/drivers/mmc/host/mmc_spi.c
>> +++ b/drivers/mmc/host/mmc_spi.c
>> @@ -1184,7 +1184,7 @@ static int mmc_spi_probe(struct spi_device *spi)
>>  	 * Docs are very explicit that sampling is on the rising edge, so
>>  	 * SPI_MODE_0 and SPI_MODE_3 having different CPOL may not matter.
>>  	 */
>> -	spi->mode |= SPI_CPOL | SPI_CPHA;
>> +	spi->mode = 0;
>>  	spi->bits_per_word = 8;
>>  
>>  	status = spi_setup(spi);
> 
> Curious.  I think it was Jan Nikitenko who reported mode 0 not
> working for SD, while mode 3 did work.  All my recent testing
> used mode 3, but I started with mode 0...

I do not recall anything about that, I had mode 3 there since the very
first version of mmc-spi patch posted by David here:
http://www.google.com/search?q=cache%3Awww.gossamer-threads.com%2Flists%2Flinux%2Fkernel%2F671939#671939

Just tried mode 0 today, and it seems to work as well, if I did not
overlook something (talking about some old version of mmc-spi, could not
get to test the latest releases yet, sorry)

> 
> Ideally, someone with access to full MMC and SD specs can see
> what they say about the SPI clocking.  The simplified SD specs
> omit the timing diagrams.

Someone with access to full mmc and sd specs told me, that the timing
diagrams there do not show needed spi mode, because the diagrams there
are like in a table form, with only CS, DataIN and DataOut signals (no
clock signal at all).

Jan

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]                 ` <200707170911.16715.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-07-18  7:35                   ` Jan Nikitenko
@ 2007-07-18 10:00                   ` Anton Vorontsov
       [not found]                     ` <20070718100047.GA9544-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
  2007-07-18 14:44                   ` Pierre Ossman
  2 siblings, 1 reply; 47+ messages in thread
From: Anton Vorontsov @ 2007-07-18 10:00 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

On Tue, Jul 17, 2007 at 09:11:15AM -0700, David Brownell wrote:
> On Tuesday 17 July 2007, Anton Vorontsov wrote:
> 
> > Uhh.. can you believe?
> > 
> > mmc_spi spi1.0: ASSUMING unshared SPI bus!
> > mmc_spi spi1.0: SD/MMC host mmc0, no DMA, no WP, no poweroff
> > mmcblk0: mmc0:0000 SD01G 1006080KiB
> >  mmcblk0: p1
> > root-/FxswC0nw/VuxbqukCiDuA@public.gmane.org:~# mount /dev/mmcblk0p1 /mnt/
> > EXT2-fs warning: mounting unchecked fs, running e2fsck is recommended
> > root-/FxswC0nw/VuxbqukCiDuA@public.gmane.org:~# ls /mnt/
> > bin  etc  include  lib  libexec  lost+found  man  sbin  share  var
> > root-/FxswC0nw/VuxbqukCiDuA@public.gmane.org:~#
> 
> Good!
> 
> 
> > Yup, I've turned debugging off, it's plainly working.
> 
> And presumably it works with debugging enabled, too ...

Yes, sure. I've just forgot to insert "because"; "I've turned
debugging off, because it's plainly working now". Sorry for the
confusion. Debugging isn't culprit. ;-)

> > The only change I've made is:
> > 
> > --- a/drivers/mmc/host/mmc_spi.c
> > +++ b/drivers/mmc/host/mmc_spi.c
> > @@ -1184,7 +1184,7 @@ static int mmc_spi_probe(struct spi_device *spi)
> >  	 * Docs are very explicit that sampling is on the rising edge, so
> >  	 * SPI_MODE_0 and SPI_MODE_3 having different CPOL may not matter.
> >  	 */
> > -	spi->mode |= SPI_CPOL | SPI_CPHA;
> > +	spi->mode = 0;
> >  	spi->bits_per_word = 8;
> >  
> >  	status = spi_setup(spi);
> 
> Curious.  I think it was Jan Nikitenko who reported mode 0 not
> working for SD, while mode 3 did work.  All my recent testing
> used mode 3, but I started with mode 0...
> 
> Ideally, someone with access to full MMC and SD specs can see
> what they say about the SPI clocking.  The simplified SD specs
> omit the timing diagrams.
>  
> 
> > Am I understanding correctly that SPI_CPOL is:
> > 
> >   ~~~| |~~| |~~~~
> >      | |  | |
> > 0    |_|  |_|
> > (does not work for me)
> > 
> > While !SPI_CPOL is:
> > 
> >      |~|  |~|
> >      | |  | |
> > 0 ___| |__| |____
> > (works great for me)
> > 
> > Is that correct? Not vice versa?
> 
> That's for the clock, I take it ... yes.  There's a pretty
> good timing diagram at Wikipedia, which should make the two
> bits (CPOL, CPHA) clear:
> 
>   http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
> 
> CPOL=0 means the clock starts at idle=low (vs high if CPOL=1).
> CPHA=0 means capture on leading clock edge (vs trailing if CPHA=1).

Ok, just as I thought. That means that mpc83xx_spi.c is correct in
that regard.

> > At least that is what I'm observing on oscilloscope. Maybe it's
> > something still messed in mpc83xx_spi.c (I've already fixed a lot),
> 
> Could you post your changes to that driver?

Sure, you'll see them shortly. Just need a bit of time.

> > or maybe something else. So, if mmc_spi modes are correct, then
> > mpc83xx_spi having inversed values, and should be fixed.
> > 
> > With only SPI_CPHA that thing does not work also.
> 
> Later today two patches to mpc83xx_spi should merge into the
> kernel.org GIT tree but they don't look like they'd relate
> to this SPI mode issue.  (And the CRC7 patch should also be
> merging...)
> 
> It's not inconceivable that the SPI controller driver is a
> bit confused with respect to interpreting the mode bits.
> We've seen that problem before.
> 
>  
> > Today I've tested it on bunch of cards, MMC Transcend 64MB,
> > MMC SanDisk 16MB, SD Kingston 128MB and 1GB. All of them work.
> 
> Presumably you tested all of them with mode 0 ... which of them
> work with mode 3?

None.

> Does it work better in mode 3 if you use
> a lower clock speed ceiling in the spi board info for that card
> slot?

Nope.

> > Thus, despite minor issues, mmc_spi works great, much thanks!
> 
> Glad to hear it!  Now, to sort out why mode 3 fails for you ...
> 
> - Dave

Much thanks,

-- 
Anton Vorontsov
email: cbou-JGs/UdohzUI@public.gmane.org
backup email: ya-cbou-o+MxOtu4lMCHXe+LvDLADg@public.gmane.org
irc://irc.freenode.net/bd2

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]                     ` <20070718100047.GA9544-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
@ 2007-07-18 14:05                       ` Anton Vorontsov
  0 siblings, 0 replies; 47+ messages in thread
From: Anton Vorontsov @ 2007-07-18 14:05 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

On Wed, Jul 18, 2007 at 02:00:47PM +0400, Anton Vorontsov wrote:
> > Later today two patches to mpc83xx_spi should merge into the
> > kernel.org GIT tree but they don't look like they'd relate
> > to this SPI mode issue.  (And the CRC7 patch should also be
> > merging...)
[...] 
> > Could you post your changes to that driver?
> 
> Sure, you'll see them shortly. Just need a bit of time.

Dammit. I've done git pull from Linus' tree, and this commit
already fixes most of things (major ones).

- - -
commit f29ba280ecb46331c1f6842b094808af01131422
Author: Joakim Tjernlund <joakim.tjernlund-SNLAxHN9vbcOP4wsBPIw7w@public.gmane.org>
Date:   Tue Jul 17 04:04:12 2007 -0700

    spi_mpc83xx.c: support QE enabled 83xx CPU's like mpc832x

    Quicc Engine enabled mpc83xx CPU's has a somewhat different HW interface to
    the SPI controller.  This patch adds a qe_mode knob that sees to that
    needed adaptions are performed.
- - -

I'm working on mpc832x too. Heh.. much time wasted... It haven't
been in -mm. :-(

-- 
Anton Vorontsov
email: cbou-JGs/UdohzUI@public.gmane.org
backup email: ya-cbou-o+MxOtu4lMCHXe+LvDLADg@public.gmane.org
irc://irc.freenode.net/bd2

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]                 ` <200707170911.16715.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-07-18  7:35                   ` Jan Nikitenko
  2007-07-18 10:00                   ` Anton Vorontsov
@ 2007-07-18 14:44                   ` Pierre Ossman
       [not found]                     ` <20070718164409.56ca0019-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  2 siblings, 1 reply; 47+ messages in thread
From: Pierre Ossman @ 2007-07-18 14:44 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Tue, 17 Jul 2007 09:11:15 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> 
> Curious.  I think it was Jan Nikitenko who reported mode 0 not
> working for SD, while mode 3 did work.  All my recent testing
> used mode 3, but I started with mode 0...
> 
> Ideally, someone with access to full MMC and SD specs can see
> what they say about the SPI clocking.  The simplified SD specs
> omit the timing diagrams.
>  

I can probably help out there.

According to the specs, both mode 0 and mode 3 are valid. The idle
polarity simply isn't specified, only that data shall be sampled on
rising edge. Hence mode 0 or 3.

Rgds
-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]                     ` <469DC2BC.5070305-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
@ 2007-07-18 17:06                       ` David Brownell
  0 siblings, 0 replies; 47+ messages in thread
From: David Brownell @ 2007-07-18 17:06 UTC (permalink / raw)
  To: Jan Nikitenko
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

On Wednesday 18 July 2007, Jan Nikitenko wrote:
> David Brownell wrote:

> > Curious.  I think it was Jan Nikitenko who reported mode 0 not
> > working for SD, while mode 3 did work.  All my recent testing
> > used mode 3, but I started with mode 0...
> 
> I do not recall anything about that, I had mode 3 there since the very
> first version of mmc-spi patch posted by David ...

Well then it was somebody else.  Doesn't much matter though.  :)

- Dave


-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]                     ` <20070718164409.56ca0019-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
@ 2007-07-18 17:27                       ` David Brownell
       [not found]                         ` <200707181027.17819.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-18 17:27 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender t,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Wednesday 18 July 2007, Pierre Ossman wrote:
> On Tue, 17 Jul 2007 09:11:15 -0700
> David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:
> 
> > Ideally, someone with access to full MMC and SD specs can see
> > what they say about the SPI clocking.  The simplified SD specs
> > omit the timing diagrams.
> >  
> 
> I can probably help out there.
> 
> According to the specs, both mode 0 and mode 3 are valid. The idle
> polarity simply isn't specified, only that data shall be sampled on
> rising edge. Hence mode 0 or 3.

Thanks.  In that case, I'm thinking there *IS* a bug of some kind
in the controller driver Anton is using, else mode 3 would work
for him like it (evidently) works for everyone else.

I'll see about making time to see if mode 0 works for me too; but
even if it does, I'd prefer to leave the driver the way it is now
instead of changing it to cover up for that mpc83xx bug ... plus,
I just like CPHA=1 modes better because they don't need to start
with that strange half-clock.  ;)

- Dave

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]                         ` <200707181027.17819.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-19 16:15                           ` Anton Vorontsov
       [not found]                             ` <20070719161553.GA15756-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: Anton Vorontsov @ 2007-07-19 16:15 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender t,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

On Wed, Jul 18, 2007 at 10:27:17AM -0700, David Brownell wrote:
> On Wednesday 18 July 2007, Pierre Ossman wrote:
> > On Tue, 17 Jul 2007 09:11:15 -0700
> > David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:
> > 
> > > Ideally, someone with access to full MMC and SD specs can see
> > > what they say about the SPI clocking.  The simplified SD specs
> > > omit the timing diagrams.
> > >  
> > 
> > I can probably help out there.
> > 
> > According to the specs, both mode 0 and mode 3 are valid. The idle
> > polarity simply isn't specified, only that data shall be sampled on
> > rising edge. Hence mode 0 or 3.
> 
> Thanks.  In that case, I'm thinking there *IS* a bug of some kind
> in the controller driver Anton is using, else mode 3 would work
> for him like it (evidently) works for everyone else.

I've checked with MPC8323E reference manual, and according to it
spi_mpc83xx is doing right thing.

> I'll see about making time to see if mode 0 works for me too; but
> even if it does, I'd prefer to leave the driver the way it is now
> instead of changing it to cover up for that mpc83xx bug ... plus,
> I just like CPHA=1 modes better because they don't need to start
> with that strange half-clock.  ;)

Well. As Pierre Ossman told, it should work at any mode. That also
proves that it's not mpc83xx's bug (because even if it would be a
bug, and spi_mpc83xx having inversed values, it's still should work).

So, it's likely depends on spi controller, maybe some timing issues
or signal shapes, which distracts SD/MMC... Not sure.

But then, may I ask for "alt_mode" (bool) platform data variable?

Thanks!

-- 
Anton Vorontsov
email: cbou-JGs/UdohzUI@public.gmane.org
backup email: ya-cbou-o+MxOtu4lMCHXe+LvDLADg@public.gmane.org
irc://irc.freenode.net/bd2

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]                             ` <20070719161553.GA15756-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
@ 2007-07-19 20:28                               ` David Brownell
       [not found]                                 ` <200707191328.18846.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-19 20:28 UTC (permalink / raw)
  To: avorontsov-hkdhdckH98+B+jHODAdFcQ
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

On Thursday 19 July 2007, Anton Vorontsov wrote:
> > 
> > Thanks.  In that case, I'm thinking there *IS* a bug of some kind
> > in the controller driver Anton is using, else mode 3 would work
> > for him like it (evidently) works for everyone else.
> 
> I've checked with MPC8323E reference manual, and according to it
> spi_mpc83xx is doing right thing.

I notice that driver disregards the cs_change instructions in the
messages ... I could imagine how that could make a big difference.

If that hardware were doing the right thing, then it would work
reliably!  Since it's not reliable, it's doing something wrong.
You seem to think it's not a hardware issue; that may be true.

Recall that the first dozen or so commands worked just fine.  The
issue was that some byte that should have been all-ones or 0xfe
reported instead an 0xf8.  That's not the kind of error that can
be explained by clock skew; it covers at least two bits.


> > I'll see about making time to see if mode 0 works for me too; but
> > even if it does, I'd prefer to leave the driver the way it is now
> > instead of changing it to cover up for that mpc83xx bug ... plus,
> > I just like CPHA=1 modes better because they don't need to start
> > with that strange half-clock.  ;)
> 
> Well. As Pierre Ossman told, it should work at any mode.

Well, either mode 0 or mode 3.  And since it doesn't work in both
modes, clearly something in your test system is buggy.


> That also 
> proves that it's not mpc83xx's bug (because even if it would be a
> bug, and spi_mpc83xx having inversed values, it's still should work).

No way could it "prove" that!!

How could it ever be possible that your system not work in mode 3, and
yet that not be a bug in your test system???

The only thing "proven" is that the mmc_spi code is basically correct;
otherwise neither clock mode could cause the right data transfers.
(And otherewise other folk wouldn't have seen it work...)

The problem is in a lower level ... maybe the SPI controller driver,
or the silicon, or the board wiring.


> So, it's likely depends on spi controller, maybe some timing issues
> or signal shapes, which distracts SD/MMC... Not sure.

What does your oscilloscope show is going on when this error triggers?

Right now I'd most suspect the lack of cs_change support in that
controller driver.


> But then, may I ask for "alt_mode" (bool) platform data variable?

Well, as already said:  (a) we know mode 3 is required to work,
(b) we know that on your test system it doesn't, so accordingly
(c) we know there's a bug in your test system.

The only reason to add such a flag would be to paper over that bug.

The idea with bugs is to *FIX* them, not paper them over so they'll
never get fixed.  Especially if you've got a lab setup with even
basic hardware debug tools, like your oscilloscope, which make it
so easy to verify that the right signals are going down the wires.

- Dave


-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]                                 ` <200707191328.18846.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-23 14:29                                   ` Anton Vorontsov
       [not found]                                     ` <20070723142923.GA28979-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: Anton Vorontsov @ 2007-07-23 14:29 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

On Thu, Jul 19, 2007 at 01:28:17PM -0700, David Brownell wrote:
> On Thursday 19 July 2007, Anton Vorontsov wrote:
> > > 
> > > Thanks.  In that case, I'm thinking there *IS* a bug of some kind
> > > in the controller driver Anton is using, else mode 3 would work
> > > for him like it (evidently) works for everyone else.
> > 
> > I've checked with MPC8323E reference manual, and according to it
> > spi_mpc83xx is doing right thing.
> 
> I notice that driver disregards the cs_change instructions in the
> messages ... I could imagine how that could make a big difference.
> 
> If that hardware were doing the right thing, then it would work
> reliably!  Since it's not reliable, it's doing something wrong.
> You seem to think it's not a hardware issue; that may be true.
> 
> Recall that the first dozen or so commands worked just fine.  The
> issue was that some byte that should have been all-ones or 0xfe
> reported instead an 0xf8.  That's not the kind of error that can
> be explained by clock skew; it covers at least two bits.

Yup, I've either noticed that 0xf8 and 0xfe differs by only two
bits (and by three if comparing to 0xff). But I can't really
explain it yet.

> > > I'll see about making time to see if mode 0 works for me too; but
> > > even if it does, I'd prefer to leave the driver the way it is now
> > > instead of changing it to cover up for that mpc83xx bug ... plus,
> > > I just like CPHA=1 modes better because they don't need to start
> > > with that strange half-clock.  ;)
> > 
> > Well. As Pierre Ossman told, it should work at any mode.
> 
> Well, either mode 0 or mode 3.  And since it doesn't work in both
> modes, clearly something in your test system is buggy.
> 
> 
> > That also 
> > proves that it's not mpc83xx's bug (because even if it would be a
> > bug, and spi_mpc83xx having inversed values, it's still should work).
> 
> No way could it "prove" that!!
> 
> How could it ever be possible that your system not work in mode 3, and
> yet that not be a bug in your test system???
> 
> The only thing "proven" is that the mmc_spi code is basically correct;
> otherwise neither clock mode could cause the right data transfers.
> (And otherewise other folk wouldn't have seen it work...)
> 
> The problem is in a lower level ... maybe the SPI controller driver,
> or the silicon, or the board wiring.
> 
> 
> > So, it's likely depends on spi controller, maybe some timing issues
> > or signal shapes, which distracts SD/MMC... Not sure.
> 
> What does your oscilloscope show is going on when this error triggers?

Well, it's not handy to catch particular bit using my oscilloscope.
It's unable to scroll in time. But as failing bits are in the last
spi message, maybe I could do something.

> Right now I'd most suspect the lack of cs_change support in that
> controller driver.

I see. Thanks for the pointer.

> > But then, may I ask for "alt_mode" (bool) platform data variable?
> 
> Well, as already said:  (a) we know mode 3 is required to work,
> (b) we know that on your test system it doesn't, so accordingly
> (c) we know there's a bug in your test system.
> 
> The only reason to add such a flag would be to paper over that bug.
> 
> The idea with bugs is to *FIX* them, not paper them over so they'll
> never get fixed.  Especially if you've got a lab setup with even
> basic hardware debug tools, like your oscilloscope, which make it
> so easy to verify that the right signals are going down the wires.

Yeah, you're right. And your whole reply makes a lot of sense, much
thanks for explanations and ideas.

-- 
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
spi-devel-general mailing list
spi-devel-general@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]                                     ` <20070723142923.GA28979-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
@ 2007-07-23 17:33                                       ` David Brownell
       [not found]                                         ` <200707231033.31717.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-23 17:33 UTC (permalink / raw)
  To: avorontsov-hkdhdckH98+B+jHODAdFcQ
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

On Monday 23 July 2007, Anton Vorontsov wrote:
> On Thu, Jul 19, 2007 at 01:28:17PM -0700, David Brownell wrote:

> > I notice that driver disregards the cs_change instructions in the
> > messages ... I could imagine how that could make a big difference.

For example, the extra flapping on the chipselect changes timings...


> > If that hardware were doing the right thing, then it would work
> > reliably!  Since it's not reliable, it's doing something wrong.
> > You seem to think it's not a hardware issue; that may be true.
> > 
> > Recall that the first dozen or so commands worked just fine.  The
> > issue was that some byte that should have been all-ones or 0xfe
> > reported instead an 0xf8.  That's not the kind of error that can
> > be explained by clock skew; it covers at least two bits.
> 
> Yup, I've either noticed that 0xf8 and 0xfe differs by only two
> bits (and by three if comparing to 0xff). But I can't really
> explain it yet.

See if fixing the cs_change handling -- so that chipselect never
goes inactive except between MMC requests -- helps.  Minimally,
you'll notice that mode 0 adds extra delays (albeit only 1/2 clock)
before the clock starts toggling; and that deslecting cards
except between requests or while the card issues BUSY, falls into
the "undefined behavior" category.  So while that might not be
able to trigger certain perversions, dropping a few clocks in
some odd cases would not seem to violate the spec...

- Dave

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]                                         ` <200707231033.31717.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-24 10:23                                           ` Anton Vorontsov
  2007-08-07  3:21                                           ` David Brownell
  1 sibling, 0 replies; 47+ messages in thread
From: Anton Vorontsov @ 2007-07-24 10:23 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

On Mon, Jul 23, 2007 at 10:33:31AM -0700, David Brownell wrote:
> On Monday 23 July 2007, Anton Vorontsov wrote:
> > On Thu, Jul 19, 2007 at 01:28:17PM -0700, David Brownell wrote:
> 
> > > I notice that driver disregards the cs_change instructions in the
> > > messages ... I could imagine how that could make a big difference.
> 
> For example, the extra flapping on the chipselect changes timings...
> 
> 
> > > If that hardware were doing the right thing, then it would work
> > > reliably!  Since it's not reliable, it's doing something wrong.
> > > You seem to think it's not a hardware issue; that may be true.
> > > 
> > > Recall that the first dozen or so commands worked just fine.  The
> > > issue was that some byte that should have been all-ones or 0xfe
> > > reported instead an 0xf8.  That's not the kind of error that can
> > > be explained by clock skew; it covers at least two bits.
> > 
> > Yup, I've either noticed that 0xf8 and 0xfe differs by only two
> > bits (and by three if comparing to 0xff). But I can't really
> > explain it yet.
> 
> See if fixing the cs_change handling -- so that chipselect never
> goes inactive except between MMC requests -- helps. 

Okay.. I've looked into cs_change for spi_mpc83xx, and it seems
bitbang framework (which is used by spi_mpc83xx driver) handle
cs_change by itself.

At least bitbang_work() doing very similar things done by other
drivers not using bitbang library.

> Minimally,
> you'll notice that mode 0 adds extra delays (albeit only 1/2 clock)
> before the clock starts toggling; and that deslecting cards
> except between requests or while the card issues BUSY, falls into
> the "undefined behavior" category.  So while that might not be
> able to trigger certain perversions, dropping a few clocks in
> some odd cases would not seem to violate the spec...
> 
> - Dave

Thanks,

-- 
Anton Vorontsov
email: cbou-JGs/UdohzUI@public.gmane.org
backup email: ya-cbou-o+MxOtu4lMCHXe+LvDLADg@public.gmane.org
irc://irc.freenode.net/bd2

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 1/4] MMC headers learn about SPI
       [not found]     ` <200707141506.00262.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-26 16:31       ` Pierre Ossman
       [not found]         ` <20070726183150.024930aa-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: Pierre Ossman @ 2007-07-26 16:31 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Sat, 14 Jul 2007 15:05:59 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> @@ -115,14 +116,16 @@ struct mmc_host {
>  	wait_queue_head_t	wq;
>  	unsigned int		claimed:1;	/* host exclusively claimed */
>  
> -	struct delayed_work	detect;
> +	unsigned int		use_spi_crc:1;
> +
>  #ifdef CONFIG_MMC_DEBUG
>  	unsigned int		removed:1;	/* host is being removed */
>  #endif
> +	unsigned int		bus_dead:1;	/* bus has been released */
> +	struct delayed_work	detect;
>  
>  	const struct mmc_bus_ops *bus_ops;	/* current bus driver */
>  	unsigned int		bus_refs;	/* reference counter */
> -	unsigned int		bus_dead:1;	/* bus has been released */
>  
>  	unsigned long		private[0] ____cacheline_aligned;
>  };

Some needless shuffling of existing members here.

> @@ -47,6 +52,20 @@ struct mmc_command {
>  #define mmc_resp_type(cmd)	((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
>  
>  /*
> + * These are the SPI response types for MMC and SD cards (not SDIO).
> + * Commands return R1, with maybe more info.  Zero is an error type;
> + * callers must always provide the appropriate MMC_RSP_SPI_Rx flags.
> + */
> +#define MMC_RSP_SPI_R1	(MMC_RSP_SPI_S1)
> +#define MMC_RSP_SPI_R1B	(MMC_RSP_SPI_S1|MMC_RSP_BUSY)

I think it's probably safe to keep the two response types separate, even though it is very unlikely that we'll have a command that only uses busy signalling in one of the modes.

Other than that, it looks fine.

Rgds
-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 2/4] MMC block learns about SPI
       [not found]     ` <200707141506.42880.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-26 16:33       ` Pierre Ossman
       [not found]         ` <20070726183339.6631243f-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: Pierre Ossman @ 2007-07-26 16:33 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Sat, 14 Jul 2007 15:06:42 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> @@ -302,13 +309,14 @@ static int mmc_blk_issue_rq(struct mmc_q
>  			goto cmd_err;
>  		}
>  
> -		if (rq_data_dir(req) != READ) {
> +		if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
>  			do {
>  				int err;
>  
>  				cmd.opcode = MMC_SEND_STATUS;
>  				cmd.arg = card->rca << 16;
> -				cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
> +				cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1
> +						| MMC_CMD_AC;
>  				err = mmc_wait_for_cmd(card->host, &cmd, 5);
>  				if (err) {
>  					printk(KERN_ERR "%s: error %d requesting status\n",

Nitpicking, but if this code isn't executed on a SPI host then a SPI response type is hardly needed. :)

Otherwise ok.

Rgds
-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 1/4] MMC headers learn about SPI
       [not found]         ` <20070726183150.024930aa-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
@ 2007-07-26 16:55           ` David Brownell
       [not found]             ` <200707260955.22967.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-26 16:55 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thursday 26 July 2007, Pierre Ossman wrote:
> On Sat, 14 Jul 2007 15:05:59 -0700
> David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:
> 
> > @@ -115,14 +116,16 @@ struct mmc_host {
> >  	wait_queue_head_t	wq;
> >  	unsigned int		claimed:1;	/* host exclusively claimed */
> >  
> > -	struct delayed_work	detect;
> > +	unsigned int		use_spi_crc:1;
> > +
> >  #ifdef CONFIG_MMC_DEBUG
> >  	unsigned int		removed:1;	/* host is being removed */
> >  #endif
> > +	unsigned int		bus_dead:1;	/* bus has been released */
> > +	struct delayed_work	detect;
> >  
> >  	const struct mmc_bus_ops *bus_ops;	/* current bus driver */
> >  	unsigned int		bus_refs;	/* reference counter */
> > -	unsigned int		bus_dead:1;	/* bus has been released */
> >  
> >  	unsigned long		private[0] ____cacheline_aligned;
> >  };
> 
> Some needless shuffling of existing members here.

As explained in the comment:  this reduces the padding.
The previous way needed one word per bitfield; this way
they all fit into the same word...


> > @@ -47,6 +52,20 @@ struct mmc_command {
> >  #define mmc_resp_type(cmd)	((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
> >  
> >  /*
> > + * These are the SPI response types for MMC and SD cards (not SDIO).
> > + * Commands return R1, with maybe more info.  Zero is an error type;
> > + * callers must always provide the appropriate MMC_RSP_SPI_Rx flags.
> > + */
> > +#define MMC_RSP_SPI_R1	(MMC_RSP_SPI_S1)
> > +#define MMC_RSP_SPI_R1B	(MMC_RSP_SPI_S1|MMC_RSP_BUSY)
> 
> I think it's probably safe to keep the two response types separate, even
> though it is very unlikely that we'll have a command that only uses busy
> signalling in one of the modes.  

That bothered me a smidgeon ...


> Other than that, it looks fine.

I'm not sure what you mean here, but then my first cup of coffee is
still trying to do its work.  Are you saying you want me to

 - increase the padding again?
 - define an MMC_RSP_SPI_BUSY, and use that?

The former would bother me a bit, but I could understand if you wanted
it to be in a different patch.  The latter is no problem at all.

- Dave

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 2/4] MMC block learns about SPI
       [not found]         ` <20070726183339.6631243f-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
@ 2007-07-26 17:00           ` David Brownell
       [not found]             ` <200707261000.17339.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-26 17:00 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thursday 26 July 2007, Pierre Ossman wrote:
> On Sat, 14 Jul 2007 15:06:42 -0700
> David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:
> 
> > @@ -302,13 +309,14 @@ static int mmc_blk_issue_rq(struct mmc_q
> >  			goto cmd_err;
> >  		}
> >  
> > -		if (rq_data_dir(req) != READ) {
> > +		if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
> >  			do {
> >  				int err;
> >  
> >  				cmd.opcode = MMC_SEND_STATUS;
> >  				cmd.arg = card->rca << 16;
> > -				cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
> > +				cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1
> > +						| MMC_CMD_AC;
> >  				err = mmc_wait_for_cmd(card->host, &cmd, 5);
> >  				if (err) {
> >  					printk(KERN_ERR "%s: error %d requesting status\n",
> 
> Nitpicking, but if this code isn't executed on a SPI host then a SPI
> response type is hardly needed. :) 

But then it can't hurt either, and will be safer to cut'n'paste.  :)

Andrew Morton regularly gives feedback which amounts to "that's correct
in this context, but please adopt this always-safe idiom" on the grounds
that a *LOT* of Linux code is developed by cloning.  It's hard for me to
disagree with that... especially when I see my code showing up in some
rather strange contexts!


> Otherwise ok.

So -- should I change that?

- Dave

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 3/4] MMC core learns about SPI
       [not found]     ` <200707141507.17484.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-26 17:18       ` Pierre Ossman
       [not found]         ` <20070726191824.569542fd-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: Pierre Ossman @ 2007-07-26 17:18 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Sat, 14 Jul 2007 15:07:16 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> @@ -541,7 +548,9 @@ void mmc_rescan(struct work_struct *work
>  
>  		err = mmc_send_app_op_cond(host, 0, &ocr);
>  		if (err == MMC_ERR_NONE) {
> -			if (mmc_attach_sd(host, ocr))
> +			if (mmc_host_is_spi(host))
> +				err = mmc_spi_read_ocr(host, &ocr);
> +			if (err != MMC_ERR_NONE || mmc_attach_sd(host, ocr))
>  				mmc_power_off(host);
>  		} else {
>  			/*

Getting the OCR is not vital to determine if it's SD or MMC, so we can move that into mmc.c and sd.c, reducing the code in here.

> @@ -550,7 +559,10 @@ void mmc_rescan(struct work_struct *work
>  			 */
>  			err = mmc_send_op_cond(host, 0, &ocr);
>  			if (err == MMC_ERR_NONE) {
> -				if (mmc_attach_mmc(host, ocr))
> +				if (mmc_host_is_spi(host))
> +					err = mmc_spi_read_ocr(host, &ocr);
> +				if (err != MMC_ERR_NONE
> +						|| mmc_attach_mmc(host, ocr))
>  					mmc_power_off(host);
>  			} else {
>  				mmc_power_off(host);

This will fail to initialise a high capacity MMC card. From the MMC spec:

"Without the CMD58 with bits [30:29] set as "10b" in prior to the CMD1 a higher than 2GB of density of memory will remain in Idle state forever."

> @@ -95,14 +106,18 @@ int mmc_send_op_cond(struct mmc_host *ho
>  
>  	cmd.opcode = MMC_SEND_OP_COND;
>  	cmd.arg = ocr;

Argument is "None", not "Ignored", so this should be 0 for SPI.

>  
> -	mmc_set_data_timeout(&data, card, 0);
> +	/* Note that for MMC_SEND_EXT_CSD we could set the timeout; but
> +	 * not for the other requests. But host->card isn't set yet!
> +	 */
>  

This will break on native hosts as a timeout of 0 means exactly that.

> +
> +int mmc_spi_send_cid(struct mmc_host *host, u32 *cid)
> +{
> +	if (!mmc_host_is_spi(host))
> +		return mmc_send_cxd_native(host, 0, cid, MMC_SEND_CID);
> +
> +	return mmc_send_cxd_data(host, MMC_SEND_CID, cid, 16);
> +}
> +

As this is not SPI specific (except for its current use), why not mmc_send_cid() ?

And the argument for it is the RCA (in native mode).

> +
> +/* Enabling software CRCs can be a significant (30%) performance cost,
> + * and for other reasons isn't always desired; so it can be disabled.
> + */
> +static int use_spi_crc = 1;
> +module_param(use_spi_crc, bool, 0);
> +

The *_ops.c only contain function wrappers of protocol commands, not policy. So I think this is better placed where mmc_spi_set_crc() is called.

> --- g26.orig/drivers/mmc/core/mmc.c	2007-07-14 14:47:12.000000000 -0700
> +++ g26/drivers/mmc/core/mmc.c	2007-07-14 14:47:54.000000000 -0700
> @@ -264,7 +264,13 @@ static int mmc_init_card(struct mmc_host
>  	/*
>  	 * Fetch CID from card.
>  	 */
> -	err = mmc_all_send_cid(host, cid);
> +	if (mmc_host_is_spi(host)) {
> +		err = mmc_spi_set_crc(host);
> +		if (err != MMC_ERR_NONE)
> +			goto err;
> +		err = mmc_spi_send_cid(host, cid);
> +	} else
> +		err = mmc_all_send_cid(host, cid);
>  	if (err != MMC_ERR_NONE)
>  		goto err;
>  

A matter of taste, but wouldn't it be clearer if you separated out the crc bit and did that earlier, before the above comment?

> --- g26.orig/drivers/mmc/core/sd.c	2007-07-14 14:47:12.000000000 -0700
> +++ g26/drivers/mmc/core/sd.c	2007-07-14 14:47:54.000000000 -0700
> @@ -321,7 +321,13 @@ static int mmc_sd_init_card(struct mmc_h
>  	/*
>  	 * Fetch CID from card.
>  	 */
> -	err = mmc_all_send_cid(host, cid);
> +	if (mmc_host_is_spi(host)) {
> +		err = mmc_spi_set_crc(host);
> +		if (err != MMC_ERR_NONE)
> +			goto err;
> +		err = mmc_spi_send_cid(host, cid);
> +	} else
> +		err = mmc_all_send_cid(host, cid);
>  	if (err != MMC_ERR_NONE)
>  		goto err;
>  

Ditto.

> --- g26.orig/drivers/mmc/core/sd_ops.c	2007-07-14 14:47:12.000000000 -0700
> +++ g26/drivers/mmc/core/sd_ops.c	2007-07-14 14:47:54.000000000 -0700
> @@ -70,6 +70,12 @@ int mmc_wait_for_app_cmd(struct mmc_host
>  		err = cmd->error;
>  		if (cmd->error == MMC_ERR_NONE)
>  			break;
> +
> +		/* no point in retrying illegal commands! */
> +		if (mmc_host_is_spi(host)) {
> +			if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
> +				break;
> +		}
>  	}
>  
>  	return err;

"Illegal command" refers to the previous command sent (and failed). So this can give false negatives.

Rgds
-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 4/4] mmc_spi host driver
       [not found]     ` <200707141508.07555.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-26 18:02       ` Pierre Ossman
       [not found]         ` <20070726200202.5e5dcf62-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: Pierre Ossman @ 2007-07-26 18:02 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Sat, 14 Jul 2007 15:08:06 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> --- g26.orig/drivers/mmc/host/Kconfig	2007-07-14 14:47:11.000000000 -0700
> +++ g26/drivers/mmc/host/Kconfig	2007-07-14 14:47:57.000000000 -0700
> @@ -100,3 +100,16 @@ config MMC_TIFM_SD
>            To compile this driver as a module, choose M here: the
>  	  module will be called tifm_sd.
>  
> +config MMC_SPI
> +	tristate "MMC/SD over SPI"
> +	depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL
> +	select CRC7
> +	select CRC_ITU_T
> +	help
> +	  Some systems accss MMC/SD cards using a SPI controller instead of
> +	  using a "native" MMC/SD controller.  This has a disadvantage of
> +	  being relatively high overhead, but a compensating advantage of
> +	  working on many systems without dedicated MMC/SD controllers.
> +
> +	  If unsure, or if your system has no SPI master driver, say N.
> +

It is customary to have "(Experimental)" in the description aswell.

> +
> +static int
> +mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len)
> +{
> +	int status;
> +
> +	if (len > sizeof *host->data) {
> +		WARN_ON(1);
> +		return -EIO;
> +	}

sizeof without parenthesis? Heathen!

> +
> +/*
> + * Note that for SPI, cmd->resp[0] is not the same data as "native" protocol
> + * hosts return!  The low byte holds R1_SPI bits.  The next byte may hold
> + * R2_SPI bits ... for SEND_STATUS, or after data read errors.
> + *
> + * cmd->resp[1] holds any four-byte response, for R3 (READ_OCR) and on
> + * newer cards R7 (IF_COND).
> + */
> +
> +static char *maptype(struct mmc_command *cmd)
> +{
> +	switch (mmc_spi_resp_type(cmd)) {
> +	case MMC_RSP_SPI_R1:	return "R1";
> +	case MMC_RSP_SPI_R1B:	return "R1B";
> +	case MMC_RSP_SPI_R2:	return "R2";
> +	case MMC_RSP_SPI_R3:	return "R3/R7";
> +	default:		return "?";
> +	}
> +}
> +

I'm trying to move all generic debugging code into the core, so I would prefer if you would have a look at extending that to fit your needs. That way everyone wins.

> +
> +	/* Status byte: the entire seven-bit R1 response.  */
> +	if (cmd->resp[0] != 0) {
> +		if (R1_SPI_COM_CRC & cmd->resp[0]) {
> +			cmd->error = MMC_ERR_BADCRC;
> +			value = -EILSEQ;
> +		} else if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS
> +					| R1_SPI_ILLEGAL_COMMAND)
> +				& cmd->resp[0]) {
> +			cmd->error = MMC_ERR_INVALID;
> +			value = -EINVAL;
> +		} else if ((R1_SPI_ERASE_SEQ | R1_SPI_ERASE_RESET)
> +				& cmd->resp[0]) {
> +			cmd->error = MMC_ERR_FAILED;
> +			value = -EINVAL;
> +		} /* else R1_SPI_IDLE, "it's resetting" */
> +
> +		if (value < 0)
> +			goto fail;
> +	}

I'd still preferred that you'd didn't try to decode this here. And your code demostrates why; CRC error and illegal command both signal problems with the previous command, not the current one, so this code would be failing the wrong request.

> +
> +	switch (mmc_spi_resp_type(cmd)) {
> +
> +	/* SPI R1B == R1 + busy; STOP_TRANSMISSION (for multiblock reads)
> +	 * and less-common stuff like various erase operations.
> +	 */
> +	case MMC_RSP_SPI_R1B:

for (i = 0;i < mmc_spi_resp_size(cmd);i++)
	cmd->resp[i/4] |= *cp++ << (24 - (i % 4) * 8);

if (cmd->flags & MMC_SPI_RSP_BUSY)
	mmc_spi_wait_unbusy();

That's the whole point of hacking up the response types into components, so that the host driver doesn't have to care.

You can keep your design if you'd like, but I really think this is cleaner and more layered.

> +
> +	/* We can handle most commands (except block reads) in one full
> +	 * duplex I/O operation before either starting the next transfer
> +	 * (data block or command) or else deselecting the card.
> +	 *
> +	 * First, write 7 bytes:
> +	 *  - an all-ones byte to ensure the card is ready
> +	 *  - opcode byte (plus start and transmission bits)
> +	 *  - four bytes of big-endian argument
> +	 *  - crc7 (plus end bit) ... always computed, it's cheap
> +	 *

I thought your argument against always using crc7 was that it wasn't cheap? Colour me confused.


> +			if (status == -EILSEQ)
> +				data->error = MMC_ERR_BADCRC;
> +			else if (status == -ETIMEDOUT)
> +				data->error = MMC_ERR_TIMEOUT;
> +			else if (status == -EINVAL)
> +				data->error = MMC_ERR_INVALID;
> +			else if (data->error == MMC_ERR_NONE)
> +				data->error = MMC_ERR_FAILED;
> +			break;
> +		}

At least we agree on error codes. This is precisely the change I have pending in my patch set to remove the MMC_ERR_* mess. :)


There are a few outstanding issues, but they are really minor so this could very soon get into my -mm branch. I need a MAINTAINERS entry aswell.

I'd like to see those REVISIT things attended to before I push the stuff to Linus though.

Rgds
-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 1/4] MMC headers learn about SPI
       [not found]             ` <200707260955.22967.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-26 18:05               ` Pierre Ossman
       [not found]                 ` <20070726200525.6a5b7d72-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: Pierre Ossman @ 2007-07-26 18:05 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thu, 26 Jul 2007 09:55:22 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> 
> As explained in the comment:  this reduces the padding.
> The previous way needed one word per bitfield; this way
> they all fit into the same word...
> 

Ah, didn't realize that. I blame the effect two weeks of vacation has on the brain. :)

Perhaps shuffling them all to the end (before private) so that it is more clear that they are grouped because of padding issues?

> 
> I'm not sure what you mean here, but then my first cup of coffee is
> still trying to do its work.  Are you saying you want me to
> 
>  - increase the padding again?
>  - define an MMC_RSP_SPI_BUSY, and use that?
> 
> The former would bother me a bit, but I could understand if you wanted
> it to be in a different patch.  The latter is no problem at all.
> 

Well, both initially. But padding is a relevant concern, so I'm content with the latter.

Rgds
-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 2/4] MMC block learns about SPI
       [not found]             ` <200707261000.17339.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-26 18:06               ` Pierre Ossman
       [not found]                 ` <20070726200639.06242858-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: Pierre Ossman @ 2007-07-26 18:06 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thu, 26 Jul 2007 10:00:16 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> 
> But then it can't hurt either, and will be safer to cut'n'paste.  :)
> 
> Andrew Morton regularly gives feedback which amounts to "that's correct
> in this context, but please adopt this always-safe idiom" on the grounds
> that a *LOT* of Linux code is developed by cloning.  It's hard for me to
> disagree with that... especially when I see my code showing up in some
> rather strange contexts!
> 

Fair enough. But then you'd probably also need to have a look at the clause terminating that while loop. I believe it is specific to the native protocol.

Rgds
-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 1/4] MMC headers learn about SPI
       [not found]                 ` <20070726200525.6a5b7d72-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
@ 2007-07-26 20:08                   ` David Brownell
  0 siblings, 0 replies; 47+ messages in thread
From: David Brownell @ 2007-07-26 20:08 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thursday 26 July 2007, Pierre Ossman wrote:
> On Thu, 26 Jul 2007 09:55:22 -0700
> David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:
> 
> > 
> > As explained in the comment:  this reduces the padding.
> > The previous way needed one word per bitfield; this way
> > they all fit into the same word...
> > 
> 
> Ah, didn't realize that. I blame the effect two weeks of
> vacation has on the brain. :) 
> 
> Perhaps shuffling them all to the end (before private) so
> that it is more clear that they are grouped because of padding issues? 

What I did was add a comment about the padding, then grouped
them all after the 'u32 ocr' to save a smidgeon more padding
(on 64bit machines).  Overall, there's more padding to remove.


> > I'm not sure what you mean here, but then my first cup of coffee is
> > still trying to do its work.  Are you saying you want me to
> > 
> >  - increase the padding again?
> >  - define an MMC_RSP_SPI_BUSY, and use that?
> > 
> > The former would bother me a bit, but I could understand if you wanted
> > it to be in a different patch.  The latter is no problem at all.
> > 
> 
> Well, both initially. But padding is a relevant concern, so I'm
> content with the latter. 

Did both.  See the appended patch.

- Dave

==========	CUT HERE
Teach the MMC/SD/SDIO system headers that some hosts use SPI mode

 - New host capabilities and status bits
    * MMC_CAP_SPI, with mmc_host_is_spi() test
    * mmc_host.use_spi_crc flag
 
 - SPI-specific declarations:
    * Response types, MMC_RSP_SPI_R*
    * Two SPI-only commands 
    * Status bits used native to SPI:  R1_SPI_*, R2_SPI_*

 - Fix a few (unrelated) whitespace bugs in the headers.

 - Reorder a few mmc_host fields, removing several bytes of padding

None of these changes affect current code.

Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
---
 include/linux/mmc/core.h |   24 ++++++++++++++++++++++--
 include/linux/mmc/host.h |   16 +++++++++++-----
 include/linux/mmc/mmc.h  |   47 +++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 74 insertions(+), 13 deletions(-)

--- g26.orig/include/linux/mmc/host.h	2007-07-26 13:06:47.000000000 -0700
+++ g26/include/linux/mmc/host.h	2007-07-26 13:06:59.000000000 -0700
@@ -90,6 +90,7 @@ struct mmc_host {
 #define MMC_CAP_BYTEBLOCK	(1 << 2)	/* Can do non-log2 block sizes */
 #define MMC_CAP_MMC_HIGHSPEED	(1 << 3)	/* Can do MMC high-speed timing */
 #define MMC_CAP_SD_HIGHSPEED	(1 << 4)	/* Can do SD high-speed timing */
+#define MMC_CAP_SPI		(1 << 5)	/* Talks only SPI protocols */
 
 	/* host specific block data */
 	unsigned int		max_seg_size;	/* see blk_queue_max_segment_size */
@@ -106,6 +107,14 @@ struct mmc_host {
 	struct mmc_ios		ios;		/* current io bus settings */
 	u32			ocr;		/* the current OCR setting */
 
+	/* group bitfields together to minimize padding */
+	unsigned int		use_spi_crc:1;
+	unsigned int		claimed:1;	/* host exclusively claimed */
+	unsigned int		bus_dead:1;	/* bus has been released */
+#ifdef CONFIG_MMC_DEBUG
+	unsigned int		removed:1;	/* host is being removed */
+#endif
+
 	unsigned int		mode;		/* current card mode of host */
 #define MMC_MODE_MMC		0
 #define MMC_MODE_SD		1
@@ -113,16 +122,11 @@ struct mmc_host {
 	struct mmc_card		*card;		/* device attached to this host */
 
 	wait_queue_head_t	wq;
-	unsigned int		claimed:1;	/* host exclusively claimed */
 
 	struct delayed_work	detect;
-#ifdef CONFIG_MMC_DEBUG
-	unsigned int		removed:1;	/* host is being removed */
-#endif
 
 	const struct mmc_bus_ops *bus_ops;	/* current bus driver */
 	unsigned int		bus_refs;	/* reference counter */
-	unsigned int		bus_dead:1;	/* bus has been released */
 
 	unsigned long		private[0] ____cacheline_aligned;
 };
@@ -137,6 +141,8 @@ static inline void *mmc_priv(struct mmc_
 	return (void *)host->private;
 }
 
+#define mmc_host_is_spi(host)	((host)->caps & MMC_CAP_SPI)
+
 #define mmc_dev(x)	((x)->parent)
 #define mmc_classdev(x)	(&(x)->class_dev)
 #define mmc_hostname(x)	((x)->class_dev.bus_id)
--- g26.orig/include/linux/mmc/core.h	2007-07-26 13:06:47.000000000 -0700
+++ g26/include/linux/mmc/core.h	2007-07-26 13:06:59.000000000 -0700
@@ -25,14 +25,20 @@ struct mmc_command {
 #define MMC_RSP_CRC	(1 << 2)		/* expect valid crc */
 #define MMC_RSP_BUSY	(1 << 3)		/* card may send busy */
 #define MMC_RSP_OPCODE	(1 << 4)		/* response contains opcode */
-#define MMC_CMD_MASK	(3 << 5)		/* command type */
+
+#define MMC_CMD_MASK	(3 << 5)		/* non-SPI command type */
 #define MMC_CMD_AC	(0 << 5)
 #define MMC_CMD_ADTC	(1 << 5)
 #define MMC_CMD_BC	(2 << 5)
 #define MMC_CMD_BCR	(3 << 5)
 
+#define MMC_RSP_SPI_S1	(1 << 7)		/* one status byte */
+#define MMC_RSP_SPI_S2	(1 << 8)		/* second status byte */
+#define MMC_RSP_SPI_B4	(1 << 9)		/* four data bytes */
+#define MMC_RSP_SPI_BUSY (1 << 10)		/* card may send busy */
+
 /*
- * These are the response types, and correspond to valid bit
+ * These are the native response types, and correspond to valid bit
  * patterns of the above flags.  One additional valid pattern
  * is all zeros, which means we don't expect a response.
  */
@@ -47,6 +53,20 @@ struct mmc_command {
 #define mmc_resp_type(cmd)	((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
 
 /*
+ * These are the SPI response types for MMC and SD cards (not SDIO).
+ * Commands return R1, with maybe more info.  Zero is an error type;
+ * callers must always provide the appropriate MMC_RSP_SPI_Rx flags.
+ */
+#define MMC_RSP_SPI_R1	(MMC_RSP_SPI_S1)
+#define MMC_RSP_SPI_R1B	(MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY)
+#define MMC_RSP_SPI_R2	(MMC_RSP_SPI_S1|MMC_RSP_SPI_S2)
+#define MMC_RSP_SPI_R3	(MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
+#define MMC_RSP_SPI_R7	(MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
+
+#define mmc_spi_resp_type(cmd)	((cmd)->flags & \
+		(MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY|MMC_RSP_SPI_S2|MMC_RSP_SPI_B4))
+
+/*
  * These are the command types.
  */
 #define mmc_cmd_type(cmd)	((cmd)->flags & MMC_CMD_MASK)
--- g26.orig/include/linux/mmc/mmc.h	2007-07-26 13:06:47.000000000 -0700
+++ g26/include/linux/mmc/mmc.h	2007-07-26 13:06:59.000000000 -0700
@@ -27,7 +27,7 @@
 
 /* Standard MMC commands (4.1)           type  argument     response */
    /* class 1 */
-#define	MMC_GO_IDLE_STATE         0   /* bc                          */
+#define MMC_GO_IDLE_STATE         0   /* bc                          */
 #define MMC_SEND_OP_COND          1   /* bcr  [31:0] OCR         R3  */
 #define MMC_ALL_SEND_CID          2   /* bcr                     R2  */
 #define MMC_SET_RELATIVE_ADDR     3   /* ac   [31:16] RCA        R1  */
@@ -39,8 +39,10 @@
 #define MMC_SEND_CID             10   /* ac   [31:16] RCA        R2  */
 #define MMC_READ_DAT_UNTIL_STOP  11   /* adtc [31:0] dadr        R1  */
 #define MMC_STOP_TRANSMISSION    12   /* ac                      R1b */
-#define MMC_SEND_STATUS	         13   /* ac   [31:16] RCA        R1  */
+#define MMC_SEND_STATUS          13   /* ac   [31:16] RCA        R1  */
 #define MMC_GO_INACTIVE_STATE    15   /* ac   [31:16] RCA            */
+#define MMC_SPI_READ_OCR         58   /* spi                  spi_R3 */
+#define MMC_SPI_CRC_ON_OFF       59   /* spi  [0:0] flag      spi_R1 */
 
   /* class 2 */
 #define MMC_SET_BLOCKLEN         16   /* ac   [31:0] block len   R1  */
@@ -90,15 +92,15 @@
  */
 
 /*
-  MMC status in R1
+  MMC status in R1, for native mode (SPI bits are different)
   Type
-  	e : error bit
+	e : error bit
 	s : status bit
 	r : detected and set for the actual command response
 	x : detected and set during command execution. the host must poll
             the card by sending status command in order to read these bits.
   Clear condition
-  	a : according to the card state
+	a : according to the card state
 	b : always related to the previous command. Reception of
             a valid command will clear it (with a delay of one command)
 	c : clear by read
@@ -124,10 +126,42 @@
 #define R1_CARD_ECC_DISABLED	(1 << 14)	/* sx, a */
 #define R1_ERASE_RESET		(1 << 13)	/* sr, c */
 #define R1_STATUS(x)            (x & 0xFFFFE000)
-#define R1_CURRENT_STATE(x)    	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
+#define R1_CURRENT_STATE(x)	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
 #define R1_READY_FOR_DATA	(1 << 8)	/* sx, a */
 #define R1_APP_CMD		(1 << 5)	/* sr, c */
 
+/*
+ * MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS
+ * R1 is the low order byte; R2 is the next highest byte, when present.
+ */
+#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 R2_SPI_CARD_LOCKED	(1 << 8)
+#define R2_SPI_WP_ERASE_SKIP	(1 << 9)	/* or lock/unlock fail */
+#define R2_SPI_LOCK_UNLOCK_FAIL	R2_SPI_WP_ERASE_SKIP
+#define R2_SPI_ERROR		(1 << 10)
+#define R2_SPI_CC_ERROR		(1 << 11)
+#define R2_SPI_CARD_ECC_ERROR	(1 << 12)
+#define R2_SPI_WP_VIOLATION	(1 << 13)
+#define R2_SPI_ERASE_PARAM	(1 << 14)
+#define R2_SPI_OUT_OF_RANGE	(1 << 15)	/* or CSD overwrite */
+#define R2_SPI_CSD_OVERWRITE	R2_SPI_OUT_OF_RANGE
+
+static inline int mmc_status_card_is_locked(struct mmc_host *host, u32 status)
+{
+	if (mmc_host_is_spi(host))
+		return status & R2_SPI_CARD_LOCKED;
+	else
+		return status & R1_CARD_IS_LOCKED;
+}
+
+
 /* These are unpacked versions of the actual responses */
 
 struct _mmc_csd {
@@ -182,6 +216,7 @@ struct _mmc_csd {
  */
 #define CCC_BASIC		(1<<0)	/* (0) Basic protocol functions */
 					/* (CMD0,1,2,3,4,7,9,10,12,13,15) */
+					/* (and for SPI, CMD58,59) */
 #define CCC_STREAM_READ		(1<<1)	/* (1) Stream read commands */
 					/* (CMD11) */
 #define CCC_BLOCK_READ		(1<<2)	/* (2) Block read commands */

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 2/4] MMC block learns about SPI
       [not found]                 ` <20070726200639.06242858-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
@ 2007-07-26 20:15                   ` David Brownell
  0 siblings, 0 replies; 47+ messages in thread
From: David Brownell @ 2007-07-26 20:15 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thursday 26 July 2007, Pierre Ossman wrote:
> On Thu, 26 Jul 2007 10:00:16 -0700
> David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:
> 
> > 
> > But then it can't hurt either, and will be safer to cut'n'paste.  :)
> > 
> > Andrew Morton regularly gives feedback which amounts to "that's correct
> > in this context, but please adopt this always-safe idiom" on the grounds
> > that a *LOT* of Linux code is developed by cloning.  It's hard for me to
> > disagree with that... especially when I see my code showing up in some
> > rather strange contexts!
> > 
> 
> Fair enough. But then you'd probably also need to have a look at the
> clause terminating that while loop. I believe it is specific to the 
> native protocol.  

Sigh; yes it is.  In this case, the code won't be cut/pastable.  ;)

Updated patch appended.  (BTW these refreshes are against current GIT.)

========	CUT HERE
Teaching the MMC/SD block card driver about SPI.

 - Provide the SPI response type flags with each request issued.  The
   model is that if no such flags are provided, it will be rejected 
   by the MMC-over-SPI host.

 - Understand that multiblock SPI writes don't use STOP_TRANSMISSION.

 - Correct check for APP_CMD failure.

Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
---
 drivers/mmc/card/block.c |   23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

--- g26.orig/drivers/mmc/card/block.c	2007-07-26 13:06:46.000000000 -0700
+++ g26/drivers/mmc/card/block.c	2007-07-26 13:13:38.000000000 -0700
@@ -151,17 +151,19 @@ static u32 mmc_sd_num_wr_blocks(struct m
 
 	cmd.opcode = MMC_APP_CMD;
 	cmd.arg = card->rca << 16;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
 
 	err = mmc_wait_for_cmd(card->host, &cmd, 0);
-	if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD))
+	if (err != MMC_ERR_NONE)
+		return (u32)-1;
+	if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD))
 		return (u32)-1;
 
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
 	cmd.opcode = SD_APP_SEND_NUM_WR_BLKS;
 	cmd.arg = 0;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 
 	memset(&data, 0, sizeof(struct mmc_data));
 
@@ -220,11 +222,11 @@ static int mmc_blk_issue_rq(struct mmc_q
 		brq.cmd.arg = req->sector;
 		if (!mmc_card_blockaddr(card))
 			brq.cmd.arg <<= 9;
-		brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+		brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 		brq.data.blksz = 1 << md->block_bits;
 		brq.stop.opcode = MMC_STOP_TRANSMISSION;
 		brq.stop.arg = 0;
-		brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
+		brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
 		brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);
 		if (brq.data.blocks > card->host->max_blk_count)
 			brq.data.blocks = card->host->max_blk_count;
@@ -244,7 +246,12 @@ static int mmc_blk_issue_rq(struct mmc_q
 
 		if (brq.data.blocks > 1) {
 			brq.data.flags |= MMC_DATA_MULTI;
-			brq.mrq.stop = &brq.stop;
+			/* SPI multiblock writes terminate using a special
+			 * token, not a STOP_TRANSMISSION request.
+			 */
+			if (!mmc_host_is_spi(card->host)
+					|| rq_data_dir(req) == READ)
+				brq.mrq.stop = &brq.stop;
 			readcmd = MMC_READ_MULTIPLE_BLOCK;
 			writecmd = MMC_WRITE_MULTIPLE_BLOCK;
 		} else {
@@ -302,7 +309,7 @@ static int mmc_blk_issue_rq(struct mmc_q
 			goto cmd_err;
 		}
 
-		if (rq_data_dir(req) != READ) {
+		if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
 			do {
 				int err;
 
@@ -510,7 +517,7 @@ mmc_blk_set_blksize(struct mmc_blk_data 
 	mmc_claim_host(card->host);
 	cmd.opcode = MMC_SET_BLOCKLEN;
 	cmd.arg = 1 << md->block_bits;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
 	err = mmc_wait_for_cmd(card->host, &cmd, 5);
 	mmc_release_host(card->host);
 

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 3/4] MMC core learns about SPI
       [not found]         ` <20070726191824.569542fd-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
@ 2007-07-26 21:58           ` David Brownell
       [not found]             ` <200707261458.55558.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-26 21:58 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thursday 26 July 2007, Pierre Ossman wrote:
> On Sat, 14 Jul 2007 15:07:16 -0700
> David Brownell <david-b@pacbell.net> wrote:
> 
> > @@ -541,7 +548,9 @@ void mmc_rescan(struct work_struct *work
> >  
> >  		err = mmc_send_app_op_cond(host, 0, &ocr);
> >  		if (err == MMC_ERR_NONE) {
> > -			if (mmc_attach_sd(host, ocr))
> > +			if (mmc_host_is_spi(host))
> > +				err = mmc_spi_read_ocr(host, &ocr);
> > +			if (err != MMC_ERR_NONE || mmc_attach_sd(host, ocr))
> >  				mmc_power_off(host);
> >  		} else {
> >  			/*
> 
> Getting the OCR is not vital to determine if it's SD or MMC, so
> we can move that into mmc.c and sd.c, reducing the code in here.

Yes we could ... but this way is IMO simpler, since it retains
a simple invariant:  that the OCR parameter to attach() methods
is always valid.  As one would expect!  Which gives another
advantage:  no surprises.

 
> > @@ -550,7 +559,10 @@ void mmc_rescan(struct work_struct *work
> >  			 */
> >  			err = mmc_send_op_cond(host, 0, &ocr);
> >  			if (err == MMC_ERR_NONE) {
> > -				if (mmc_attach_mmc(host, ocr))
> > +				if (mmc_host_is_spi(host))
> > +					err = mmc_spi_read_ocr(host, &ocr);
> > +				if (err != MMC_ERR_NONE
> > +						|| mmc_attach_mmc(host, ocr))
> >  					mmc_power_off(host);
> >  			} else {
> >  				mmc_power_off(host);
> 
> This will fail to initialise a high capacity MMC card. From the MMC spec:
> 
> "Without the CMD58 with bits [30:29] set as "10b" in prior to the CMD1 a
> higher than 2GB of density of memory will remain in Idle state forever." 

The first thing mmc_attach_mmc() does is reset the card...

Are you sure that "forever" means that reset will fail?
That would be a very foolish thing to specify.  And while
I've seen strange things in specs, that would be one of
the worst that's not a vendor-specific part of a papering
over errata ...

I'd be inclined to leave this alone unless someone gets
such a card and notices that it doesn't work.  After all,
lack of testing on those new (and-I-still-can't-find-one)
cards is a general disclaimer.


> > @@ -95,14 +106,18 @@ int mmc_send_op_cond(struct mmc_host *ho
> >  
> >  	cmd.opcode = MMC_SEND_OP_COND;
> >  	cmd.arg = ocr;
> 
> Argument is "None", not "Ignored", so this should be 0 for SPI.

Indeed.  The specs would make more sense if they said "zero"
in such cases, so the language isn't in conflict with the
day-to-day/commonsense uses of those words!


> > -	mmc_set_data_timeout(&data, card, 0);
> > +	/* Note that for MMC_SEND_EXT_CSD we could set the timeout; but
> > +	 * not for the other requests. But host->card isn't set yet!
> > +	 */
> >  
> 
> This will break on native hosts as a timeout of 0 means exactly that.

Urgh.  OK; I just grew the parameter list by one more member,
which is NULL before there's a card.  (You might consider
setting host->card earlier...)


> > +
> > +int mmc_spi_send_cid(struct mmc_host *host, u32 *cid)
> > +{
> > +	if (!mmc_host_is_spi(host))
> > +		return mmc_send_cxd_native(host, 0, cid, MMC_SEND_CID);
> > +
> > +	return mmc_send_cxd_data(host, MMC_SEND_CID, cid, 16);
> > +}
> > +
> 
> As this is not SPI specific (except for its current use), why not mmc_send_cid() ?
> 
> And the argument for it is the RCA (in native mode).

OK ... renamed, and passed "rca << 16".  At least, if host->card
has been set, which only happens *after* the enumeration sequence
needs to fetch the CID ... which is done before it knows whether
to create a card struct or not.  (The SEND_ALL_CID request is used
for non-SPI hosts.)


> > +
> > +/* Enabling software CRCs can be a significant (30%) performance cost,
> > + * and for other reasons isn't always desired; so it can be disabled.
> > + */
> > +static int use_spi_crc = 1;
> > +module_param(use_spi_crc, bool, 0);
> > +
> 
> The *_ops.c only contain function wrappers of protocol commands, not
> policy. So I think this is better placed where mmc_spi_set_crc() is called. 

You mean, have separate module params for SD and for MMC?
It seems cleaner this way...


> > --- g26.orig/drivers/mmc/core/mmc.c	2007-07-14 14:47:12.000000000 -0700
> > +++ g26/drivers/mmc/core/mmc.c	2007-07-14 14:47:54.000000000 -0700
> > @@ -264,7 +264,13 @@ static int mmc_init_card(struct mmc_host
> >  	/*
> >  	 * Fetch CID from card.
> >  	 */
> > -	err = mmc_all_send_cid(host, cid);
> > +	if (mmc_host_is_spi(host)) {
> > +		err = mmc_spi_set_crc(host);
> > +		if (err != MMC_ERR_NONE)
> > +			goto err;
> > +		err = mmc_spi_send_cid(host, cid);
> > +	} else
> > +		err = mmc_all_send_cid(host, cid);
> >  	if (err != MMC_ERR_NONE)
> >  		goto err;
> >  
> 
> A matter of taste, but wouldn't it be clearer if you separated out
> the crc bit and did that earlier, before the above comment?
>	[ ditto for SD ]

OK.  The comment probably should have been updated, but I'd expect
GCC will merge the two adjacent "if (it's spi)" blocks which will
be the result.


> > --- g26.orig/drivers/mmc/core/sd_ops.c	2007-07-14 14:47:12.000000000 -0700
> > +++ g26/drivers/mmc/core/sd_ops.c	2007-07-14 14:47:54.000000000 -0700
> > @@ -70,6 +70,12 @@ int mmc_wait_for_app_cmd(struct mmc_host
> >  		err = cmd->error;
> >  		if (cmd->error == MMC_ERR_NONE)
> >  			break;
> > +
> > +		/* no point in retrying illegal commands! */
> > +		if (mmc_host_is_spi(host)) {
> > +			if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
> > +				break;
> > +		}
> >  	}
> >  
> >  	return err;
> 
> "Illegal command" refers to the previous command sent (and failed).
> So this can give false negatives. 

The immediately preceding command would be MMC_APP_CMD ... ?
I didn't see any language in the specs which could let me
interpret this as anything other than APP_CMD failure:

   When an error bit is detected in “R” mode the card will
   report the error in the response to the command that raised
   the exception. The command will not be executed and the
   associated state transition will not take place. 

What this does is to quickly abort retries of SD card ops
that were sent to MMC cards, and which always fail with
ILLEGAL_COMMAND.

- Dave


========	CUT HERE
Teach the MMC/SD/SDIO core about using SPI mode.

 - Use mmc_host_is_spi() so enumeration works through SPI signaling
   and protocols, not just the native versions.

 - Provide the SPI response type flags with each request issued,
   including requests from the new lock/unlock code.

 - Understand that some commands act a bit differently ... notably:
     * OP_COND command reports busy status differently
     * OP_COND command doesn't return the OCR
     * APP_CMD status doesn't have an R1_APP_CMD analogue

 - Understand that cmd->resp[0] and mmc_get_status() results for SPI
   return different values than for "native" MMC/SD protocol; this
   affects checking card lock status, and some others.

Those changes required some new and updated primitives:

 - Provide utilities to access two SPI-only requests, and one
   request that wasn't previously needed:
     * mmc_spi_read_ocr() ... SPI only
     * mmc_spi_set_crc() ... SPI only (override by module parm)
     * mmc_spi_send_cid() ... works without broadcast mode

 - Updated internal routines:
     * Previous mmc_send_csd() modified into mmc_send_cxd_native();
       it uses native "R2" responses, which include 16 bytes of data.
     * Previous mmc_send_ext_csd() becomes new mmc_send_cxd_data()
       helper for command-and-data access
     * Bugfix to that mmc_send_cxd_data() code:  dma-to-stack is
       unsafe/nonportable, so kmalloc a bounce buffer instead.

 - Modified mmc_send_ext_csd() now uses mmc_send_cxd_data() helper

 - Modified mmc_send_csd(), and new mmc_spi_send_cid(), routines use
   those helper routines based on whether they're native or SPI

The SPI support hasn't been tested on the new MMC4 cards (they're not
widely available); likewise SD cards with 4GB and up may have surprises
lurking.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
 drivers/mmc/core/core.c    |   24 ++++--
 drivers/mmc/core/mmc.c     |   39 +++++++---
 drivers/mmc/core/mmc_ops.c |  165 ++++++++++++++++++++++++++++++++++++---------
 drivers/mmc/core/mmc_ops.h |    3 
 drivers/mmc/core/sd.c      |   37 +++++++---
 drivers/mmc/core/sd_ops.c  |   32 ++++++--
 6 files changed, 232 insertions(+), 68 deletions(-)

--- g26.orig/drivers/mmc/core/mmc_ops.h	2007-07-26 13:13:23.000000000 -0700
+++ g26/drivers/mmc/core/mmc_ops.h	2007-07-26 14:54:35.000000000 -0700
@@ -22,6 +22,9 @@ int mmc_send_csd(struct mmc_card *card, 
 int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
 int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
 int mmc_send_status(struct mmc_card *card, u32 *status);
+int mmc_send_cid(struct mmc_host *host, u32 *cid);
+int mmc_spi_read_ocr(struct mmc_host *host, u32 *ocrp);
+int mmc_spi_set_crc(struct mmc_host *host);
 
 #endif
 
--- g26.orig/drivers/mmc/core/core.c	2007-07-26 13:13:24.000000000 -0700
+++ g26/drivers/mmc/core/core.c	2007-07-26 14:54:35.000000000 -0700
@@ -404,8 +404,13 @@ static void mmc_power_up(struct mmc_host
 	int bit = fls(host->ocr_avail) - 1;
 
 	host->ios.vdd = bit;
-	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
-	host->ios.chip_select = MMC_CS_DONTCARE;
+	if (mmc_host_is_spi(host)) {
+		host->ios.chip_select = MMC_CS_HIGH;
+		host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+	} else {
+		host->ios.chip_select = MMC_CS_DONTCARE;
+		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+	}
 	host->ios.power_mode = MMC_POWER_UP;
 	host->ios.bus_width = MMC_BUS_WIDTH_1;
 	host->ios.timing = MMC_TIMING_LEGACY;
@@ -424,8 +429,10 @@ static void mmc_power_off(struct mmc_hos
 {
 	host->ios.clock = 0;
 	host->ios.vdd = 0;
-	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
-	host->ios.chip_select = MMC_CS_DONTCARE;
+	if (!mmc_host_is_spi(host)) {
+		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+		host->ios.chip_select = MMC_CS_DONTCARE;
+	}
 	host->ios.power_mode = MMC_POWER_OFF;
 	host->ios.bus_width = MMC_BUS_WIDTH_1;
 	host->ios.timing = MMC_TIMING_LEGACY;
@@ -541,7 +548,9 @@ void mmc_rescan(struct work_struct *work
 
 		err = mmc_send_app_op_cond(host, 0, &ocr);
 		if (err == MMC_ERR_NONE) {
-			if (mmc_attach_sd(host, ocr))
+			if (mmc_host_is_spi(host))
+				err = mmc_spi_read_ocr(host, &ocr);
+			if (err != MMC_ERR_NONE || mmc_attach_sd(host, ocr))
 				mmc_power_off(host);
 		} else {
 			/*
@@ -550,7 +559,10 @@ void mmc_rescan(struct work_struct *work
 			 */
 			err = mmc_send_op_cond(host, 0, &ocr);
 			if (err == MMC_ERR_NONE) {
-				if (mmc_attach_mmc(host, ocr))
+				if (mmc_host_is_spi(host))
+					err = mmc_spi_read_ocr(host, &ocr);
+				if (err != MMC_ERR_NONE
+						|| mmc_attach_mmc(host, ocr))
 					mmc_power_off(host);
 			} else {
 				mmc_power_off(host);
--- g26.orig/drivers/mmc/core/mmc_ops.c	2007-07-26 13:13:23.000000000 -0700
+++ g26/drivers/mmc/core/mmc_ops.c	2007-07-26 14:54:35.000000000 -0700
@@ -63,23 +63,34 @@ int mmc_go_idle(struct mmc_host *host)
 	int err;
 	struct mmc_command cmd;
 
-	mmc_set_chip_select(host, MMC_CS_HIGH);
-
-	mmc_delay(1);
+	/*
+	 * Non-SPI hosts need to prevent chipselect going active during
+	 * GO_IDLE; that would put chips into SPI mode.  Remind them of
+	 * that in case of hardware that won't pull up DAT3/nCS otherwise.
+	 *
+	 * SPI hosts ignore ios.chip_select; it's managed according to
+	 * rules that must accomodate non-MMC slaves which this layer
+	 * won't even know about.
+	 */
+	if (!mmc_host_is_spi(host)) {
+		mmc_set_chip_select(host, MMC_CS_HIGH);
+		mmc_delay(1);
+	}
 
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
 	cmd.opcode = MMC_GO_IDLE_STATE;
 	cmd.arg = 0;
-	cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
 
 	err = mmc_wait_for_cmd(host, &cmd, 0);
 
 	mmc_delay(1);
 
-	mmc_set_chip_select(host, MMC_CS_DONTCARE);
-
-	mmc_delay(1);
+	if (!mmc_host_is_spi(host)) {
+		mmc_set_chip_select(host, MMC_CS_DONTCARE);
+		mmc_delay(1);
+	}
 
 	return err;
 }
@@ -94,15 +105,19 @@ int mmc_send_op_cond(struct mmc_host *ho
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
 	cmd.opcode = MMC_SEND_OP_COND;
-	cmd.arg = ocr;
-	cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+	cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
 
 	for (i = 100; i; i--) {
 		err = mmc_wait_for_cmd(host, &cmd, 0);
 		if (err != MMC_ERR_NONE)
 			break;
 
-		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+		if (mmc_host_is_spi(host)) {
+			/* wait until reset completes */
+			if (!(cmd.resp[0] & R1_SPI_IDLE))
+				break;
+		} else if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
 			break;
 
 		err = MMC_ERR_TIMEOUT;
@@ -110,7 +125,7 @@ int mmc_send_op_cond(struct mmc_host *ho
 		mmc_delay(10);
 	}
 
-	if (rocr)
+	if (rocr && !mmc_host_is_spi(host))
 		*rocr = cmd.resp[0];
 
 	return err;
@@ -160,40 +175,46 @@ int mmc_set_relative_addr(struct mmc_car
 	return MMC_ERR_NONE;
 }
 
-int mmc_send_csd(struct mmc_card *card, u32 *csd)
+static int
+mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
 {
 	int err;
 	struct mmc_command cmd;
 
-	BUG_ON(!card);
-	BUG_ON(!card->host);
-	BUG_ON(!csd);
+	BUG_ON(!host);
+	BUG_ON(!cxd);
 
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
-	cmd.opcode = MMC_SEND_CSD;
-	cmd.arg = card->rca << 16;
+	cmd.opcode = opcode;
+	cmd.arg = arg;
 	cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
 
-	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
 	if (err != MMC_ERR_NONE)
 		return err;
 
-	memcpy(csd, cmd.resp, sizeof(u32) * 4);
+	memcpy(cxd, cmd.resp, sizeof(u32) * 4);
 
 	return MMC_ERR_NONE;
 }
 
-int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
+static int
+mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
+		u32 opcode, void *buf, unsigned len)
 {
 	struct mmc_request mrq;
 	struct mmc_command cmd;
 	struct mmc_data data;
 	struct scatterlist sg;
+	void *data_buf;
 
-	BUG_ON(!card);
-	BUG_ON(!card->host);
-	BUG_ON(!ext_csd);
+	/* dma onto stack is unsafe/nonportable, but callers here
+	 * normally provide temporary on-stack buffers.
+	 */
+	data_buf = kmalloc(len, GFP_KERNEL);
+	if (data_buf == NULL)
+		return -ENOMEM;
 
 	memset(&mrq, 0, sizeof(struct mmc_request));
 	memset(&cmd, 0, sizeof(struct mmc_command));
@@ -202,21 +223,31 @@ int mmc_send_ext_csd(struct mmc_card *ca
 	mrq.cmd = &cmd;
 	mrq.data = &data;
 
-	cmd.opcode = MMC_SEND_EXT_CSD;
+	cmd.opcode = opcode;
 	cmd.arg = 0;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
 
-	data.blksz = 512;
+	/* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
+	 * rely on callers to never use this with "native" calls for reading
+	 * CSD or CID.  Native versions of those commands use the R2 type,
+	 * not R1 plus a data block.
+	 */
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	data.blksz = len;
 	data.blocks = 1;
 	data.flags = MMC_DATA_READ;
 	data.sg = &sg;
 	data.sg_len = 1;
 
-	sg_init_one(&sg, ext_csd, 512);
+	sg_init_one(&sg, data_buf, len);
 
-	mmc_set_data_timeout(&data, card, 0);
+	if (card)
+		mmc_set_data_timeout(&data, card, 0);
 
-	mmc_wait_for_req(card->host, &mrq);
+	mmc_wait_for_req(host, &mrq);
+
+	memcpy(buf, data_buf, len);
+	kfree(data_buf);
 
 	if (cmd.error != MMC_ERR_NONE)
 		return cmd.error;
@@ -226,6 +257,72 @@ int mmc_send_ext_csd(struct mmc_card *ca
 	return MMC_ERR_NONE;
 }
 
+int mmc_send_csd(struct mmc_card *card, u32 *csd)
+{
+	if (!mmc_host_is_spi(card->host))
+		return mmc_send_cxd_native(card->host, card->rca << 16,
+				csd, MMC_SEND_CSD);
+
+	return mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd, 16);
+}
+
+int mmc_send_cid(struct mmc_host *host, u32 *cid)
+{
+	if (!mmc_host_is_spi(host)) {
+		if (!host->card)
+			return MMC_ERR_INVALID;
+		return mmc_send_cxd_native(host->card, host->card->rca << 16,
+				cid, MMC_SEND_CID);
+	}
+
+	return mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid, 16);
+}
+
+int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
+{
+	return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
+			ext_csd, 512);
+}
+
+int mmc_spi_read_ocr(struct mmc_host *host, u32 *ocrp)
+{
+	struct mmc_command cmd;
+	int err;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = MMC_SPI_READ_OCR;
+	cmd.flags = MMC_RSP_SPI_R3;
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+
+	*ocrp = cmd.resp[1];
+	return err;
+}
+
+/* Enabling software CRCs can be a significant (30%) performance cost,
+ * and for other reasons isn't always desired; so it can be disabled.
+ */
+static int use_spi_crc = 1;
+module_param(use_spi_crc, bool, 0);
+
+int mmc_spi_set_crc(struct mmc_host *host)
+{
+	struct mmc_command cmd;
+	int err;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = MMC_SPI_CRC_ON_OFF;
+	cmd.flags = MMC_RSP_SPI_R1;
+	cmd.arg = use_spi_crc;
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err == MMC_ERR_NONE)
+		host->use_spi_crc = use_spi_crc;
+	return err;
+}
+
 int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
 {
 	int err;
@@ -241,7 +338,7 @@ int mmc_switch(struct mmc_card *card, u8
 		  (index << 16) |
 		  (value << 8) |
 		  set;
-	cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+	cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
 
 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
 	if (err != MMC_ERR_NONE)
@@ -261,13 +358,17 @@ int mmc_send_status(struct mmc_card *car
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
 	cmd.opcode = MMC_SEND_STATUS;
-	cmd.arg = card->rca << 16;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+	if (!mmc_host_is_spi(card->host))
+		cmd.arg = card->rca << 16;
+	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
 
 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
 	if (err != MMC_ERR_NONE)
 		return err;
 
+	/* NOTE: callers are required to understand the difference
+	 * between "native" and SPI format status words!
+	 */
 	if (status)
 		*status = cmd.resp[0];
 
--- g26.orig/drivers/mmc/core/mmc.c	2007-07-26 13:13:24.000000000 -0700
+++ g26/drivers/mmc/core/mmc.c	2007-07-26 14:54:35.000000000 -0700
@@ -262,9 +262,21 @@ static int mmc_init_card(struct mmc_host
 		goto err;
 
 	/*
+	 * For SPI, enable CRC as appropriate.
+	 */
+	if (mmc_host_is_spi(host)) {
+		err = mmc_spi_set_crc(host);
+		if (err != MMC_ERR_NONE)
+			goto err;
+	}
+
+	/*
 	 * Fetch CID from card.
 	 */
-	err = mmc_all_send_cid(host, cid);
+	if (mmc_host_is_spi(host))
+		err = mmc_send_cid(host, cid);
+	else
+		err = mmc_all_send_cid(host, cid);
 	if (err != MMC_ERR_NONE)
 		goto err;
 
@@ -287,13 +299,15 @@ static int mmc_init_card(struct mmc_host
 	}
 
 	/*
-	 * Set card RCA.
+	 * For native busses:  set card RCA and leave open drain mode.
 	 */
-	err = mmc_set_relative_addr(card);
-	if (err != MMC_ERR_NONE)
-		goto free_card;
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_set_relative_addr(card);
+		if (err != MMC_ERR_NONE)
+			goto free_card;
 
-	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+	}
 
 	if (!oldcard) {
 		/*
@@ -314,13 +328,15 @@ static int mmc_init_card(struct mmc_host
 	/*
 	 * Select card, as all following commands rely on that.
 	 */
-	err = mmc_select_card(card);
-	if (err != MMC_ERR_NONE)
-		goto free_card;
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_select_card(card);
+		if (err != MMC_ERR_NONE)
+			goto free_card;
+	}
 
 	if (!oldcard) {
 		/*
-		 * Fetch and process extened CSD.
+		 * Fetch and process extended CSD.
 		 */
 		err = mmc_read_ext_csd(card);
 		if (err != MMC_ERR_NONE)
@@ -480,7 +496,8 @@ static void mmc_suspend(struct mmc_host 
 	BUG_ON(!host->card);
 
 	mmc_claim_host(host);
-	mmc_deselect_cards(host);
+	if (!mmc_host_is_spi(host))
+		mmc_deselect_cards(host);
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 	mmc_release_host(host);
 }
--- g26.orig/drivers/mmc/core/sd.c	2007-07-26 13:13:23.000000000 -0700
+++ g26/drivers/mmc/core/sd.c	2007-07-26 14:54:35.000000000 -0700
@@ -319,9 +319,21 @@ static int mmc_sd_init_card(struct mmc_h
 		goto err;
 
 	/*
+	 * For SPI, enable CRC as appropriate.
+	 */
+	if (mmc_host_is_spi(host)) {
+		err = mmc_spi_set_crc(host);
+		if (err != MMC_ERR_NONE)
+			goto err;
+	}
+
+	/*
 	 * Fetch CID from card.
 	 */
-	err = mmc_all_send_cid(host, cid);
+	if (mmc_host_is_spi(host))
+		err = mmc_send_cid(host, cid);
+	else
+		err = mmc_all_send_cid(host, cid);
 	if (err != MMC_ERR_NONE)
 		goto err;
 
@@ -343,13 +355,15 @@ static int mmc_sd_init_card(struct mmc_h
 	}
 
 	/*
-	 * Set card RCA.
+	 * For native busses:  get card RCA and leave open drain mode.
 	 */
-	err = mmc_send_relative_addr(host, &card->rca);
-	if (err != MMC_ERR_NONE)
-		goto free_card;
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_send_relative_addr(host, &card->rca);
+		if (err != MMC_ERR_NONE)
+			goto free_card;
 
-	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+	}
 
 	if (!oldcard) {
 		/*
@@ -369,9 +383,11 @@ static int mmc_sd_init_card(struct mmc_h
 	/*
 	 * Select card, as all following commands rely on that.
 	 */
-	err = mmc_select_card(card);
-	if (err != MMC_ERR_NONE)
-		goto free_card;
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_select_card(card);
+		if (err != MMC_ERR_NONE)
+			goto free_card;
+	}
 
 	if (!oldcard) {
 		/*
@@ -554,7 +570,8 @@ static void mmc_sd_suspend(struct mmc_ho
 	BUG_ON(!host->card);
 
 	mmc_claim_host(host);
-	mmc_deselect_cards(host);
+	if (!mmc_host_is_spi(host))
+		mmc_deselect_cards(host);
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 	mmc_release_host(host);
 }
--- g26.orig/drivers/mmc/core/sd_ops.c	2007-07-26 13:13:24.000000000 -0700
+++ g26/drivers/mmc/core/sd_ops.c	2007-07-26 14:54:35.000000000 -0700
@@ -70,6 +70,12 @@ int mmc_wait_for_app_cmd(struct mmc_host
 		err = cmd->error;
 		if (cmd->error == MMC_ERR_NONE)
 			break;
+
+		/* no point in retrying illegal commands! */
+		if (mmc_host_is_spi(host)) {
+			if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
+				break;
+		}
 	}
 
 	return err;
@@ -89,10 +95,10 @@ int mmc_app_cmd(struct mmc_host *host, s
 
 	if (card) {
 		cmd.arg = card->rca << 16;
-		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+		cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
 	} else {
 		cmd.arg = 0;
-		cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR;
+		cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR;
 	}
 
 	err = mmc_wait_for_cmd(host, &cmd, 0);
@@ -100,8 +106,8 @@ int mmc_app_cmd(struct mmc_host *host, s
 		return err;
 
 	/* Check that card supported application commands */
-	if (!(cmd.resp[0] & R1_APP_CMD))
-		return MMC_ERR_FAILED;
+	if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
+		return (u32)-1;
 
 	return MMC_ERR_NONE;
 }
@@ -148,14 +154,18 @@ int mmc_send_app_op_cond(struct mmc_host
 
 	cmd.opcode = SD_APP_OP_COND;
 	cmd.arg = ocr;
-	cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
 
 	for (i = 100; i; i--) {
 		err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
 		if (err != MMC_ERR_NONE)
 			break;
 
-		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+		if (mmc_host_is_spi(host)) {
+			/* wait until reset completes */
+			if (!(cmd.resp[0] & R1_SPI_IDLE))
+				break;
+		} else if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
 			break;
 
 		err = MMC_ERR_TIMEOUT;
@@ -182,7 +192,7 @@ int mmc_send_if_cond(struct mmc_host *ho
 	 */
 	cmd.opcode = SD_SEND_IF_COND;
 	cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
-	cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
+	cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;
 
 	err = mmc_wait_for_cmd(host, &cmd, 0);
 	if (err != MMC_ERR_NONE)
@@ -229,6 +239,8 @@ int mmc_app_send_scr(struct mmc_card *ca
 	BUG_ON(!card->host);
 	BUG_ON(!scr);
 
+	/* NOTE: caller guarantees scr is heap-allocated */
+
 	err = mmc_app_cmd(card->host, card);
 	if (err != MMC_ERR_NONE)
 		return err;
@@ -242,7 +254,7 @@ int mmc_app_send_scr(struct mmc_card *ca
 
 	cmd.opcode = SD_APP_SEND_SCR;
 	cmd.arg = 0;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 
 	data.blksz = 8;
 	data.blocks = 1;
@@ -278,6 +290,8 @@ int mmc_sd_switch(struct mmc_card *card,
 	BUG_ON(!card);
 	BUG_ON(!card->host);
 
+	/* NOTE: caller guarantees resp is heap-allocated */
+
 	mode = !!mode;
 	value &= 0xF;
 
@@ -292,7 +306,7 @@ int mmc_sd_switch(struct mmc_card *card,
 	cmd.arg = mode << 31 | 0x00FFFFFF;
 	cmd.arg &= ~(0xF << (group * 4));
 	cmd.arg |= value << (group * 4);
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 
 	data.blksz = 64;
 	data.blocks = 1;

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
spi-devel-general mailing list
spi-devel-general@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

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

* Re: [patch 2.6.22-git5 3/4] MMC core learns about SPI
       [not found]             ` <200707261458.55558.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-07-26 22:22               ` David Brownell
  2007-08-01 15:02               ` Pierre Ossman
  1 sibling, 0 replies; 47+ messages in thread
From: David Brownell @ 2007-07-26 22:22 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

Grr ... sent wrong version.  This passes "host" not "host->card" in
one place (GCC would warn, and is otherwise the same.

======	CUT HERE
Teach the MMC/SD/SDIO core about using SPI mode.

 - Use mmc_host_is_spi() so enumeration works through SPI signaling
   and protocols, not just the native versions.

 - Provide the SPI response type flags with each request issued,
   including requests from the new lock/unlock code.

 - Understand that some commands act a bit differently ... notably:
     * OP_COND command reports busy status differently
     * OP_COND command doesn't return the OCR
     * APP_CMD status doesn't have an R1_APP_CMD analogue

 - Understand that cmd->resp[0] and mmc_get_status() results for SPI
   return different values than for "native" MMC/SD protocol; this
   affects checking card lock status, and some others.

Those changes required some new and updated primitives:

 - Provide utilities to access two SPI-only requests, and one
   request that wasn't previously needed:
     * mmc_spi_read_ocr() ... SPI only
     * mmc_spi_set_crc() ... SPI only (override by module parm)
     * mmc_spi_send_cid() ... works without broadcast mode

 - Updated internal routines:
     * Previous mmc_send_csd() modified into mmc_send_cxd_native();
       it uses native "R2" responses, which include 16 bytes of data.
     * Previous mmc_send_ext_csd() becomes new mmc_send_cxd_data()
       helper for command-and-data access
     * Bugfix to that mmc_send_cxd_data() code:  dma-to-stack is
       unsafe/nonportable, so kmalloc a bounce buffer instead.

 - Modified mmc_send_ext_csd() now uses mmc_send_cxd_data() helper

 - Modified mmc_send_csd(), and new mmc_spi_send_cid(), routines use
   those helper routines based on whether they're native or SPI

The SPI support hasn't been tested on the new MMC4 cards (they're not
widely available); likewise SD cards with 4GB and up may have surprises
lurking.

Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
---
 drivers/mmc/core/core.c    |   24 ++++--
 drivers/mmc/core/mmc.c     |   39 +++++++---
 drivers/mmc/core/mmc_ops.c |  165 ++++++++++++++++++++++++++++++++++++---------
 drivers/mmc/core/mmc_ops.h |    3 
 drivers/mmc/core/sd.c      |   37 +++++++---
 drivers/mmc/core/sd_ops.c  |   32 ++++++--
 6 files changed, 232 insertions(+), 68 deletions(-)

--- g26.orig/drivers/mmc/core/mmc_ops.h	2007-07-26 13:13:23.000000000 -0700
+++ g26/drivers/mmc/core/mmc_ops.h	2007-07-26 14:54:35.000000000 -0700
@@ -22,6 +22,9 @@ int mmc_send_csd(struct mmc_card *card, 
 int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
 int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
 int mmc_send_status(struct mmc_card *card, u32 *status);
+int mmc_send_cid(struct mmc_host *host, u32 *cid);
+int mmc_spi_read_ocr(struct mmc_host *host, u32 *ocrp);
+int mmc_spi_set_crc(struct mmc_host *host);
 
 #endif
 
--- g26.orig/drivers/mmc/core/core.c	2007-07-26 13:13:24.000000000 -0700
+++ g26/drivers/mmc/core/core.c	2007-07-26 14:54:35.000000000 -0700
@@ -404,8 +404,13 @@ static void mmc_power_up(struct mmc_host
 	int bit = fls(host->ocr_avail) - 1;
 
 	host->ios.vdd = bit;
-	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
-	host->ios.chip_select = MMC_CS_DONTCARE;
+	if (mmc_host_is_spi(host)) {
+		host->ios.chip_select = MMC_CS_HIGH;
+		host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+	} else {
+		host->ios.chip_select = MMC_CS_DONTCARE;
+		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+	}
 	host->ios.power_mode = MMC_POWER_UP;
 	host->ios.bus_width = MMC_BUS_WIDTH_1;
 	host->ios.timing = MMC_TIMING_LEGACY;
@@ -424,8 +429,10 @@ static void mmc_power_off(struct mmc_hos
 {
 	host->ios.clock = 0;
 	host->ios.vdd = 0;
-	host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
-	host->ios.chip_select = MMC_CS_DONTCARE;
+	if (!mmc_host_is_spi(host)) {
+		host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+		host->ios.chip_select = MMC_CS_DONTCARE;
+	}
 	host->ios.power_mode = MMC_POWER_OFF;
 	host->ios.bus_width = MMC_BUS_WIDTH_1;
 	host->ios.timing = MMC_TIMING_LEGACY;
@@ -541,7 +548,9 @@ void mmc_rescan(struct work_struct *work
 
 		err = mmc_send_app_op_cond(host, 0, &ocr);
 		if (err == MMC_ERR_NONE) {
-			if (mmc_attach_sd(host, ocr))
+			if (mmc_host_is_spi(host))
+				err = mmc_spi_read_ocr(host, &ocr);
+			if (err != MMC_ERR_NONE || mmc_attach_sd(host, ocr))
 				mmc_power_off(host);
 		} else {
 			/*
@@ -550,7 +559,10 @@ void mmc_rescan(struct work_struct *work
 			 */
 			err = mmc_send_op_cond(host, 0, &ocr);
 			if (err == MMC_ERR_NONE) {
-				if (mmc_attach_mmc(host, ocr))
+				if (mmc_host_is_spi(host))
+					err = mmc_spi_read_ocr(host, &ocr);
+				if (err != MMC_ERR_NONE
+						|| mmc_attach_mmc(host, ocr))
 					mmc_power_off(host);
 			} else {
 				mmc_power_off(host);
--- g26.orig/drivers/mmc/core/mmc_ops.c	2007-07-26 13:13:23.000000000 -0700
+++ g26/drivers/mmc/core/mmc_ops.c	2007-07-26 15:21:57.000000000 -0700
@@ -63,23 +63,34 @@ int mmc_go_idle(struct mmc_host *host)
 	int err;
 	struct mmc_command cmd;
 
-	mmc_set_chip_select(host, MMC_CS_HIGH);
-
-	mmc_delay(1);
+	/*
+	 * Non-SPI hosts need to prevent chipselect going active during
+	 * GO_IDLE; that would put chips into SPI mode.  Remind them of
+	 * that in case of hardware that won't pull up DAT3/nCS otherwise.
+	 *
+	 * SPI hosts ignore ios.chip_select; it's managed according to
+	 * rules that must accomodate non-MMC slaves which this layer
+	 * won't even know about.
+	 */
+	if (!mmc_host_is_spi(host)) {
+		mmc_set_chip_select(host, MMC_CS_HIGH);
+		mmc_delay(1);
+	}
 
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
 	cmd.opcode = MMC_GO_IDLE_STATE;
 	cmd.arg = 0;
-	cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
 
 	err = mmc_wait_for_cmd(host, &cmd, 0);
 
 	mmc_delay(1);
 
-	mmc_set_chip_select(host, MMC_CS_DONTCARE);
-
-	mmc_delay(1);
+	if (!mmc_host_is_spi(host)) {
+		mmc_set_chip_select(host, MMC_CS_DONTCARE);
+		mmc_delay(1);
+	}
 
 	return err;
 }
@@ -94,15 +105,19 @@ int mmc_send_op_cond(struct mmc_host *ho
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
 	cmd.opcode = MMC_SEND_OP_COND;
-	cmd.arg = ocr;
-	cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+	cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
 
 	for (i = 100; i; i--) {
 		err = mmc_wait_for_cmd(host, &cmd, 0);
 		if (err != MMC_ERR_NONE)
 			break;
 
-		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+		if (mmc_host_is_spi(host)) {
+			/* wait until reset completes */
+			if (!(cmd.resp[0] & R1_SPI_IDLE))
+				break;
+		} else if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
 			break;
 
 		err = MMC_ERR_TIMEOUT;
@@ -110,7 +125,7 @@ int mmc_send_op_cond(struct mmc_host *ho
 		mmc_delay(10);
 	}
 
-	if (rocr)
+	if (rocr && !mmc_host_is_spi(host))
 		*rocr = cmd.resp[0];
 
 	return err;
@@ -160,40 +175,46 @@ int mmc_set_relative_addr(struct mmc_car
 	return MMC_ERR_NONE;
 }
 
-int mmc_send_csd(struct mmc_card *card, u32 *csd)
+static int
+mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
 {
 	int err;
 	struct mmc_command cmd;
 
-	BUG_ON(!card);
-	BUG_ON(!card->host);
-	BUG_ON(!csd);
+	BUG_ON(!host);
+	BUG_ON(!cxd);
 
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
-	cmd.opcode = MMC_SEND_CSD;
-	cmd.arg = card->rca << 16;
+	cmd.opcode = opcode;
+	cmd.arg = arg;
 	cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
 
-	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
 	if (err != MMC_ERR_NONE)
 		return err;
 
-	memcpy(csd, cmd.resp, sizeof(u32) * 4);
+	memcpy(cxd, cmd.resp, sizeof(u32) * 4);
 
 	return MMC_ERR_NONE;
 }
 
-int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
+static int
+mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
+		u32 opcode, void *buf, unsigned len)
 {
 	struct mmc_request mrq;
 	struct mmc_command cmd;
 	struct mmc_data data;
 	struct scatterlist sg;
+	void *data_buf;
 
-	BUG_ON(!card);
-	BUG_ON(!card->host);
-	BUG_ON(!ext_csd);
+	/* dma onto stack is unsafe/nonportable, but callers here
+	 * normally provide temporary on-stack buffers.
+	 */
+	data_buf = kmalloc(len, GFP_KERNEL);
+	if (data_buf == NULL)
+		return -ENOMEM;
 
 	memset(&mrq, 0, sizeof(struct mmc_request));
 	memset(&cmd, 0, sizeof(struct mmc_command));
@@ -202,21 +223,31 @@ int mmc_send_ext_csd(struct mmc_card *ca
 	mrq.cmd = &cmd;
 	mrq.data = &data;
 
-	cmd.opcode = MMC_SEND_EXT_CSD;
+	cmd.opcode = opcode;
 	cmd.arg = 0;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
 
-	data.blksz = 512;
+	/* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
+	 * rely on callers to never use this with "native" calls for reading
+	 * CSD or CID.  Native versions of those commands use the R2 type,
+	 * not R1 plus a data block.
+	 */
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	data.blksz = len;
 	data.blocks = 1;
 	data.flags = MMC_DATA_READ;
 	data.sg = &sg;
 	data.sg_len = 1;
 
-	sg_init_one(&sg, ext_csd, 512);
+	sg_init_one(&sg, data_buf, len);
 
-	mmc_set_data_timeout(&data, card, 0);
+	if (card)
+		mmc_set_data_timeout(&data, card, 0);
 
-	mmc_wait_for_req(card->host, &mrq);
+	mmc_wait_for_req(host, &mrq);
+
+	memcpy(buf, data_buf, len);
+	kfree(data_buf);
 
 	if (cmd.error != MMC_ERR_NONE)
 		return cmd.error;
@@ -226,6 +257,72 @@ int mmc_send_ext_csd(struct mmc_card *ca
 	return MMC_ERR_NONE;
 }
 
+int mmc_send_csd(struct mmc_card *card, u32 *csd)
+{
+	if (!mmc_host_is_spi(card->host))
+		return mmc_send_cxd_native(card->host, card->rca << 16,
+				csd, MMC_SEND_CSD);
+
+	return mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd, 16);
+}
+
+int mmc_send_cid(struct mmc_host *host, u32 *cid)
+{
+	if (!mmc_host_is_spi(host)) {
+		if (!host->card)
+			return MMC_ERR_INVALID;
+		return mmc_send_cxd_native(host, host->card->rca << 16,
+				cid, MMC_SEND_CID);
+	}
+
+	return mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid, 16);
+}
+
+int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
+{
+	return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
+			ext_csd, 512);
+}
+
+int mmc_spi_read_ocr(struct mmc_host *host, u32 *ocrp)
+{
+	struct mmc_command cmd;
+	int err;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = MMC_SPI_READ_OCR;
+	cmd.flags = MMC_RSP_SPI_R3;
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+
+	*ocrp = cmd.resp[1];
+	return err;
+}
+
+/* Enabling software CRCs can be a significant (30%) performance cost,
+ * and for other reasons isn't always desired; so it can be disabled.
+ */
+static int use_spi_crc = 1;
+module_param(use_spi_crc, bool, 0);
+
+int mmc_spi_set_crc(struct mmc_host *host)
+{
+	struct mmc_command cmd;
+	int err;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = MMC_SPI_CRC_ON_OFF;
+	cmd.flags = MMC_RSP_SPI_R1;
+	cmd.arg = use_spi_crc;
+
+	err = mmc_wait_for_cmd(host, &cmd, 0);
+	if (err == MMC_ERR_NONE)
+		host->use_spi_crc = use_spi_crc;
+	return err;
+}
+
 int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
 {
 	int err;
@@ -241,7 +338,7 @@ int mmc_switch(struct mmc_card *card, u8
 		  (index << 16) |
 		  (value << 8) |
 		  set;
-	cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+	cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
 
 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
 	if (err != MMC_ERR_NONE)
@@ -261,13 +358,17 @@ int mmc_send_status(struct mmc_card *car
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
 	cmd.opcode = MMC_SEND_STATUS;
-	cmd.arg = card->rca << 16;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+	if (!mmc_host_is_spi(card->host))
+		cmd.arg = card->rca << 16;
+	cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
 
 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
 	if (err != MMC_ERR_NONE)
 		return err;
 
+	/* NOTE: callers are required to understand the difference
+	 * between "native" and SPI format status words!
+	 */
 	if (status)
 		*status = cmd.resp[0];
 
--- g26.orig/drivers/mmc/core/mmc.c	2007-07-26 13:13:24.000000000 -0700
+++ g26/drivers/mmc/core/mmc.c	2007-07-26 14:54:35.000000000 -0700
@@ -262,9 +262,21 @@ static int mmc_init_card(struct mmc_host
 		goto err;
 
 	/*
+	 * For SPI, enable CRC as appropriate.
+	 */
+	if (mmc_host_is_spi(host)) {
+		err = mmc_spi_set_crc(host);
+		if (err != MMC_ERR_NONE)
+			goto err;
+	}
+
+	/*
 	 * Fetch CID from card.
 	 */
-	err = mmc_all_send_cid(host, cid);
+	if (mmc_host_is_spi(host))
+		err = mmc_send_cid(host, cid);
+	else
+		err = mmc_all_send_cid(host, cid);
 	if (err != MMC_ERR_NONE)
 		goto err;
 
@@ -287,13 +299,15 @@ static int mmc_init_card(struct mmc_host
 	}
 
 	/*
-	 * Set card RCA.
+	 * For native busses:  set card RCA and leave open drain mode.
 	 */
-	err = mmc_set_relative_addr(card);
-	if (err != MMC_ERR_NONE)
-		goto free_card;
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_set_relative_addr(card);
+		if (err != MMC_ERR_NONE)
+			goto free_card;
 
-	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+	}
 
 	if (!oldcard) {
 		/*
@@ -314,13 +328,15 @@ static int mmc_init_card(struct mmc_host
 	/*
 	 * Select card, as all following commands rely on that.
 	 */
-	err = mmc_select_card(card);
-	if (err != MMC_ERR_NONE)
-		goto free_card;
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_select_card(card);
+		if (err != MMC_ERR_NONE)
+			goto free_card;
+	}
 
 	if (!oldcard) {
 		/*
-		 * Fetch and process extened CSD.
+		 * Fetch and process extended CSD.
 		 */
 		err = mmc_read_ext_csd(card);
 		if (err != MMC_ERR_NONE)
@@ -480,7 +496,8 @@ static void mmc_suspend(struct mmc_host 
 	BUG_ON(!host->card);
 
 	mmc_claim_host(host);
-	mmc_deselect_cards(host);
+	if (!mmc_host_is_spi(host))
+		mmc_deselect_cards(host);
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 	mmc_release_host(host);
 }
--- g26.orig/drivers/mmc/core/sd.c	2007-07-26 13:13:23.000000000 -0700
+++ g26/drivers/mmc/core/sd.c	2007-07-26 14:54:35.000000000 -0700
@@ -319,9 +319,21 @@ static int mmc_sd_init_card(struct mmc_h
 		goto err;
 
 	/*
+	 * For SPI, enable CRC as appropriate.
+	 */
+	if (mmc_host_is_spi(host)) {
+		err = mmc_spi_set_crc(host);
+		if (err != MMC_ERR_NONE)
+			goto err;
+	}
+
+	/*
 	 * Fetch CID from card.
 	 */
-	err = mmc_all_send_cid(host, cid);
+	if (mmc_host_is_spi(host))
+		err = mmc_send_cid(host, cid);
+	else
+		err = mmc_all_send_cid(host, cid);
 	if (err != MMC_ERR_NONE)
 		goto err;
 
@@ -343,13 +355,15 @@ static int mmc_sd_init_card(struct mmc_h
 	}
 
 	/*
-	 * Set card RCA.
+	 * For native busses:  get card RCA and leave open drain mode.
 	 */
-	err = mmc_send_relative_addr(host, &card->rca);
-	if (err != MMC_ERR_NONE)
-		goto free_card;
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_send_relative_addr(host, &card->rca);
+		if (err != MMC_ERR_NONE)
+			goto free_card;
 
-	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+	}
 
 	if (!oldcard) {
 		/*
@@ -369,9 +383,11 @@ static int mmc_sd_init_card(struct mmc_h
 	/*
 	 * Select card, as all following commands rely on that.
 	 */
-	err = mmc_select_card(card);
-	if (err != MMC_ERR_NONE)
-		goto free_card;
+	if (!mmc_host_is_spi(host)) {
+		err = mmc_select_card(card);
+		if (err != MMC_ERR_NONE)
+			goto free_card;
+	}
 
 	if (!oldcard) {
 		/*
@@ -554,7 +570,8 @@ static void mmc_sd_suspend(struct mmc_ho
 	BUG_ON(!host->card);
 
 	mmc_claim_host(host);
-	mmc_deselect_cards(host);
+	if (!mmc_host_is_spi(host))
+		mmc_deselect_cards(host);
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 	mmc_release_host(host);
 }
--- g26.orig/drivers/mmc/core/sd_ops.c	2007-07-26 13:13:24.000000000 -0700
+++ g26/drivers/mmc/core/sd_ops.c	2007-07-26 14:54:35.000000000 -0700
@@ -70,6 +70,12 @@ int mmc_wait_for_app_cmd(struct mmc_host
 		err = cmd->error;
 		if (cmd->error == MMC_ERR_NONE)
 			break;
+
+		/* no point in retrying illegal commands! */
+		if (mmc_host_is_spi(host)) {
+			if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
+				break;
+		}
 	}
 
 	return err;
@@ -89,10 +95,10 @@ int mmc_app_cmd(struct mmc_host *host, s
 
 	if (card) {
 		cmd.arg = card->rca << 16;
-		cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+		cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
 	} else {
 		cmd.arg = 0;
-		cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR;
+		cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR;
 	}
 
 	err = mmc_wait_for_cmd(host, &cmd, 0);
@@ -100,8 +106,8 @@ int mmc_app_cmd(struct mmc_host *host, s
 		return err;
 
 	/* Check that card supported application commands */
-	if (!(cmd.resp[0] & R1_APP_CMD))
-		return MMC_ERR_FAILED;
+	if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
+		return (u32)-1;
 
 	return MMC_ERR_NONE;
 }
@@ -148,14 +154,18 @@ int mmc_send_app_op_cond(struct mmc_host
 
 	cmd.opcode = SD_APP_OP_COND;
 	cmd.arg = ocr;
-	cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
 
 	for (i = 100; i; i--) {
 		err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
 		if (err != MMC_ERR_NONE)
 			break;
 
-		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
+		if (mmc_host_is_spi(host)) {
+			/* wait until reset completes */
+			if (!(cmd.resp[0] & R1_SPI_IDLE))
+				break;
+		} else if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
 			break;
 
 		err = MMC_ERR_TIMEOUT;
@@ -182,7 +192,7 @@ int mmc_send_if_cond(struct mmc_host *ho
 	 */
 	cmd.opcode = SD_SEND_IF_COND;
 	cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
-	cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
+	cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;
 
 	err = mmc_wait_for_cmd(host, &cmd, 0);
 	if (err != MMC_ERR_NONE)
@@ -229,6 +239,8 @@ int mmc_app_send_scr(struct mmc_card *ca
 	BUG_ON(!card->host);
 	BUG_ON(!scr);
 
+	/* NOTE: caller guarantees scr is heap-allocated */
+
 	err = mmc_app_cmd(card->host, card);
 	if (err != MMC_ERR_NONE)
 		return err;
@@ -242,7 +254,7 @@ int mmc_app_send_scr(struct mmc_card *ca
 
 	cmd.opcode = SD_APP_SEND_SCR;
 	cmd.arg = 0;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 
 	data.blksz = 8;
 	data.blocks = 1;
@@ -278,6 +290,8 @@ int mmc_sd_switch(struct mmc_card *card,
 	BUG_ON(!card);
 	BUG_ON(!card->host);
 
+	/* NOTE: caller guarantees resp is heap-allocated */
+
 	mode = !!mode;
 	value &= 0xF;
 
@@ -292,7 +306,7 @@ int mmc_sd_switch(struct mmc_card *card,
 	cmd.arg = mode << 31 | 0x00FFFFFF;
 	cmd.arg &= ~(0xF << (group * 4));
 	cmd.arg |= value << (group * 4);
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 
 	data.blksz = 64;
 	data.blocks = 1;

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 4/4] mmc_spi host driver
       [not found]         ` <20070726200202.5e5dcf62-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
@ 2007-07-26 23:32           ` David Brownell
       [not found]             ` <200707261632.37011.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-07-26 23:32 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thursday 26 July 2007, Pierre Ossman wrote:
> On Sat, 14 Jul 2007 15:08:06 -0700
> David Brownell <david-b@pacbell.net> wrote:
> 
> > --- g26.orig/drivers/mmc/host/Kconfig	2007-07-14 14:47:11.000000000 -0700
> > +++ g26/drivers/mmc/host/Kconfig	2007-07-14 14:47:57.000000000 -0700
> > @@ -100,3 +100,16 @@ config MMC_TIFM_SD
> >            To compile this driver as a module, choose M here: the
> >  	  module will be called tifm_sd.
> >  
> > +config MMC_SPI
> > +	tristate "MMC/SD over SPI"
> > +	depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL
> > ...
> 
> It is customary to have "(Experimental)" in the description aswell.

OK ... actually "(EXPERIMENTAL)" shouting it to the rooftops.


> > +static int
> > +mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len)
> > +{
> > +	int status;
> > +
> > +	if (len > sizeof *host->data) {
> > +		WARN_ON(1);
> > +		return -EIO;
> > +	}
> 
> sizeof without parenthesis? Heathen!

Most style guides I've used say to do it that way; it's not
a function call!  It should now meet your pagan mores.


> > +static char *maptype(struct mmc_command *cmd)
> > +{
> > +	switch (mmc_spi_resp_type(cmd)) {
> > +	case MMC_RSP_SPI_R1:	return "R1";
> > +	case MMC_RSP_SPI_R1B:	return "R1B";
> > +	case MMC_RSP_SPI_R2:	return "R2";
> > +	case MMC_RSP_SPI_R3:	return "R3/R7";
> > +	default:		return "?";
> > +	}
> > +}
> > +
> 
> I'm trying to move all generic debugging code into the core,
> so I would prefer if you would have a look at extending that
> to fit your needs. That way everyone wins.  

That's not generic, it's driver-specific.  :)

If anything comes to mind, I'll see about doing that.
In that case I'd as soon have the thing compile itself
completely out of the picture on non-debug builds...


> > +
> > +	/* Status byte: the entire seven-bit R1 response.  */
> > +	if (cmd->resp[0] != 0) {
> > +		if (R1_SPI_COM_CRC & cmd->resp[0]) {
> > +			cmd->error = MMC_ERR_BADCRC;
> > +			value = -EILSEQ;
> > +		} else if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS
> > +					| R1_SPI_ILLEGAL_COMMAND)
> > +				& cmd->resp[0]) {
> > +			cmd->error = MMC_ERR_INVALID;
> > +			value = -EINVAL;
> > +		} else if ((R1_SPI_ERASE_SEQ | R1_SPI_ERASE_RESET)
> > +				& cmd->resp[0]) {
> > +			cmd->error = MMC_ERR_FAILED;
> > +			value = -EINVAL;
> > +		} /* else R1_SPI_IDLE, "it's resetting" */
> > +
> > +		if (value < 0)
> > +			goto fail;
> > +	}
> 
> I'd still preferred that you'd didn't try to decode this here.

You said you weren't going to update the MMC core to check
the status bits after every request, and I'm certainly not
about to change *EVERY MMC/SD REQUEST* to adopt a radically
different callling convention either!!

Which leaves us needing a solution that actually works, and
this seems to be the only option:  continue to use the current
calling convention, ->error fields report the failures.


> And your code demostrates why; CRC error and illegal command
> both signal problems with the previous command, not the current
> one, so this code would be failing the wrong request.

Not according to any docs I've got.  Both of those are 'R'
mode fault reports.  My previous reply quoted the relevant
text:

   When an error bit is detected in “R” mode the card will
   report the error in the response to the command that raised
   the exception. The command will not be executed and the
   associated state transition will not take place.

Note:  ** "in the response to the command that raised the
exception" ** ... no mention of any delay.  You're talking
as if these were type "X" instead!


> 
> > +
> > +	switch (mmc_spi_resp_type(cmd)) {
> > +
> > +	/* SPI R1B == R1 + busy; STOP_TRANSMISSION (for multiblock reads)
> > +	 * and less-common stuff like various erase operations.
> > +	 */
> > +	case MMC_RSP_SPI_R1B:
> 
> for (i = 0;i < mmc_spi_resp_size(cmd);i++)
> 	cmd->resp[i/4] |= *cp++ << (24 - (i % 4) * 8);

The test for the new MMC_SPI_RSP_BUSY could be done;
I won't do it for now since it'd just take more time
to finish this stuff.

 
> if (cmd->flags & MMC_SPI_RSP_BUSY)
> 	mmc_spi_wait_unbusy();

However, that bit looks deeply messy.  You'd be splitting
the OCR (and other SPI four byte responses) into two
different words, for example!

Or to put it differently, this way the structure that's
passed up to the core is actually meaningful:  one word
of status, and maybe another word of data.  And *never*
any mixed status/data words that'd need disentangling.

After all, if you really wanted to provide raw protocol
data to the mmc core, cmd->resp[] would be bytes, not
32-bit words in cpu-order.


> That's the whole point of hacking up the response types into
> components, so that the host driver doesn't have to care. 
> 
> You can keep your design if you'd like, but I really think
> this is cleaner and more layered. 

I guess we'll have to disagree on this for now.  If it
matters, it can be changed later; but I certainly couldn't
call mixing data and status as an improvement!! 


> > +
> > +	/* We can handle most commands (except block reads) in one full
> > +	 * duplex I/O operation before either starting the next transfer
> > +	 * (data block or command) or else deselecting the card.
> > +	 *
> > +	 * First, write 7 bytes:
> > +	 *  - an all-ones byte to ensure the card is ready
> > +	 *  - opcode byte (plus start and transmission bits)
> > +	 *  - four bytes of big-endian argument
> > +	 *  - crc7 (plus end bit) ... always computed, it's cheap
> > +	 *
> 
> I thought your argument against always using crc7 was that it
> wasn't cheap? Colour me confused. 

The data block CRC is quite expensive, yes.  The CRC7 isn't;
and in any case, you had pretty much demanded that it always
be computed (to eliminate the #define for the crc needed in
the card reset command) ...


> > +			if (status == -EILSEQ)
> > +				data->error = MMC_ERR_BADCRC;
> > +			else if (status == -ETIMEDOUT)
> > +				data->error = MMC_ERR_TIMEOUT;
> > +			else if (status == -EINVAL)
> > +				data->error = MMC_ERR_INVALID;
> > +			else if (data->error == MMC_ERR_NONE)
> > +				data->error = MMC_ERR_FAILED;
> > +			break;
> > +		}
> 
> At least we agree on error codes. This is precisely the change
> I have pending in my patch set to remove the MMC_ERR_* mess. :) 

I had to pick something.  ;)


> There are a few outstanding issues, but they are really minor so
> this could very soon get into my -mm branch.

Appended is a version of the mmc_spi patch with most of the
issues above addressed.

I'll be glad to see this get into your MM branch ... that'll
mean it can more easily get to the people who will use it,
and who will start shaking out the next set of issues.


> I need a MAINTAINERS entry aswell.  

I was hoping someone would sign up for that, so I don't
have to stick myself there with "Odd fixes" status...


> I'd like to see those REVISIT things attended to before I push
> the stuff to Linus though. 

In this driver?  They're mostly performance tuning issues, which
are normally best left till later:

  - Using msleep -- usleep? -- to avoid some busy-waiting.
    That's got to be done with caution, since it can slow
    things down and since card timings are all over the map.
    Given working HRT this should be practical.

  - Eliminating byte-at-a-time reads on the RX block paths,
    probably using some kind of bounce buffer scheme.  Needs
    some investigation; maybe your new bouncebuffer stuff
    ought to help out...

  - Double checking that another device doesn't get added
    to the bus ... that's easy enough to do.  (Lurking there
    is the need for a new SPI primitive, too.)

  - dma_map_single() operations could theoretically fail.
    Not that it'd be likely on hardware that uses this driver;
    but that's simple error path cleanup.

More significant are the two FIXMEs related to card reset.
One will require someone with relevant hardware (Jan?),
the other still needs investigation.

- Dave

==============	CUT HERE
This is the latest version of the MMC-over-SPI support.  It works
on 2.6.23-rc1-git, along with the preceding patches which teach the
rest of the MMC stack about SPI so that this host driver can focus
on doing only lowlevel SPI-specific stuff, and add CRC7 support.

It's been lightly tested on MMC and SD cards, reading and writing
ext3 filesystems (including fsck).  This includes CRC mode, which
is now enabled by default.

The main issue of note is that sometimes cards need to be power
cycled to recover after certain faults.  In some cases, it may be
necessary to disable CRCs.  ("modprobe mmc_core use_spi_crc=n")

Changes from previous versions include more code shrinkage and cleanup,
reducing the number of I/O requests needed per operation, and moving
even more nuances into the MMC core code.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Cc: mikael.starvik@axis.com,
Cc: Hans-Peter Nilsson <hp@axis.com>
Cc: Jan Nikitenko <jan.nikitenko@gmail.com>
Cc: Mike Lavender <mike@steroidmicros.com>
---
 drivers/mmc/host/Kconfig    |   13 
 drivers/mmc/host/Makefile   |    1 
 drivers/mmc/host/mmc_spi.c  | 1416 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/mmc_spi.h |   33 +
 4 files changed, 1463 insertions(+)

--- avr.orig/drivers/mmc/host/Kconfig	2007-07-25 22:06:46.000000000 -0700
+++ avr/drivers/mmc/host/Kconfig	2007-07-26 15:23:20.000000000 -0700
@@ -100,3 +100,16 @@ config MMC_TIFM_SD
           To compile this driver as a module, choose M here: the
 	  module will be called tifm_sd.
 
+config MMC_SPI
+	tristate "MMC/SD over SPI (EXPERIMENTAL)"
+	depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL
+	select CRC7
+	select CRC_ITU_T
+	help
+	  Some systems accss MMC/SD cards using a SPI controller instead of
+	  using a "native" MMC/SD controller.  This has a disadvantage of
+	  being relatively high overhead, but a compensating advantage of
+	  working on many systems without dedicated MMC/SD controllers.
+
+	  If unsure, or if your system has no SPI master driver, say N.
+
--- avr.orig/drivers/mmc/host/Makefile	2007-07-25 22:06:46.000000000 -0700
+++ avr/drivers/mmc/host/Makefile	2007-07-25 22:19:58.000000000 -0700
@@ -15,4 +15,5 @@ obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)		+= omap.o
 obj-$(CONFIG_MMC_AT91)		+= at91_mci.o
 obj-$(CONFIG_MMC_TIFM_SD)	+= tifm_sd.o
+obj-$(CONFIG_MMC_SPI)		+= mmc_spi.o
 
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ avr/include/linux/spi/mmc_spi.h	2007-07-25 22:19:58.000000000 -0700
@@ -0,0 +1,33 @@
+#ifndef __LINUX_SPI_MMC_SPI_H
+#define __LINUX_SPI_MMC_SPI_H
+
+struct device;
+struct mmc_host;
+
+/* Put this in platform_data of a device being used to manage an MMC/SD
+ * card slot.  (Modeled after PXA mmc glue; see that for usage examples.)
+ *
+ * REVISIT This is not a spi-specific notion.  Any card slot should be
+ * able to handle it.  If the MMC core doesn't adopt this kind of notion,
+ * switch the "struct device *" parameters over to "struct spi_device *".
+ */
+struct mmc_spi_platform_data {
+	/* driver activation and (optional) card detect irq hookup */
+	int (*init)(struct device *,
+		irqreturn_t (*)(int, void *),
+		void *);
+	void (*exit)(struct device *, void *);
+
+	/* sense switch on sd cards */
+	int (*get_ro)(struct device *);
+
+	/* how long to debounce card detect, in msecs */
+	u16 detect_delay;
+
+	/* power management */
+	u16 powerup_msecs;		/* delay of up to 250 msec */
+	u32 ocr_mask;			/* available voltages */
+	void (*setpower)(struct device *, unsigned int maskval);
+};
+
+#endif /* __LINUX_SPI_MMC_SPI_H */
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ avr/drivers/mmc/host/mmc_spi.c	2007-07-26 15:26:27.000000000 -0700
@@ -0,0 +1,1416 @@
+/*
+ * mmc_spi.c - Access SD/MMC cards through SPI master controllers
+ *
+ * (C) Copyright 2005, Intec Automation,
+ *		Mike Lavender (mike@steroidmicros)
+ * (C) Copyright 2006-2007, David Brownell
+ * (C) Copyright 2007, Axis Communications,
+ *		Hans-Peter Nilsson (hp@axis.com)
+ * (C) Copyright 2007, ATRON electronic GmbH,
+ *		Jan Nikitenko <jan.nikitenko@gmail.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/crc7.h>
+#include <linux/crc-itu-t.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>		/* for R1_SPI_* bit values */
+
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
+
+#include <asm/unaligned.h>
+
+
+/* NOTES:
+ *
+ * - For now, we won't try to interoperate with a real mmc/sd/sdio
+ *   controller, although some of them do have hardware support for
+ *   SPI protocol.  The main reason for such configs would be mmc-ish
+ *   cards like DataFlash, which don't support that "native" protocol.
+ *
+ *   We don't have a "DataFlash/MMC/SD/SDIO card slot" abstraction to
+ *   switch between driver stacks, and in any case if "native" mode
+ *   is available, it will be faster and hence preferable.
+ *
+ * - MMC depends on a different chipselect management policy than the
+ *   SPI interface currently supports for shared bus segments:  it needs
+ *   to issue multiple spi_message requests with the chipselect active,
+ *   using the results of one message to decide the next one to issue.
+ *
+ *   Pending updates to the programming interface, this driver insists
+ *   that it not share the bus with other drivers (preventing conflicts).
+ *
+ * - We tell the controller to keep the chipselect active from the
+ *   beginning of an mmc_host_ops.request until the end.  So beware
+ *   of SPI controller drivers that mis-handle the cs_change flag!
+ *
+ *   However, many cards seem OK with chipselect flapping up/down
+ *   during that time ... at least on unshared bus segments.
+ */
+
+
+/*
+ * Local protocol constants, internal to data block protocols.
+ */
+
+/* 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 */
+
+#define MMC_SPI_BLOCKSIZE	512
+
+
+/* These fixed timeouts come from the latest SD specs, which say to ignore
+ * the CSD values.  The R1B value is for card erase (e.g. the "I forgot the
+ * card's password" scenario); it's mostly applied to STOP_TRANSMISSION after
+ * reads which takes nowhere near that long.  Older cards may be able to use
+ * shorter timeouts ... but why bother?
+ */
+#define readblock_timeout	ktime_set(0, 100 * 1000 * 1000)
+#define writeblock_timeout	ktime_set(0, 250 * 1000 * 1000)
+#define r1b_timeout		ktime_set(3, 0)
+
+
+/****************************************************************************/
+
+/*
+ * Local Data Structures
+ */
+
+/* "scratch" is per-{command,block} data exchanged with the card */
+struct scratch {
+	u8			status[29];
+	u8			data_token;
+	__be16			crc_val;
+};
+
+struct mmc_spi_host {
+	struct mmc_host		*mmc;
+	struct spi_device	*spi;
+
+	unsigned char		power_mode;
+	u16			powerup_msecs;
+
+	struct mmc_spi_platform_data	*pdata;
+
+	/* for bulk data transfers */
+	struct spi_transfer	token, t, crc, early_status;
+	struct spi_message	m;
+
+	/* for status readback */
+	struct spi_transfer	status;
+	struct spi_message	readback;
+
+	/* underlying DMA-aware controller, or null */
+	struct device		*dma_dev;
+
+	/* buffer used for commands and for message "overhead" */
+	struct scratch		*data;
+	dma_addr_t		data_dma;
+
+	/* Specs say to write ones most of the time, even when the card
+	 * has no need to read its input data; and many cards won't care.
+	 * This is our source of those ones.
+	 */
+	void			*ones;
+	dma_addr_t		ones_dma;
+};
+
+
+/****************************************************************************/
+
+/*
+ * MMC-over-SPI protocol glue, used by the MMC stack interface
+ */
+
+static inline int mmc_cs_off(struct mmc_spi_host *host)
+{
+	/* chipselect will always be inactive after setup() */
+	return spi_setup(host->spi);
+}
+
+static int
+mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len)
+{
+	int status;
+
+	if (len > sizeof(*host->data)) {
+		WARN_ON(1);
+		return -EIO;
+	}
+
+	host->status.len = len;
+
+	if (host->dma_dev)
+		dma_sync_single_for_device(host->dma_dev,
+				host->data_dma, sizeof(*host->data),
+				DMA_FROM_DEVICE);
+
+	status = spi_sync(host->spi, &host->readback);
+	if (status == 0)
+		status = host->readback.status;
+
+	if (host->dma_dev)
+		dma_sync_single_for_cpu(host->dma_dev,
+				host->data_dma, sizeof(*host->data),
+				DMA_FROM_DEVICE);
+
+	return status;
+}
+
+static int
+mmc_spi_skip(struct mmc_spi_host *host, ktime_t timeout, unsigned n, u8 byte)
+{
+	u8		*cp = host->data->status;
+
+	timeout = ktime_add(timeout, ktime_get());
+
+	while (1) {
+		int		status;
+		unsigned	i;
+
+		status = mmc_spi_readbytes(host, n);
+		if (status < 0)
+			return status;
+
+		for (i = 0; i < n; i++) {
+			if (cp[i] != byte)
+				return cp[i];
+		}
+
+		/* REVISIT investigate msleep() to avoid busy-wait I/O
+		 * in at least some cases.
+		 */
+		if (ktime_to_ns(ktime_sub(ktime_get(), timeout)) > 0)
+			break;
+	}
+	return -ETIMEDOUT;
+}
+
+static inline int
+mmc_spi_wait_unbusy(struct mmc_spi_host *host, ktime_t timeout)
+{
+	return mmc_spi_skip(host, timeout, sizeof(host->data->status), 0);
+}
+
+static int mmc_spi_readtoken(struct mmc_spi_host *host)
+{
+	return mmc_spi_skip(host, readblock_timeout, 1, 0xff);
+}
+
+
+/*
+ * Note that for SPI, cmd->resp[0] is not the same data as "native" protocol
+ * hosts return!  The low byte holds R1_SPI bits.  The next byte may hold
+ * R2_SPI bits ... for SEND_STATUS, or after data read errors.
+ *
+ * cmd->resp[1] holds any four-byte response, for R3 (READ_OCR) and on
+ * newer cards R7 (IF_COND).
+ */
+
+static char *maptype(struct mmc_command *cmd)
+{
+	switch (mmc_spi_resp_type(cmd)) {
+	case MMC_RSP_SPI_R1:	return "R1";
+	case MMC_RSP_SPI_R1B:	return "R1B";
+	case MMC_RSP_SPI_R2:	return "R2";
+	case MMC_RSP_SPI_R3:	return "R3/R7";
+	default:		return "?";
+	}
+}
+
+/* return zero, else negative errno after setting cmd->error */
+static int mmc_spi_response_get(struct mmc_spi_host *host,
+		struct mmc_command *cmd, int cs_on)
+{
+	u8	*cp = host->data->status;
+	u8	*end = cp + host->t.len;
+	int	value = 0;
+	char	tag[32];
+
+	snprintf(tag, sizeof(tag), "  ... CMD%d response SPI_%s",
+		cmd->opcode, maptype(cmd));
+
+	/* Except for data block reads, the whole response will already
+	 * be stored in the scratch buffer.  It's somewhere after the
+	 * command and the first byte we read after it.  We ignore that
+	 * first byte.  After STOP_TRANSMISSION command it may include
+	 * two data bits, but otherwise it's all ones.
+	 */
+	cp += 8;
+	while (cp < end && *cp == 0xff)
+		cp++;
+
+	/* Data block reads (R1 response types) may need more data... */
+	if (cp == end) {
+		unsigned	i;
+
+		cp = host->data->status;
+
+		/* Card sends N(CR) (== 1..8) bytes of all-ones then one
+		 * status byte ... and we already scanned 2 bytes.
+		 *
+		 * REVISIT block read paths use nasty byte-at-a-time I/O
+		 * so it can always DMA directly into the target buffer.
+		 * It'd probably be better to memcpy() the first chunk and
+		 * avoid extra i/o calls...
+		 */
+		for (i = 2; i < 9; i++) {
+			value = mmc_spi_readbytes(host, 1);
+			if (value < 0) {
+				cmd->error = MMC_ERR_FAILED;
+				goto done;
+			}
+			if (*cp != 0xff)
+				goto checkstatus;
+		}
+		value = -ETIMEDOUT;
+		cmd->error = MMC_ERR_TIMEOUT;
+		goto done;
+	}
+
+checkstatus:
+	if (*cp & 0x80) {
+		dev_dbg(&host->spi->dev, "%s: INVALID RESPONSE, %02x\n",
+					tag, *cp);
+		cmd->error = MMC_ERR_FAILED;
+		value = -EBADR;
+		goto done;
+	}
+
+	cmd->resp[0] = *cp++;
+	cmd->error = MMC_ERR_NONE;
+
+	/* Status byte: the entire seven-bit R1 response.  */
+	if (cmd->resp[0] != 0) {
+		if (R1_SPI_COM_CRC & cmd->resp[0]) {
+			cmd->error = MMC_ERR_BADCRC;
+			value = -EILSEQ;
+		} else if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS
+					| R1_SPI_ILLEGAL_COMMAND)
+				& cmd->resp[0]) {
+			cmd->error = MMC_ERR_INVALID;
+			value = -EINVAL;
+		} else if ((R1_SPI_ERASE_SEQ | R1_SPI_ERASE_RESET)
+				& cmd->resp[0]) {
+			cmd->error = MMC_ERR_FAILED;
+			value = -EINVAL;
+		} /* else R1_SPI_IDLE, "it's resetting" */
+
+		if (value < 0)
+			goto fail;
+	}
+
+	switch (mmc_spi_resp_type(cmd)) {
+
+	/* SPI R1B == R1 + busy; STOP_TRANSMISSION (for multiblock reads)
+	 * and less-common stuff like various erase operations.
+	 */
+	case MMC_RSP_SPI_R1B:
+		/* maybe we read all the busy tokens already */
+		while (cp < end && *cp == 0)
+			cp++;
+		if (cp == end)
+			mmc_spi_wait_unbusy(host, r1b_timeout);
+		break;
+
+	/* SPI R2 == R1 + second status byte; SEND_STATUS */
+	case MMC_RSP_SPI_R2:
+		cmd->resp[0] |= *cp << 8;
+		break;
+
+	/* SPI R3 or R7 == R1 + 4 bytes */
+	case MMC_RSP_SPI_R3:
+		cmd->resp[1] = be32_to_cpu(get_unaligned((u32 *)cp));
+		break;
+
+	/* SPI R1 == just one status byte */
+	case MMC_RSP_SPI_R1:
+		break;
+
+	default:
+		dev_dbg(&host->spi->dev, "bad response type %04x\n",
+				mmc_spi_resp_type(cmd));
+		if (value >= 0) {
+			cmd->error = MMC_ERR_INVALID;
+			value = -EINVAL;
+		}
+	}
+
+fail:
+	if (value < 0)
+		dev_dbg(&host->spi->dev, "%s: resp %04x %08x\n",
+			tag, cmd->resp[0], cmd->resp[1]);
+
+	/* disable chipselect on errors and some success cases */
+	if (value >= 0 && cs_on)
+		return value;
+done:
+	mmc_cs_off(host);
+	return value;
+}
+
+/* Issue command and read its response.
+ * Returns zero on success, negative for error.
+ *
+ * On error, caller must cope with mmc core retry mechanism.  That
+ * means immediate low-level resubmit, which affects the bus lock...
+ */
+static int
+mmc_spi_command_send(struct mmc_spi_host *host,
+		struct mmc_request *mrq,
+		struct mmc_command *cmd, int cs_on)
+{
+	struct scratch		*data = host->data;
+	u8			*cp = data->status;
+	u32			arg = cmd->arg;
+	int			status;
+	struct spi_transfer	*t;
+
+	/* We can handle most commands (except block reads) in one full
+	 * duplex I/O operation before either starting the next transfer
+	 * (data block or command) or else deselecting the card.
+	 *
+	 * First, write 7 bytes:
+	 *  - an all-ones byte to ensure the card is ready
+	 *  - opcode byte (plus start and transmission bits)
+	 *  - four bytes of big-endian argument
+	 *  - crc7 (plus end bit) ... always computed, it's cheap
+	 *
+	 * We init the whole buffer to all-ones, which is what we need
+	 * to write while we're reading (later) response data.
+	 */
+	memset(cp++, 0xff, sizeof(data->status));
+
+	*cp++ = 0x40 | cmd->opcode;
+	*cp++ = (u8)(arg >> 24);
+	*cp++ = (u8)(arg >> 16);
+	*cp++ = (u8)(arg >> 8);
+	*cp++ = (u8)arg;
+	*cp++ = (crc7(0, &data->status[1], 5) << 1) | 0x01;
+
+	/* Then, read up to 13 bytes (while writing all-ones):
+	 *  - N(CR) (== 1..8) bytes of all-ones
+	 *  - status byte (for all response types)
+	 *  - the rest of the response, either:
+	 *      + nothing, for R1 or R1B responses
+	 *	+ second status byte, for R2 responses
+	 *	+ four data bytes, for R3 and R7 responses
+	 *
+	 * Finally, read some more bytes ... in the nice cases we know in
+	 * advance how many, and reading 1 more is always OK:
+	 *  - N(EC) (== 0..N) bytes of all-ones, before deselect/finish
+	 *  - N(RC) (== 1..N) bytes of all-ones, before next command
+	 *  - N(WR) (== 1..N) bytes of all-ones, before data write
+	 *
+	 * So in those cases one full duplex I/O of at most 21 bytes will
+	 * handle the whole command, leaving the card ready to receive a
+	 * data block or new command.  We do that whenever we can, shaving
+	 * CPU and IRQ costs (especially when using DMA or FIFOs).
+	 *
+	 * There are two other cases, where it's not generally practical
+	 * to rely on a single I/O:
+	 *
+	 *  - R1B responses need at least N(EC) bytes of all-zeroes.
+	 *
+	 *    In this case we can *try* to fit it into one I/O, then
+	 *    maybe read more data later.
+	 *
+	 *  - Data block reads are more troublesome, since a variable
+	 *    number of padding bytes precede the token and data.
+	 *      + N(CX) (== 0..8) bytes of all-ones, before CSD or CID
+	 *      + N(AC) (== 1..many) bytes of all-ones
+	 *
+	 *    In this case we currently only have minimal speedups here:
+	 *    when N(CR) == 1 we can avoid I/O in response_get().
+	 */
+	if (cs_on && (mrq->data->flags & MMC_DATA_READ)) {
+		cp += 2;	/* min(N(CR)) + status */
+		/* R1 */
+	} else {
+		cp += 10;	/* max(N(CR)) + status + min(N(RC),N(WR)) */
+		if (cmd->flags & MMC_RSP_SPI_S2)	/* R2 */
+			cp++;
+		else if (cmd->flags & MMC_RSP_SPI_B4)	/* R3, R7 */
+			cp += 4;
+		else if (cmd->flags & MMC_RSP_BUSY)	/* R1B */
+			cp = data->status + sizeof(data->status);
+		/* else:  R1 (most commands) */
+	}
+
+	dev_dbg(&host->spi->dev, "  mmc_spi: CMD%d, MMC_SPI_%s\n",
+		cmd->opcode, maptype(cmd));
+
+	/* send command, leaving chipselect active */
+	spi_message_init(&host->m);
+
+	t = &host->t;
+	memset(t, 0, sizeof(*t));
+	t->tx_buf = t->rx_buf = data->status;
+	t->tx_dma = t->rx_dma = host->data_dma;
+	t->len = cp - data->status;
+	t->cs_change = 1;
+	spi_message_add_tail(t, &host->m);
+
+	if (host->dma_dev) {
+		host->m.is_dma_mapped = 1;
+		dma_sync_single_for_device(host->dma_dev,
+				host->data_dma, sizeof(*host->data),
+				DMA_BIDIRECTIONAL);
+	}
+	status = spi_sync(host->spi, &host->m);
+	if (status == 0)
+		status = host->m.status;
+
+	if (host->dma_dev)
+		dma_sync_single_for_cpu(host->dma_dev,
+				host->data_dma, sizeof(*host->data),
+				DMA_BIDIRECTIONAL);
+	if (status < 0) {
+		dev_dbg(&host->spi->dev, "  ... write returned %d\n", status);
+		cmd->error = MMC_ERR_FAILED;
+		return status;
+	}
+
+	/* after no-data commands and STOP_TRANSMISSION, chipselect off */
+	return mmc_spi_response_get(host, cmd, cs_on);
+}
+
+/* Build data message with up to four separate transfers.  For TX, we
+ * start by writing the data token.  And in most cases, we finish with
+ * a status transfer.
+ *
+ * We always provide TX data for data and CRC.  The MMC/SD protocol
+ * requires us to write ones; but Linux defaults to writing zeroes;
+ * so we explicitly initialize it to all ones on RX paths.
+ *
+ * We also handle DMA mapping, so the underlying SPI controller does
+ * not need to (re)do it for each message.
+ */
+static void
+mmc_spi_setup_data_message(
+	struct mmc_spi_host	*host,
+	int			multiple,
+	enum dma_data_direction	direction)
+{
+	struct spi_transfer	*t;
+	struct scratch		*scratch = host->data;
+	dma_addr_t		dma = host->data_dma;
+
+	spi_message_init(&host->m);
+	if (dma)
+		host->m.is_dma_mapped = 1;
+
+	/* for reads, readblock() skips 0xff bytes before finding
+	 * the token; for writes, this transfer issues that token.
+	 */
+	if (direction == DMA_TO_DEVICE) {
+		t = &host->token;
+		memset(t, 0, sizeof(*t));
+		t->len = 1;
+		if (multiple)
+			scratch->data_token = SPI_TOKEN_MULTI_WRITE;
+		else
+			scratch->data_token = SPI_TOKEN_SINGLE;
+		t->tx_buf = &scratch->data_token;
+		if (dma)
+			t->tx_dma = dma + offsetof(struct scratch, data_token);
+		spi_message_add_tail(t, &host->m);
+	}
+
+	/* Body of transfer is buffer, then CRC ...
+	 * either TX-only, or RX with TX-ones.
+	 */
+	t = &host->t;
+	memset(t, 0, sizeof(*t));
+	t->tx_buf = host->ones;
+	t->tx_dma = host->ones_dma;
+	/* length and actual buffer info are written later */
+	spi_message_add_tail(t, &host->m);
+
+	t = &host->crc;
+	memset(t, 0, sizeof(*t));
+	t->len = 2;
+	if (direction == DMA_TO_DEVICE) {
+		/* the actual CRC may get written later */
+		t->tx_buf = &scratch->crc_val;
+		if (dma)
+			t->tx_dma = dma + offsetof(struct scratch, crc_val);
+	} else {
+		t->tx_buf = host->ones;
+		t->tx_dma = host->ones_dma;
+		t->rx_buf = &scratch->crc_val;
+		if (dma)
+			t->rx_dma = dma + offsetof(struct scratch, crc_val);
+	}
+	spi_message_add_tail(t, &host->m);
+
+	/*
+	 * A single block read is followed by N(EC) [0+] all-ones bytes
+	 * before deselect ... don't bother.
+	 *
+	 * Multiblock reads are followed by N(AC) [1+] all-ones bytes before
+	 * the next block is read, or a STOP_TRANSMISSION is issued.  We'll
+	 * collect that single byte, so readblock() doesn't need to.
+	 *
+	 * For a write, the one-byte data response follows immediately, then
+	 * come zero or more busy bytes, then N(WR) [1+] all-ones bytes.
+	 * Then single block reads may deselect, and multiblock ones issue
+	 * the next token (next data block, or STOP_TRAN).  We can try to
+	 * minimize I/O ops by using a single read to collect end-of-busy.
+	 */
+	if (multiple || direction == DMA_TO_DEVICE) {
+		t = &host->early_status;
+		memset(t, 0, sizeof(*t));
+		t->len = (direction == DMA_TO_DEVICE)
+				? sizeof(scratch->status)
+				: 1;
+		t->tx_buf = host->ones;
+		t->tx_dma = host->ones_dma;
+		t->rx_buf = scratch->status;
+		if (dma)
+			t->rx_dma = dma + offsetof(struct scratch, status);
+		t->cs_change = 1;
+		spi_message_add_tail(t, &host->m);
+	}
+}
+
+/*
+ * Write one block:
+ *  - caller handled preceding N(WR) [1+] all-ones bytes
+ *  - data block
+ *	+ token
+ *	+ data bytes
+ *	+ crc16
+ *  - an all-ones byte ... card writes a data-response byte
+ *  - followed by N(EC) [0+] all-ones bytes, card writes zero/'busy'
+ *
+ * Return negative errno, else success.
+ */
+static int
+mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t)
+{
+	struct spi_device	*spi = host->spi;
+	int			status, i;
+	struct scratch		*scratch = host->data;
+
+	if (host->mmc->use_spi_crc)
+		scratch->crc_val = cpu_to_be16(
+				crc_itu_t(0, t->tx_buf, t->len));
+	if (host->dma_dev)
+		dma_sync_single_for_device(host->dma_dev,
+				host->data_dma, sizeof(*scratch),
+				DMA_BIDIRECTIONAL);
+
+	status = spi_sync(spi, &host->m);
+	if (status == 0)
+		status = host->m.status;
+
+	if (status != 0) {
+		dev_dbg(&spi->dev, "write error (%d)\n", status);
+		return status;
+	}
+
+	if (host->dma_dev)
+		dma_sync_single_for_cpu(host->dma_dev,
+				host->data_dma, sizeof(*scratch),
+				DMA_BIDIRECTIONAL);
+
+	/*
+	 * Get the transmission data-response reply.  It must follow
+	 * immediately after the data block we transferred.  This reply
+	 * doesn't necessarily tell whether the write operation succeeded;
+	 * it just says if the transmission was ok and whether *earlier*
+	 * writes succeeded; see the standard.
+	 */
+	switch (SPI_MMC_RESPONSE_CODE(scratch->status[0])) {
+	case SPI_RESPONSE_ACCEPTED:
+		status = 0;
+		break;
+	case SPI_RESPONSE_CRC_ERR:
+		/* host shall then issue MMC_STOP_TRANSMISSION */
+		status = -EILSEQ;
+		break;
+	case SPI_RESPONSE_WRITE_ERR:
+		/* host shall then issue MMC_STOP_TRANSMISSION,
+		 * and should MMC_SEND_STATUS to sort it out
+		 */
+		status = -EIO;
+		break;
+	default:
+		status = -EPROTO;
+		break;
+	}
+	if (status != 0) {
+		dev_dbg(&spi->dev, "write error %02x (%d)\n",
+			scratch->status[0], status);
+		return status;
+	}
+
+	t->tx_buf += t->len;
+	if (host->dma_dev)
+		t->tx_dma += t->len;
+
+	/* Return when not busy.  If we didn't collect that status yet,
+	 * we'll need some more I/O.
+	 */
+	for (i = 1; i < sizeof(scratch->status); i++) {
+		if (scratch->status[i] != 0)
+			return 0;
+	}
+	return mmc_spi_wait_unbusy(host, writeblock_timeout);
+}
+
+/*
+ * Read one block:
+ *  - skip leading all-ones bytes ... either
+ *      + N(AC) [1..f(clock,CSD)] usually, else
+ *      + N(CX) [0..8] when reading CSD or CID
+ *  - data block
+ *	+ token ... if error token, no data or crc
+ *	+ data bytes
+ *	+ crc16
+ *
+ * After single block reads, we're done; N(EC) [0+] all-ones bytes follow
+ * before dropping chipselect.
+ *
+ * For multiblock reads, caller either reads the next block or issues a
+ * STOP_TRANSMISSION command.
+ */
+static int
+mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t)
+{
+	struct spi_device	*spi = host->spi;
+	int			status;
+	struct scratch		*scratch = host->data;
+
+	/* At least one SD card sends an all-zeroes byte when N(CX)
+	 * applies, before the all-ones bytes ... just cope with that.
+	 */
+	status = mmc_spi_readbytes(host, 1);
+	if (status < 0)
+		return status;
+	status = scratch->status[0];
+	if (status == 0xff || status == 0)
+		status = mmc_spi_readtoken(host);
+
+	if (status == SPI_TOKEN_SINGLE) {
+		if (host->dma_dev)
+			dma_sync_single_for_device(host->dma_dev,
+					host->data_dma, sizeof(*scratch),
+					DMA_BIDIRECTIONAL);
+
+		status = spi_sync(spi, &host->m);
+		if (status == 0)
+			status = host->m.status;
+
+		if (host->dma_dev)
+			dma_sync_single_for_cpu(host->dma_dev,
+					host->data_dma, sizeof(*scratch),
+					DMA_BIDIRECTIONAL);
+	} else {
+		dev_dbg(&spi->dev, "read error %02x (%d)\n", status, status);
+
+		/* we've read extra garbage, timed out, etc */
+		if (status < 0)
+			return status;
+
+		/* low four bits are an R2 subset, fifth seems to be
+		 * vendor specific ... map them all to generic error..
+		 */
+		return -EIO;
+	}
+
+	if (host->mmc->use_spi_crc) {
+		u16 crc = crc_itu_t(0, t->rx_buf, t->len);
+
+		be16_to_cpus(&scratch->crc_val);
+		if (scratch->crc_val != crc) {
+			dev_dbg(&spi->dev, "read - crc error: crc_val=0x%04x, "
+					"computed=0x%04x len=%d\n",
+					scratch->crc_val, crc, t->len);
+			return -EILSEQ;
+		}
+	}
+
+	t->rx_buf += t->len;
+	if (host->dma_dev)
+		t->rx_dma += t->len;
+
+	return 0;
+}
+
+/*
+ * An MMC/SD data stage includes one or more blocks, optional CRCs,
+ * and inline handshaking.  That handhaking makes it unlike most
+ * other SPI protocol stacks.
+ */
+static void
+mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
+		struct mmc_data *data, u32 blk_size)
+{
+	struct spi_device	*spi = host->spi;
+	struct device		*dma_dev = host->dma_dev;
+	struct spi_transfer	*t;
+	enum dma_data_direction	direction;
+	struct scatterlist	*sg;
+	unsigned		n_sg;
+	int			multiple = (data->blocks > 1);
+
+	if (data->flags & MMC_DATA_READ)
+		direction = DMA_FROM_DEVICE;
+	else
+		direction = DMA_TO_DEVICE;
+	mmc_spi_setup_data_message(host, multiple, direction);
+	t = &host->t;
+
+	/* Handle scatterlist segments one at a time, with synch for
+	 * each 512-byte block
+	 */
+	for (sg = data->sg, n_sg = data->sg_len; n_sg; n_sg--, sg++) {
+		int			status = 0;
+		dma_addr_t		dma_addr = 0;
+		void			*kmap_addr;
+		unsigned		length = sg->length;
+
+		/* set up dma mapping for controller drivers that might
+		 * use DMA ... though they may fall back to PIO
+		 */
+		if (dma_dev) {
+			dma_addr = dma_map_page(dma_dev, sg->page, 0,
+						PAGE_SIZE, direction);
+			if (direction == DMA_TO_DEVICE)
+				t->tx_dma = dma_addr + sg->offset;
+			else
+				t->rx_dma = dma_addr + sg->offset;
+		}
+
+		/* allow pio too; we don't allow highmem */
+		kmap_addr = kmap(sg->page);
+		if (direction == DMA_TO_DEVICE)
+			t->tx_buf = kmap_addr + sg->offset;
+		else
+			t->rx_buf = kmap_addr + sg->offset;
+
+		/* transfer each block, and update request status */
+		while (length) {
+			t->len = min(length, blk_size);
+
+			dev_dbg(&host->spi->dev,
+				"    mmc_spi: %s block, %d bytes\n",
+				(direction == DMA_TO_DEVICE)
+				? "write"
+				: "read",
+				t->len);
+
+			if (direction == DMA_TO_DEVICE)
+				status = mmc_spi_writeblock(host, t);
+			else
+				status = mmc_spi_readblock(host, t);
+			if (status < 0)
+				break;
+
+			data->bytes_xfered += t->len;
+			length -= t->len;
+
+			if (!multiple)
+				break;
+		}
+
+		/* discard mappings */
+		if (direction == DMA_FROM_DEVICE)
+			flush_kernel_dcache_page(sg->page);
+		kunmap(sg->page);
+		if (dma_dev)
+			dma_unmap_page(dma_dev, dma_addr,
+					PAGE_SIZE, direction);
+
+		if (status < 0) {
+			dev_dbg(&spi->dev, "%s status %d\n",
+				(direction == DMA_TO_DEVICE)
+					? "write" : "read",
+				status);
+			if (status == -EILSEQ)
+				data->error = MMC_ERR_BADCRC;
+			else if (status == -ETIMEDOUT)
+				data->error = MMC_ERR_TIMEOUT;
+			else if (status == -EINVAL)
+				data->error = MMC_ERR_INVALID;
+			else if (data->error == MMC_ERR_NONE)
+				data->error = MMC_ERR_FAILED;
+			break;
+		}
+	}
+
+	/* NOTE some docs describe an MMC-only SET_BLOCK_COUNT (CMD23) that
+	 * can be issued before multiblock writes.  Unlike its more widely
+	 * documented analogue for SD cards (SET_WR_BLK_ERASE_COUNT, ACMD23),
+	 * that can affect the STOP_TRAN logic.   Complete (and current)
+	 * MMC specs should sort that out before Linux starts using CMD23.
+	 */
+	if (direction == DMA_TO_DEVICE && multiple) {
+		struct scratch	*scratch = host->data;
+		int		tmp;
+		const unsigned	statlen = sizeof(scratch->status);
+
+		dev_dbg(&spi->dev, "    mmc_spi: STOP_TRAN\n");
+
+		/* Tweak the per-block message we set up earlier by morphing
+		 * it to hold single buffer with the token followed by some
+		 * all-ones bytes ... skip N(BR) (0..1), scan the rest for
+		 * "not busy any longer" status, and leave chip selected.
+		 */
+		INIT_LIST_HEAD(&host->m.transfers);
+		list_add(&host->early_status.transfer_list,
+				&host->m.transfers);
+
+		scratch->status[0] = SPI_TOKEN_STOP_TRAN;
+		memset(scratch->status + 1, 0xff, statlen - 1);
+		host->early_status.tx_buf = host->early_status.rx_buf;
+		host->early_status.tx_dma = host->early_status.rx_dma;
+		host->early_status.len = 1 + statlen;
+
+		if (host->dma_dev)
+			dma_sync_single_for_device(host->dma_dev,
+					host->data_dma, sizeof(*scratch),
+					DMA_BIDIRECTIONAL);
+
+		tmp = spi_sync(spi, &host->m);
+		if (tmp == 0)
+			tmp = host->m.status;
+
+		if (host->dma_dev)
+			dma_sync_single_for_cpu(host->dma_dev,
+					host->data_dma, sizeof(*scratch),
+					DMA_BIDIRECTIONAL);
+
+		if (tmp < 0) {
+			if (data->error == MMC_ERR_NONE)
+				data->error = MMC_ERR_FAILED;
+			return;
+		}
+
+		/* Ideally we collected "not busy" status with one I/O,
+		 * avoiding wasteful byte-at-a-time scanning... but more
+		 * I/O is often needed.
+		 */
+		for (tmp = 2; tmp < statlen; tmp++) {
+			if (scratch->status[tmp] != 0)
+				return;
+		}
+		tmp = mmc_spi_wait_unbusy(host, writeblock_timeout);
+		if (tmp < 0 && data->error == MMC_ERR_NONE) {
+			if (tmp == -ETIMEDOUT)
+				data->error = MMC_ERR_TIMEOUT;
+			else
+				data->error = MMC_ERR_FAILED;
+		}
+	}
+}
+
+/****************************************************************************/
+
+/*
+ * MMC driver implementation -- the interface to the MMC stack
+ */
+
+static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct mmc_spi_host	*host = mmc_priv(mmc);
+	int			status = -EINVAL;
+
+#ifdef DEBUG
+	/* MMC core and layered drivers *MUST* issue SPI-aware commands */
+	{
+		struct mmc_command	*cmd;
+		int			invalid = 0;
+
+		cmd = mrq->cmd;
+		if (!mmc_spi_resp_type(cmd)) {
+			dev_dbg(&host->spi->dev, "bogus command\n");
+			cmd->error = MMC_ERR_INVALID;
+			invalid = 1;
+		}
+
+		cmd = mrq->stop;
+		if (cmd && !mmc_spi_resp_type(cmd)) {
+			dev_dbg(&host->spi->dev, "bogus STOP command\n");
+			cmd->error = MMC_ERR_INVALID;
+			invalid = 1;
+		}
+
+		if (invalid) {
+			dump_stack();
+			mmc_request_done(host->mmc, mrq);
+			return;
+		}
+	}
+#endif
+
+	/* issue command; then optionally data and stop */
+	status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL);
+	if (status == 0 && mrq->data) {
+		mmc_spi_data_do(host, mrq->cmd, mrq->data, mrq->data->blksz);
+		if (mrq->stop)
+			status = mmc_spi_command_send(host, mrq, mrq->stop, 0);
+		else
+			mmc_cs_off(host);
+	}
+
+	mmc_request_done(host->mmc, mrq);
+}
+
+/* See Section 6.4.1, in SD "Simplified Physical Layer Specification 2.0"
+ *
+ * NOTE that here we can't know that the card has just been powered up;
+ * not all MMC/SD sockets support power switching.
+ *
+ * FIXME when the card is still in SPI mode, e.g. from a previous kernel,
+ * this doesn't seem to do the right thing at all...
+ */
+static void mmc_spi_initsequence(struct mmc_spi_host *host)
+{
+	/* Try to be very sure any previous command has completed;
+	 * wait till not-busy, skip debris from any old commands.
+	 */
+	mmc_spi_wait_unbusy(host, r1b_timeout);
+	mmc_spi_readbytes(host, 10);
+
+	/*
+	 * Do a burst with chipselect active-high.  We need to do this to
+	 * meet the requirement of 74 clock cycles with both chipselect
+	 * and CMD (MOSI) high before CMD0 ... after the card has been
+	 * powered up to Vdd(min), and so is ready to take commands.
+	 *
+	 * Some cards are particularly needy of this (e.g. Viking "SD256")
+	 * while most others don't seem to care.
+	 *
+	 * Note that this is one of the places MMC/SD plays games with the
+	 * SPI protocol.  Another is that when chipselect is released while
+	 * the card returns BUSY status, the clock must issue several cycles
+	 * with chipselect high before the card will stop driving its output.
+	 */
+	host->spi->mode |= SPI_CS_HIGH;
+	if (spi_setup(host->spi) != 0) {
+		/* Just warn; most cards work without it. */
+		dev_warn(&host->spi->dev,
+				"can't change chip-select polarity\n");
+		host->spi->mode &= ~SPI_CS_HIGH;
+	} else {
+		mmc_spi_readbytes(host, 18);
+
+		host->spi->mode &= ~SPI_CS_HIGH;
+		if (spi_setup(host->spi) != 0) {
+			/* Wot, we can't get the same setup we had before? */
+			dev_err(&host->spi->dev,
+					"can't restore chip-select polarity\n");
+		}
+	}
+}
+
+static char *mmc_powerstring(u8 power_mode)
+{
+	switch (power_mode) {
+	case MMC_POWER_OFF: return "off";
+	case MMC_POWER_UP:  return "up";
+	case MMC_POWER_ON:  return "on";
+	}
+	return "?";
+}
+
+static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct mmc_spi_host *host = mmc_priv(mmc);
+
+	if (host->power_mode != ios->power_mode) {
+		int		canpower;
+
+		canpower = host->pdata && host->pdata->setpower;
+
+		dev_dbg(&host->spi->dev, "mmc_spi: power %s (%d)%s\n",
+				mmc_powerstring(ios->power_mode),
+				ios->vdd,
+				canpower ? ", can switch" : "");
+
+		/* switch power on/off if possible, accounting for
+		 * max 250msec powerup time if needed.
+		 */
+		if (canpower) {
+			switch (ios->power_mode) {
+			case MMC_POWER_OFF:
+			case MMC_POWER_UP:
+				host->pdata->setpower(&host->spi->dev,
+						ios->vdd);
+				if (ios->power_mode == MMC_POWER_UP)
+					msleep(host->powerup_msecs);
+			}
+		}
+
+		/* See 6.4.1 in the simplified SD card physical spec 2.0 */
+		if (ios->power_mode == MMC_POWER_ON)
+			mmc_spi_initsequence(host);
+
+		/* If powering down, ground all card inputs to avoid power
+		 * delivery from data lines!  On a shared SPI bus, this
+		 * will probably be temporary; 6.4.2 of the simplified SD
+		 * spec says this must last at least 1msec.
+		 *
+		 *   - Clock low means CPOL 0, e.g. mode 0
+		 *   - MOSI low comes from writing zero
+		 *   - Chipselect is usually active low...
+		 */
+		if (canpower && ios->power_mode == MMC_POWER_OFF) {
+			int mres;
+
+			host->spi->mode &= ~(SPI_CPOL|SPI_CPHA);
+			mres = spi_setup(host->spi);
+			if (mres < 0)
+				dev_dbg(&host->spi->dev,
+					"switch to SPI mode 0 failed\n");
+
+			if (spi_w8r8(host->spi, 0x00) < 0)
+				dev_dbg(&host->spi->dev,
+					"put spi signals to low failed\n");
+
+			/*
+			 * Now clock should be low due to spi mode 0;
+			 * MOSI should be low because of written 0x00;
+			 * chipselect should be low (it is active low)
+			 * power supply is off, so now MMC is off too!
+			 *
+			 * FIXME no, chipselect can be high since the
+			 * device is inactive and SPI_CS_HIGH is clear...
+			 */
+			msleep(10);
+			if (mres == 0) {
+				host->spi->mode |= (SPI_CPOL|SPI_CPHA);
+				mres = spi_setup(host->spi);
+				if (mres < 0)
+					dev_dbg(&host->spi->dev,
+						"switch back to SPI mode 3"
+						" failed\n");
+			}
+		}
+
+		host->power_mode = ios->power_mode;
+	}
+
+	if (host->spi->max_speed_hz != ios->clock && ios->clock != 0) {
+		int		status;
+
+		host->spi->max_speed_hz = ios->clock;
+		status = spi_setup(host->spi);
+		dev_dbg(&host->spi->dev,
+			"mmc_spi:  clock to %d Hz, %d\n",
+			host->spi->max_speed_hz, status);
+	}
+}
+
+static int mmc_spi_get_ro(struct mmc_host *mmc)
+{
+	struct mmc_spi_host *host = mmc_priv(mmc);
+
+	if (host->pdata && host->pdata->get_ro)
+		return host->pdata->get_ro(mmc->parent);
+	/* board doesn't support read only detection; assume writeable */
+	return 0;
+}
+
+
+static const struct mmc_host_ops mmc_spi_ops = {
+	.request	= mmc_spi_request,
+	.set_ios	= mmc_spi_set_ios,
+	.get_ro		= mmc_spi_get_ro,
+};
+
+
+/****************************************************************************/
+
+/*
+ * SPI driver implementation
+ */
+
+static irqreturn_t
+mmc_spi_detect_irq(int irq, void *mmc)
+{
+	struct mmc_spi_host *host = mmc_priv(mmc);
+	u16 delay_msec = max(host->pdata->detect_delay, (u16)100);
+
+	mmc_detect_change(mmc, msecs_to_jiffies(delay_msec));
+	return IRQ_HANDLED;
+}
+
+static int mmc_spi_probe(struct spi_device *spi)
+{
+	void			*ones;
+	struct mmc_host		*mmc;
+	struct mmc_spi_host	*host;
+	int			status;
+
+	/* SanDisk and Hitachi MMC docs both show clock timing diagrams
+	 * with clock starting low (CPOL=0) and sampling on leading edge
+	 * (CPHA=0); clock is measured between rising edges.  Sandisk SD
+	 * docs show clock starting high (CPOL=1) and sampling on trailing
+	 * edge (CPHA=1), measuring between falling edges.
+	 *
+	 * Docs are very explicit that sampling is on the rising edge, so
+	 * SPI_MODE_0 and SPI_MODE_3 having different CPOL may not matter.
+	 */
+	spi->mode |= SPI_CPOL | SPI_CPHA;
+	spi->bits_per_word = 8;
+
+	status = spi_setup(spi);
+	if (status < 0) {
+		dev_dbg(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n",
+				spi->mode, spi->max_speed_hz / 1000,
+				status);
+		return status;
+	}
+
+	/* We can use the bus safely iff nobody else will interfere with
+	 * us.  That is, either we have the experimental exclusive access
+	 * primitives ... or else there's nobody to share it with.
+	 */
+	if (spi->master->num_chipselect > 1) {
+		struct device	*parent = spi->dev.parent;
+
+		/* If there are multiple devices on this bus, we
+		 * can't proceed.
+		 */
+		spin_lock(&parent->klist_children.k_lock);
+		if (parent->klist_children.k_list.next
+				!= parent->klist_children.k_list.prev)
+			status = -EMLINK;
+		else
+			status = 0;
+		spin_unlock(&parent->klist_children.k_lock);
+		if (status < 0) {
+			dev_err(&spi->dev, "can't share SPI bus\n");
+			return status;
+		}
+
+		/* REVISIT we can't guarantee another device won't
+		 * be added later.  It's uncommon though ... for now,
+		 * work as if this is safe.
+		 */
+		dev_warn(&spi->dev, "ASSUMING unshared SPI bus!\n");
+	}
+
+	/* We need a supply of ones to transmit.  This is the only time
+	 * the CPU touches these, so cache coherency isn't a concern.
+	 *
+	 * NOTE if many systems use more than one MMC-over-SPI connector
+	 * it'd save some memory to share this.  That's evidently rare.
+	 */
+	status = -ENOMEM;
+	ones = kmalloc(MMC_SPI_BLOCKSIZE, GFP_KERNEL);
+	if (!ones)
+		goto nomem;
+	memset(ones, 0xff, MMC_SPI_BLOCKSIZE);
+
+	mmc = mmc_alloc_host(sizeof(*host), &spi->dev);
+	if (!mmc)
+		goto nomem;
+
+	mmc->ops = &mmc_spi_ops;
+	mmc->max_blk_size = MMC_SPI_BLOCKSIZE;
+
+	/* As long as we keep track of the number of successfully
+	 * transmitted blocks, we're good for multiwrite.
+	 */
+	mmc->caps = MMC_CAP_SPI | MMC_CAP_MULTIWRITE;
+
+	/* SPI doesn't need the lowspeed device identification thing for
+	 * MMC or SD cards, since it never comes up in open drain mode.
+	 * That's good; some SPI masters can't handle very low speeds!
+	 *
+	 * However, low speed SDIO cards need not handle over 400 KHz;
+	 * that's the only reason not to use a few MHz for f_min (until
+	 * the upper layer reads the target frequency from the CSD).
+	 */
+	mmc->f_min = 400000;
+	mmc->f_max = spi->max_speed_hz;
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->spi = spi;
+
+	host->ones = ones;
+
+	/* Platform data is used to hook up things like card sensing
+	 * and power switching gpios.
+	 */
+	host->pdata = spi->dev.platform_data;
+	if (host->pdata)
+		mmc->ocr_avail = host->pdata->ocr_mask;
+	if (!mmc->ocr_avail) {
+		dev_warn(&spi->dev, "ASSUMING 3.2-3.4 V slot power\n");
+		mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+	}
+	if (host->pdata && host->pdata->setpower) {
+		host->powerup_msecs = host->pdata->powerup_msecs;
+		if (!host->powerup_msecs || host->powerup_msecs > 250)
+			host->powerup_msecs = 250;
+	}
+
+	dev_set_drvdata(&spi->dev, mmc);
+
+	/* preallocate dma buffers */
+	host->data = kmalloc(sizeof(*host->data), GFP_KERNEL);
+	if (!host->data)
+		goto fail_nobuf1;
+
+	if (spi->master->cdev.dev->dma_mask) {
+		struct device	*dev = spi->master->cdev.dev;
+
+		host->dma_dev = dev;
+		host->ones_dma = dma_map_single(dev, ones,
+				MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
+		host->data_dma = dma_map_single(dev, host->data,
+				sizeof(*host->data), DMA_BIDIRECTIONAL);
+
+		/* REVISIT in theory those map operations can fail... */
+
+		dma_sync_single_for_cpu(host->dma_dev,
+				host->data_dma, sizeof(*host->data),
+				DMA_BIDIRECTIONAL);
+	}
+
+	/* setup message for status/busy readback */
+	spi_message_init(&host->readback);
+	host->readback.is_dma_mapped = (host->dma_dev != NULL);
+
+	spi_message_add_tail(&host->status, &host->readback);
+	host->status.tx_buf = host->ones;
+	host->status.tx_dma = host->ones_dma;
+	host->status.rx_buf = &host->data->status;
+	host->status.rx_dma = host->data_dma + offsetof(struct scratch, status);
+	host->status.cs_change = 1;
+
+	/* register card detect irq */
+	if (host->pdata && host->pdata->init) {
+		status = host->pdata->init(&spi->dev, mmc_spi_detect_irq, mmc);
+		if (status != 0)
+			goto fail_glue_init;
+	}
+
+	status = mmc_add_host(mmc);
+	if (status != 0)
+		goto fail_add_host;
+
+	dev_info(&spi->dev, "SD/MMC host %s%s%s%s\n",
+			mmc->class_dev.bus_id,
+			host->dma_dev ? "" : ", no DMA",
+			(host->pdata && host->pdata->get_ro)
+				? "" : ", no WP",
+			(host->pdata && host->pdata->setpower)
+				? "" : ", no poweroff");
+	return 0;
+
+fail_add_host:
+	mmc_remove_host (mmc);
+fail_glue_init:
+	if (host->dma_dev)
+		dma_unmap_single(host->dma_dev, host->data_dma,
+				sizeof(*host->data), DMA_BIDIRECTIONAL);
+	kfree(host->data);
+
+fail_nobuf1:
+	mmc_free_host(mmc);
+	dev_set_drvdata(&spi->dev, NULL);
+
+nomem:
+	kfree(ones);
+	return status;
+}
+
+
+static int __devexit mmc_spi_remove(struct spi_device *spi)
+{
+	struct mmc_host		*mmc = dev_get_drvdata(&spi->dev);
+	struct mmc_spi_host	*host;
+
+	if (mmc) {
+		host = mmc_priv(mmc);
+
+		/* prevent new mmc_detect_change() calls */
+		if (host->pdata && host->pdata->exit)
+			host->pdata->exit(&spi->dev, mmc);
+
+		mmc_remove_host(mmc);
+
+		if (host->dma_dev) {
+			dma_unmap_single(host->dma_dev, host->ones_dma,
+				MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
+			dma_unmap_single(host->dma_dev, host->data_dma,
+				sizeof(*host->data), DMA_BIDIRECTIONAL);
+		}
+
+		kfree(host->data);
+		kfree(host->ones);
+
+		spi->max_speed_hz = mmc->f_max;
+		mmc_free_host(mmc);
+		dev_set_drvdata(&spi->dev, NULL);
+	}
+	return 0;
+}
+
+
+static struct spi_driver mmc_spi_driver = {
+	.driver = {
+		.name =		"mmc_spi",
+		.bus =		&spi_bus_type,
+		.owner =	THIS_MODULE,
+	},
+	.probe =	mmc_spi_probe,
+	.remove =	__devexit_p(mmc_spi_remove),
+};
+
+
+static int __init mmc_spi_init(void)
+{
+	return spi_register_driver(&mmc_spi_driver);
+}
+module_init(mmc_spi_init);
+
+
+static void __exit mmc_spi_exit(void)
+{
+	spi_unregister_driver(&mmc_spi_driver);
+}
+module_exit(mmc_spi_exit);
+
+
+MODULE_AUTHOR("Mike Lavender, David Brownell, "
+		"Hans-Peter Nilsson, Jan Nikitenko");
+MODULE_DESCRIPTION("SPI SD/MMC host driver");
+MODULE_LICENSE("GPL");

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
spi-devel-general mailing list
spi-devel-general@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

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

* Re: [patch 2.6.22-git5 3/4] MMC core learns about SPI
       [not found]             ` <200707261458.55558.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-07-26 22:22               ` David Brownell
@ 2007-08-01 15:02               ` Pierre Ossman
       [not found]                 ` <20070801170220.3c5ccff6-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  1 sibling, 1 reply; 47+ messages in thread
From: Pierre Ossman @ 2007-08-01 15:02 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thu, 26 Jul 2007 14:58:54 -0700
David Brownell <david-b@pacbell.net> wrote:

> 
> Yes we could ... but this way is IMO simpler, since it retains
> a simple invariant:  that the OCR parameter to attach() methods
> is always valid.  As one would expect!  Which gives another
> advantage:  no surprises.
> 

Fair enough. I'm not that attached to it.

> > 
> > This will fail to initialise a high capacity MMC card. From the MMC
> > spec:
> > 
> > "Without the CMD58 with bits [30:29] set as "10b" in prior to the
> > CMD1 a higher than 2GB of density of memory will remain in Idle
> > state forever." 
> 
> The first thing mmc_attach_mmc() does is reset the card...
> 
> Are you sure that "forever" means that reset will fail?
> That would be a very foolish thing to specify.  And while
> I've seen strange things in specs, that would be one of
> the worst that's not a vendor-specific part of a papering
> over errata ...
> 

Well, it's rather straight-forward if you think about it. As they've
changed the addressing scheme, but retain the original commands, a host
that does not understand the new scheme would utterly destroy the data.
So the card refuses to init unless the host can present some evidence
that it understands the new scheme.

> I'd be inclined to leave this alone unless someone gets
> such a card and notices that it doesn't work.  After all,
> lack of testing on those new (and-I-still-can't-find-one)
> cards is a general disclaimer.
> 

Too bad I have no SPI setup here. :(

Please add a comment that we know that MMC 4.2 is broken and can be
fixed once someone is able to test.

> > 
> > The *_ops.c only contain function wrappers of protocol commands, not
> > policy. So I think this is better placed where mmc_spi_set_crc() is
> > called. 
> 
> You mean, have separate module params for SD and for MMC?
> It seems cleaner this way...
> 

No, that would be silly. You'd probably have to stick it in core.c and
reference it from mmc.c and sd.c.

The reason I complain is that seeing mmc_spi_set_crc() will look like
it always activates CRC, as all other functions in *_ops.c are simple
wrappers around protocol ops.

> > 
> > "Illegal command" refers to the previous command sent (and failed).
> > So this can give false negatives. 
> 
> The immediately preceding command would be MMC_APP_CMD ... ?
> I didn't see any language in the specs which could let me
> interpret this as anything other than APP_CMD failure:
> 
>    When an error bit is detected in “R” mode the card will
>    report the error in the response to the command that raised
>    the exception. The command will not be executed and the
>    associated state transition will not take place. 
> 
> What this does is to quickly abort retries of SD card ops
> that were sent to MMC cards, and which always fail with
> ILLEGAL_COMMAND.
> 

I hate this part of the spec. The SD spec also claims:

"Clear condition B: Always related to the previous command"

But in the SPI section, they back out of that claim. So for SPI, things
seems somewhat sane.

(The MMC spec is even more hilarious. I lists clear condition A and B,
but never explains what they mean. And looking at some older card
specs, it seems that the MMC spec had the same conditions and
definitions as SD *sigh*)

We can proceed with your code, although I'm still a bit nervous about
sticking that parsing in there.

Rgds
-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
spi-devel-general mailing list
spi-devel-general@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

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

* Re: [patch 2.6.22-git5 4/4] mmc_spi host driver
       [not found]             ` <200707261632.37011.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-08-01 15:12               ` Pierre Ossman
       [not found]                 ` <20070801171217.1478267b-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: Pierre Ossman @ 2007-08-01 15:12 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thu, 26 Jul 2007 16:32:36 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> 
> You said you weren't going to update the MMC core to check
> the status bits after every request, and I'm certainly not
> about to change *EVERY MMC/SD REQUEST* to adopt a radically
> different callling convention either!!
> 
> Which leaves us needing a solution that actually works, and
> this seems to be the only option:  continue to use the current
> calling convention, ->error fields report the failures.
> 

I'm more concerned with where you check this, rather than how. If you
want to apply it broadly, the request handling functions in core.c
would seem more sane.

> 
> However, that bit looks deeply messy.  You'd be splitting
> the OCR (and other SPI four byte responses) into two
> different words, for example!
> 
> Or to put it differently, this way the structure that's
> passed up to the core is actually meaningful:  one word
> of status, and maybe another word of data.  And *never*
> any mixed status/data words that'd need disentangling.
> 
> After all, if you really wanted to provide raw protocol
> data to the mmc core, cmd->resp[] would be bytes, not
> 32-bit words in cpu-order.
> 

It should, as that's how it's used everywhere. And I'd gladly accept a
patch that fixes this.

> 
> I guess we'll have to disagree on this for now.  If it
> matters, it can be changed later; but I certainly couldn't
> call mixing data and status as an improvement!! 
> 

>From my point of view, it's all data. :)

> 
> The data block CRC is quite expensive, yes.  The CRC7 isn't;
> and in any case, you had pretty much demanded that it always
> be computed (to eliminate the #define for the crc needed in
> the card reset command) ...
> 

Ah, didn't think of that distinction. Carry on.

> > 
> > At least we agree on error codes. This is precisely the change
> > I have pending in my patch set to remove the MMC_ERR_* mess. :) 
> 
> I had to pick something.  ;)
> 

I have committed that to -mm now. Could I bother you with an update of
your patch to remove all MMC_ERR_ stuff? :)

> 
> > I need a MAINTAINERS entry aswell.  
> 
> I was hoping someone would sign up for that, so I don't
> have to stick myself there with "Odd fixes" status...
> 

Have you poked someone? I don't even have any SPI hardware, so I'm
definitely the wrong person.

> 
> > I'd like to see those REVISIT things attended to before I push
> > the stuff to Linus though. 
> 
> In this driver?  They're mostly performance tuning issues, which
> are normally best left till later:
> 
>   - dma_map_single() operations could theoretically fail.
>     Not that it'd be likely on hardware that uses this driver;
>     but that's simple error path cleanup.
> 

This is the one I'm mostly concerned with. Dangling error paths give me
the willies.

> More significant are the two FIXMEs related to card reset.
> One will require someone with relevant hardware (Jan?),
> the other still needs investigation.
> 

Ok.

Rgds
-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 3/4] MMC core learns about SPI
       [not found]                 ` <20070801170220.3c5ccff6-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
@ 2007-08-01 17:02                   ` David Brownell
       [not found]                     ` <200708011002.28801.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-08-01 17:02 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Wednesday 01 August 2007, Pierre Ossman wrote:
> On Thu, 26 Jul 2007 14:58:54 -0700
> David Brownell <david-b@pacbell.net> wrote:
> 
> > > This will fail to initialise a high capacity MMC card. From the MMC
> > > spec:
> > > 
> > > "Without the CMD58 with bits [30:29] set as "10b" in prior to the
> > > CMD1 a higher than 2GB of density of memory will remain in Idle
> > > state forever." 
> > 
> > The first thing mmc_attach_mmc() does is reset the card...
> > 
> > Are you sure that "forever" means that reset will fail?
> > That would be a very foolish thing to specify.  And while
> > I've seen strange things in specs, that would be one of
> > the worst that's not a vendor-specific part of a papering
> > over errata ...
> > 
> 
> Well, it's rather straight-forward if you think about it. As they've
> changed the addressing scheme,

Since I've not seen the MMC4 spec, I couldn't know.  :)

And the nearest approximation of MMC4 I've got (an early Samsung
spec) says CMD58 has argument "none" (== 0) ... while one of the
cards I've got rejected CMD58 before CMD1.  So "straightforward"
is not a word I could apply here...


> but retain the original commands, a host 
> that does not understand the new scheme would utterly destroy the data.
> So the card refuses to init unless the host can present some evidence
> that it understands the new scheme.

Right, but that doesn't answer my question:  whether the card
is specified to lock up "forever", or whether (as I'd expect)
reinitialization (starting with reset) works the normal way.


> > I'd be inclined to leave this alone unless someone gets
> > such a card and notices that it doesn't work.  After all,
> > lack of testing on those new (and-I-still-can't-find-one)
> > cards is a general disclaimer.
> > 
> 
> Too bad I have no SPI setup here. :(

My current setup uses a breadboard, so it can hook up to the
real SPI hardware ... I started out using bitbanged SPI to a
card which normally muxed to a "native" controller.  Maybe
one of those could work for you.


> Please add a comment that we know that MMC 4.2 is broken and can be
> fixed once someone is able to test.

Doesn't the general disclaimer that MMC4 cards haven't been
tested already cover that part?  If you like, just tweak the
patch comments to clarify.


> > > The *_ops.c only contain function wrappers of protocol commands, not
> > > policy. So I think this is better placed where mmc_spi_set_crc() is
> > > called. 
> > 
> > You mean, have separate module params for SD and for MMC?
> > It seems cleaner this way...
> > 
> 
> No, that would be silly. You'd probably have to stick it in core.c and
> reference it from mmc.c and sd.c.
> 
> The reason I complain is that seeing mmc_spi_set_crc() will look like
> it always activates CRC, as all other functions in *_ops.c are simple
> wrappers around protocol ops.

So, move the param and mmc_spi_set_crc() into core.c then, and
leave the calls where they are?

Can we do that as a separate patch?  You said below we can proceed,
which suggests to me that now's the time to convert to incrmentals.
I don't mind a few more update patches, but it'd be easier to do
them against say a git-mmc patch against 2.6.23-rc2 ...


> > > "Illegal command" refers to the previous command sent (and failed).
> > > So this can give false negatives. 
> > 
> > The immediately preceding command would be MMC_APP_CMD ... ?
> > I didn't see any language in the specs which could let me
> > interpret this as anything other than APP_CMD failure:
> > 
> >    When an error bit is detected in “R” mode the card will
> >    report the error in the response to the command that raised
> >    the exception. The command will not be executed and the
> >    associated state transition will not take place. 
> > 
> > What this does is to quickly abort retries of SD card ops
> > that were sent to MMC cards, and which always fail with
> > ILLEGAL_COMMAND.
> > 
> 
> I hate this part of the spec. The SD spec also claims:
> 
> "Clear condition B: Always related to the previous command"
> 
> But in the SPI section, they back out of that claim. So for SPI, things
> seems somewhat sane.

I don't have either of those specs.  "Previous command" isn't
particularly clear, though maybe in context it could be ... it
might mean the immediately preceding command, or the one before
that, depending on usage.


> (The MMC spec is even more hilarious. I lists clear condition A and B,
> but never explains what they mean. And looking at some older card
> specs, it seems that the MMC spec had the same conditions and
> definitions as SD *sigh*)

Yeech!

 
> We can proceed with your code, although I'm still a bit nervous about
> sticking that parsing in there.

I don't think you need to be nervous about that.  It clearly works,
and to those of us who've worked this driver and had to become one
with the docs we have, this hasn't raised flags.

It is however *much* better not to need to map the SPI status codes
into the "native" ones.  As you implicitly noted above, expecting
them to behave the same doesn't seem like a good idea!

- Dave


 
> Rgds
> -- 
>      -- Pierre Ossman
> 
>   Linux kernel, MMC maintainer        http://www.kernel.org
>   PulseAudio, core developer          http://pulseaudio.org
>   rdesktop, core developer          http://www.rdesktop.org
> 



-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
spi-devel-general mailing list
spi-devel-general@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

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

* Re: [patch 2.6.22-git5 4/4] mmc_spi host driver
       [not found]                 ` <20070801171217.1478267b-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
@ 2007-08-01 18:17                   ` David Brownell
       [not found]                     ` <200708011117.24664.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-08-01 18:40                   ` David Brownell
  1 sibling, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-08-01 18:17 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Wednesday 01 August 2007, Pierre Ossman wrote:
> On Thu, 26 Jul 2007 16:32:36 -0700
> David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:
> 
> > 
> > You said you weren't going to update the MMC core to check
> > the status bits after every request, and I'm certainly not
> > about to change *EVERY MMC/SD REQUEST* to adopt a radically
> > different callling convention either!!
> > 
> > Which leaves us needing a solution that actually works, and
> > this seems to be the only option:  continue to use the current
> > calling convention, ->error fields report the failures.
> > 
> 
> I'm more concerned with where you check this, rather than how. If you
> want to apply it broadly, the request handling functions in core.c
> would seem more sane.

That's not how any other host controller driver works though...

I'm concerned with not becoming a guinea pig for experimenting
with deep MMC stack changes which are not directly related to
the SPI support.


> > However, that bit looks deeply messy.  You'd be splitting
> > the OCR (and other SPI four byte responses) into two
> > different words, for example!
> > 
> > Or to put it differently, this way the structure that's
> > passed up to the core is actually meaningful:  one word
> > of status, and maybe another word of data.  And *never*
> > any mixed status/data words that'd need disentangling.
> > 
> > After all, if you really wanted to provide raw protocol
> > data to the mmc core, cmd->resp[] would be bytes, not
> > 32-bit words in cpu-order.
> > 
> 
> It should, as that's how it's used everywhere. And I'd gladly accept a
> patch that fixes this.

Erm, I don't follow.  It *is* used as 32-bit words
everywhere I've had occasion to check.


> > I guess we'll have to disagree on this for now.  If it
> > matters, it can be changed later; but I certainly couldn't
> > call mixing data and status as an improvement!! 
> > 
> 
> From my point of view, it's all data. :)

Which of your points of view, though?  Clearly at some
point the MMC stack recognizes the different semantics
associated with for example OCR vs the command response
codes.  Data isn't necessarily opaque ... 
 

> > The data block CRC is quite expensive, yes.  The CRC7 isn't;
> > and in any case, you had pretty much demanded that it always
> > be computed (to eliminate the #define for the crc needed in
> > the card reset command) ...
> > 
> 
> Ah, didn't think of that distinction. Carry on.
> 
> > > 
> > > At least we agree on error codes. This is precisely the change
> > > I have pending in my patch set to remove the MMC_ERR_* mess. :) 
> > 
> > I had to pick something.  ;)
> > 
> 
> I have committed that to -mm now. Could I bother you with an update of
> your patch to remove all MMC_ERR_ stuff? :)

You mean, git-mmc.patch from 2.6.23-rc1-mm2 which I see has
just come out?

It really would have been easier all around if you had made
those changes *after* merging at least the core parts of the
SPI support.  It looks like most of the SPI patches won't even
apply any more, so I'm "gifted" with a needless quantity of
make-work.  The kind of stuff I've been trying to avoid by
submitting the SPI stuff *before* any of those newer patches
which jumped ahead of it in the queue ... :(


> > > I'd like to see those REVISIT things attended to before I push
> > > the stuff to Linus though. 
> > 
> > In this driver?  They're mostly performance tuning issues, which
> > are normally best left till later:
> > 
> >   - dma_map_single() operations could theoretically fail.
> >     Not that it'd be likely on hardware that uses this driver;
> >     but that's simple error path cleanup.
> > 
> 
> This is the one I'm mostly concerned with. Dangling error paths give me
> the willies.

That will be a trivial incremental patch.  You are aware
that almost all DMA mapping calls in the kernel don't check
for errors, right?  Because the ability to return errors is
quite new ... and last I checked, not even documented.

So while I'm glad that the DMA API finally changed (I recall
pointing out that gaping hole almost four years ago by now),
this particular issue shouldn't worry much at all.


> > More significant are the two FIXMEs related to card reset.
> > One will require someone with relevant hardware (Jan?),
> > the other still needs investigation.
> > 
> 
> Ok.

I can't see those getting investigated before these patches
merge.

- Dave

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 4/4] mmc_spi host driver
       [not found]                 ` <20070801171217.1478267b-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  2007-08-01 18:17                   ` David Brownell
@ 2007-08-01 18:40                   ` David Brownell
  1 sibling, 0 replies; 47+ messages in thread
From: David Brownell @ 2007-08-01 18:40 UTC (permalink / raw)
  To: Hans-Peter Nilsson, Mike Lavender, jan.nikitenko-Re5JQEeQqe8AvxtiuMwx3w
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Mikael Starvik, Pierre Ossman

On Wednesday 01 August 2007, Pierre Ossman wrote:
> 
> > > I need a MAINTAINERS entry aswell.  
> > 
> > I was hoping someone would sign up for that, so I don't
> > have to stick myself there with "Odd fixes" status...
> > 
> 
> Have you poked someone? I don't even have any SPI hardware, so I'm
> definitely the wrong person.

Well, I certainly asked for a volunteer with one of the earlier
patchsets I posted ... as I started cleaning this up so that it
could be merged.

Mike, Hans-Peter, Jan ... you've all got platforms that need
this driver.  At least two of you even shipped products using
earlier versions.  Would one of you want to sign up as the
maintainer for the mmc_spi host?

- Dave


-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 3/4] MMC core learns about SPI
       [not found]                     ` <200708011002.28801.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-08-02 12:54                       ` Pierre Ossman
       [not found]                         ` <20070802145445.1118d1e7-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: Pierre Ossman @ 2007-08-02 12:54 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Wed, 1 Aug 2007 10:02:28 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> On Wednesday 01 August 2007, Pierre Ossman wrote:
> > 
> > Well, it's rather straight-forward if you think about it. As they've
> > changed the addressing scheme,
> 
> Since I've not seen the MMC4 spec, I couldn't know.  :)
> 

You can see the same thing in SD with the if cond thingy.

> And the nearest approximation of MMC4 I've got (an early Samsung
> spec) says CMD58 has argument "none" (== 0) ... while one of the
> cards I've got rejected CMD58 before CMD1.  So "straightforward"
> is not a word I could apply here...
> 

We can just code it so that failure is acceptable for that initial
CMD58.

> 
> > but retain the original commands, a host 
> > that does not understand the new scheme would utterly destroy the
> > data. So the card refuses to init unless the host can present some
> > evidence that it understands the new scheme.
> 
> Right, but that doesn't answer my question:  whether the card
> is specified to lock up "forever", or whether (as I'd expect)
> reinitialization (starting with reset) works the normal way.
> 

Like SDHC, it never goes out of idle mode.

> 
> My current setup uses a breadboard, so it can hook up to the
> real SPI hardware ... I started out using bitbanged SPI to a
> card which normally muxed to a "native" controller.  Maybe
> one of those could work for you.
> 

Well, I'd need something to do the banging.

> 
> > Please add a comment that we know that MMC 4.2 is broken and can be
> > fixed once someone is able to test.
> 
> Doesn't the general disclaimer that MMC4 cards haven't been
> tested already cover that part?  If you like, just tweak the
> patch comments to clarify.
> 

The disclaimer is in the commit message. And people rarely go digging
into the history for documentation.

I'll add the comment before committing.

> 
> So, move the param and mmc_spi_set_crc() into core.c then, and
> leave the calls where they are?
> 

mmc_spi_set_crc() fits quite nicely into mmc_ops.c, so no need for
moving that. I was thinking more along the lines of:

extern int mmc_spi_crc_enabled;

mmc_spi_set_crc(host, mmc_spi_crc_enabled);

> Can we do that as a separate patch?  You said below we can proceed,
> which suggests to me that now's the time to convert to incrmentals.
> I don't mind a few more update patches, but it'd be easier to do
> them against say a git-mmc patch against 2.6.23-rc2 ...
> 

Sure.

> > 
> > I hate this part of the spec. The SD spec also claims:
> > 
> > "Clear condition B: Always related to the previous command"
> > 
> > But in the SPI section, they back out of that claim. So for SPI,
> > things seems somewhat sane.
> 
> I don't have either of those specs.  "Previous command" isn't
> particularly clear, though maybe in context it could be ... it
> might mean the immediately preceding command, or the one before
> that, depending on usage.
> 

That's from the public SD physical spec. And the wording doesn't get
much clearer. Sometimes they also talk about the "last" command, which
is equally ambiguous.

Rgds
-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 4/4] mmc_spi host driver
       [not found]                     ` <200708011117.24664.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-08-02 13:06                       ` Pierre Ossman
       [not found]                         ` <20070802150615.36e073c6-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: Pierre Ossman @ 2007-08-02 13:06 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Wed, 1 Aug 2007 11:17:24 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> On Wednesday 01 August 2007, Pierre Ossman wrote:
> > 
> > I'm more concerned with where you check this, rather than how. If
> > you want to apply it broadly, the request handling functions in
> > core.c would seem more sane.
> 
> That's not how any other host controller driver works though...
> 

Other controllers don't care at all about status bits. Except for omap,
which caused it to break in fun and interesting ways.

> I'm concerned with not becoming a guinea pig for experimenting
> with deep MMC stack changes which are not directly related to
> the SPI support.
> 

You're the one who started poking the error handling bear ;)

But we can ignore it for now.

> > > After all, if you really wanted to provide raw protocol
> > > data to the mmc core, cmd->resp[] would be bytes, not
> > > 32-bit words in cpu-order.
> > > 
> > 
> > It should, as that's how it's used everywhere. And I'd gladly
> > accept a patch that fixes this.
> 
> Erm, I don't follow.  It *is* used as 32-bit words
> everywhere I've had occasion to check.
> 

The only place it is used as that is when checking status bits (in the
app cmd handling). In all other places it is treated as a data buffer.
Just look at the UNSTUFF_BITS macro in mmc.c and sd.c.

> > 
> > From my point of view, it's all data. :)
> 
> Which of your points of view, though?  Clearly at some
> point the MMC stack recognizes the different semantics
> associated with for example OCR vs the command response
> codes.  Data isn't necessarily opaque ... 
>  

Right. I'm talking about the layer the host driver handles. There things
should be:

<header> <payload> <tail>

> > 
> > I have committed that to -mm now. Could I bother you with an update
> > of your patch to remove all MMC_ERR_ stuff? :)
> 
> You mean, git-mmc.patch from 2.6.23-rc1-mm2 which I see has
> just come out?
> 

Unfortunately I have little insight into Andrew's schedule for
producing new -mm. I push stuff to my "for-andrew" branch and he
eventually pulls from that.

> It really would have been easier all around if you had made
> those changes *after* merging at least the core parts of the
> SPI support.  It looks like most of the SPI patches won't even
> apply any more, so I'm "gifted" with a needless quantity of
> make-work.  The kind of stuff I've been trying to avoid by
> submitting the SPI stuff *before* any of those newer patches
> which jumped ahead of it in the queue ... :(
> 

I don't see how it would have been easier. The only difference is that
I'd get a merge problem when I applied the new stuff. So the difference
is really whose lap it would have fallen in.

I can fix your patches if you'd like, but you'd probably have better
insight into selecting relevant error codes now that the entire errno
is available.

> 
> That will be a trivial incremental patch.  You are aware
> that almost all DMA mapping calls in the kernel don't check
> for errors, right?  Because the ability to return errors is
> quite new ... and last I checked, not even documented.
> 

Somehow, that doesn't make me feel any better.

> > > More significant are the two FIXMEs related to card reset.
> > > One will require someone with relevant hardware (Jan?),
> > > the other still needs investigation.
> > > 
> > 
> > Ok.
> 
> I can't see those getting investigated before these patches
> merge.
> 

Sure, that's fine.

-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 3/4] MMC core learns about SPI
       [not found]                         ` <20070802145445.1118d1e7-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
@ 2007-08-02 19:23                           ` David Brownell
  0 siblings, 0 replies; 47+ messages in thread
From: David Brownell @ 2007-08-02 19:23 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thursday 02 August 2007, Pierre Ossman wrote:
> 
> > So, move the param and mmc_spi_set_crc() into core.c then, and
> > leave the calls where they are?
> > 
> 
> mmc_spi_set_crc() fits quite nicely into mmc_ops.c, so no need for
> moving that. I was thinking more along the lines of:
> 
> extern int mmc_spi_crc_enabled;
> 
> mmc_spi_set_crc(host, mmc_spi_crc_enabled);

OK.


> > Can we do that as a separate patch?  You said below we can proceed,
> > which suggests to me that now's the time to convert to incrmentals.
> > I don't mind a few more update patches, but it'd be easier to do
> > them against say a git-mmc patch against 2.6.23-rc2 ...
> > 
> 
> Sure.
> 



-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 4/4] mmc_spi host driver
       [not found]                         ` <20070802150615.36e073c6-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
@ 2007-08-02 20:34                           ` David Brownell
       [not found]                             ` <200708021334.50670.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-08-02 20:34 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thursday 02 August 2007, Pierre Ossman wrote:
> On Wed, 1 Aug 2007 11:17:24 -0700
> David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:
> 
> > On Wednesday 01 August 2007, Pierre Ossman wrote:
> > > 
> > > I'm more concerned with where you check this, rather than how. If
> > > you want to apply it broadly, the request handling functions in
> > > core.c would seem more sane.
> > 
> > That's not how any other host controller driver works though...
> > 
> 
> Other controllers don't care at all about status bits.

Maybe not, but they *do* report failures.  And in this case
there's no other way to report failures ... since SPI doesn't
have any integral error checks whatsoever.  The only way to
detect errors is to look at those bits after each transaction.

(Without hardware feedback, you can't even tell if there's a
device present on any given chipselect.  The SPI host can
fling bits out, and read back pulled-up/down bits in any
quantity, without error.)


> Except for omap, 
> which caused it to break in fun and interesting ways.

I think the only time I touched that code was to teach it
how to do SD ... 4wire support, writeprotect sensing, and
funky block sizes.

The intended design of an MMC/SD host driver has never
been all that clear.  It's becoming more clear with your
recent work ... but it's not there yet, and there aren't
even test scripts or suites for de-facto requirements
(and to facilitate regression testing).


> > I'm concerned with not becoming a guinea pig for experimenting
> > with deep MMC stack changes which are not directly related to
> > the SPI support.
> > 
> 
> You're the one who started poking the error handling bear ;)

Was I?  Didn't think so.  How was I to know that whiteboard
scribbles would animate themselves?  :)


> But we can ignore it for now.
> 
> > > > After all, if you really wanted to provide raw protocol
> > > > data to the mmc core, cmd->resp[] would be bytes, not
> > > > 32-bit words in cpu-order.
> > > > 
> > > 
> > > It should, as that's how it's used everywhere. And I'd gladly
> > > accept a patch that fixes this.
> > 
> > Erm, I don't follow.  It *is* used as 32-bit words
> > everywhere I've had occasion to check.
> > 
> 
> The only place it is used as that is when checking status bits (in the
> app cmd handling). In all other places it is treated as a data buffer.
> Just look at the UNSTUFF_BITS macro in mmc.c and sd.c.

And it all but ignores those status bits too, which seems kind
of problematic.  (Relies on controller hardware to mirror protocol
faults by hardware faults of some kind...)  But I see what you
mean about UNSTUFF_BITS.


> > > From my point of view, it's all data. :)
> > 
> > Which of your points of view, though?  Clearly at some
> > point the MMC stack recognizes the different semantics
> > associated with for example OCR vs the command response
> > codes.  Data isn't necessarily opaque ... 
> >  
> 
> Right. I'm talking about the layer the host driver handles. There things
> should be:
> 
> <header> <payload> <tail>

Fault handling being orthogonal to that.  And at this time,
like it or (clearly!) not, the mmc core does virtually no
fault checking ... it relies on reports from host drivers.

You're probably on to something with the notion that the
I/O wrappers should check the status codes.  But that's
a substantial change from how the stack has worked, ever
since it was written.


> > > I have committed that to -mm now. Could I bother you with an update
> > > of your patch to remove all MMC_ERR_ stuff? :)
> > 
> > You mean, git-mmc.patch from 2.6.23-rc1-mm2 which I see has
> > just come out?
> > 
> 
> Unfortunately I have little insight into Andrew's schedule for
> producing new -mm. I push stuff to my "for-andrew" branch and he
> eventually pulls from that.

That's no problem.


> > It really would have been easier all around if you had made
> > those changes *after* merging at least the core parts of the
> > SPI support.  It looks like most of the SPI patches won't even
> > apply any more, so I'm "gifted" with a needless quantity of
> > make-work.  The kind of stuff I've been trying to avoid by
> > submitting the SPI stuff *before* any of those newer patches
> > which jumped ahead of it in the queue ... :(
> > 
> 
> I don't see how it would have been easier. The only difference is that
> I'd get a merge problem when I applied the new stuff. So the difference
> is really whose lap it would have fallen in.

Not exactly ... you'd have been developing that new stuff against
current GIT, which would already have had some of those patches,
so there wouldn't even *be* a merge problem for that stuff.


> I can fix your patches if you'd like, but you'd probably have better
> insight into selecting relevant error codes now that the entire errno
> is available.

Patches #1 and #2 were easy fixes; about 2/3 of #3 is rejected,
and I've not yet looked at the details ... that's all the core
changes, which haven't previously cared about much more than
success-or-fault.  #4 should be easy to cope with.


> > That will be a trivial incremental patch.  You are aware
> > that almost all DMA mapping calls in the kernel don't check
> > for errors, right?  Because the ability to return errors is
> > quite new ... and last I checked, not even documented.
> > 
> 
> Somehow, that doesn't make me feel any better.

It's just an indication of how seldom DMA mapping errors
have ever made problems.

 
> > > > More significant are the two FIXMEs related to card reset.
> > > > One will require someone with relevant hardware (Jan?),
> > > > the other still needs investigation.
> > > > 
> > > 
> > > Ok.
> > 
> > I can't see those getting investigated before these patches
> > merge.
> > 
> 
> Sure, that's fine.
>

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 4/4] mmc_spi host driver
       [not found]                             ` <200708021334.50670.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-08-04 13:14                               ` Pierre Ossman
       [not found]                                 ` <20070804151452.0efaa5a9-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
  2007-08-07 12:35                               ` Pierre Ossman
  1 sibling, 1 reply; 47+ messages in thread
From: Pierre Ossman @ 2007-08-04 13:14 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Thu, 2 Aug 2007 13:34:49 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> > Except for omap, 
> > which caused it to break in fun and interesting ways.
> 
> I think the only time I touched that code was to teach it
> how to do SD ... 4wire support, writeprotect sensing, and
> funky block sizes.
> 
> The intended design of an MMC/SD host driver has never
> been all that clear.  It's becoming more clear with your
> recent work ... but it's not there yet, and there aren't
> even test scripts or suites for de-facto requirements
> (and to facilitate regression testing).
> 

I agree. There are still a bunch of corner cases that are fuzzy at
best. Unfortunately, all difficult cases usually involve error
conditions. So I haven't been able to come up with a way to test things
without having a custom card that can fail requests in different exotic
ways.

> 
> Fault handling being orthogonal to that.  And at this time,
> like it or (clearly!) not, the mmc core does virtually no
> fault checking ... it relies on reports from host drivers.
> 
> You're probably on to something with the notion that the
> I/O wrappers should check the status codes.  But that's
> a substantial change from how the stack has worked, ever
> since it was written.
> 

Indeed. The core lacks proper error checking on that level because
those errors are so extremely rare. People are lazy and "good enough"
goes a long way. I'd love to see better error handling, but with these
low odds, other things get prioritized.

> > 
> > I don't see how it would have been easier. The only difference is
> > that I'd get a merge problem when I applied the new stuff. So the
> > difference is really whose lap it would have fallen in.
> 
> Not exactly ... you'd have been developing that new stuff against
> current GIT, which would already have had some of those patches,
> so there wouldn't even *be* a merge problem for that stuff.
> 

I'm afraid my development is far from serial. All code that needs
further testing gets its own branch, as there is still some uncertainty
as to when it will be pushed upstream.

> 
> Patches #1 and #2 were easy fixes; about 2/3 of #3 is rejected,
> and I've not yet looked at the details ... that's all the core
> changes, which haven't previously cared about much more than
> success-or-fault.  #4 should be easy to cope with.
> 

It should just be a matter of replacing MMC_ERR_ codes with -Esomething.

Rgds
-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 4/4] mmc_spi host driver
       [not found]                                 ` <20070804151452.0efaa5a9-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
@ 2007-08-04 17:32                                   ` David Brownell
       [not found]                                     ` <200708041032.10001.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 47+ messages in thread
From: David Brownell @ 2007-08-04 17:32 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Saturday 04 August 2007, Pierre Ossman wrote:
> On Thu, 2 Aug 2007 13:34:49 -0700
> David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:
> 
> > The intended design of an MMC/SD host driver has never
> > been all that clear.  It's becoming more clear with your
> > recent work ... but it's not there yet, and there aren't
> > even test scripts or suites for de-facto requirements
> > (and to facilitate regression testing).
> 
> I agree. There are still a bunch of corner cases that are fuzzy at
> best. Unfortunately, all difficult cases usually involve error
> conditions. So I haven't been able to come up with a way to test things
> without having a custom card that can fail requests in different exotic
> ways.

There's always writing negative tests ... i.e. issue requests
that will fail, and make sure they fail "correctly".  One of
those might be an alternative to the current block driver.

One expects that code developed outside any test framework
will have fair coverage for success paths (e.g. whatever is
needed to make MMC and SD memory cards work), and the most
common fault paths (e.g. what's triggered during enumeration).
But beyond that ... passing illegal params, reading past end
of card, "unusual" write sizes, and other things are unlikely
to behave consistently between drivers.


> > Patches #1 and #2 were easy fixes; about 2/3 of #3 is rejected,
> > and I've not yet looked at the details ... that's all the core
> > changes, which haven't previously cared about much more than
> > success-or-fault.  #4 should be easy to cope with.
> 
> It should just be a matter of replacing MMC_ERR_ codes with -Esomething.

Or MMC_ERR_NONE with zero.  Almost ... thing is, the new SDIO stuff
seems to bork things in some cases, more with SD cards than MMC.

- Dave

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 4/4] mmc_spi host driver
       [not found]                                     ` <200708041032.10001.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-08-04 21:32                                       ` Pierre Ossman
  0 siblings, 0 replies; 47+ messages in thread
From: Pierre Ossman @ 2007-08-04 21:32 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Sat, 4 Aug 2007 10:32:09 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> 
> There's always writing negative tests ... i.e. issue requests
> that will fail, and make sure they fail "correctly".  One of
> those might be an alternative to the current block driver.
> 

I've thought about it, and the interesting cases are difficult to
achieve with a standard card. If you have some ideas, I'd love to hear
them.

Also, a common problem is drivers that break layering. So my number one
wanted test is to issue a command opcode that doesn't match what the
spec says in terms of response and semantics.

> One expects that code developed outside any test framework
> will have fair coverage for success paths (e.g. whatever is
> needed to make MMC and SD memory cards work), and the most
> common fault paths (e.g. what's triggered during enumeration).
> But beyond that ... passing illegal params, reading past end
> of card, "unusual" write sizes, and other things are unlikely
> to behave consistently between drivers.
> 

Now if only there was time. :)

> > 
> > It should just be a matter of replacing MMC_ERR_ codes with
> > -Esomething.
> 
> Or MMC_ERR_NONE with zero.  Almost ... thing is, the new SDIO stuff
> seems to bork things in some cases, more with SD cards than MMC.
> 

Well, the sdio code so far hasn't paid any attention to sdio. Perhaps
it's best to just avoid that detection on a spi host for now.

-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 0/4] MMC-over-SPI
       [not found]                                         ` <200707231033.31717.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-07-24 10:23                                           ` Anton Vorontsov
@ 2007-08-07  3:21                                           ` David Brownell
  1 sibling, 0 replies; 47+ messages in thread
From: David Brownell @ 2007-08-07  3:21 UTC (permalink / raw)
  To: avorontsov-hkdhdckH98+B+jHODAdFcQ
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Pierre Ossman

On Monday 23 July 2007, David Brownell wrote:
> > > If that hardware were doing the right thing, then it would work
> > > reliably!  Since it's not reliable, it's doing something wrong.
> > > You seem to think it's not a hardware issue; that may be true.
> > > 
> > > Recall that the first dozen or so commands worked just fine.  The
> > > issue was that some byte that should have been all-ones or 0xfe
> > > reported instead an 0xf8.  That's not the kind of error that can
> > > be explained by clock skew; it covers at least two bits.
> > 
> > Yup, I've either noticed that 0xf8 and 0xfe differs by only two
> > bits (and by three if comparing to 0xff). But I can't really
> > explain it yet.

Just for the record -- on my test rig, SPI mode 3 stopped working
entirely ... I'd get one-bit errors, so that CMD0 status would be
"0x03" instead of "0x01" (indicating CRC error plus still-resetting).
Switching to SPI mode 0 made it all work again; masking out the 0x02
bit made everything time out.  So I'm thinking that *was* somehow a
shift of one bit.

I think this was caused by removing wires for one SPI device from
that breadboard ... but it's hard to say for sure.  For a while it
seemed like MMC cards worked OK but not SD cards!  I'm working on a
more permanent rig.  But I think when I send the next revision to
Pierre, it'll be using SPI_MODE_0.  :)

I thought you might be amused, given the strange behavior you saw!

- Dave


-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

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

* Re: [patch 2.6.22-git5 4/4] mmc_spi host driver
       [not found]                             ` <200708021334.50670.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-08-04 13:14                               ` Pierre Ossman
@ 2007-08-07 12:35                               ` Pierre Ossman
  1 sibling, 0 replies; 47+ messages in thread
From: Pierre Ossman @ 2007-08-07 12:35 UTC (permalink / raw)
  To: David Brownell
  Cc: Hans-Peter Nilsson, Mikael Starvik, Mike Lavender,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

[-- Attachment #1: Type: text/plain, Size: 717 bytes --]

On Thu, 2 Aug 2007 13:34:49 -0700
David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org> wrote:

> 
> The intended design of an MMC/SD host driver has never
> been all that clear.  It's becoming more clear with your
> recent work ... but it's not there yet, and there aren't
> even test scripts or suites for de-facto requirements
> (and to facilitate regression testing).
> 

I got annoyed by the olpc xo and wrote some tests. Enjoy.

(Not SPI enabled, but I'm sure you can make some additions ;))

-- 
     -- Pierre Ossman

  Linux kernel, MMC maintainer        http://www.kernel.org
  PulseAudio, core developer          http://pulseaudio.org
  rdesktop, core developer          http://www.rdesktop.org

[-- Attachment #2: mmc-test.patch --]
[-- Type: text/x-patch, Size: 12130 bytes --]

commit e966ae990cd2ef3be48ece8cde0f97f04a0d6024
Author: Pierre Ossman <drzeus-p3sGCRWkH8CeZLLa646FqQ@public.gmane.org>
Date:   Tue Aug 7 14:27:17 2007 +0200

    mmc: mmc host test driver
    
    A dummy driver that performs a series of requests that are often mis-
    handled by host drivers.
    
    Signed-off-by: Pierre Ossman <drzeus-p3sGCRWkH8CeZLLa646FqQ@public.gmane.org>

diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index aa8a4e4..28db50d 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -32,6 +32,12 @@ config MMC_BLOCK_BOUNCE
 
 	  If unsure, say Y here.
 
+config MMC_TEST
+	tristate "MMC host test driver"
+	default n
+	help
+	  Dummy driver that tests that a host performs as it should.
+
 config SDIO_UART
 	tristate "SDIO UART/GPS class support"
 	depends on MMC
diff --git a/drivers/mmc/card/Makefile b/drivers/mmc/card/Makefile
index fc5a784..944447d 100644
--- a/drivers/mmc/card/Makefile
+++ b/drivers/mmc/card/Makefile
@@ -9,5 +9,6 @@ endif
 obj-$(CONFIG_MMC_BLOCK)		+= mmc_block.o
 mmc_block-objs			:= block.o queue.o
 
+obj-$(CONFIG_MMC_TEST)		+= mmc_test.o
 obj-$(CONFIG_SDIO_UART)		+= sdio_uart.o
 
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
new file mode 100644
index 0000000..06c9ef2
--- /dev/null
+++ b/drivers/mmc/card/mmc_test.c
@@ -0,0 +1,520 @@
+/*
+ *  linux/drivers/mmc/card/mmc_test.c
+ *
+ *  Copyright 2007 Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+
+#include <linux/scatterlist.h>
+
+static int set_blksize(struct mmc_card *card, unsigned size)
+{
+	struct mmc_command cmd;
+	int ret;
+
+	cmd.opcode = MMC_SET_BLOCKLEN;
+	cmd.arg = size;
+	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+	ret = mmc_wait_for_cmd(card->host, &cmd, 5);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int do_transfer(struct mmc_card *card, struct mmc_request *mrq,
+		int write, unsigned blocks, unsigned blksz)
+{
+	int ret, i;
+	u8 *buffer;
+
+	struct scatterlist sg;
+
+	buffer = kzalloc((blocks + 1) * blksz, GFP_KERNEL);
+	if (!buffer) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (!write)
+		memset(buffer, 0xDF, blocks * blksz);
+
+	memset(mrq->data, 0, sizeof(struct mmc_data));
+
+	mrq->data->blksz = blksz;
+	mrq->data->blocks = blocks;
+	mrq->data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+	mrq->data->sg = &sg;
+	mrq->data->sg_len = 1;
+
+	sg_init_one(&sg, buffer, blocks * blksz);
+
+	mmc_set_data_timeout(mrq->data, card, write);
+
+	mmc_wait_for_req(card->host, mrq);
+
+	if (!write) {
+		for (i = 0;i < blocks * blksz;i++) {
+			if (buffer[i] == 0xDF) {
+				ret = -EIO;
+				break;
+			}
+		}
+
+		for (i = 0;i < blksz;i++) {
+			if (buffer[blocks * blksz + i] != 0x00) {
+				ret = -EIO;
+				break;
+			}
+		}
+	}
+
+	ret = 0;
+
+out:
+	kfree(buffer);
+	return ret;
+}
+
+static int do_valid_transfer(struct mmc_card *card, struct mmc_request *mrq,
+		int write, unsigned blocks, unsigned blksz)
+{
+	int ret;
+
+	ret = set_blksize(card, blksz);
+	if (ret)
+		return ret;
+
+	memset(mrq->cmd, 0, sizeof(struct mmc_command));
+
+	if (blocks > 1)
+		mrq->cmd->opcode = write ? MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK;
+	else
+		mrq->cmd->opcode = write ? MMC_WRITE_BLOCK : MMC_READ_SINGLE_BLOCK;
+
+	mrq->cmd->arg = 0;
+	mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	memset(mrq->stop, 0, sizeof(struct mmc_command));
+
+	if (blocks > 1) {
+		mrq->stop->opcode = MMC_STOP_TRANSMISSION;
+		mrq->stop->arg = 0;
+		mrq->stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
+	} else {
+		mrq->stop->opcode = MMC_SEND_STATUS;
+		mrq->stop->arg = card->rca << 16;
+		mrq->stop->flags = MMC_RSP_R1 | MMC_CMD_AC;
+	}
+
+	return do_transfer(card, mrq, write, blocks, blksz);
+}
+
+static int do_invalid_transfer(struct mmc_card *card, struct mmc_request *mrq,
+		int write, unsigned blocks, unsigned blksz)
+{
+	memset(mrq->cmd, 0, sizeof(struct mmc_command));
+
+	mrq->cmd->opcode = MMC_SEND_STATUS;
+	mrq->cmd->arg = card->rca << 16;
+	mrq->cmd->flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	memset(mrq->stop, 0, sizeof(struct mmc_command));
+
+	mrq->stop->opcode = MMC_SEND_STATUS;
+	mrq->stop->arg = card->rca << 16;
+	mrq->stop->flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+	return do_transfer(card, mrq, write, blocks, blksz);
+}
+
+static int test_pow2_reads(struct mmc_card *card)
+{
+	int ret, i;
+
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_command stop;
+	struct mmc_data data;
+
+	printk(KERN_INFO "%s: Testing reading power of two block sizes...\n",
+		mmc_hostname(card->host));
+
+	for (i = 1;i <= 512;i <<= 1) {
+		memset(&mrq, 0, sizeof(struct mmc_request));
+
+		mrq.cmd = &cmd;
+		mrq.data = &data;
+		mrq.stop = &stop;
+
+		ret = do_valid_transfer(card, &mrq, 0, 1, i);
+		if (ret)
+			goto out;
+
+		ret = 0;
+
+		if (!ret && cmd.error)
+			ret = cmd.error;
+		if (!ret && data.error)
+			ret = data.error;
+		if (!ret && stop.error)
+			ret = stop.error;
+		if (!ret && data.bytes_xfered != i)
+			ret = -EIO;
+
+		if (ret)
+			break;
+	}
+
+out:
+	printk(KERN_INFO "%s: Result: %s\n", mmc_hostname(card->host),
+		ret ? "FAIL" : "OK");
+
+	return ret;
+}
+
+static int test_pow2_writes(struct mmc_card *card)
+{
+	int ret, i;
+
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_command stop;
+	struct mmc_data data;
+
+	printk(KERN_INFO "%s: Testing writing power of two block sizes...\n",
+		mmc_hostname(card->host));
+
+	for (i = 1;i <= 512;i <<= 1) {
+		memset(&mrq, 0, sizeof(struct mmc_request));
+
+		mrq.cmd = &cmd;
+		mrq.data = &data;
+		mrq.stop = &stop;
+
+		ret = do_valid_transfer(card, &mrq, 1, 1, i);
+		if (ret)
+			goto out;
+
+		ret = 0;
+
+		if (!ret && cmd.error)
+			ret = cmd.error;
+		if (!ret && data.error)
+			ret = data.error;
+		if (!ret && stop.error)
+			ret = stop.error;
+		if (!ret && data.bytes_xfered != i)
+			ret = -EIO;
+
+		if (ret)
+			break;
+	}
+
+out:
+	printk(KERN_INFO "%s: Result: %s\n", mmc_hostname(card->host),
+		ret ? "FAIL" : "OK");
+
+	return ret;
+}
+
+static int test_weird_reads(struct mmc_card *card)
+{
+	int ret, i;
+
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_command stop;
+	struct mmc_data data;
+
+	printk(KERN_INFO "%s: Testing reading unusual block sizes...\n",
+		mmc_hostname(card->host));
+
+	for (i = 3;i <= 512;i += 7) {
+		memset(&mrq, 0, sizeof(struct mmc_request));
+
+		mrq.cmd = &cmd;
+		mrq.data = &data;
+		mrq.stop = &stop;
+
+		ret = do_valid_transfer(card, &mrq, 0, 1, i);
+		if (ret)
+			goto out;
+
+		ret = 0;
+
+		if (!ret && cmd.error)
+			ret = cmd.error;
+		if (!ret && data.error)
+			ret = data.error;
+		if (!ret && stop.error)
+			ret = stop.error;
+		if (!ret && data.bytes_xfered != i)
+			ret = -EIO;
+
+		if (ret)
+			break;
+	}
+
+out:
+	printk(KERN_INFO "%s: Result: %s\n", mmc_hostname(card->host),
+		ret ? "FAIL" : "OK");
+
+	return ret;
+}
+
+static int test_weird_writes(struct mmc_card *card)
+{
+	int ret, i;
+
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_command stop;
+	struct mmc_data data;
+
+	printk(KERN_INFO "%s: Testing writing unusual block sizes...\n",
+		mmc_hostname(card->host));
+
+	for (i = 3;i <= 512;i += 7) {
+		memset(&mrq, 0, sizeof(struct mmc_request));
+
+		mrq.cmd = &cmd;
+		mrq.data = &data;
+		mrq.stop = &stop;
+
+		ret = do_valid_transfer(card, &mrq, 1, 1, i);
+		if (ret)
+			goto out;
+
+		ret = 0;
+
+		if (!ret && cmd.error)
+			ret = cmd.error;
+		if (!ret && data.error)
+			ret = data.error;
+		if (!ret && stop.error)
+			ret = stop.error;
+		if (!ret && data.bytes_xfered != i)
+			ret = -EIO;
+
+		if (ret)
+			break;
+	}
+
+out:
+	printk(KERN_INFO "%s: Result: %s\n", mmc_hostname(card->host),
+		ret ? "FAIL" : "OK");
+
+	return ret;
+}
+
+static int test_bytes_xfer_single(struct mmc_card *card)
+{
+	int ret;
+
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_command stop;
+	struct mmc_data data;
+
+	printk(KERN_INFO "%s: Testing correct bytes_xfered for a single block...\n",
+		mmc_hostname(card->host));
+
+	if (!(card->host->caps & MMC_CAP_MULTIWRITE)) {
+		printk(KERN_INFO "%s: Result: UNSUPPORTED\n",
+			mmc_hostname(card->host));
+		return -EINVAL;
+	}
+
+	memset(&mrq, 0, sizeof(struct mmc_request));
+
+	mrq.cmd = &cmd;
+	mrq.data = &data;
+	mrq.stop = &stop;
+
+	ret = do_invalid_transfer(card, &mrq, 1, 1, 512);
+	if (ret)
+		goto out;
+
+	ret = 0;
+
+	if (!ret && cmd.error)
+		ret = cmd.error;
+	if (!ret && data.error != -ETIMEDOUT)
+		ret = data.error;
+	if (!ret && stop.error)
+		ret = stop.error;
+	if (!ret && data.bytes_xfered != 0)
+		ret = -EINVAL;
+
+out:
+	printk(KERN_INFO "%s: Result: %s\n", mmc_hostname(card->host),
+		ret ? "FAIL" : "OK");
+
+	return ret;
+}
+
+static int test_bytes_xfer_multi(struct mmc_card *card)
+{
+	int ret;
+
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_command stop;
+	struct mmc_data data;
+
+	printk(KERN_INFO "%s: Testing correct bytes_xfered for multiple blocks...\n",
+		mmc_hostname(card->host));
+
+	if (!(card->host->caps & MMC_CAP_MULTIWRITE) ||
+			(card->host->max_blk_count < 2) ||
+			(card->host->max_req_size < 1024) ||
+			(card->host->max_seg_size < 1024)) {
+		printk(KERN_INFO "%s: Result: UNSUPPORTED\n",
+			mmc_hostname(card->host));
+		return -EINVAL;
+	}
+
+	memset(&mrq, 0, sizeof(struct mmc_request));
+
+	mrq.cmd = &cmd;
+	mrq.data = &data;
+	mrq.stop = &stop;
+
+	ret = do_invalid_transfer(card, &mrq, 1, 2, 512);
+	if (ret)
+		goto out;
+
+	ret = 0;
+
+	if (!ret && cmd.error)
+		ret = cmd.error;
+	if (!ret && data.error != -ETIMEDOUT)
+		ret = data.error;
+	if (!ret && stop.error)
+		ret = stop.error;
+	if (!ret && data.bytes_xfered != 0)
+		ret = -EINVAL;
+
+out:
+	printk(KERN_INFO "%s: Result: %s\n", mmc_hostname(card->host),
+		ret ? "FAIL" : "OK");
+
+	return ret;
+}
+
+static int test_busy_wait(struct mmc_card *card)
+{
+	int ret;
+
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_command stop;
+	struct mmc_data data;
+
+	printk(KERN_INFO "%s: Testing that host waits for busy...\n",
+		mmc_hostname(card->host));
+
+	memset(&mrq, 0, sizeof(struct mmc_request));
+
+	mrq.cmd = &cmd;
+	mrq.data = &data;
+	mrq.stop = &stop;
+
+	ret = do_valid_transfer(card, &mrq, 1, 10, 512);
+	if (ret)
+		goto out;
+
+	ret = 0;
+
+	if (!ret && cmd.error)
+		ret = cmd.error;
+	if (!ret && data.error)
+		ret = data.error;
+	if (!ret && stop.error)
+		ret = stop.error;
+	if (!ret && data.bytes_xfered != 10 * 512)
+		ret = -EIO;
+
+	if (ret)
+		goto out;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = MMC_SEND_STATUS;
+	cmd.arg = card->rca << 16;
+	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	ret = mmc_wait_for_cmd(card->host, &cmd, 0);
+	if (ret)
+		goto out;
+
+	if (!(cmd.resp[0] & R1_READY_FOR_DATA))
+		ret = -EIO;
+
+out:
+	printk(KERN_INFO "%s: Result: %s\n", mmc_hostname(card->host),
+		ret ? "FAIL" : "OK (uncertain, test multiple cards)");
+
+	return ret;
+}
+
+static int mmc_test_probe(struct mmc_card *card)
+{
+	mmc_claim_host(card->host);
+
+	test_pow2_writes(card);
+	test_pow2_reads(card);
+
+	test_weird_writes(card);
+	test_weird_reads(card);
+
+	test_bytes_xfer_single(card);
+	test_bytes_xfer_multi(card);
+
+	test_busy_wait(card);
+
+	mmc_release_host(card->host);
+
+	return -ENODEV;
+}
+
+static void mmc_test_remove(struct mmc_card *card)
+{
+}
+
+static struct mmc_driver mmc_driver = {
+	.drv		= {
+		.name	= "mmc_test",
+	},
+	.probe		= mmc_test_probe,
+	.remove		= mmc_test_remove,
+};
+
+static int __init mmc_test_init(void)
+{
+	return mmc_register_driver(&mmc_driver);
+}
+
+static void __exit mmc_test_exit(void)
+{
+	mmc_unregister_driver(&mmc_driver);
+}
+
+module_init(mmc_test_init);
+module_exit(mmc_test_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Multimedia Card (MMC) host test driver");
+MODULE_AUTHOR("Pierre Ossman");
+
+

[-- Attachment #3: Type: text/plain, Size: 315 bytes --]

-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/

[-- Attachment #4: Type: text/plain, Size: 210 bytes --]

_______________________________________________
spi-devel-general mailing list
spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

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

end of thread, other threads:[~2007-08-07 12:35 UTC | newest]

Thread overview: 47+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-07-14 22:04 [patch 2.6.22-git5 0/4] MMC-over-SPI David Brownell
     [not found] ` <200707141504.51950.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-14 22:05   ` [patch 2.6.22-git5 1/4] MMC headers learn about SPI David Brownell
     [not found]     ` <200707141506.00262.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-26 16:31       ` Pierre Ossman
     [not found]         ` <20070726183150.024930aa-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
2007-07-26 16:55           ` David Brownell
     [not found]             ` <200707260955.22967.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-26 18:05               ` Pierre Ossman
     [not found]                 ` <20070726200525.6a5b7d72-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
2007-07-26 20:08                   ` David Brownell
2007-07-14 22:06   ` [patch 2.6.22-git5 2/4] MMC block learns " David Brownell
     [not found]     ` <200707141506.42880.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-26 16:33       ` Pierre Ossman
     [not found]         ` <20070726183339.6631243f-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
2007-07-26 17:00           ` David Brownell
     [not found]             ` <200707261000.17339.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-26 18:06               ` Pierre Ossman
     [not found]                 ` <20070726200639.06242858-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
2007-07-26 20:15                   ` David Brownell
2007-07-14 22:07   ` [patch 2.6.22-git5 3/4] MMC core " David Brownell
     [not found]     ` <200707141507.17484.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-26 17:18       ` Pierre Ossman
     [not found]         ` <20070726191824.569542fd-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
2007-07-26 21:58           ` David Brownell
     [not found]             ` <200707261458.55558.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-26 22:22               ` David Brownell
2007-08-01 15:02               ` Pierre Ossman
     [not found]                 ` <20070801170220.3c5ccff6-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
2007-08-01 17:02                   ` David Brownell
     [not found]                     ` <200708011002.28801.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-08-02 12:54                       ` Pierre Ossman
     [not found]                         ` <20070802145445.1118d1e7-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
2007-08-02 19:23                           ` David Brownell
2007-07-14 22:08   ` [patch 2.6.22-git5 4/4] mmc_spi host driver David Brownell
     [not found]     ` <200707141508.07555.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-26 18:02       ` Pierre Ossman
     [not found]         ` <20070726200202.5e5dcf62-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
2007-07-26 23:32           ` David Brownell
     [not found]             ` <200707261632.37011.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-08-01 15:12               ` Pierre Ossman
     [not found]                 ` <20070801171217.1478267b-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
2007-08-01 18:17                   ` David Brownell
     [not found]                     ` <200708011117.24664.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-08-02 13:06                       ` Pierre Ossman
     [not found]                         ` <20070802150615.36e073c6-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
2007-08-02 20:34                           ` David Brownell
     [not found]                             ` <200708021334.50670.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-08-04 13:14                               ` Pierre Ossman
     [not found]                                 ` <20070804151452.0efaa5a9-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
2007-08-04 17:32                                   ` David Brownell
     [not found]                                     ` <200708041032.10001.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-08-04 21:32                                       ` Pierre Ossman
2007-08-07 12:35                               ` Pierre Ossman
2007-08-01 18:40                   ` David Brownell
2007-07-16 16:48   ` [patch 2.6.22-git5 0/4] MMC-over-SPI Anton Vorontsov
     [not found]     ` <20070716164837.GA18707-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-07-16 18:54       ` David Brownell
     [not found]         ` <200707161154.54728.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-17 15:13           ` Anton Vorontsov
     [not found]             ` <20070717151350.GA3752-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-07-17 16:11               ` David Brownell
     [not found]                 ` <200707170911.16715.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-18  7:35                   ` Jan Nikitenko
     [not found]                     ` <469DC2BC.5070305-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2007-07-18 17:06                       ` David Brownell
2007-07-18 10:00                   ` Anton Vorontsov
     [not found]                     ` <20070718100047.GA9544-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-07-18 14:05                       ` Anton Vorontsov
2007-07-18 14:44                   ` Pierre Ossman
     [not found]                     ` <20070718164409.56ca0019-mgABNEgzgxm+PRNnhPf8W5YgPPQkE1Si@public.gmane.org>
2007-07-18 17:27                       ` David Brownell
     [not found]                         ` <200707181027.17819.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-19 16:15                           ` Anton Vorontsov
     [not found]                             ` <20070719161553.GA15756-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-07-19 20:28                               ` David Brownell
     [not found]                                 ` <200707191328.18846.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-23 14:29                                   ` Anton Vorontsov
     [not found]                                     ` <20070723142923.GA28979-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-07-23 17:33                                       ` David Brownell
     [not found]                                         ` <200707231033.31717.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-07-24 10:23                                           ` Anton Vorontsov
2007-08-07  3:21                                           ` David Brownell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).