linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch 2.6.22-rc4 0/7] latest MMC-over-SPI patchset
@ 2007-06-05  3:25 David Brownell
       [not found] ` <200706042025.18252.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 11+ messages in thread
From: David Brownell @ 2007-06-05  3:25 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Mikael Starvik, Hans-Peter Nilsson, Mike Lavender, Pierre Ossman

It's been a while since the mmc-over-spi host driver has been
updated, and I think it's time to do so ... and start getting
it mergeable.  Hence the following series of patches.

Kudos to Mike Lavender, original author; Hans-Peter Nilsson,
who got this version to really work; Jan Nikitenko, who made
more rough edges vanish and added CRC support; and Pierre Ossman,
who has supported the notion of a bit more surgery on mmc-core
to help keep some crap out of the host driver!

The first few patches are pretty separable:

  - CRC7 support (already in MM)
  - SD 4wire bugfix (submitted to Pierre)
  - SPI "exclusive mode" patch ... experimental, optional

The rest are the guts of the MMC-over-SPI support, following
Pierre's suggestion to teach the mmc core about SPI so that
the host adapter doesn't have so much work to do.

  - MMC headers expose SPI constants; add mmc_host_is_spi()
  - MMC core uses those to support MMC or SD enumeration
  - MMC block needed tweaks too
  - Last but not least ... mmc_spi host adapter.

This is distributed for testing and comments.  I believe that
Pierre will have a few issues with the core patches.  The SPI
"exclusive mode" patch will remain experimental for some time.

I'd also like to see someone sign up as mmc_spi maintainer;
ideally that person would start sheparding this the rest of
the way upstream.

- 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] 11+ messages in thread

* [patch 2.6.22-rc4 1/7] CRC7 support
       [not found] ` <200706042025.18252.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-06-05  3:26   ` David Brownell
  2007-06-05  3:28   ` [patch 2.6.22-rc4 2/7] SD 4wire bugfix David Brownell
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: David Brownell @ 2007-06-05  3:26 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Mikael Starvik, Hans-Peter Nilsson, Mike Lavender, Pierre Ossman

From: Jan Nikitenko <jan.nikitenko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Add CRC7 routines, used for example in MMC over SPI communication.
Kerneldoc updates

Signed-off-by: Jan Nikitenko <jan.nikitenko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
---
NOTE:  this is already in the MM tree.

 Documentation/DocBook/kernel-api.tmpl |    4 +-
 include/linux/crc7.h                  |   14 +++++++
 lib/Kconfig                           |    8 ++++
 lib/Makefile                          |    1 
 lib/crc7.c                            |   68 ++++++++++++++++++++++++++++++++++
 5 files changed, 94 insertions(+), 1 deletion(-)

--- g26.orig/Documentation/DocBook/kernel-api.tmpl	2007-05-11 00:49:19.000000000 -0700
+++ g26/Documentation/DocBook/kernel-api.tmpl	2007-05-11 01:00:03.000000000 -0700
@@ -139,8 +139,10 @@ X!Ilib/string.c
 !Elib/cmdline.c
      </sect1>
 
-     <sect1><title>CRC Functions</title>
+     <sect1 id="crc"><title>CRC Functions</title>
+!Elib/crc7.c
 !Elib/crc16.c
+!Elib/crc-itu-t.c
 !Elib/crc32.c
 !Elib/crc-ccitt.c
      </sect1>
--- g26.orig/lib/Kconfig	2007-05-11 00:46:44.000000000 -0700
+++ g26/lib/Kconfig	2007-05-11 01:14:06.000000000 -0700
@@ -41,6 +41,14 @@ config CRC32
 	  kernel tree does. Such modules that use library CRC32 functions
 	  require M here.
 
+config CRC7
+	tristate "CRC7 functions"
+	help
+	  This option is provided for the case where no in-kernel-tree
+	  modules require CRC7 functions, but a module built outside
+	  the kernel tree does. Such modules that use library CRC7
+	  functions require M here.
+
 config LIBCRC32C
 	tristate "CRC32c (Castagnoli, et al) Cyclic Redundancy-Check"
 	help
--- g26.orig/lib/Makefile	2007-05-11 00:46:44.000000000 -0700
+++ g26/lib/Makefile	2007-05-11 01:14:06.000000000 -0700
@@ -43,6 +43,7 @@ obj-$(CONFIG_CRC_CCITT)	+= crc-ccitt.o
 obj-$(CONFIG_CRC16)	+= crc16.o
 obj-$(CONFIG_CRC_ITU_T)	+= crc-itu-t.o
 obj-$(CONFIG_CRC32)	+= crc32.o
+obj-$(CONFIG_CRC7)	+= crc7.o
 obj-$(CONFIG_LIBCRC32C)	+= libcrc32c.o
 obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o
 
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ g26/include/linux/crc7.h	2007-05-11 00:49:35.000000000 -0700
@@ -0,0 +1,14 @@
+#ifndef _LINUX_CRC7_H
+#define _LINUX_CRC7_H
+#include <linux/types.h>
+
+extern const u8 crc7_syndrome_table[256];
+
+static inline u8 crc7_byte(u8 crc, const u8 data)
+{
+	return crc7_syndrome_table[(crc << 1) ^ data];
+}
+
+extern u8 crc7(u8 crc, const u8 *buffer, size_t len);
+
+#endif
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ g26/lib/crc7.c	2007-05-11 00:57:30.000000000 -0700
@@ -0,0 +1,68 @@
+/*
+ *      crc7.c
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/crc7.h>
+
+
+/* Table for CRC-7 (polynomial x^7 + x^3 + 1) */
+const u8 crc7_syndrome_table[256] = {
+	0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f,
+	0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
+	0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26,
+	0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e,
+	0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d,
+	0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45,
+	0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14,
+	0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c,
+	0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b,
+	0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13,
+	0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42,
+	0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a,
+	0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69,
+	0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21,
+	0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70,
+	0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38,
+	0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e,
+	0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36,
+	0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67,
+	0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f,
+	0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
+	0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04,
+	0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55,
+	0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d,
+	0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a,
+	0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52,
+	0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03,
+	0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b,
+	0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28,
+	0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60,
+	0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31,
+	0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79
+};
+EXPORT_SYMBOL(crc7_syndrome_table);
+
+/**
+ * crc7 - update the CRC7 for the data buffer
+ * @crc:     previous CRC7 value
+ * @buffer:  data pointer
+ * @len:     number of bytes in the buffer
+ * Context: any
+ *
+ * Returns the updated CRC7 value.
+ */
+u8 crc7(u8 crc, const u8 *buffer, size_t len)
+{
+	while (len--)
+		crc = crc7_byte(crc, *buffer++);
+	return crc;
+}
+EXPORT_SYMBOL(crc7);
+
+MODULE_DESCRIPTION("CRC7 calculations");
+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] 11+ messages in thread

* [patch 2.6.22-rc4 2/7] SD 4wire bugfix
       [not found] ` <200706042025.18252.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-06-05  3:26   ` [patch 2.6.22-rc4 1/7] CRC7 support David Brownell
@ 2007-06-05  3:28   ` David Brownell
  2007-06-05  3:31   ` [patch 2.6.22-rc4 3/7] SPI "exclusive access" (experimental) David Brownell
                     ` (5 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: David Brownell @ 2007-06-05  3:28 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Mikael Starvik, Hans-Peter Nilsson, Mike Lavender, Pierre Ossman

Once upon a debug dreary, while I pondered weak and weary,
Over many a quaint and curious volume of forgotten lore--
While I nodded, nearly napping, suddenly there came a trapping,
As of some code gently thrashing, thrashing at a chip once more.
"'Tis some bugginess," I muttered, "trapping MMC_CAP_4
    Only this, and nothing more."

Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
---
Sent already to Pierre ... this should get into 2.6.22-rc5!

 drivers/mmc/core/sd.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

--- g26.orig/drivers/mmc/core/sd.c	2007-05-06 08:27:52.000000000 -0700
+++ g26/drivers/mmc/core/sd.c	2007-06-04 19:58:43.000000000 -0700
@@ -402,7 +402,7 @@ static int mmc_sd_init_card(struct mmc_h
 	/*
 	 * Switch to wider bus (if supported).
 	 */
-	if ((host->caps && MMC_CAP_4_BIT_DATA) &&
+	if ((host->caps & MMC_CAP_4_BIT_DATA) &&
 		(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
 		err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
 		if (err != MMC_ERR_NONE)

-------------------------------------------------------------------------
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] 11+ messages in thread

* [patch 2.6.22-rc4 3/7] SPI "exclusive access" (experimental)
       [not found] ` <200706042025.18252.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  2007-06-05  3:26   ` [patch 2.6.22-rc4 1/7] CRC7 support David Brownell
  2007-06-05  3:28   ` [patch 2.6.22-rc4 2/7] SD 4wire bugfix David Brownell
@ 2007-06-05  3:31   ` David Brownell
  2007-06-05  3:34   ` [patch 2.6.22-rc4 4/7] MMC headers understand SPI David Brownell
                     ` (4 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: David Brownell @ 2007-06-05  3:31 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Mikael Starvik, Hans-Peter Nilsson, Mike Lavender, Pierre Ossman

This is a draft API and reference implementation for a new SPI protocol
tweaking option, which supports more complex protocol stacks over SPI.

Examples: MMC/SD-over-SPI, wireless network chips, and hardware tools
at the JTAG/ISP level.

  - A new SPI protocol tweaking interface, giving one driver exclusive
    access to a SPI bus for transfer sequences too complex to fit into
    one spi_message.

  - An initial implementation of that, for the "bitbang" core.  I'm
    sure someone can come up with a much better implementation for this,
    which I'd expect to feed back to improve that interface.

It behaves in light testing without contention for the bus lock.

Includes secondary queue code from Jan Nikitenko <jan.nikitenko-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
to make submissions to other devices act "normally"; they're executed
when the exclusion is released.

NOT YET READY for prime time.  An alternate scheme has recently been
suggested, more amenable to IRQ-driven code that needs to start and
sustain "exclusion" in contexts that can't sleep.

---
 drivers/spi/spi_bitbang.c       |   92 +++++++++++++++++++++++++++++++++++++---
 include/linux/spi/spi.h         |   58 +++++++++++++++++++++++++
 include/linux/spi/spi_bitbang.h |    3 +
 3 files changed, 148 insertions(+), 5 deletions(-)

--- g26.orig/include/linux/spi/spi.h	2007-06-04 19:57:23.000000000 -0700
+++ g26/include/linux/spi/spi.h	2007-06-04 19:57:45.000000000 -0700
@@ -182,6 +182,9 @@ static inline void spi_unregister_driver
  *	the device whose settings are being modified.
  * @transfer: adds a message to the controller's transfer queue.
  * @cleanup: frees controller-specific state
+ * @get_exclusive: Waits until the message queue is empty, then reserves
+ *	the bus for exclusive use with that devices.
+ * @put_exclusive: Reverses the effect of @get_exclusive.
  *
  * Each SPI master controller can communicate with one or more @spi_device
  * children.  These make a small bus, sharing MOSI, MISO and SCK signals
@@ -237,6 +240,12 @@ struct spi_master {
 
 	/* called on release() to free memory provided by spi_master */
 	void			(*cleanup)(struct spi_device *spi);
+
+	/* Some protocols, like MMC or SD over SPI, need to lock a device's
+	 * chipselect for an extended period of time.
+	 */
+	int			(*get_exclusive)(struct spi_device *spi);
+	void			(*put_exclusive)(struct spi_device *spi);
 };
 
 static inline void *spi_master_get_devdata(struct spi_master *master)
@@ -538,6 +547,55 @@ spi_async(struct spi_device *spi, struct
 	return spi->master->transfer(spi, message);
 }
 
+
+#define SPI_HAS_EXCLUSIVE
+
+/**
+ * spi_get_exclusive - temporarily acquire exclusive access to a SPI bus
+ * @spi: device which will have exclusive access to the SPI bus.
+ * Context: can sleep
+ *
+ * Some SPI protocols, such as those for MMC and SD cards, involve keeping
+ * a given device's chipselect active for more complex sequences of transfers
+ * than the known-in-advance contents of one spi_message.  Drivers for such
+ * protocols should use this method and its sibling spi_put_exclusive().
+ *
+ * When spi_get_exclusive() returns success, any pending transfers on that
+ * bus will have completed, and that device's chipselect will be active.
+ * From that point until spi_put_exclusive() is called, only spi_message
+ * transfers to that device will be acted on, and the device will only be
+ * (briefly) deselected mid-message, according to by spi_transfer.cs_change.
+ *
+ * Message being sent to other devices on this bus are queued for processing
+ * after the matching put_exclusive() call.
+ *
+ * This call may only be from contexts which can sleep.  It returns zero
+ * on success, else a negative error number.
+ */
+static inline int spi_get_exclusive(struct spi_device *spi)
+{
+	if (!spi->master->get_exclusive)
+		return -EIO;
+	return spi->master->get_exclusive(spi);
+}
+
+/**
+ * spi_put_exclusive - release exclusive access to a SPI bus
+ * @spi: device which has been granted that access
+ * Context: can sleep
+ *
+ * This reverses the effect of a successful spi_get_exclusive(), and
+ * deselects the device.
+ *
+ * This call may only be from contexts which can sleep.  It is an error to
+ * call this when messages are still queued to the specified device, or
+ * from code that doesn't hold the exclusive access.
+ */
+static inline void spi_put_exclusive(struct spi_device *spi)
+{
+	spi->master->put_exclusive(spi);
+}
+
 /*---------------------------------------------------------------------------*/
 
 /* All these synchronous SPI transfer routines are utilities layered
--- g26.orig/include/linux/spi/spi_bitbang.h	2007-06-04 19:57:23.000000000 -0700
+++ g26/include/linux/spi/spi_bitbang.h	2007-06-04 19:57:45.000000000 -0700
@@ -29,6 +29,9 @@ struct spi_bitbang {
 	u8			flags;		/* extra spi->mode support */
 
 	struct spi_master	*master;
+	struct spi_device	*exclusive;
+	wait_queue_head_t	xwait;
+	struct list_head	xqueue;
 
 	/* setup_transfer() changes clock and/or wordsize to match settings
 	 * for this transfer; zeroes restore defaults from spi_device.
--- g26.orig/drivers/spi/spi_bitbang.c	2007-06-04 19:57:23.000000000 -0700
+++ g26/drivers/spi/spi_bitbang.c	2007-06-04 19:57:45.000000000 -0700
@@ -284,7 +284,6 @@ static void bitbang_work(struct work_str
 
 		m = container_of(bitbang->queue.next, struct spi_message,
 				queue);
-		list_del_init(&m->queue);
 		spin_unlock_irqrestore(&bitbang->lock, flags);
 
 		/* FIXME this is made-up ... the correct value is known to
@@ -295,7 +294,7 @@ static void bitbang_work(struct work_str
 
 		spi = m->spi;
 		tmp = 0;
-		cs_change = 1;
+		cs_change = (spi != bitbang->exclusive);
 		status = 0;
 		setup_transfer = NULL;
 
@@ -369,7 +368,6 @@ static void bitbang_work(struct work_str
 		}
 
 		m->status = status;
-		m->complete(m->context);
 
 		/* restore speed and wordsize */
 		if (setup_transfer)
@@ -377,17 +375,24 @@ static void bitbang_work(struct work_str
 
 		/* normally deactivate chipselect ... unless no error and
 		 * cs_change has hinted that the next message will probably
-		 * be for this chip too.
+		 * be for this chip too, or this device reserved the bus
 		 */
-		if (!(status == 0 && cs_change)) {
+		if (!(status == 0 && cs_change) && spi != bitbang->exclusive) {
 			ndelay(nsecs);
 			bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
 			ndelay(nsecs);
 		}
 
 		spin_lock_irqsave(&bitbang->lock, flags);
+		list_del_init(&m->queue);
+		spin_unlock_irqrestore(&bitbang->lock, flags);
+
+		m->complete(m->context);
+
+		spin_lock_irqsave(&bitbang->lock, flags);
 	}
 	bitbang->busy = 0;
+	wake_up(&bitbang->xwait);
 	spin_unlock_irqrestore(&bitbang->lock, flags);
 }
 
@@ -408,6 +413,8 @@ int spi_bitbang_transfer(struct spi_devi
 	spin_lock_irqsave(&bitbang->lock, flags);
 	if (!spi->max_speed_hz)
 		status = -ENETDOWN;
+	else if (bitbang->exclusive && bitbang->exclusive != spi)
+		list_add_tail(&m->queue, &bitbang->xqueue);
 	else {
 		list_add_tail(&m->queue, &bitbang->queue);
 		queue_work(bitbang->workqueue, &bitbang->work);
@@ -418,6 +425,75 @@ int spi_bitbang_transfer(struct spi_devi
 }
 EXPORT_SYMBOL_GPL(spi_bitbang_transfer);
 
+/*
+ * NOTE:  This current version of the exclusive access calls is not cast
+ * in stone either for interface or implementation.
+ *
+ * In particular, if it gets used very widely, it's possible that handling
+ * EBUSY returns from get_exclusive() might too annoying...
+ */
+
+static int check_empty(struct spi_bitbang *bitbang)
+{
+	unsigned long		flags;
+	int			value;
+
+	/* this "knows" that bitbang_work() leaves requests at the head
+	 * of the queue until it's done processing them.
+	 */
+	spin_lock_irqsave(&bitbang->lock, flags);
+	value = list_empty(&bitbang->queue);
+	spin_unlock_irqrestore(&bitbang->lock, flags);
+	return value;
+}
+
+static int spi_bitbang_get_exclusive(struct spi_device *spi)
+{
+	struct spi_bitbang	*bitbang;
+	int			status = 0;
+
+	bitbang = spi_master_get_devdata(spi->master);
+	might_sleep();
+
+	/* from now on, no more new messages except those to that device */
+	spin_lock_irq(&bitbang->lock);
+	if (bitbang->exclusive)
+		status = -EBUSY;		/* REVISIT better to wait! */
+	else
+		bitbang->exclusive = spi;
+	spin_unlock_irq(&bitbang->lock);
+
+	/* ... and chipselect for that device stays active */
+	if (!status) {
+		wait_event(bitbang->xwait, check_empty(bitbang));
+		bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
+	}
+	return status;
+}
+
+static void spi_bitbang_put_exclusive(struct spi_device *spi)
+{
+	struct spi_bitbang	*bitbang;
+
+	bitbang = spi_master_get_devdata(spi->master);
+	might_sleep();
+
+	if (bitbang->exclusive == spi) {
+		wait_event(bitbang->xwait, check_empty(bitbang));
+		bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
+
+		spin_lock_irq(&bitbang->lock);
+		bitbang->exclusive = NULL;
+		if (!list_empty(&bitbang->xqueue)) {
+			__list_splice(&bitbang->xqueue, &bitbang->queue);
+			INIT_LIST_HEAD(&bitbang->xqueue);
+			queue_work(bitbang->workqueue, &bitbang->work);
+		}
+		spin_unlock_irq(&bitbang->lock);
+
+	}
+}
+
 /*----------------------------------------------------------------------*/
 
 /**
@@ -454,6 +530,11 @@ int spi_bitbang_start(struct spi_bitbang
 	spin_lock_init(&bitbang->lock);
 	INIT_LIST_HEAD(&bitbang->queue);
 
+	init_waitqueue_head(&bitbang->xwait);
+	INIT_LIST_HEAD(&bitbang->xqueue);
+	bitbang->master->get_exclusive = spi_bitbang_get_exclusive;
+	bitbang->master->put_exclusive = spi_bitbang_put_exclusive;
+
 	if (!bitbang->master->transfer)
 		bitbang->master->transfer = spi_bitbang_transfer;
 	if (!bitbang->txrx_bufs) {
@@ -502,6 +583,7 @@ int spi_bitbang_stop(struct spi_bitbang 
 	spi_unregister_master(bitbang->master);
 
 	WARN_ON(!list_empty(&bitbang->queue));
+	WARN_ON(!list_empty(&bitbang->xqueue));
 
 	destroy_workqueue(bitbang->workqueue);
 

-------------------------------------------------------------------------
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] 11+ messages in thread

* [patch 2.6.22-rc4 4/7] MMC headers understand SPI
       [not found] ` <200706042025.18252.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
                     ` (2 preceding siblings ...)
  2007-06-05  3:31   ` [patch 2.6.22-rc4 3/7] SPI "exclusive access" (experimental) David Brownell
@ 2007-06-05  3:34   ` David Brownell
  2007-06-05  3:37   ` [patch 2.6.22-rc4 5/7] MMC core understands SPI David Brownell
                     ` (3 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: David Brownell @ 2007-06-05  3:34 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Mikael Starvik, Hans-Peter Nilsson, Mike Lavender, Pierre Ossman

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

 - New host capabilities bits
    * MMC_CAP_SPI, with mmc_host_is_spi() test
    * MMC_CAP_SPI_CRC, if it's ready to use CRCs
 
 - 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.

None of these changes affects current code.

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

--- g26.orig/include/linux/mmc/host.h	2007-05-06 08:28:03.000000000 -0700
+++ g26/include/linux/mmc/host.h	2007-06-04 19:58:56.000000000 -0700
@@ -90,6 +90,8 @@ 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 */
+#define MMC_CAP_SPI_CRC		(1 << 6)	/* Handles CRC option in SPI */
 
 	/* host specific block data */
 	unsigned int		max_seg_size;	/* see blk_queue_max_segment_size */
@@ -137,6 +139,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-05-06 08:28:03.000000000 -0700
+++ g26/include/linux/mmc/core.h	2007-06-04 19:58:56.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_OCR	(1 << 9)		/* OCR */
+
 /*
- * 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,22 @@ 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.  Commands return R1, with maybe
+ * more info.  Commands like SEND_CSD return a data block too, which
+ * we call 'R1D'.  Zero is an invalid type, meaning that the caller
+ * forgot to say which response type applies to this command.
+ *
+ * FIXME remove R1D; update SEND_CxD in the core.
+ */
+#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_OCR)
+#define MMC_RSP_SPI_R1D	(MMC_RSP_SPI_S1|MMC_RSP_136)
+
+#define mmc_spi_resp_type(cmd)	((cmd)->flags & (MMC_RSP_SPI_S1|MMC_RSP_BUSY|MMC_RSP_SPI_S2|MMC_RSP_SPI_OCR|MMC_RSP_136))
+
+/*
  * These are the command types.
  */
 #define mmc_cmd_type(cmd)	((cmd)->flags & MMC_CMD_MASK)
--- g26.orig/include/linux/mmc/mmc.h	2007-05-06 08:28:03.000000000 -0700
+++ g26/include/linux/mmc/mmc.h	2007-06-04 19:58:56.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 */
 
+#define R1_STATE_IDLE		0		/* resetting; maybe opendrain */
+#define R1_STATE_READY		1		/* opendrain; ident soon */
+#define R1_STATE_IDENT		2		/* opendrain; setaddr soon */
+#define R1_STATE_STBY		3		/* addressed, ready to use */
+#define R1_STATE_TRAN		4
+#define R1_STATE_DATA		5
+#define R1_STATE_RCV		6
+#define R1_STATE_PRG		7
+#define R1_STATE_DIS		8
+#define R1_STATE(x)		(((x) & 0xf) << 9)
+
+/*
+ * MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS
+ */
+#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)
+
+#define R2_SPI_CARD_LOCKED	(1 << 0)
+#define R2_SPI_WP_ERASE_SKIP	(1 << 1)	/* or lock/unlock fail */
+#define R2_SPI_ERROR		(1 << 2)
+#define R2_SPI_CC_ERROR		(1 << 3)
+#define R2_SPI_CARD_ECC_ERROR	(1 << 4)
+#define R2_SPI_WP_VIOLATION	(1 << 5)
+#define R2_SPI_ERASE_PARAM	(1 << 6)
+#define R2_SPI_OUT_OF_RANGE	(1 << 7)	/* or CSD overwrite */
+
+
 /* These are unpacked versions of the actual responses */
 
 struct _mmc_csd {
@@ -181,7 +215,7 @@ struct _mmc_csd {
  * Card Command Classes (CCC)
  */
 #define CCC_BASIC		(1<<0)	/* (0) Basic protocol functions */
-					/* (CMD0,1,2,3,4,7,9,10,12,13,15) */
+					/* (CMD0,1,2,3,4,7,9,10,12,13,15,58,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] 11+ messages in thread

* [patch 2.6.22-rc4 5/7] MMC core understands SPI
       [not found] ` <200706042025.18252.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
                     ` (3 preceding siblings ...)
  2007-06-05  3:34   ` [patch 2.6.22-rc4 4/7] MMC headers understand SPI David Brownell
@ 2007-06-05  3:37   ` David Brownell
  2007-06-05  3:38   ` [patch 2.6.22-rc4 6/7] MMC block " David Brownell
                     ` (2 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: David Brownell @ 2007-06-05  3:37 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Mikael Starvik, Hans-Peter Nilsson, Mike Lavender, Pierre Ossman

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

 - 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
    * mmc_spi_send_cid() ... works without broadcast mode

 - Use mmc_host_is_spi() so enumeration works through SPI protocols,
   not just the native (and sometimes parallel) ones

 - 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.  (Some requests are invalid over SPI.)

 - Understand that some commands act a bit differently ... notably the
   OP_COND command doesn't return the OCR, and does status differently.

The current assumption is that nobody will want a controller driver
to handle both SPI *and* the faster "native" MMC/SD protocols ... even
though some MMC controllers could do that.

Evidently the very newest MMC cards (like v4.2) differ from older ones
in how they handle things like CMD8 ... lacking specs, much less one
of the high capacity cards, this doesn't attempt to handle those cases.

Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
---
 drivers/mmc/core/core.c    |   38 ++++++++++++++---
 drivers/mmc/core/mmc.c     |   28 ++++++++----
 drivers/mmc/core/mmc_ops.c |   97 +++++++++++++++++++++++++++++++++++----------
 drivers/mmc/core/mmc_ops.h |    4 +
 drivers/mmc/core/sd.c      |   36 +++++++++++-----
 drivers/mmc/core/sd_ops.c  |   17 +++++--
 6 files changed, 166 insertions(+), 54 deletions(-)

--- g26.orig/drivers/mmc/core/mmc_ops.h	2007-05-06 08:27:52.000000000 -0700
+++ g26/drivers/mmc/core/mmc_ops.h	2007-06-04 19:58:58.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-05-09 22:59:25.000000000 -0700
+++ g26/drivers/mmc/core/core.c	2007-06-04 19:58:58.000000000 -0700
@@ -400,8 +400,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;
@@ -420,8 +425,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;
@@ -512,6 +519,19 @@ void mmc_detect_change(struct mmc_host *
 EXPORT_SYMBOL(mmc_detect_change);
 
 
+static int mmc_spi_fixup(struct mmc_host *host, u32 *ocrp)
+{
+	int err;
+
+	if (!mmc_host_is_spi(host))
+		return MMC_ERR_NONE;
+
+	err = mmc_spi_read_ocr(host, ocrp);
+	if (err == MMC_ERR_NONE);
+		err = mmc_spi_set_crc(host);
+	return err;
+}
+
 static void mmc_rescan(struct work_struct *work)
 {
 	struct mmc_host *host =
@@ -533,11 +553,13 @@ static void mmc_rescan(struct work_struc
 		mmc_power_up(host);
 		mmc_go_idle(host);
 
-		mmc_send_if_cond(host, host->ocr_avail);
+		if (!mmc_host_is_spi(host))
+			mmc_send_if_cond(host, host->ocr_avail);
 
 		err = mmc_send_app_op_cond(host, 0, &ocr);
 		if (err == MMC_ERR_NONE) {
-			if (mmc_attach_sd(host, ocr))
+			err = mmc_spi_fixup(host, &ocr);
+			if (err != MMC_ERR_NONE || mmc_attach_sd(host, ocr))
 				mmc_power_off(host);
 		} else {
 			/*
@@ -546,7 +568,9 @@ static void mmc_rescan(struct work_struc
 			 */
 			err = mmc_send_op_cond(host, 0, &ocr);
 			if (err == MMC_ERR_NONE) {
-				if (mmc_attach_mmc(host, ocr))
+				err = mmc_spi_fixup(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-05-06 08:27:52.000000000 -0700
+++ g26/drivers/mmc/core/mmc_ops.c	2007-06-04 19:58:58.000000000 -0700
@@ -27,6 +27,9 @@ static int _mmc_select_card(struct mmc_h
 
 	BUG_ON(!host);
 
+	if (mmc_host_is_spi(host))
+		return MMC_ERR_NONE;
+
 	memset(&cmd, 0, sizeof(struct mmc_command));
 
 	cmd.opcode = MMC_SELECT_CARD;
@@ -63,23 +66,30 @@ 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.
+	 * SPI hosts necessarily ignore host->chip_select.
+	 */
+	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 +105,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[2] & R1_SPI_IDLE))
+				break;
+		} else if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
 			break;
 
 		err = MMC_ERR_TIMEOUT;
@@ -110,7 +124,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,30 +174,70 @@ 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(struct mmc_host *host, unsigned rca, 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.flags = MMC_RSP_R2 | MMC_CMD_AC;
+	cmd.opcode = opcode;
+	cmd.arg = rca << 16;
+	cmd.flags = MMC_RSP_SPI_R1D | 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_csd(struct mmc_card *card, u32 *csd)
+{
+	return mmc_send_cxd(card->host, card->rca, csd, MMC_SEND_CSD);
+}
+
+int mmc_spi_send_cid(struct mmc_host *host, u32 *cid)
+{
+	return mmc_send_cxd(host, 0, cid, MMC_SEND_CID);
+}
+
+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;
+}
+
+int mmc_spi_set_crc(struct mmc_host *host)
+{
+	struct mmc_command cmd;
+
+	memset(&cmd, 0, sizeof(struct mmc_command));
+
+	cmd.opcode = MMC_SPI_CRC_ON_OFF;
+	cmd.flags = MMC_RSP_SPI_R1;
+	if (host->caps & MMC_CAP_SPI_CRC)
+		cmd.arg = 1;
+
+	return mmc_wait_for_cmd(host, &cmd, 0);
+}
+
 int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
 {
 	struct mmc_request mrq;
@@ -261,8 +315,9 @@ 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)
--- g26.orig/drivers/mmc/core/mmc.c	2007-05-06 08:27:52.000000000 -0700
+++ g26/drivers/mmc/core/mmc.c	2007-06-04 19:58:58.000000000 -0700
@@ -263,9 +263,15 @@ static int mmc_sd_init_card(struct mmc_h
 	/*
 	 * Fetch CID from card.
 	 */
-	err = mmc_all_send_cid(host, cid);
-	if (err != MMC_ERR_NONE)
-		goto err;
+	if (mmc_host_is_spi(host)) {
+		err = mmc_spi_send_cid(host, cid);
+		if (err != MMC_ERR_NONE)
+			goto err;
+	} else {
+		err = mmc_all_send_cid(host, cid);
+		if (err != MMC_ERR_NONE)
+			goto err;
+	}
 
 	if (oldcard) {
 		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
@@ -286,13 +292,15 @@ static int mmc_sd_init_card(struct mmc_h
 	}
 
 	/*
-	 * 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) {
 		/*
@@ -317,9 +325,9 @@ static int mmc_sd_init_card(struct mmc_h
 	if (err != MMC_ERR_NONE)
 		goto free_card;
 
-	if (!oldcard) {
+	if (!oldcard && !mmc_host_is_spi(host)) {
 		/*
-		 * Fetch and process extened CSD.
+		 * Fetch and process extended CSD.
 		 */
 		err = mmc_read_ext_csd(card);
 		if (err != MMC_ERR_NONE)
--- g26.orig/drivers/mmc/core/sd.c	2007-06-04 19:58:43.000000000 -0700
+++ g26/drivers/mmc/core/sd.c	2007-06-04 19:58:58.000000000 -0700
@@ -306,9 +306,19 @@ static int mmc_sd_init_card(struct mmc_h
 	/*
 	 * Fetch CID from card.
 	 */
-	err = mmc_all_send_cid(host, cid);
-	if (err != MMC_ERR_NONE)
-		goto err;
+	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);
+		if (err != MMC_ERR_NONE)
+			goto err;
+	} else {
+		err = mmc_all_send_cid(host, cid);
+		if (err != MMC_ERR_NONE)
+			goto err;
+	}
+
 
 	if (oldcard) {
 		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
@@ -328,13 +338,15 @@ static int mmc_sd_init_card(struct mmc_h
 	}
 
 	/*
-	 * Set card RCA.
+	 * For native busses:  set 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) {
 		/*
@@ -373,9 +385,11 @@ static int mmc_sd_init_card(struct mmc_h
 		/*
 		 * Fetch switch information from card.
 		 */
-		err = mmc_read_switch(card);
-		if (err != MMC_ERR_NONE)
-			goto free_card;
+		if (!mmc_host_is_spi(host)) {
+			err = mmc_read_switch(card);
+			if (err != MMC_ERR_NONE)
+				goto free_card;
+		}
 	}
 
 	/*
--- g26.orig/drivers/mmc/core/sd_ops.c	2007-05-06 08:27:52.000000000 -0700
+++ g26/drivers/mmc/core/sd_ops.c	2007-06-04 19:58:58.000000000 -0700
@@ -89,10 +89,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);
@@ -148,14 +148,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[2] & R1_SPI_IDLE))
+				break;
+		} else if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
 			break;
 
 		err = MMC_ERR_TIMEOUT;
@@ -175,6 +179,9 @@ int mmc_send_if_cond(struct mmc_host *ho
 	int err;
 	static const u8 test_pattern = 0xAA;
 
+	if (mmc_host_is_spi(host))
+		return MMC_ERR_FAILED;
+
 	/*
 	 * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
 	 * before SD_APP_OP_COND. This command will harmlessly fail for
@@ -242,7 +249,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;

-------------------------------------------------------------------------
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] 11+ messages in thread

* [patch 2.6.22-rc4 6/7] MMC block understands SPI
       [not found] ` <200706042025.18252.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
                     ` (4 preceding siblings ...)
  2007-06-05  3:37   ` [patch 2.6.22-rc4 5/7] MMC core understands SPI David Brownell
@ 2007-06-05  3:38   ` David Brownell
  2007-06-05  3:50   ` [patch 2.6.22-rc4 7/7] mmc_spi host driver David Brownell
  2007-06-05 17:13   ` [patch 2.6.22-rc4 8/7] mmc_spi cid/csd/ext_csd updates, CRCs on David Brownell
  7 siblings, 0 replies; 11+ messages in thread
From: David Brownell @ 2007-06-05  3:38 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Mikael Starvik, Hans-Peter Nilsson, Mike Lavender, Pierre Ossman

Teach 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.

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

--- g26.orig/drivers/mmc/card/block.c	2007-05-24 12:04:46.000000000 -0700
+++ g26/drivers/mmc/card/block.c	2007-06-04 19:59:00.000000000 -0700
@@ -151,7 +151,7 @@ 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))
@@ -161,7 +161,7 @@ static u32 mmc_sd_num_wr_blocks(struct m
 
 	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 +220,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 +244,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 {
@@ -303,7 +308,8 @@ static int mmc_blk_issue_rq(struct mmc_q
 
 				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",
@@ -506,7 +512,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] 11+ messages in thread

* [patch 2.6.22-rc4 7/7] mmc_spi host driver
       [not found] ` <200706042025.18252.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
                     ` (5 preceding siblings ...)
  2007-06-05  3:38   ` [patch 2.6.22-rc4 6/7] MMC block " David Brownell
@ 2007-06-05  3:50   ` David Brownell
  2007-06-05 17:13   ` [patch 2.6.22-rc4 8/7] mmc_spi cid/csd/ext_csd updates, CRCs on David Brownell
  7 siblings, 0 replies; 11+ messages in thread
From: David Brownell @ 2007-06-05  3:50 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Mikael Starvik, Hans-Peter Nilsson, Mike Lavender, Pierre Ossman

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

It's been lightly tested on MMC and SD cards, both reading and writing
ext3 filesystems (including fsck).  I didn't test the CRC mode, and
suspect that's been broken in the CSD and CID reading 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>
---
I'm not expecting this to merge in quite this form.  The CRC should
be verified to work again, and the "R1D" response type should go away
(in conjunction with MMC core updates).  The "exclusive" hooks will
likely need to be removed; they can be added when the SPI stack gets
such primitives.  In general, this version marks a big cleanup/change
from the previous monolithic code.

 drivers/mmc/host/Kconfig    |   13 
 drivers/mmc/host/Makefile   |    1 
 drivers/mmc/host/mmc_spi.c  | 1462 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/mmc_spi.h |   32 
 4 files changed, 1508 insertions(+)

--- g26.orig/drivers/mmc/host/Kconfig	2007-06-04 19:44:16.000000000 -0700
+++ g26/drivers/mmc/host/Kconfig	2007-06-04 19:59:01.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 && EXPERIMENTAL
+	select CRC7
+	select CRC_ITU_T
+	help
+	  Some systems accss MMC/SD cards using the SPI protocol instead of
+	  using an MMC/SD controller.  The disadvantage of using SPI is that
+	  it's often not as fast; its compensating advantage is that SPI is
+	  available on many systems without MMC/SD controllers.
+
+	  If unsure, or if your system has no SPI controller driver, say N.
+
--- g26.orig/drivers/mmc/host/Makefile	2007-06-04 19:44:16.000000000 -0700
+++ g26/drivers/mmc/host/Makefile	2007-06-04 19:59:01.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-06-04 19:59:01.000000000 -0700
@@ -0,0 +1,32 @@
+#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 *);
+
+	/* how long to debounce card detect, in msecs */
+	unsigned detect_delay;
+
+	/* sense switch on sd cards */
+	int (*get_ro)(struct device *);
+
+	/* power management */
+	unsigned int 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-06-04 19:59:01.000000000 -0700
@@ -0,0 +1,1462 @@
+/*
+ * mmc_spi.c - Access an SD/MMC card through a SPI master controller
+ *
+ * (C) Copyright 2005, Intec Automation,
+ *		Mike Lavender (mike@steroidmicros)
+ * (C) Copyright 2006, 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/init.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
+#include <linux/crc7.h>
+#include <linux/crc-itu-t.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.
+ *   SPI mode is a bit slower than non-parallel versions of MMC.
+ *
+ * - Likewise we don't try to detect DataFlash cards, which would
+ *   imply switching to a different driver.  Not many folk folk use
+ *   both DataFlash cards and MMC/SD cards, and Linux doesn't have
+ *   an "MMC/SD card interface" abstraction for coupling to drivers.
+ *
+ * - Protocol details, including timings, need to be audited
+ */
+
+#ifndef SPI_HAS_EXCLUSIVE
+
+/*
+ * There are two ways we can end up without the exclusive access
+ * extensions:  (a) the kernel may not have it (it's not yet ready
+ * for merging), or (b) the controller driver may not have it.  We
+ * make the former case look like the latter ... then when either
+ * applies, we just insist that the bus segment not be shared.
+ */
+
+static inline int spi_get_exclusive(struct spi_device *spi)
+{
+	return -EIO;
+}
+
+static inline void spi_put_exclusive(struct spi_device *spi)
+{
+}
+
+#endif
+
+/*
+ * Local protocol constants ... ones that shouldn't ever need
+ * to be visible to upper layer code.
+ */
+
+#define SPI_MMC_COMMAND		0x40	/* mask into mmc command */
+
+/* response tokens used to ack each block written: */
+#define SPI_MMC_RESPONSE_CODE(x)	((x) & (7 << 1))
+#define SPI_RESPONSE_ACCEPTED		(2 << 1)
+#define SPI_RESPONSE_CRC_ERR		(5 << 1)
+#define SPI_RESPONSE_WRITE_ERR		(6 << 1)
+
+/* read and write blocks start with these tokens and end with crc;
+ * on error, read tokens act like 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 CRC_GO_IDLE_STATE	0x95	/* constant CRC for GO_IDLE */
+#define CRC_NO_CRC		0x01	/* placeholder for no-crc cmds */
+
+#define	MMC_POWERCYCLE_MSECS	20		/* board-specific? */
+
+
+/* The unit for these timeouts is milliseconds.  See mmc_spi_scanbyte.  */
+#define MINI_TIMEOUT		1
+#define READ_TIMEOUT		100
+#define WRITE_TIMEOUT		250
+
+
+/****************************************************************************/
+
+/*
+ * Local Data Structures
+ */
+
+union mmc_spi_command {
+	u8 buf[7];
+	struct {
+		u8 dummy;
+		u8 code;
+		u8 addr1;
+		u8 addr2;
+		u8 addr3;
+		u8 addr4;
+		u8 crc;
+	} command;
+};
+
+
+struct mmc_spi_host {
+	struct mmc_host		*mmc;
+	struct spi_device	*spi;
+	u8			*rx_buf;
+	u8			*tx_buf;
+	u32			tx_idx;
+	u32			rx_idx;
+	u8			app_cmd;
+	u8			exclusive;
+
+	struct mmc_spi_platform_data	*pdata;
+
+	/* for bulk data transfers */
+	struct spi_transfer	token, t, crc;
+	struct spi_message	m;
+	struct spi_transfer	early_status;
+
+	/* for status readback */
+	struct spi_transfer	status;
+	struct spi_message	readback;
+
+	/* underlying controller might support dma, but we can't
+	 * rely on it being used for any particular request
+	 */
+	struct device		*dma_dev;
+	dma_addr_t		dma;		/* of mmc_spi_host */
+
+	/* pre-allocated dma-safe buffers */
+	union mmc_spi_command	command;
+	u8			data_token;
+	u8			status_byte;
+	u16			crc_val;
+	u8			response[2];
+	u8			bundled_status[2];
+
+	/* specs describe always writing ones even if we
+	 * don't think the card should care what it sees.
+	 * this block is our source of ones.
+	 */
+	u8			ones[512];
+};
+
+#ifdef	DEBUG
+static unsigned debug = 1;
+module_param(debug, uint, 0644);
+#else
+#define	debug	0
+#endif
+
+/* FIXME turn this back on by default ... needs retesting */
+#if 0
+static unsigned use_crc = 1;
+#else
+static unsigned use_crc = 0;
+#endif
+
+module_param(use_crc, uint, 0);
+
+
+/****************************************************************************/
+
+static inline int mmc_spi_readbyte(struct mmc_spi_host *host)
+{
+	int status = spi_sync(host->spi, &host->readback);
+	if (status < 0)
+		return status;
+	return host->status_byte;
+}
+
+static inline int
+mmc_spi_readbytes(struct mmc_spi_host *host, void *bytes, unsigned len)
+{
+	int status;
+	int dma_mapped = host->readback.is_dma_mapped;
+
+	host->status.rx_buf = bytes;
+	host->status.len = len;
+
+	host->readback.is_dma_mapped = 0;
+	status = spi_sync(host->spi, &host->readback);
+	host->readback.is_dma_mapped = dma_mapped;
+
+	host->status.rx_buf = &host->status_byte;
+	host->status.len = 1;
+	return status;
+}
+
+
+/* REVISIT:  is this fast enough?  these kinds of sync points could
+ * easily be offloaded to irq-ish code called by controller drivers,
+ * eliminating context switch costs.
+ *
+ * REVISIT:  after writes and erases, mmc_spi_busy() == true might be
+ * a fair hint to yield exclusive access to the card (so another driver
+ * can use the bus).  Busy-wait won't be an issue, since we already
+ * yield the CPU during all synchronous I/O calls.
+ */
+static int mmc_spi_busy(u8 byte)
+{
+	return byte == 0;
+}
+
+static int mmc_spi_delayed(u8 byte)
+{
+	return byte == 0xff;
+}
+
+static int
+mmc_spi_scanbyte(struct mmc_spi_host *host, int (*fail)(u8), unsigned delay)
+{
+	int		value;
+	unsigned	wait;
+	unsigned long	end_wait;
+
+	/*
+	 * Because we might (we will, for bitbanged SPI) be scheduled
+	 * out for extensive periods in this call, we'd get an
+	 * abundance of timeouts if we counted in jiffies on a system
+	 * with load, so instead we calculate it in the max number of
+	 * bytes we could theoretically scan before the timeout, if
+	 * everything else took zero time.
+	 *
+	 * REVISIT max_speed_hz may be a lot faster than our actual
+	 * transfer rate ...
+	 */
+	end_wait = delay * host->spi->max_speed_hz / 1000 / 8;
+
+	for (wait = 0; wait < end_wait; wait++) {
+		value = mmc_spi_readbyte(host);
+		if (value < 0)
+			return value;
+		if (!fail(value)) {
+			if (debug > 1)
+				dev_dbg(&host->spi->dev,
+					"  mmc_spi: token %02x, wait %d\n",
+					value, wait);
+			return value;
+		}
+	}
+
+	return -ETIMEDOUT;
+}
+
+/*
+ * We provide raw command status as well as mapped status:
+ *	cmd->resp[0] - mapped, like 'native' mmc/sd
+ *	cmd->resp[1] - OCR (for READ_OCR only)
+ *	cmd->resp[2] - SPI R1
+ *	cmd->resp[3] - SPI R2 (for SEND_STATUS only)
+ *
+ * REVISIT presumably we can't escape providng mapped status,
+ * even though few callers actually check for it ...
+ */
+
+static inline void mmc_spi_map_r1(struct mmc_command *cmd, u8 r1)
+{
+	u32	mapped = 0;
+
+	cmd->resp[2] = r1;
+
+	/* spi mode doesn't expose the mmc/sd state machine, but
+	 * we can at least avoid lying about the IDLE state
+	 */
+	if (!(r1 & R1_SPI_IDLE))
+		mapped |= R1_STATE(R1_STATE_STBY);
+
+	/* type 'sr' (not an error) */
+	if (r1 & R1_SPI_ERASE_RESET)
+		mapped |= R1_ERASE_RESET;
+
+	/* types 'er' or 'erx', generic cmd->error code */
+	if (r1 & (R1_SPI_ERASE_SEQ
+			| R1_SPI_ADDRESS
+			| R1_SPI_PARAMETER)) {
+		cmd->error = MMC_ERR_FAILED;
+		if (r1 & R1_SPI_ERASE_SEQ)
+			mapped |= R1_ERASE_SEQ_ERROR;
+		/* erx */
+		if (r1 & R1_SPI_ADDRESS)
+			mapped |= R1_ADDRESS_ERROR;
+		/* REVISIT how to map R1_SPI_PARAMETER?
+		 * this collides with R2_SPI_OUT_OF_RANGE...
+		 */
+		if (r1 & R1_SPI_PARAMETER)
+			mapped |= R1_OUT_OF_RANGE;
+	}
+
+	/* type 'er' with special cmd->error codes */
+	if (r1 & R1_SPI_ILLEGAL_COMMAND) {
+		cmd->error = MMC_ERR_INVALID;
+		mapped |= R1_ILLEGAL_COMMAND;
+	}
+	if (r1 & R1_SPI_COM_CRC) {
+		cmd->error = MMC_ERR_BADCRC;
+		mapped |= R1_COM_CRC_ERROR;
+	}
+
+	cmd->resp[0] = mapped;
+}
+
+static void mmc_spi_map_r2(struct mmc_command *cmd, u8 r2)
+{
+	u32	mapped = 0;
+
+	cmd->resp[3] = r2;
+	if (!r2)
+		return;
+
+	/* type 'erx' */
+	if (r2 & R2_SPI_ERROR)
+		mapped |= R1_ERROR;
+	if (r2 & R2_SPI_CC_ERROR)
+		mapped |= R1_CC_ERROR;
+	if (r2 & R2_SPI_WP_VIOLATION)
+		mapped |= R1_WP_VIOLATION;
+	if (r2 & R2_SPI_OUT_OF_RANGE)
+		mapped |= R1_OUT_OF_RANGE;
+
+	/* type 'ex' */
+	if (r2 & R2_SPI_CARD_ECC_ERROR)
+		mapped |= R1_CARD_ECC_FAILED;
+	if (r2 & R2_SPI_ERASE_PARAM)
+		mapped |= R1_ERASE_PARAM;
+
+	/* NOTE:  we never set cmd->error, that would indicate that
+	 * the SEND_STATUS command failed ...
+	 */
+
+	/* type 'sx' */
+	if (r2 & R2_SPI_CARD_LOCKED)
+		mapped |= R1_CARD_IS_LOCKED;
+	if (r2 & R2_SPI_WP_ERASE_SKIP)
+		mapped |= R1_WP_ERASE_SKIP;
+
+	cmd->resp[0] |= mapped;
+}
+
+static void mmc_spi_map_data_err(struct mmc_command *cmd, u8 token)
+{
+	cmd->resp[0] = 0;
+	mmc_spi_map_r2(cmd, (token & 0x0f) << 2);
+	if (token & 0x10)
+		cmd->resp[0] |= R1_CARD_IS_LOCKED;
+}
+
+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";
+	case MMC_RSP_SPI_R1D:	return "R1D";
+	default:		return "?";
+	}
+}
+
+static void mmc_spi_read_cXd(struct mmc_spi_host *host, struct mmc_command *cmd)
+{
+	int status;
+
+	/* skip till first byte of data block */
+	status = mmc_spi_scanbyte(host, mmc_spi_delayed, READ_TIMEOUT);
+
+	/* if we found the data block, read it; else report timeout */
+	if (status == SPI_TOKEN_SINGLE) {
+
+		spi_message_init(&host->m);
+		memset(&host->t, 0, sizeof(host->t));
+		spi_message_add_tail(&host->t, &host->m);
+
+		memset(cmd->resp, 0xff, 16);
+		host->t.tx_buf = cmd->resp;
+		host->t.rx_buf = cmd->resp;
+		host->t.len = 16;
+
+		/* REVISIT 16 bit CRC ... ? */
+
+		status = spi_sync(host->spi, &host->m);
+		if (status < 0)
+			cmd->error = MMC_ERR_FAILED;
+		else {
+			be32_to_cpus(&cmd->resp[0]);
+			be32_to_cpus(&cmd->resp[1]);
+			be32_to_cpus(&cmd->resp[2]);
+			be32_to_cpus(&cmd->resp[3]);
+		}
+	} else {
+		if (status > 0)
+			mmc_spi_map_data_err(cmd, status);
+		dev_dbg(&host->spi->dev,
+			"mmc_spi: read cXd, %02x %d \n",
+			status & 0xff, status);
+		cmd->error = MMC_ERR_TIMEOUT;
+	}
+}
+
+static int
+mmc_spi_response_get(struct mmc_spi_host *host, struct mmc_command *cmd)
+{
+	int value;
+	char tag[32];
+
+	snprintf(tag, sizeof tag, "  ... %sCMD%d response SPI_%s",
+		host->app_cmd ? "A" : "",
+		cmd->opcode, maptype(cmd));
+
+	if (cmd->opcode == MMC_STOP_TRANSMISSION) {
+		/*
+		 * We can't tell whether we read block data or the
+		 * command reply, so to cope with trash data during
+		 * the latency, we just read in 14 bytes (8 would be
+		 * enough according to the MMC spec; SD doesn't say)
+		 * after the command and fake a clean reply.  We could
+		 * avoid this if we saved what the card sent us while
+		 * we sent the command, and treat it like a normal
+		 * response if we didn't get a SPI_TOKEN_SINGLE.
+		 */
+		(void) mmc_spi_readbytes(host, host->command.buf,
+				sizeof host->command.buf);
+		(void) mmc_spi_readbytes(host, host->command.buf,
+				sizeof host->command.buf);
+		value = 0;
+	} else
+		value = mmc_spi_scanbyte(host, mmc_spi_delayed, MINI_TIMEOUT);
+
+	if (value < 0) {
+		dev_dbg(&host->spi->dev,
+			"%s: response error, %d\n", tag, value);
+		cmd->error = MMC_ERR_FAILED;
+		return value;
+	}
+
+	if (value & 0x80) {
+		dev_dbg(&host->spi->dev, "%s: INVALID RESPONSE, %02x\n",
+					tag, host->response[0]);
+		cmd->error = MMC_ERR_FAILED;
+		return -EBADR;
+	}
+
+	host->response[0] = value;
+	host->response[1] = 0;
+
+	cmd->error = MMC_ERR_NONE;
+	mmc_spi_map_r1(cmd, host->response[0]);
+
+	switch (mmc_spi_resp_type(cmd)) {
+
+	/* SPI R1B == R1 + busy; STOP_TRANSMISSION and less-useful stuff */
+	case MMC_RSP_SPI_R1B:
+		/* REVISIT this might be a shorter timeout */
+		(void) mmc_spi_scanbyte(host, mmc_spi_busy, WRITE_TIMEOUT);
+		break;
+
+	/* SPI R1D == R1 + 16 bytes data; SEND_CSD, SEND_CID
+	 * for non-SPI this would be R2 type status
+	 *
+	 * FIXME remove the R1D "message type"; mmc core should
+	 * explicitly issue a data stage, handling CRCs right.
+	 */
+	case MMC_RSP_SPI_R1D:
+		mmc_spi_read_cXd(host, cmd);
+		dev_dbg(&host->spi->dev, "%s: status %d\n",
+			tag, cmd->error);
+		return 0;
+
+	/* SPI R2 == R1 + second status byte; SEND_STATUS */
+	case MMC_RSP_SPI_R2:
+		host->response[1] = mmc_spi_readbyte(host);
+		mmc_spi_map_r2(cmd, host->response[1]);
+		cmd->resp[0] |= R1_READY_FOR_DATA;
+		/* we aren't reporting that SEND_STATUS failed... */
+		cmd->error = MMC_ERR_NONE;
+		break;
+
+	/* SPI R3 == R1 + OCR; used only by READ_OCR */
+	case MMC_RSP_SPI_R3:
+		(void) mmc_spi_readbytes(host, &cmd->resp[1], 4);
+		be32_to_cpus(&cmd->resp[1]);
+		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));
+		BUG();
+	}
+
+	if (!host->app_cmd
+			&& cmd->error == MMC_ERR_NONE
+			&& cmd->opcode == MMC_APP_CMD) {
+		host->app_cmd = 1;
+		cmd->resp[0] |= R1_APP_CMD;
+	}
+	dev_dbg(&host->spi->dev,
+		"%s: resp %02x.%02x\n",
+		tag,
+		host->response[1],
+		host->response[0]);
+	return 0;
+}
+
+/* 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, u8 crc,
+		struct mmc_command *cmd)
+{
+	union mmc_spi_command	*tx = &host->command;
+	u32			arg = cmd->arg;
+	int			status;
+	unsigned		opcode = cmd->opcode;
+
+	/* after 8 clock cycles the card is ready, and done previous cmd */
+	tx->command.dummy = 0xFF;
+
+	tx->command.code = opcode | SPI_MMC_COMMAND;
+	tx->command.addr1 = (u8)(arg >> 24);
+	tx->command.addr2 = (u8)(arg >> 16);
+	tx->command.addr3 = (u8)(arg >> 8);
+	tx->command.addr4 = (u8)arg;
+	if (use_crc)
+		tx->command.crc = (crc7(0, &tx->command.code, 5) << 1) | 0x01;
+	else
+		tx->command.crc = crc;
+
+	dev_dbg(&host->spi->dev, "  mmc_spi: %sCMD%d, MMC_SPI_%s\n",
+		host->app_cmd ? "A" : "", opcode,
+		maptype(cmd));
+
+	status = spi_write(host->spi, tx->buf, sizeof(tx->buf));
+	if (status < 0) {
+		dev_dbg(&host->spi->dev, "  ... write returned %d\n", status);
+		cmd->error = MMC_ERR_FAILED;
+		return status;
+	}
+
+	status = mmc_spi_response_get(host, cmd);
+
+	/*
+	 * If this was part of a successful request with a stop-part,
+	 * our caller signals the request as done.
+	 */
+	if (status == 0 && mrq->stop == NULL)
+		mmc_request_done(host->mmc, mrq);
+	return status;
+}
+
+/* Set up data message: first byte, data block (filled in later), then CRC. */
+static void
+mmc_spi_setup_data_message(
+	struct mmc_spi_host	*host,
+	int			multiple,
+	enum dma_data_direction	direction)
+{
+	struct device		*dma_dev = host->dma_dev;
+	struct spi_transfer	*t;
+
+	spi_message_init(&host->m);
+	if (dma_dev)
+		host->m.is_dma_mapped = 1;
+
+	/* for reads, we (manually) skip 0xff bytes before finding
+	 * the token; for writes, we issue it ourselves.
+	 */
+	if (direction == DMA_TO_DEVICE) {
+		t = &host->token;
+		memset(t, 0, sizeof *t);
+		t->len = 1;
+		if (multiple)
+			host->data_token = SPI_TOKEN_MULTI_WRITE;
+		else
+			host->data_token = SPI_TOKEN_SINGLE;
+		t->tx_buf = &host->data_token;
+		spi_message_add_tail(t, &host->m);
+	}
+
+	t = &host->t;
+	memset(t, 0, sizeof *t);
+	spi_message_add_tail(t, &host->m);
+
+	t = &host->crc;
+	memset(t, 0, sizeof *t);
+	t->len = 2;
+	spi_message_add_tail(t, &host->m);
+
+	t = &host->early_status;
+	memset(t, 0, sizeof *t);
+
+	/*
+	 * If this is a read, we need room for 0xFF (for
+	 * N\subscript{AC}) and the next token.  For a write, we need
+	 * room just for the one-byte data response.
+	 */
+	t->len = (direction == DMA_FROM_DEVICE) ? 2 : 1;
+	spi_message_add_tail(t, &host->m);
+	t->rx_buf = host->bundled_status;
+	if (dma_dev)
+		t->rx_dma = host->dma
+			+ offsetof(struct mmc_spi_host, bundled_status);
+
+/* REVISIT don't need extra memory, just txbuf = rxbuf */
+	t->tx_buf = &host->ones;
+	if (dma_dev)
+		t->tx_dma = host->dma
+			+ offsetof(struct mmc_spi_host, ones);
+
+	t = &host->crc;
+
+	/* REVISIT crc wordsize == 2, avoid byteswap issues ... */
+
+	if (direction == DMA_TO_DEVICE) {
+		host->crc_val = CRC_NO_CRC;
+		t->tx_buf = &host->crc_val;
+		if (dma_dev) {
+			host->token.tx_dma = host->dma
+				+ offsetof(struct mmc_spi_host, data_token);
+			t->tx_dma = host->dma
+				+ offsetof(struct mmc_spi_host, crc_val);
+		}
+	} else {
+		t->rx_buf = &host->crc_val;
+		if (dma_dev)
+			t->rx_dma = host->dma
+				+ offsetof(struct mmc_spi_host, crc_val);
+
+		/* while we read data, write all-ones */
+/* REVISIT don't need extra memory, just txbuf = rxbuf */
+		t->tx_buf = host->t.tx_buf = &host->ones;
+		if (dma_dev)
+			t->tx_dma = host->t.tx_dma = host->dma
+				+ offsetof(struct mmc_spi_host, ones);
+	}
+}
+
+
+static inline int resp2status(u8 write_resp)
+{
+	switch (SPI_MMC_RESPONSE_CODE(write_resp)) {
+	case SPI_RESPONSE_ACCEPTED:
+		return 0;
+	case SPI_RESPONSE_CRC_ERR:
+	case SPI_RESPONSE_WRITE_ERR:
+		/* host shall then issue MMC_STOP_TRANSMISSION */
+		return -EIO;
+	default:
+		return -EILSEQ;
+	}
+}
+
+/*
+ * 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;
+
+	if (data->flags & MMC_DATA_READ) {
+		direction = DMA_FROM_DEVICE;
+		multiple = (cmd->opcode == MMC_READ_MULTIPLE_BLOCK);
+
+		/*
+		 * We need to scan for the SPI_TOKEN_SINGLE token
+		 * *before* we issue the first (of multiple)
+		 * spi_messages reading the data plus two extra bytes,
+		 * (implying N\subscript{AC} and the *next* token), so
+		 * to avoid looking at garbage from an earlier
+		 * command, we reset the location where we'll read in
+		 * subsequent tokens.
+		 */
+		host->bundled_status[0] = 0xff;
+		host->bundled_status[1] = 0xff;
+	} else {
+		direction = DMA_TO_DEVICE;
+		multiple = (cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK);
+	}
+	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;
+			dma_sync_single_for_device(host->dma_dev,
+				host->dma, sizeof *host, direction);
+		}
+
+		/* allow pio too, with kmap handling any 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 && status == 0) {
+			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) {
+				int	response;
+
+				if (use_crc)
+					host->crc_val = cpu_to_be16(
+						crc_itu_t(0, t->tx_buf,
+							t->len));
+
+				status = spi_sync(spi, &host->m);
+				if (status != 0) {
+					dev_dbg(&spi->dev,
+						"write error (%d)\n", status);
+					break;
+				}
+
+				/*
+				 * 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 tells
+				 * that the transmission was ok and
+				 * whether *earlier* writes succeeded;
+				 * see the standard.
+				 */
+				response = host->bundled_status[0];
+				if (response == 0xff) {
+					dev_dbg(&spi->dev,
+						"missing card response\n");
+					status = -EIO;
+					break;
+				}
+
+				if (response < 0)
+					status = response;
+				else
+					status = resp2status(response);
+				if (status != 0) {
+					dev_dbg(&spi->dev,
+						"write error %02x (%d)\n",
+						response, status);
+					break;
+				}
+				t->tx_buf += t->len;
+				if (dma_dev)
+					t->tx_dma += t->len;
+
+				/* Wait until not busy.  */
+				response = mmc_spi_scanbyte(host, mmc_spi_busy,
+							WRITE_TIMEOUT);
+			} else {
+				/*
+				 * Note that N\subscript{AC} is *at
+				 * least* one byte, so we should never
+				 * see a card that responds in the
+				 * first byte (otherwise defined to be
+				 * 0xff).  Right, better assert that...
+				 */
+				if (host->bundled_status[0] != 0xff) {
+					/* We either make it an error or
+					 * somehow wedge in the next byte,
+					 * because that's then the first
+					 * in the block we read.  */
+					dev_dbg(&spi->dev,
+						"too-early card "
+						"response %02x %02x\n",
+						host->bundled_status[0],
+						host->bundled_status[1]);
+					status = -EIO;
+					break;
+				}
+
+				if (host->bundled_status[1] != 0xff)
+					status = host->bundled_status[1];
+				else
+					status = mmc_spi_scanbyte(host,
+							mmc_spi_delayed,
+							READ_TIMEOUT);
+
+				if (status == SPI_TOKEN_SINGLE) {
+					status = spi_sync(spi, &host->m);
+					dma_sync_single_for_cpu(host->dma_dev,
+						host->dma, sizeof *host,
+						direction);
+				} else {
+					/* we've read extra garbage */
+					dev_dbg(&spi->dev,
+						"read error %02x\n",
+						status);
+					mmc_spi_map_data_err(cmd, status);
+					if (cmd->error == MMC_ERR_NONE)
+						cmd->error = MMC_ERR_FAILED;
+					break;
+				}
+
+				if (use_crc) {
+					u16 crc = crc_itu_t(0, t->rx_buf,
+						t->len);
+					be16_to_cpus(&host->crc_val);
+					if (host->crc_val != crc) {
+						cmd->error = MMC_ERR_BADCRC;
+						cmd->resp[0] = R1_COM_CRC_ERROR;
+						dev_dbg(&spi->dev,
+							"read - crc error: "
+							"crc_val=0x%04x, "
+							"computed=0x%04x "
+							"len=%d\n",
+							host->crc_val, crc,
+							t->len);
+						break;
+					}
+				}
+
+				t->rx_buf += t->len;
+				if (dma_dev)
+					t->rx_dma += t->len;
+			}
+
+			data->bytes_xfered += t->len;
+			if (status == 0) {
+				status = host->m.status;
+				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 (cmd->error == MMC_ERR_NONE)
+				cmd->error = MMC_ERR_FAILED;
+			break;
+		}
+	}
+
+	if (direction == DMA_TO_DEVICE && multiple) {
+		u8 dat = SPI_TOKEN_STOP_TRAN;
+		ssize_t status;
+
+		/*
+		 * Send the SPI_TOKEN_STOP_TRAN byte, ignoring the
+		 * received byte (presumably 0xff).
+		 */
+		status = spi_write(spi, &dat, 1);
+		if (status < 0) {
+			cmd->error = MMC_ERR_FAILED;
+			return;
+		}
+
+		/*
+		 * Then skip the next byte.  This is the maximum
+		 * non-busy time before the first busy-token.  If we
+		 * don't skip it, we'll mistake it for the end of the
+		 * busy-period.  See also "Figure 5-28" in SanDisk's
+		 * ProdManRS-MMCv1.3.pdf; this is marked "X"
+		 * (undefined value) of length N\subscript{BR} (min 0
+		 * max 1 byte).
+		 */
+		status = mmc_spi_readbyte(host);
+		if (status < 0) {
+			cmd->error = MMC_ERR_FAILED;
+			return;
+		}
+
+		/*
+		 * Now wait until the end of the busy period.  If
+		 * N\subscript{BR} (see ref above) was 0, we'll never
+		 * see any busy period.
+		 */
+		status = mmc_spi_scanbyte(host, mmc_spi_busy, WRITE_TIMEOUT);
+		if (status < 0) {
+			cmd->error = MMC_ERR_FAILED;
+			return;
+		}
+	}
+}
+
+/* handle three MMC request stages:  commmand (required), data, and stop */
+static int
+mmc_spi_command_do(struct mmc_spi_host *host, struct mmc_request *mrq)
+{
+	int status;
+
+	status = mmc_spi_command_send(host, mrq, CRC_NO_CRC, mrq->cmd);
+
+	if (status == 0 && mrq->data)
+		mmc_spi_data_do(host, mrq->cmd, mrq->data,
+				mrq->data->blksz);
+	if (mrq->stop) {
+		if (status == 0) {
+			status = mmc_spi_command_send(host, mrq,
+					CRC_NO_CRC, mrq->stop);
+			if (status != 0)
+				mrq->stop->error = MMC_ERR_FAILED;
+			mmc_request_done(host->mmc, mrq);
+		}
+	}
+
+	/*
+	 * No need to wait before the next command.  The minimum time
+	 * between commands is handled by the "dummy" byte in the command.
+	 */
+
+	return status;
+}
+
+/*
+ * RESET is started when cmd->opcode == MMC_GO_IDLE_STATE.  This can't
+ * be just a standard protocol operation.
+ *
+ * We expect the MMC core to be responsible for "very hard" resets like
+ * power cycling the card; there's no accessible reset signal.
+ */
+static int
+mmc_spi_initialize(struct mmc_spi_host *host, struct mmc_request *mrq)
+{
+	struct mmc_command	*cmd = mrq->cmd;
+	int			status;
+	int			could_invert_cs = 0;
+
+	host->app_cmd = 0;
+
+	/* Try to be very sure any previous command has completed;
+	 * wait till not-busy, skip debris from any old commands.
+	 */
+	(void) mmc_spi_scanbyte(host, mmc_spi_busy, WRITE_TIMEOUT);
+	(void) mmc_spi_readbytes(host, host->command.buf,
+			sizeof host->command.buf);
+
+	/*
+	 * Do a burst with chipselect deactivated.  We need to do this
+	 * to meet the requirement of 74 clock cycles with chipselect
+	 * high before CMD0.  (Section 6.4.1, in "Simplified Physical
+	 * Layer Specification 2.0".)  Some cards are particularly
+	 * needy of this (e.g. Viking "SD256") while most others don't
+	 * seem to care.  Note that it's not enough to deactivate
+	 * chipselect without toggling the clock.  Beware of the hack:
+	 * we "know" that mmc_spi_readbytes uses the host->status
+	 * spi_transfer.
+	 *
+	 * Note that this is one of two places MMC/SD plays games with
+	 * the SPI chipselect.  The other 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 a warning; most cards work without it. */
+		dev_warn(&host->spi->dev,
+				"can't invert the active chip-select level\n");
+	else
+		could_invert_cs = 1;
+
+	(void) mmc_spi_readbytes(host, host->command.buf,
+			sizeof host->command.buf);
+	(void) mmc_spi_readbytes(host, host->command.buf,
+			sizeof host->command.buf);
+
+	host->spi->mode &= ~SPI_CS_HIGH;
+	if (spi_setup(host->spi) != 0) {
+		/* Wot, we can't get (back) the same setup we had before? */
+		dev_err(&host->spi->dev,
+				"failed restoring chip-select level\n");
+		return -EIO;
+	}
+
+	/* issue software reset */
+	cmd->arg = 0;
+	status = mmc_spi_command_send(host, mrq, CRC_GO_IDLE_STATE, cmd);
+	if (status < 0) {
+		/* Maybe:
+		 *  - there's no card present
+		 *  - the card isn't seated correctly (bad contacts)
+		 *  - it didn't leave MMC/SD mode
+		 *  - there's other confusion in the card state
+		 *
+		 * Power cycling the card ought to help a lot.
+		 * At any rate, let's try again.
+		 */
+		status = mmc_spi_command_send(host, mrq,
+				CRC_GO_IDLE_STATE, cmd);
+		if (status < 0)
+			dev_dbg(&host->spi->dev,
+				"can't initialize; no card%s?\n",
+				could_invert_cs
+					? ""
+					: " or chip-select error");
+	}
+	return status;
+}
+
+/****************************************************************************/
+
+/*
+ * 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;
+	struct mmc_command	*cmd;
+
+	/* MMC core and layered drivers *MUST* issue SPI-aware commands */
+	cmd = mrq->stop;
+	if (cmd && !mmc_spi_resp_type(cmd)) {
+		dev_dbg(&host->spi->dev, "bogus STOP command\n");
+		dump_stack();
+		cmd->error = MMC_ERR_FAILED;
+		goto fail;
+	}
+
+	cmd = mrq->cmd;
+	if (!mmc_spi_resp_type(cmd)) {
+		dev_dbg(&host->spi->dev, "bogus command\n");
+		dump_stack();
+		cmd->error = MMC_ERR_FAILED;
+		goto fail;
+	}
+
+	/* insist on exclusive SPI bus access during MMC operations */
+	if (host->exclusive) {
+		status = spi_get_exclusive(host->spi);
+		if (status < 0) {
+			dev_err(&host->spi->dev,
+					"can't get exclusive access, %d\n",
+					status);
+			cmd->error = MMC_ERR_FAILED;
+			goto out_done;
+		}
+	}
+
+	if (!host->app_cmd) {
+		if (cmd->opcode == MMC_GO_IDLE_STATE)
+			status = mmc_spi_initialize(host, mrq);
+		else
+			status = mmc_spi_command_do(host, mrq);
+	} else {
+		status = mmc_spi_command_do(host, mrq);
+		host->app_cmd = 0;
+	}
+
+out_done:
+	/*
+	 * We can't report faults while holding the bus lock,
+	 * else retries from the mmc core couldn't grab it...
+	 */
+	if (host->exclusive)
+		spi_put_exclusive(host->spi);
+
+fail:
+	/*
+	 * If status was ok, the request would have been signalled done by
+	 * mmc_spi_command_do.
+	 */
+	if (status < 0)
+		mmc_request_done(host->mmc, mrq);
+}
+
+
+static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct mmc_spi_host *host = mmc_priv(mmc);
+
+	if (host->pdata && host->pdata->setpower) {
+		dev_dbg(&host->spi->dev,
+			"mmc_spi:  power %08x\n", ios->vdd);
+		host->pdata->setpower(&host->spi->dev, ios->vdd);
+		if (ios->vdd)
+			msleep(MMC_POWERCYCLE_MSECS);
+		else {
+			int mres, xres;
+
+			/*
+			 * We need to put all spi wires to low,
+			 * otherwise MMC card is powered from them
+			 * regardless it's power supply state!
+			 *
+			 * We leave chipselect active low,
+			 * switch mode to 0 to put clock to low.
+			 */
+			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 (host->exclusive) {
+				xres = spi_get_exclusive(host->spi);
+				if (xres < 0) {
+					dev_err(&host->spi->dev,
+						"can't get exclusive "
+						"access: %d\n",
+						xres);
+					/* FIXME handle error */
+				}
+			} else
+				xres = -EIO;
+
+			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!
+			 */
+			msleep(MMC_POWERCYCLE_MSECS);
+			if (xres == 0)
+				spi_put_exclusive(host->spi);
+			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");
+			}
+		}
+	}
+
+	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 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);
+
+	mmc_detect_change(mmc, msecs_to_jiffies(host->pdata->detect_delay));
+	return IRQ_HANDLED;
+}
+
+static int mmc_spi_probe(struct spi_device *spi)
+{
+	struct mmc_host		*mmc;
+	struct mmc_spi_host	*host;
+	int			status;
+	int			power_manageable = 1;
+	int			exclusive = 0;
+
+	/* 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
+	 * the difference between SPI_MODE_0 and SPI_MODE_3 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 if 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.
+	 */
+	status = spi_get_exclusive(spi);
+	if (status == 0) {
+		exclusive = 1;
+		spi_put_exclusive(spi);
+	} else 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");
+	}
+
+	mmc = mmc_alloc_host(sizeof *host, &spi->dev);
+	if (!mmc)
+		return -ENOMEM;
+
+	mmc->ops = &mmc_spi_ops;
+
+	/* 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;
+	if (use_crc)
+		mmc->caps |= MMC_CAP_SPI_CRC;
+
+	/* 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->exclusive = exclusive;
+	memset(host->ones, 0xff, sizeof host->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;
+	}
+
+	dev_set_drvdata(&spi->dev, mmc);
+
+	/* setup message for status readback/write-ones */
+	spi_message_init(&host->readback);
+	spi_message_add_tail(&host->status, &host->readback);
+	host->status.tx_buf = host->ones;
+	host->status.rx_buf = &host->status_byte;
+	host->status.len = 1;
+
+	if (spi->master->cdev.dev->dma_mask) {
+		host->dma_dev = spi->master->cdev.dev;
+		host->dma = dma_map_single(host->dma_dev, host,
+				sizeof *host, DMA_BIDIRECTIONAL);
+#ifdef	CONFIG_HIGHMEM
+		dev_dbg(&spi->dev, "highmem + dma-or-pio ...\n");
+#endif
+	}
+
+	if (host->pdata && host->pdata->init) {
+		/* we should call platform init before setpower */
+		status = host->pdata->init(&spi->dev,
+				mmc_spi_detect_irq, mmc);
+		if (status != 0)
+			goto fail_glue_init;
+	}
+
+	/* Once card enters SPI mode it stays that way till power cycled.
+	 * Power cycling can be used as a hard reset for fault recovery.
+	 */
+	if (!host->pdata || !host->pdata->setpower)
+		power_manageable = 0;
+	else
+		host->pdata->setpower(&spi->dev, 0);
+
+	status = mmc_add_host(mmc);
+	if (status != 0)
+		goto fail_add_host;
+
+	dev_info(&spi->dev, "SD/MMC host %s%s%s\n",
+			mmc->class_dev.bus_id,
+			use_crc ? ", use CRCs" : "",
+			power_manageable ? ", card poweron/off" : "");
+	return 0;
+
+fail_add_host:
+	mmc_remove_host (mmc);
+	if (host->dma_dev)
+		dma_unmap_single(host->dma_dev, host->dma,
+				sizeof *host, DMA_BIDIRECTIONAL);
+fail_glue_init:
+	mmc_free_host(mmc);
+	dev_set_drvdata(&spi->dev, NULL);
+	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) {
+		mmc_remove_host(mmc);
+		host = mmc_priv(mmc);
+
+		if (host->pdata && host->pdata->exit)
+			host->pdata->exit(&spi->dev, mmc);
+		if (host->dma_dev)
+			dma_unmap_single(host->dma_dev, host->dma,
+				sizeof *host, DMA_BIDIRECTIONAL);
+
+		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] 11+ messages in thread

* [patch 2.6.22-rc4 8/7] mmc_spi cid/csd/ext_csd updates, CRCs on
       [not found] ` <200706042025.18252.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
                     ` (6 preceding siblings ...)
  2007-06-05  3:50   ` [patch 2.6.22-rc4 7/7] mmc_spi host driver David Brownell
@ 2007-06-05 17:13   ` David Brownell
       [not found]     ` <200706051013.44971.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  7 siblings, 1 reply; 11+ messages in thread
From: David Brownell @ 2007-06-05 17:13 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Mikael Starvik, Hans-Peter Nilsson, Mike Lavender, Pierre Ossman

Fix some of the "register"/descriptor access glitches in the
preceding patches:

  - Updated internal routines:
      * previous mmc_send_cxd() renamed to 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

  - Modified mmc_send_ext_csd() now uses mmc_send_cxd_data() helper

  - New mmc_send_csd() and mmc_spi_send_cid() routines now use one
    of those helper routines based on whether they're native or SPI;

  - Remove ugly "R1D" response pseudo-type for SPI

  - Make it OK for MMC cards to try SEND_EXT_CSD; v4+ needs that

  - Turn CRCs back on by default with the SPI protocol

So this resolves most of the technical issues I know about, leaving
nontechnical ones like "is this code clean enough" or "is this how
we want to solve that problem".

Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>
---
 drivers/mmc/core/mmc.c     |    2 
 drivers/mmc/core/mmc_ops.c |   93 +++++++++++++++++++++++++++++++++------------
 drivers/mmc/host/mmc_spi.c |   59 ----------------------------
 include/linux/mmc/core.h   |    8 ---
 4 files changed, 72 insertions(+), 90 deletions(-)

--- g26.orig/drivers/mmc/core/mmc_ops.c	2007-06-04 19:58:58.000000000 -0700
+++ g26/drivers/mmc/core/mmc_ops.c	2007-06-05 10:05:20.000000000 -0700
@@ -175,7 +175,7 @@ int mmc_set_relative_addr(struct mmc_car
 }
 
 static int
-mmc_send_cxd(struct mmc_host *host, unsigned rca, u32 *cxd, int opcode)
+mmc_send_cxd_native(struct mmc_host *host, unsigned rca, u32 *cxd, int opcode)
 {
 	int err;
 	struct mmc_command cmd;
@@ -187,7 +187,7 @@ mmc_send_cxd(struct mmc_host *host, unsi
 
 	cmd.opcode = opcode;
 	cmd.arg = rca << 16;
-	cmd.flags = MMC_RSP_SPI_R1D | MMC_RSP_R2 | MMC_CMD_AC;
+	cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
 
 	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
 	if (err != MMC_ERR_NONE)
@@ -198,16 +198,6 @@ mmc_send_cxd(struct mmc_host *host, unsi
 	return MMC_ERR_NONE;
 }
 
-int mmc_send_csd(struct mmc_card *card, u32 *csd)
-{
-	return mmc_send_cxd(card->host, card->rca, csd, MMC_SEND_CSD);
-}
-
-int mmc_spi_send_cid(struct mmc_host *host, u32 *cid)
-{
-	return mmc_send_cxd(host, 0, cid, MMC_SEND_CID);
-}
-
 int mmc_spi_read_ocr(struct mmc_host *host, u32 *ocrp)
 {
 	struct mmc_command cmd;
@@ -238,17 +228,22 @@ int mmc_spi_set_crc(struct mmc_host *hos
 	return mmc_wait_for_cmd(host, &cmd, 0);
 }
 
-int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
+struct mmc_cxd {
+	struct mmc_card	*card;		/* optional */
+	void		*buf;
+	unsigned	len;
+	u32		opcode;
+	u32		arg;
+	unsigned	flags;
+};
+
+static int mmc_send_cxd_data(struct mmc_host *host, struct mmc_cxd *cxd)
 {
 	struct mmc_request mrq;
 	struct mmc_command cmd;
 	struct mmc_data data;
 	struct scatterlist sg;
 
-	BUG_ON(!card);
-	BUG_ON(!card->host);
-	BUG_ON(!ext_csd);
-
 	memset(&mrq, 0, sizeof(struct mmc_request));
 	memset(&cmd, 0, sizeof(struct mmc_command));
 	memset(&data, 0, sizeof(struct mmc_data));
@@ -256,21 +251,22 @@ int mmc_send_ext_csd(struct mmc_card *ca
 	mrq.cmd = &cmd;
 	mrq.data = &data;
 
-	cmd.opcode = MMC_SEND_EXT_CSD;
-	cmd.arg = 0;
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+	cmd.opcode = cxd->opcode;
+	cmd.arg = cxd->arg;
+	cmd.flags = cxd->flags;
 
-	data.blksz = 512;
+	data.blksz = cxd->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, cxd->buf, cxd->len);
 
-	mmc_set_data_timeout(&data, card, 0);
+	if (cxd->card)
+		mmc_set_data_timeout(&data, cxd->card, 0);
 
-	mmc_wait_for_req(card->host, &mrq);
+	mmc_wait_for_req(host, &mrq);
 
 	if (cmd.error != MMC_ERR_NONE)
 		return cmd.error;
@@ -280,6 +276,55 @@ int mmc_send_ext_csd(struct mmc_card *ca
 	return MMC_ERR_NONE;
 }
 
+int mmc_send_csd(struct mmc_card *card, u32 *csd)
+{
+	struct mmc_cxd	cxd;
+
+	if (!mmc_host_is_spi(card->host))
+		return mmc_send_cxd_native(card->host, card->rca,
+				csd, MMC_SEND_CSD);
+
+	cxd.card = card;
+	cxd.buf = csd;
+	cxd.len = 16;
+	cxd.opcode = MMC_SEND_CSD;
+	cxd.arg = 0;
+	cxd.flags = MMC_RSP_SPI_R1;
+
+	return mmc_send_cxd_data(card->host, &cxd);
+}
+
+int mmc_spi_send_cid(struct mmc_host *host, u32 *cid)
+{
+	struct mmc_cxd	cxd;
+
+	if (!mmc_host_is_spi(host))
+		return mmc_send_cxd_native(host, 0, cid, MMC_SEND_CID);
+
+	cxd.card = NULL;
+	cxd.buf = cid;
+	cxd.len = 16;
+	cxd.opcode = MMC_SEND_CID;
+	cxd.arg = 0;
+	cxd.flags = MMC_RSP_SPI_R1;
+
+	return mmc_send_cxd_data(host, &cxd);
+}
+
+int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
+{
+	struct mmc_cxd	cxd;
+
+	cxd.card = card;
+	cxd.buf = ext_csd;
+	cxd.len = 512;
+	cxd.opcode = MMC_SEND_EXT_CSD;
+	cxd.arg = 0;
+	cxd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+	return mmc_send_cxd_data(card->host, &cxd);
+}
+
 int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
 {
 	int err;
--- g26.orig/drivers/mmc/host/mmc_spi.c	2007-06-04 19:59:01.000000000 -0700
+++ g26/drivers/mmc/host/mmc_spi.c	2007-06-05 10:05:20.000000000 -0700
@@ -181,13 +181,7 @@ module_param(debug, uint, 0644);
 #define	debug	0
 #endif
 
-/* FIXME turn this back on by default ... needs retesting */
-#if 0
 static unsigned use_crc = 1;
-#else
-static unsigned use_crc = 0;
-#endif
-
 module_param(use_crc, uint, 0);
 
 
@@ -384,51 +378,10 @@ static char *maptype(struct mmc_command 
 	case MMC_RSP_SPI_R1B:	return "R1B";
 	case MMC_RSP_SPI_R2:	return "R2";
 	case MMC_RSP_SPI_R3:	return "R3";
-	case MMC_RSP_SPI_R1D:	return "R1D";
 	default:		return "?";
 	}
 }
 
-static void mmc_spi_read_cXd(struct mmc_spi_host *host, struct mmc_command *cmd)
-{
-	int status;
-
-	/* skip till first byte of data block */
-	status = mmc_spi_scanbyte(host, mmc_spi_delayed, READ_TIMEOUT);
-
-	/* if we found the data block, read it; else report timeout */
-	if (status == SPI_TOKEN_SINGLE) {
-
-		spi_message_init(&host->m);
-		memset(&host->t, 0, sizeof(host->t));
-		spi_message_add_tail(&host->t, &host->m);
-
-		memset(cmd->resp, 0xff, 16);
-		host->t.tx_buf = cmd->resp;
-		host->t.rx_buf = cmd->resp;
-		host->t.len = 16;
-
-		/* REVISIT 16 bit CRC ... ? */
-
-		status = spi_sync(host->spi, &host->m);
-		if (status < 0)
-			cmd->error = MMC_ERR_FAILED;
-		else {
-			be32_to_cpus(&cmd->resp[0]);
-			be32_to_cpus(&cmd->resp[1]);
-			be32_to_cpus(&cmd->resp[2]);
-			be32_to_cpus(&cmd->resp[3]);
-		}
-	} else {
-		if (status > 0)
-			mmc_spi_map_data_err(cmd, status);
-		dev_dbg(&host->spi->dev,
-			"mmc_spi: read cXd, %02x %d \n",
-			status & 0xff, status);
-		cmd->error = MMC_ERR_TIMEOUT;
-	}
-}
-
 static int
 mmc_spi_response_get(struct mmc_spi_host *host, struct mmc_command *cmd)
 {
@@ -486,18 +439,6 @@ mmc_spi_response_get(struct mmc_spi_host
 		(void) mmc_spi_scanbyte(host, mmc_spi_busy, WRITE_TIMEOUT);
 		break;
 
-	/* SPI R1D == R1 + 16 bytes data; SEND_CSD, SEND_CID
-	 * for non-SPI this would be R2 type status
-	 *
-	 * FIXME remove the R1D "message type"; mmc core should
-	 * explicitly issue a data stage, handling CRCs right.
-	 */
-	case MMC_RSP_SPI_R1D:
-		mmc_spi_read_cXd(host, cmd);
-		dev_dbg(&host->spi->dev, "%s: status %d\n",
-			tag, cmd->error);
-		return 0;
-
 	/* SPI R2 == R1 + second status byte; SEND_STATUS */
 	case MMC_RSP_SPI_R2:
 		host->response[1] = mmc_spi_readbyte(host);
--- g26.orig/include/linux/mmc/core.h	2007-06-04 19:58:56.000000000 -0700
+++ g26/include/linux/mmc/core.h	2007-06-05 10:05:20.000000000 -0700
@@ -53,19 +53,15 @@ struct mmc_command {
 
 /*
  * These are the SPI response types.  Commands return R1, with maybe
- * more info.  Commands like SEND_CSD return a data block too, which
- * we call 'R1D'.  Zero is an invalid type, meaning that the caller
+ * more info.  Zero is an invalid type, meaning that the caller
  * forgot to say which response type applies to this command.
- *
- * FIXME remove R1D; update SEND_CxD in the core.
  */
 #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_OCR)
-#define MMC_RSP_SPI_R1D	(MMC_RSP_SPI_S1|MMC_RSP_136)
 
-#define mmc_spi_resp_type(cmd)	((cmd)->flags & (MMC_RSP_SPI_S1|MMC_RSP_BUSY|MMC_RSP_SPI_S2|MMC_RSP_SPI_OCR|MMC_RSP_136))
+#define mmc_spi_resp_type(cmd)	((cmd)->flags & (MMC_RSP_SPI_S1|MMC_RSP_BUSY|MMC_RSP_SPI_S2|MMC_RSP_SPI_OCR))
 
 /*
  * These are the command types.
--- g26.orig/drivers/mmc/core/mmc.c	2007-06-04 19:58:58.000000000 -0700
+++ g26/drivers/mmc/core/mmc.c	2007-06-05 10:05:20.000000000 -0700
@@ -325,7 +325,7 @@ static int mmc_sd_init_card(struct mmc_h
 	if (err != MMC_ERR_NONE)
 		goto free_card;
 
-	if (!oldcard && !mmc_host_is_spi(host)) {
+	if (!oldcard) {
 		/*
 		 * Fetch and process extended CSD.
 		 */

-------------------------------------------------------------------------
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] 11+ messages in thread

* Re: [patch 2.6.22-rc4 8/7] mmc_spi cid/csd/ext_csd updates, CRCs on
       [not found]     ` <200706051013.44971.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-06-09 20:55       ` Pierre Ossman
       [not found]         ` <466B13C5.3050502-p3sGCRWkH8CeZLLa646FqQ@public.gmane.org>
  0 siblings, 1 reply; 11+ messages in thread
From: Pierre Ossman @ 2007-06-09 20:55 UTC (permalink / raw)
  To: David Brownell
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Mikael Starvik, Hans-Peter Nilsson, Mike Lavender

David Brownell wrote:
> Fix some of the "register"/descriptor access glitches in the
> preceding patches:
> 
>   - Updated internal routines:
>       * previous mmc_send_cxd() renamed to 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
> 
>   - Modified mmc_send_ext_csd() now uses mmc_send_cxd_data() helper
> 
>   - New mmc_send_csd() and mmc_spi_send_cid() routines now use one
>     of those helper routines based on whether they're native or SPI;
> 
>   - Remove ugly "R1D" response pseudo-type for SPI
> 
>   - Make it OK for MMC cards to try SEND_EXT_CSD; v4+ needs that
> 
>   - Turn CRCs back on by default with the SPI protocol
> 
> So this resolves most of the technical issues I know about, leaving
> nontechnical ones like "is this code clean enough" or "is this how
> we want to solve that problem".
> 
> Signed-off-by: David Brownell <dbrownell-Rn4VEauK+AKRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>

Instead of me reviewing the broken set of patches, can you merge these fixes in
and resend the set?

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] 11+ messages in thread

* Re: [patch 2.6.22-rc4 8/7] mmc_spi cid/csd/ext_csd updates, CRCs on
       [not found]         ` <466B13C5.3050502-p3sGCRWkH8CeZLLa646FqQ@public.gmane.org>
@ 2007-06-10 19:43           ` David Brownell
  0 siblings, 0 replies; 11+ messages in thread
From: David Brownell @ 2007-06-10 19:43 UTC (permalink / raw)
  To: Pierre Ossman
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Mikael Starvik, Hans-Peter Nilsson, Mike Lavender

On Saturday 09 June 2007, Pierre Ossman wrote:

> Instead of me reviewing the broken set of patches, can you merge these fixes in
> and resend the set?

Well, "broken" is a bit excessive.  :)

But I'll send an update against rc4 plus two patches now found
in the MM tree:  git-mmc (notable change, the card lock/unlock
facility), and the crc7 patch.

- 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] 11+ messages in thread

end of thread, other threads:[~2007-06-10 19:43 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-06-05  3:25 [patch 2.6.22-rc4 0/7] latest MMC-over-SPI patchset David Brownell
     [not found] ` <200706042025.18252.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-06-05  3:26   ` [patch 2.6.22-rc4 1/7] CRC7 support David Brownell
2007-06-05  3:28   ` [patch 2.6.22-rc4 2/7] SD 4wire bugfix David Brownell
2007-06-05  3:31   ` [patch 2.6.22-rc4 3/7] SPI "exclusive access" (experimental) David Brownell
2007-06-05  3:34   ` [patch 2.6.22-rc4 4/7] MMC headers understand SPI David Brownell
2007-06-05  3:37   ` [patch 2.6.22-rc4 5/7] MMC core understands SPI David Brownell
2007-06-05  3:38   ` [patch 2.6.22-rc4 6/7] MMC block " David Brownell
2007-06-05  3:50   ` [patch 2.6.22-rc4 7/7] mmc_spi host driver David Brownell
2007-06-05 17:13   ` [patch 2.6.22-rc4 8/7] mmc_spi cid/csd/ext_csd updates, CRCs on David Brownell
     [not found]     ` <200706051013.44971.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-06-09 20:55       ` Pierre Ossman
     [not found]         ` <466B13C5.3050502-p3sGCRWkH8CeZLLa646FqQ@public.gmane.org>
2007-06-10 19:43           ` 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).