mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 14:34:27 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			7564 lines
		
	
	
		
			188 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			7564 lines
		
	
	
		
			188 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
This is a port of the MMC-SPI driver from 2.6.24.3
 | 
						|
--mb
 | 
						|
 | 
						|
 | 
						|
--- a/drivers/mmc/host/Kconfig
 | 
						|
+++ b/drivers/mmc/host/Kconfig
 | 
						|
@@ -100,3 +100,16 @@
 | 
						|
           To compile this driver as a module, choose M here: the
 | 
						|
 	  module will be called tifm_sd.
 | 
						|
 
 | 
						|
+config MMC_SPI
 | 
						|
+	tristate "MMC/SD over SPI (EXPERIMENTAL)"
 | 
						|
+	depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL
 | 
						|
+	select CRC7
 | 
						|
+	select CRC_ITU_T
 | 
						|
+	help
 | 
						|
+	  Some systems accss MMC/SD cards using a SPI controller instead of
 | 
						|
+	  using a "native" MMC/SD controller.  This has a disadvantage of
 | 
						|
+	  being relatively high overhead, but a compensating advantage of
 | 
						|
+	  working on many systems without dedicated MMC/SD controllers.
 | 
						|
+
 | 
						|
+	  If unsure, or if your system has no SPI master driver, say N.
 | 
						|
+
 | 
						|
--- a/drivers/mmc/host/Makefile
 | 
						|
+++ b/drivers/mmc/host/Makefile
 | 
						|
@@ -15,4 +15,5 @@
 | 
						|
 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
 | 
						|
+++ b/drivers/mmc/host/mmc_spi.c
 | 
						|
@@ -0,0 +1,1419 @@
 | 
						|
+/*
 | 
						|
+ * mmc_spi.c - Access SD/MMC cards through SPI master controllers
 | 
						|
+ *
 | 
						|
+ * (C) Copyright 2005, Intec Automation,
 | 
						|
+ *		Mike Lavender (mike@steroidmicros)
 | 
						|
+ * (C) Copyright 2006-2007, David Brownell
 | 
						|
+ * (C) Copyright 2007, Axis Communications,
 | 
						|
+ *		Hans-Peter Nilsson (hp@axis.com)
 | 
						|
+ * (C) Copyright 2007, ATRON electronic GmbH,
 | 
						|
+ *		Jan Nikitenko <jan.nikitenko@gmail.com>
 | 
						|
+ *
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or modify
 | 
						|
+ * it under the terms of the GNU General Public License as published by
 | 
						|
+ * the Free Software Foundation; either version 2 of the License, or
 | 
						|
+ * (at your option) any later version.
 | 
						|
+ *
 | 
						|
+ * This program is distributed in the hope that it will be useful,
 | 
						|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
+ * GNU General Public License for more details.
 | 
						|
+ *
 | 
						|
+ * You should have received a copy of the GNU General Public License
 | 
						|
+ * along with this program; if not, write to the Free Software
 | 
						|
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
						|
+ */
 | 
						|
+#include <linux/hrtimer.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+#include <linux/bio.h>
 | 
						|
+#include <linux/dma-mapping.h>
 | 
						|
+#include <linux/crc7.h>
 | 
						|
+#include <linux/crc-itu-t.h>
 | 
						|
+#include <linux/scatterlist.h>
 | 
						|
+
 | 
						|
+#include <linux/mmc/host.h>
 | 
						|
+#include <linux/mmc/mmc.h>		/* for R1_SPI_* bit values */
 | 
						|
+
 | 
						|
+#include <linux/spi/spi.h>
 | 
						|
+#include <linux/spi/mmc_spi.h>
 | 
						|
+
 | 
						|
+#include <asm/unaligned.h>
 | 
						|
+
 | 
						|
+
 | 
						|
+#define sg_page(sg)	(sg)->page
 | 
						|
+
 | 
						|
+
 | 
						|
+/* NOTES:
 | 
						|
+ *
 | 
						|
+ * - For now, we won't try to interoperate with a real mmc/sd/sdio
 | 
						|
+ *   controller, although some of them do have hardware support for
 | 
						|
+ *   SPI protocol.  The main reason for such configs would be mmc-ish
 | 
						|
+ *   cards like DataFlash, which don't support that "native" protocol.
 | 
						|
+ *
 | 
						|
+ *   We don't have a "DataFlash/MMC/SD/SDIO card slot" abstraction to
 | 
						|
+ *   switch between driver stacks, and in any case if "native" mode
 | 
						|
+ *   is available, it will be faster and hence preferable.
 | 
						|
+ *
 | 
						|
+ * - MMC depends on a different chipselect management policy than the
 | 
						|
+ *   SPI interface currently supports for shared bus segments:  it needs
 | 
						|
+ *   to issue multiple spi_message requests with the chipselect active,
 | 
						|
+ *   using the results of one message to decide the next one to issue.
 | 
						|
+ *
 | 
						|
+ *   Pending updates to the programming interface, this driver expects
 | 
						|
+ *   that it not share the bus with other drivers (precluding conflicts).
 | 
						|
+ *
 | 
						|
+ * - We tell the controller to keep the chipselect active from the
 | 
						|
+ *   beginning of an mmc_host_ops.request until the end.  So beware
 | 
						|
+ *   of SPI controller drivers that mis-handle the cs_change flag!
 | 
						|
+ *
 | 
						|
+ *   However, many cards seem OK with chipselect flapping up/down
 | 
						|
+ *   during that time ... at least on unshared bus segments.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Local protocol constants, internal to data block protocols.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/* Response tokens used to ack each block written: */
 | 
						|
+#define SPI_MMC_RESPONSE_CODE(x)	((x) & 0x1f)
 | 
						|
+#define SPI_RESPONSE_ACCEPTED		((2 << 1)|1)
 | 
						|
+#define SPI_RESPONSE_CRC_ERR		((5 << 1)|1)
 | 
						|
+#define SPI_RESPONSE_WRITE_ERR		((6 << 1)|1)
 | 
						|
+
 | 
						|
+/* Read and write blocks start with these tokens and end with crc;
 | 
						|
+ * on error, read tokens act like a subset of R2_SPI_* values.
 | 
						|
+ */
 | 
						|
+#define SPI_TOKEN_SINGLE	0xfe	/* single block r/w, multiblock read */
 | 
						|
+#define SPI_TOKEN_MULTI_WRITE	0xfc	/* multiblock write */
 | 
						|
+#define SPI_TOKEN_STOP_TRAN	0xfd	/* terminate multiblock write */
 | 
						|
+
 | 
						|
+#define MMC_SPI_BLOCKSIZE	512
 | 
						|
+
 | 
						|
+
 | 
						|
+/* These fixed timeouts come from the latest SD specs, which say to ignore
 | 
						|
+ * the CSD values.  The R1B value is for card erase (e.g. the "I forgot the
 | 
						|
+ * card's password" scenario); it's mostly applied to STOP_TRANSMISSION after
 | 
						|
+ * reads which takes nowhere near that long.  Older cards may be able to use
 | 
						|
+ * shorter timeouts ... but why bother?
 | 
						|
+ */
 | 
						|
+#define readblock_timeout	ktime_set(0, 100 * 1000 * 1000)
 | 
						|
+#define writeblock_timeout	ktime_set(0, 250 * 1000 * 1000)
 | 
						|
+#define r1b_timeout		ktime_set(3, 0)
 | 
						|
+
 | 
						|
+
 | 
						|
+/****************************************************************************/
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Local Data Structures
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/* "scratch" is per-{command,block} data exchanged with the card */
 | 
						|
+struct scratch {
 | 
						|
+	u8			status[29];
 | 
						|
+	u8			data_token;
 | 
						|
+	__be16			crc_val;
 | 
						|
+};
 | 
						|
+
 | 
						|
+struct mmc_spi_host {
 | 
						|
+	struct mmc_host		*mmc;
 | 
						|
+	struct spi_device	*spi;
 | 
						|
+
 | 
						|
+	unsigned char		power_mode;
 | 
						|
+	u16			powerup_msecs;
 | 
						|
+
 | 
						|
+	struct mmc_spi_platform_data	*pdata;
 | 
						|
+
 | 
						|
+	/* for bulk data transfers */
 | 
						|
+	struct spi_transfer	token, t, crc, early_status;
 | 
						|
+	struct spi_message	m;
 | 
						|
+
 | 
						|
+	/* for status readback */
 | 
						|
+	struct spi_transfer	status;
 | 
						|
+	struct spi_message	readback;
 | 
						|
+
 | 
						|
+	/* underlying DMA-aware controller, or null */
 | 
						|
+	struct device		*dma_dev;
 | 
						|
+
 | 
						|
+	/* buffer used for commands and for message "overhead" */
 | 
						|
+	struct scratch		*data;
 | 
						|
+	dma_addr_t		data_dma;
 | 
						|
+
 | 
						|
+	/* Specs say to write ones most of the time, even when the card
 | 
						|
+	 * has no need to read its input data; and many cards won't care.
 | 
						|
+	 * This is our source of those ones.
 | 
						|
+	 */
 | 
						|
+	void			*ones;
 | 
						|
+	dma_addr_t		ones_dma;
 | 
						|
+};
 | 
						|
+
 | 
						|
+
 | 
						|
+/****************************************************************************/
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * MMC-over-SPI protocol glue, used by the MMC stack interface
 | 
						|
+ */
 | 
						|
+
 | 
						|
+static inline int mmc_cs_off(struct mmc_spi_host *host)
 | 
						|
+{
 | 
						|
+	/* chipselect will always be inactive after setup() */
 | 
						|
+	return spi_setup(host->spi);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int
 | 
						|
+mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len)
 | 
						|
+{
 | 
						|
+	int status;
 | 
						|
+
 | 
						|
+	if (len > sizeof(*host->data)) {
 | 
						|
+		WARN_ON(1);
 | 
						|
+		return -EIO;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	host->status.len = len;
 | 
						|
+
 | 
						|
+	if (host->dma_dev)
 | 
						|
+		dma_sync_single_for_device(host->dma_dev,
 | 
						|
+				host->data_dma, sizeof(*host->data),
 | 
						|
+				DMA_FROM_DEVICE);
 | 
						|
+
 | 
						|
+	status = spi_sync(host->spi, &host->readback);
 | 
						|
+
 | 
						|
+	if (host->dma_dev)
 | 
						|
+		dma_sync_single_for_cpu(host->dma_dev,
 | 
						|
+				host->data_dma, sizeof(*host->data),
 | 
						|
+				DMA_FROM_DEVICE);
 | 
						|
+
 | 
						|
+	return status;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int
 | 
						|
+mmc_spi_skip(struct mmc_spi_host *host, ktime_t timeout, unsigned n, u8 byte)
 | 
						|
+{
 | 
						|
+	u8		*cp = host->data->status;
 | 
						|
+
 | 
						|
+	timeout = ktime_add(timeout, ktime_get());
 | 
						|
+
 | 
						|
+	while (1) {
 | 
						|
+		int		status;
 | 
						|
+		unsigned	i;
 | 
						|
+
 | 
						|
+		status = mmc_spi_readbytes(host, n);
 | 
						|
+		if (status < 0)
 | 
						|
+			return status;
 | 
						|
+
 | 
						|
+		for (i = 0; i < n; i++) {
 | 
						|
+			if (cp[i] != byte)
 | 
						|
+				return cp[i];
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/* REVISIT investigate msleep() to avoid busy-wait I/O
 | 
						|
+		 * in at least some cases.
 | 
						|
+		 */
 | 
						|
+		if (ktime_to_ns(ktime_sub(ktime_get(), timeout)) > 0)
 | 
						|
+			break;
 | 
						|
+	}
 | 
						|
+	return -ETIMEDOUT;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static inline int
 | 
						|
+mmc_spi_wait_unbusy(struct mmc_spi_host *host, ktime_t timeout)
 | 
						|
+{
 | 
						|
+	return mmc_spi_skip(host, timeout, sizeof(host->data->status), 0);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int mmc_spi_readtoken(struct mmc_spi_host *host)
 | 
						|
+{
 | 
						|
+	return mmc_spi_skip(host, readblock_timeout, 1, 0xff);
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Note that for SPI, cmd->resp[0] is not the same data as "native" protocol
 | 
						|
+ * hosts return!  The low byte holds R1_SPI bits.  The next byte may hold
 | 
						|
+ * R2_SPI bits ... for SEND_STATUS, or after data read errors.
 | 
						|
+ *
 | 
						|
+ * cmd->resp[1] holds any four-byte response, for R3 (READ_OCR) and on
 | 
						|
+ * newer cards R7 (IF_COND).
 | 
						|
+ */
 | 
						|
+
 | 
						|
+static char *maptype(struct mmc_command *cmd)
 | 
						|
+{
 | 
						|
+	switch (mmc_spi_resp_type(cmd)) {
 | 
						|
+	case MMC_RSP_SPI_R1:	return "R1";
 | 
						|
+	case MMC_RSP_SPI_R1B:	return "R1B";
 | 
						|
+	case MMC_RSP_SPI_R2:	return "R2/R5";
 | 
						|
+	case MMC_RSP_SPI_R3:	return "R3/R4/R7";
 | 
						|
+	default:		return "?";
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* return zero, else negative errno after setting cmd->error */
 | 
						|
+static int mmc_spi_response_get(struct mmc_spi_host *host,
 | 
						|
+		struct mmc_command *cmd, int cs_on)
 | 
						|
+{
 | 
						|
+	u8	*cp = host->data->status;
 | 
						|
+	u8	*end = cp + host->t.len;
 | 
						|
+	int	value = 0;
 | 
						|
+	char	tag[32];
 | 
						|
+
 | 
						|
+	snprintf(tag, sizeof(tag), "  ... CMD%d response SPI_%s",
 | 
						|
+		cmd->opcode, maptype(cmd));
 | 
						|
+
 | 
						|
+	/* Except for data block reads, the whole response will already
 | 
						|
+	 * be stored in the scratch buffer.  It's somewhere after the
 | 
						|
+	 * command and the first byte we read after it.  We ignore that
 | 
						|
+	 * first byte.  After STOP_TRANSMISSION command it may include
 | 
						|
+	 * two data bits, but otherwise it's all ones.
 | 
						|
+	 */
 | 
						|
+	cp += 8;
 | 
						|
+	while (cp < end && *cp == 0xff)
 | 
						|
+		cp++;
 | 
						|
+
 | 
						|
+	/* Data block reads (R1 response types) may need more data... */
 | 
						|
+	if (cp == end) {
 | 
						|
+		unsigned	i;
 | 
						|
+
 | 
						|
+		cp = host->data->status;
 | 
						|
+
 | 
						|
+		/* Card sends N(CR) (== 1..8) bytes of all-ones then one
 | 
						|
+		 * status byte ... and we already scanned 2 bytes.
 | 
						|
+		 *
 | 
						|
+		 * REVISIT block read paths use nasty byte-at-a-time I/O
 | 
						|
+		 * so it can always DMA directly into the target buffer.
 | 
						|
+		 * It'd probably be better to memcpy() the first chunk and
 | 
						|
+		 * avoid extra i/o calls...
 | 
						|
+		 */
 | 
						|
+		for (i = 2; i < 9; i++) {
 | 
						|
+			value = mmc_spi_readbytes(host, 1);
 | 
						|
+			if (value < 0)
 | 
						|
+				goto done;
 | 
						|
+			if (*cp != 0xff)
 | 
						|
+				goto checkstatus;
 | 
						|
+		}
 | 
						|
+		value = -ETIMEDOUT;
 | 
						|
+		goto done;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+checkstatus:
 | 
						|
+	if (*cp & 0x80) {
 | 
						|
+		dev_dbg(&host->spi->dev, "%s: INVALID RESPONSE, %02x\n",
 | 
						|
+					tag, *cp);
 | 
						|
+		value = -EBADR;
 | 
						|
+		goto done;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	cmd->resp[0] = *cp++;
 | 
						|
+	cmd->error = 0;
 | 
						|
+
 | 
						|
+	/* Status byte: the entire seven-bit R1 response.  */
 | 
						|
+	if (cmd->resp[0] != 0) {
 | 
						|
+		if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS
 | 
						|
+					| R1_SPI_ILLEGAL_COMMAND)
 | 
						|
+				& cmd->resp[0])
 | 
						|
+			value = -EINVAL;
 | 
						|
+		else if (R1_SPI_COM_CRC & cmd->resp[0])
 | 
						|
+			value = -EILSEQ;
 | 
						|
+		else if ((R1_SPI_ERASE_SEQ | R1_SPI_ERASE_RESET)
 | 
						|
+				& cmd->resp[0])
 | 
						|
+			value = -EIO;
 | 
						|
+		/* else R1_SPI_IDLE, "it's resetting" */
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	switch (mmc_spi_resp_type(cmd)) {
 | 
						|
+
 | 
						|
+	/* SPI R1B == R1 + busy; STOP_TRANSMISSION (for multiblock reads)
 | 
						|
+	 * and less-common stuff like various erase operations.
 | 
						|
+	 */
 | 
						|
+	case MMC_RSP_SPI_R1B:
 | 
						|
+		/* maybe we read all the busy tokens already */
 | 
						|
+		while (cp < end && *cp == 0)
 | 
						|
+			cp++;
 | 
						|
+		if (cp == end)
 | 
						|
+			mmc_spi_wait_unbusy(host, r1b_timeout);
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	/* SPI R2 == R1 + second status byte; SEND_STATUS
 | 
						|
+	 * SPI R5 == R1 + data byte; IO_RW_DIRECT
 | 
						|
+	 */
 | 
						|
+	case MMC_RSP_SPI_R2:
 | 
						|
+		cmd->resp[0] |= *cp << 8;
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	/* SPI R3, R4, or R7 == R1 + 4 bytes */
 | 
						|
+	case MMC_RSP_SPI_R3:
 | 
						|
+		cmd->resp[1] = be32_to_cpu(get_unaligned((u32 *)cp));
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	/* SPI R1 == just one status byte */
 | 
						|
+	case MMC_RSP_SPI_R1:
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	default:
 | 
						|
+		dev_dbg(&host->spi->dev, "bad response type %04x\n",
 | 
						|
+				mmc_spi_resp_type(cmd));
 | 
						|
+		if (value >= 0)
 | 
						|
+			value = -EINVAL;
 | 
						|
+		goto done;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (value < 0)
 | 
						|
+		dev_dbg(&host->spi->dev, "%s: resp %04x %08x\n",
 | 
						|
+			tag, cmd->resp[0], cmd->resp[1]);
 | 
						|
+
 | 
						|
+	/* disable chipselect on errors and some success cases */
 | 
						|
+	if (value >= 0 && cs_on)
 | 
						|
+		return value;
 | 
						|
+done:
 | 
						|
+	if (value < 0)
 | 
						|
+		cmd->error = value;
 | 
						|
+	mmc_cs_off(host);
 | 
						|
+	return value;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* Issue command and read its response.
 | 
						|
+ * Returns zero on success, negative for error.
 | 
						|
+ *
 | 
						|
+ * On error, caller must cope with mmc core retry mechanism.  That
 | 
						|
+ * means immediate low-level resubmit, which affects the bus lock...
 | 
						|
+ */
 | 
						|
+static int
 | 
						|
+mmc_spi_command_send(struct mmc_spi_host *host,
 | 
						|
+		struct mmc_request *mrq,
 | 
						|
+		struct mmc_command *cmd, int cs_on)
 | 
						|
+{
 | 
						|
+	struct scratch		*data = host->data;
 | 
						|
+	u8			*cp = data->status;
 | 
						|
+	u32			arg = cmd->arg;
 | 
						|
+	int			status;
 | 
						|
+	struct spi_transfer	*t;
 | 
						|
+
 | 
						|
+	/* We can handle most commands (except block reads) in one full
 | 
						|
+	 * duplex I/O operation before either starting the next transfer
 | 
						|
+	 * (data block or command) or else deselecting the card.
 | 
						|
+	 *
 | 
						|
+	 * First, write 7 bytes:
 | 
						|
+	 *  - an all-ones byte to ensure the card is ready
 | 
						|
+	 *  - opcode byte (plus start and transmission bits)
 | 
						|
+	 *  - four bytes of big-endian argument
 | 
						|
+	 *  - crc7 (plus end bit) ... always computed, it's cheap
 | 
						|
+	 *
 | 
						|
+	 * We init the whole buffer to all-ones, which is what we need
 | 
						|
+	 * to write while we're reading (later) response data.
 | 
						|
+	 */
 | 
						|
+	memset(cp++, 0xff, sizeof(data->status));
 | 
						|
+
 | 
						|
+	*cp++ = 0x40 | cmd->opcode;
 | 
						|
+	*cp++ = (u8)(arg >> 24);
 | 
						|
+	*cp++ = (u8)(arg >> 16);
 | 
						|
+	*cp++ = (u8)(arg >> 8);
 | 
						|
+	*cp++ = (u8)arg;
 | 
						|
+	*cp++ = (crc7(0, &data->status[1], 5) << 1) | 0x01;
 | 
						|
+
 | 
						|
+	/* Then, read up to 13 bytes (while writing all-ones):
 | 
						|
+	 *  - N(CR) (== 1..8) bytes of all-ones
 | 
						|
+	 *  - status byte (for all response types)
 | 
						|
+	 *  - the rest of the response, either:
 | 
						|
+	 *      + nothing, for R1 or R1B responses
 | 
						|
+	 *	+ second status byte, for R2 responses
 | 
						|
+	 *	+ four data bytes, for R3 and R7 responses
 | 
						|
+	 *
 | 
						|
+	 * Finally, read some more bytes ... in the nice cases we know in
 | 
						|
+	 * advance how many, and reading 1 more is always OK:
 | 
						|
+	 *  - N(EC) (== 0..N) bytes of all-ones, before deselect/finish
 | 
						|
+	 *  - N(RC) (== 1..N) bytes of all-ones, before next command
 | 
						|
+	 *  - N(WR) (== 1..N) bytes of all-ones, before data write
 | 
						|
+	 *
 | 
						|
+	 * So in those cases one full duplex I/O of at most 21 bytes will
 | 
						|
+	 * handle the whole command, leaving the card ready to receive a
 | 
						|
+	 * data block or new command.  We do that whenever we can, shaving
 | 
						|
+	 * CPU and IRQ costs (especially when using DMA or FIFOs).
 | 
						|
+	 *
 | 
						|
+	 * There are two other cases, where it's not generally practical
 | 
						|
+	 * to rely on a single I/O:
 | 
						|
+	 *
 | 
						|
+	 *  - R1B responses need at least N(EC) bytes of all-zeroes.
 | 
						|
+	 *
 | 
						|
+	 *    In this case we can *try* to fit it into one I/O, then
 | 
						|
+	 *    maybe read more data later.
 | 
						|
+	 *
 | 
						|
+	 *  - Data block reads are more troublesome, since a variable
 | 
						|
+	 *    number of padding bytes precede the token and data.
 | 
						|
+	 *      + N(CX) (== 0..8) bytes of all-ones, before CSD or CID
 | 
						|
+	 *      + N(AC) (== 1..many) bytes of all-ones
 | 
						|
+	 *
 | 
						|
+	 *    In this case we currently only have minimal speedups here:
 | 
						|
+	 *    when N(CR) == 1 we can avoid I/O in response_get().
 | 
						|
+	 */
 | 
						|
+	if (cs_on && (mrq->data->flags & MMC_DATA_READ)) {
 | 
						|
+		cp += 2;	/* min(N(CR)) + status */
 | 
						|
+		/* R1 */
 | 
						|
+	} else {
 | 
						|
+		cp += 10;	/* max(N(CR)) + status + min(N(RC),N(WR)) */
 | 
						|
+		if (cmd->flags & MMC_RSP_SPI_S2)	/* R2/R5 */
 | 
						|
+			cp++;
 | 
						|
+		else if (cmd->flags & MMC_RSP_SPI_B4)	/* R3/R4/R7 */
 | 
						|
+			cp += 4;
 | 
						|
+		else if (cmd->flags & MMC_RSP_BUSY)	/* R1B */
 | 
						|
+			cp = data->status + sizeof(data->status);
 | 
						|
+		/* else:  R1 (most commands) */
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	dev_dbg(&host->spi->dev, "  mmc_spi: CMD%d, resp %s\n",
 | 
						|
+		cmd->opcode, maptype(cmd));
 | 
						|
+
 | 
						|
+	/* send command, leaving chipselect active */
 | 
						|
+	spi_message_init(&host->m);
 | 
						|
+
 | 
						|
+	t = &host->t;
 | 
						|
+	memset(t, 0, sizeof(*t));
 | 
						|
+	t->tx_buf = t->rx_buf = data->status;
 | 
						|
+	t->tx_dma = t->rx_dma = host->data_dma;
 | 
						|
+	t->len = cp - data->status;
 | 
						|
+	t->cs_change = 1;
 | 
						|
+	spi_message_add_tail(t, &host->m);
 | 
						|
+
 | 
						|
+	if (host->dma_dev) {
 | 
						|
+		host->m.is_dma_mapped = 1;
 | 
						|
+		dma_sync_single_for_device(host->dma_dev,
 | 
						|
+				host->data_dma, sizeof(*host->data),
 | 
						|
+				DMA_BIDIRECTIONAL);
 | 
						|
+	}
 | 
						|
+	status = spi_sync(host->spi, &host->m);
 | 
						|
+
 | 
						|
+	if (host->dma_dev)
 | 
						|
+		dma_sync_single_for_cpu(host->dma_dev,
 | 
						|
+				host->data_dma, sizeof(*host->data),
 | 
						|
+				DMA_BIDIRECTIONAL);
 | 
						|
+	if (status < 0) {
 | 
						|
+		dev_dbg(&host->spi->dev, "  ... write returned %d\n", status);
 | 
						|
+		cmd->error = status;
 | 
						|
+		return status;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* after no-data commands and STOP_TRANSMISSION, chipselect off */
 | 
						|
+	return mmc_spi_response_get(host, cmd, cs_on);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* Build data message with up to four separate transfers.  For TX, we
 | 
						|
+ * start by writing the data token.  And in most cases, we finish with
 | 
						|
+ * a status transfer.
 | 
						|
+ *
 | 
						|
+ * We always provide TX data for data and CRC.  The MMC/SD protocol
 | 
						|
+ * requires us to write ones; but Linux defaults to writing zeroes;
 | 
						|
+ * so we explicitly initialize it to all ones on RX paths.
 | 
						|
+ *
 | 
						|
+ * We also handle DMA mapping, so the underlying SPI controller does
 | 
						|
+ * not need to (re)do it for each message.
 | 
						|
+ */
 | 
						|
+static void
 | 
						|
+mmc_spi_setup_data_message(
 | 
						|
+	struct mmc_spi_host	*host,
 | 
						|
+	int			multiple,
 | 
						|
+	enum dma_data_direction	direction)
 | 
						|
+{
 | 
						|
+	struct spi_transfer	*t;
 | 
						|
+	struct scratch		*scratch = host->data;
 | 
						|
+	dma_addr_t		dma = host->data_dma;
 | 
						|
+
 | 
						|
+	spi_message_init(&host->m);
 | 
						|
+	if (dma)
 | 
						|
+		host->m.is_dma_mapped = 1;
 | 
						|
+
 | 
						|
+	/* for reads, readblock() skips 0xff bytes before finding
 | 
						|
+	 * the token; for writes, this transfer issues that token.
 | 
						|
+	 */
 | 
						|
+	if (direction == DMA_TO_DEVICE) {
 | 
						|
+		t = &host->token;
 | 
						|
+		memset(t, 0, sizeof(*t));
 | 
						|
+		t->len = 1;
 | 
						|
+		if (multiple)
 | 
						|
+			scratch->data_token = SPI_TOKEN_MULTI_WRITE;
 | 
						|
+		else
 | 
						|
+			scratch->data_token = SPI_TOKEN_SINGLE;
 | 
						|
+		t->tx_buf = &scratch->data_token;
 | 
						|
+		if (dma)
 | 
						|
+			t->tx_dma = dma + offsetof(struct scratch, data_token);
 | 
						|
+		spi_message_add_tail(t, &host->m);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Body of transfer is buffer, then CRC ...
 | 
						|
+	 * either TX-only, or RX with TX-ones.
 | 
						|
+	 */
 | 
						|
+	t = &host->t;
 | 
						|
+	memset(t, 0, sizeof(*t));
 | 
						|
+	t->tx_buf = host->ones;
 | 
						|
+	t->tx_dma = host->ones_dma;
 | 
						|
+	/* length and actual buffer info are written later */
 | 
						|
+	spi_message_add_tail(t, &host->m);
 | 
						|
+
 | 
						|
+	t = &host->crc;
 | 
						|
+	memset(t, 0, sizeof(*t));
 | 
						|
+	t->len = 2;
 | 
						|
+	if (direction == DMA_TO_DEVICE) {
 | 
						|
+		/* the actual CRC may get written later */
 | 
						|
+		t->tx_buf = &scratch->crc_val;
 | 
						|
+		if (dma)
 | 
						|
+			t->tx_dma = dma + offsetof(struct scratch, crc_val);
 | 
						|
+	} else {
 | 
						|
+		t->tx_buf = host->ones;
 | 
						|
+		t->tx_dma = host->ones_dma;
 | 
						|
+		t->rx_buf = &scratch->crc_val;
 | 
						|
+		if (dma)
 | 
						|
+			t->rx_dma = dma + offsetof(struct scratch, crc_val);
 | 
						|
+	}
 | 
						|
+	spi_message_add_tail(t, &host->m);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * A single block read is followed by N(EC) [0+] all-ones bytes
 | 
						|
+	 * before deselect ... don't bother.
 | 
						|
+	 *
 | 
						|
+	 * Multiblock reads are followed by N(AC) [1+] all-ones bytes before
 | 
						|
+	 * the next block is read, or a STOP_TRANSMISSION is issued.  We'll
 | 
						|
+	 * collect that single byte, so readblock() doesn't need to.
 | 
						|
+	 *
 | 
						|
+	 * For a write, the one-byte data response follows immediately, then
 | 
						|
+	 * come zero or more busy bytes, then N(WR) [1+] all-ones bytes.
 | 
						|
+	 * Then single block reads may deselect, and multiblock ones issue
 | 
						|
+	 * the next token (next data block, or STOP_TRAN).  We can try to
 | 
						|
+	 * minimize I/O ops by using a single read to collect end-of-busy.
 | 
						|
+	 */
 | 
						|
+	if (multiple || direction == DMA_TO_DEVICE) {
 | 
						|
+		t = &host->early_status;
 | 
						|
+		memset(t, 0, sizeof(*t));
 | 
						|
+		t->len = (direction == DMA_TO_DEVICE)
 | 
						|
+				? sizeof(scratch->status)
 | 
						|
+				: 1;
 | 
						|
+		t->tx_buf = host->ones;
 | 
						|
+		t->tx_dma = host->ones_dma;
 | 
						|
+		t->rx_buf = scratch->status;
 | 
						|
+		if (dma)
 | 
						|
+			t->rx_dma = dma + offsetof(struct scratch, status);
 | 
						|
+		t->cs_change = 1;
 | 
						|
+		spi_message_add_tail(t, &host->m);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Write one block:
 | 
						|
+ *  - caller handled preceding N(WR) [1+] all-ones bytes
 | 
						|
+ *  - data block
 | 
						|
+ *	+ token
 | 
						|
+ *	+ data bytes
 | 
						|
+ *	+ crc16
 | 
						|
+ *  - an all-ones byte ... card writes a data-response byte
 | 
						|
+ *  - followed by N(EC) [0+] all-ones bytes, card writes zero/'busy'
 | 
						|
+ *
 | 
						|
+ * Return negative errno, else success.
 | 
						|
+ */
 | 
						|
+static int
 | 
						|
+mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t)
 | 
						|
+{
 | 
						|
+	struct spi_device	*spi = host->spi;
 | 
						|
+	int			status, i;
 | 
						|
+	struct scratch		*scratch = host->data;
 | 
						|
+
 | 
						|
+	if (host->mmc->use_spi_crc)
 | 
						|
+		scratch->crc_val = cpu_to_be16(
 | 
						|
+				crc_itu_t(0, t->tx_buf, t->len));
 | 
						|
+	if (host->dma_dev)
 | 
						|
+		dma_sync_single_for_device(host->dma_dev,
 | 
						|
+				host->data_dma, sizeof(*scratch),
 | 
						|
+				DMA_BIDIRECTIONAL);
 | 
						|
+
 | 
						|
+	status = spi_sync(spi, &host->m);
 | 
						|
+
 | 
						|
+	if (status != 0) {
 | 
						|
+		dev_dbg(&spi->dev, "write error (%d)\n", status);
 | 
						|
+		return status;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (host->dma_dev)
 | 
						|
+		dma_sync_single_for_cpu(host->dma_dev,
 | 
						|
+				host->data_dma, sizeof(*scratch),
 | 
						|
+				DMA_BIDIRECTIONAL);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Get the transmission data-response reply.  It must follow
 | 
						|
+	 * immediately after the data block we transferred.  This reply
 | 
						|
+	 * doesn't necessarily tell whether the write operation succeeded;
 | 
						|
+	 * it just says if the transmission was ok and whether *earlier*
 | 
						|
+	 * writes succeeded; see the standard.
 | 
						|
+	 */
 | 
						|
+	switch (SPI_MMC_RESPONSE_CODE(scratch->status[0])) {
 | 
						|
+	case SPI_RESPONSE_ACCEPTED:
 | 
						|
+		status = 0;
 | 
						|
+		break;
 | 
						|
+	case SPI_RESPONSE_CRC_ERR:
 | 
						|
+		/* host shall then issue MMC_STOP_TRANSMISSION */
 | 
						|
+		status = -EILSEQ;
 | 
						|
+		break;
 | 
						|
+	case SPI_RESPONSE_WRITE_ERR:
 | 
						|
+		/* host shall then issue MMC_STOP_TRANSMISSION,
 | 
						|
+		 * and should MMC_SEND_STATUS to sort it out
 | 
						|
+		 */
 | 
						|
+		status = -EIO;
 | 
						|
+		break;
 | 
						|
+	default:
 | 
						|
+		status = -EPROTO;
 | 
						|
+		break;
 | 
						|
+	}
 | 
						|
+	if (status != 0) {
 | 
						|
+		dev_dbg(&spi->dev, "write error %02x (%d)\n",
 | 
						|
+			scratch->status[0], status);
 | 
						|
+		return status;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	t->tx_buf += t->len;
 | 
						|
+	if (host->dma_dev)
 | 
						|
+		t->tx_dma += t->len;
 | 
						|
+
 | 
						|
+	/* Return when not busy.  If we didn't collect that status yet,
 | 
						|
+	 * we'll need some more I/O.
 | 
						|
+	 */
 | 
						|
+	for (i = 1; i < sizeof(scratch->status); i++) {
 | 
						|
+		if (scratch->status[i] != 0)
 | 
						|
+			return 0;
 | 
						|
+	}
 | 
						|
+	return mmc_spi_wait_unbusy(host, writeblock_timeout);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Read one block:
 | 
						|
+ *  - skip leading all-ones bytes ... either
 | 
						|
+ *      + N(AC) [1..f(clock,CSD)] usually, else
 | 
						|
+ *      + N(CX) [0..8] when reading CSD or CID
 | 
						|
+ *  - data block
 | 
						|
+ *	+ token ... if error token, no data or crc
 | 
						|
+ *	+ data bytes
 | 
						|
+ *	+ crc16
 | 
						|
+ *
 | 
						|
+ * After single block reads, we're done; N(EC) [0+] all-ones bytes follow
 | 
						|
+ * before dropping chipselect.
 | 
						|
+ *
 | 
						|
+ * For multiblock reads, caller either reads the next block or issues a
 | 
						|
+ * STOP_TRANSMISSION command.
 | 
						|
+ */
 | 
						|
+static int
 | 
						|
+mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t)
 | 
						|
+{
 | 
						|
+	struct spi_device	*spi = host->spi;
 | 
						|
+	int			status;
 | 
						|
+	struct scratch		*scratch = host->data;
 | 
						|
+
 | 
						|
+	/* At least one SD card sends an all-zeroes byte when N(CX)
 | 
						|
+	 * applies, before the all-ones bytes ... just cope with that.
 | 
						|
+	 */
 | 
						|
+	status = mmc_spi_readbytes(host, 1);
 | 
						|
+	if (status < 0)
 | 
						|
+		return status;
 | 
						|
+	status = scratch->status[0];
 | 
						|
+	if (status == 0xff || status == 0)
 | 
						|
+		status = mmc_spi_readtoken(host);
 | 
						|
+
 | 
						|
+	if (status == SPI_TOKEN_SINGLE) {
 | 
						|
+		if (host->dma_dev) {
 | 
						|
+			dma_sync_single_for_device(host->dma_dev,
 | 
						|
+					host->data_dma, sizeof(*scratch),
 | 
						|
+					DMA_BIDIRECTIONAL);
 | 
						|
+			dma_sync_single_for_device(host->dma_dev,
 | 
						|
+					t->rx_dma, t->len,
 | 
						|
+					DMA_FROM_DEVICE);
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		status = spi_sync(spi, &host->m);
 | 
						|
+
 | 
						|
+		if (host->dma_dev) {
 | 
						|
+			dma_sync_single_for_cpu(host->dma_dev,
 | 
						|
+					host->data_dma, sizeof(*scratch),
 | 
						|
+					DMA_BIDIRECTIONAL);
 | 
						|
+			dma_sync_single_for_cpu(host->dma_dev,
 | 
						|
+					t->rx_dma, t->len,
 | 
						|
+					DMA_FROM_DEVICE);
 | 
						|
+		}
 | 
						|
+
 | 
						|
+	} else {
 | 
						|
+		dev_dbg(&spi->dev, "read error %02x (%d)\n", status, status);
 | 
						|
+
 | 
						|
+		/* we've read extra garbage, timed out, etc */
 | 
						|
+		if (status < 0)
 | 
						|
+			return status;
 | 
						|
+
 | 
						|
+		/* low four bits are an R2 subset, fifth seems to be
 | 
						|
+		 * vendor specific ... map them all to generic error..
 | 
						|
+		 */
 | 
						|
+		return -EIO;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (host->mmc->use_spi_crc) {
 | 
						|
+		u16 crc = crc_itu_t(0, t->rx_buf, t->len);
 | 
						|
+
 | 
						|
+		be16_to_cpus(&scratch->crc_val);
 | 
						|
+		if (scratch->crc_val != crc) {
 | 
						|
+			dev_dbg(&spi->dev, "read - crc error: crc_val=0x%04x, "
 | 
						|
+					"computed=0x%04x len=%d\n",
 | 
						|
+					scratch->crc_val, crc, t->len);
 | 
						|
+			return -EILSEQ;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	t->rx_buf += t->len;
 | 
						|
+	if (host->dma_dev)
 | 
						|
+		t->rx_dma += t->len;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * An MMC/SD data stage includes one or more blocks, optional CRCs,
 | 
						|
+ * and inline handshaking.  That handhaking makes it unlike most
 | 
						|
+ * other SPI protocol stacks.
 | 
						|
+ */
 | 
						|
+static void
 | 
						|
+mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
 | 
						|
+		struct mmc_data *data, u32 blk_size)
 | 
						|
+{
 | 
						|
+	struct spi_device	*spi = host->spi;
 | 
						|
+	struct device		*dma_dev = host->dma_dev;
 | 
						|
+	struct spi_transfer	*t;
 | 
						|
+	enum dma_data_direction	direction;
 | 
						|
+	struct scatterlist	*sg;
 | 
						|
+	unsigned		n_sg;
 | 
						|
+	int			multiple = (data->blocks > 1);
 | 
						|
+
 | 
						|
+	if (data->flags & MMC_DATA_READ)
 | 
						|
+		direction = DMA_FROM_DEVICE;
 | 
						|
+	else
 | 
						|
+		direction = DMA_TO_DEVICE;
 | 
						|
+	mmc_spi_setup_data_message(host, multiple, direction);
 | 
						|
+	t = &host->t;
 | 
						|
+
 | 
						|
+	/* Handle scatterlist segments one at a time, with synch for
 | 
						|
+	 * each 512-byte block
 | 
						|
+	 */
 | 
						|
+	for (sg = data->sg, n_sg = data->sg_len; n_sg; n_sg--, sg++) {
 | 
						|
+		int			status = 0;
 | 
						|
+		dma_addr_t		dma_addr = 0;
 | 
						|
+		void			*kmap_addr;
 | 
						|
+		unsigned		length = sg->length;
 | 
						|
+		enum dma_data_direction	dir = direction;
 | 
						|
+
 | 
						|
+		/* set up dma mapping for controller drivers that might
 | 
						|
+		 * use DMA ... though they may fall back to PIO
 | 
						|
+		 */
 | 
						|
+		if (dma_dev) {
 | 
						|
+			/* never invalidate whole *shared* pages ... */
 | 
						|
+			if ((sg->offset != 0 || length != PAGE_SIZE)
 | 
						|
+					&& dir == DMA_FROM_DEVICE)
 | 
						|
+				dir = DMA_BIDIRECTIONAL;
 | 
						|
+
 | 
						|
+			dma_addr = dma_map_page(dma_dev, sg_page(sg), 0,
 | 
						|
+						PAGE_SIZE, dir);
 | 
						|
+			if (direction == DMA_TO_DEVICE)
 | 
						|
+				t->tx_dma = dma_addr + sg->offset;
 | 
						|
+			else
 | 
						|
+				t->rx_dma = dma_addr + sg->offset;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/* allow pio too; we don't allow highmem */
 | 
						|
+		kmap_addr = kmap(sg_page(sg));
 | 
						|
+		if (direction == DMA_TO_DEVICE)
 | 
						|
+			t->tx_buf = kmap_addr + sg->offset;
 | 
						|
+		else
 | 
						|
+			t->rx_buf = kmap_addr + sg->offset;
 | 
						|
+
 | 
						|
+		/* transfer each block, and update request status */
 | 
						|
+		while (length) {
 | 
						|
+			t->len = min(length, blk_size);
 | 
						|
+
 | 
						|
+			dev_dbg(&host->spi->dev,
 | 
						|
+				"    mmc_spi: %s block, %d bytes\n",
 | 
						|
+				(direction == DMA_TO_DEVICE)
 | 
						|
+				? "write"
 | 
						|
+				: "read",
 | 
						|
+				t->len);
 | 
						|
+
 | 
						|
+			if (direction == DMA_TO_DEVICE)
 | 
						|
+				status = mmc_spi_writeblock(host, t);
 | 
						|
+			else
 | 
						|
+				status = mmc_spi_readblock(host, t);
 | 
						|
+			if (status < 0)
 | 
						|
+				break;
 | 
						|
+
 | 
						|
+			data->bytes_xfered += t->len;
 | 
						|
+			length -= t->len;
 | 
						|
+
 | 
						|
+			if (!multiple)
 | 
						|
+				break;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/* discard mappings */
 | 
						|
+		if (direction == DMA_FROM_DEVICE)
 | 
						|
+			flush_kernel_dcache_page(sg_page(sg));
 | 
						|
+		kunmap(sg_page(sg));
 | 
						|
+		if (dma_dev)
 | 
						|
+			dma_unmap_page(dma_dev, dma_addr, PAGE_SIZE, dir);
 | 
						|
+
 | 
						|
+		if (status < 0) {
 | 
						|
+			data->error = status;
 | 
						|
+			dev_dbg(&spi->dev, "%s status %d\n",
 | 
						|
+				(direction == DMA_TO_DEVICE)
 | 
						|
+					? "write" : "read",
 | 
						|
+				status);
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* NOTE some docs describe an MMC-only SET_BLOCK_COUNT (CMD23) that
 | 
						|
+	 * can be issued before multiblock writes.  Unlike its more widely
 | 
						|
+	 * documented analogue for SD cards (SET_WR_BLK_ERASE_COUNT, ACMD23),
 | 
						|
+	 * that can affect the STOP_TRAN logic.   Complete (and current)
 | 
						|
+	 * MMC specs should sort that out before Linux starts using CMD23.
 | 
						|
+	 */
 | 
						|
+	if (direction == DMA_TO_DEVICE && multiple) {
 | 
						|
+		struct scratch	*scratch = host->data;
 | 
						|
+		int		tmp;
 | 
						|
+		const unsigned	statlen = sizeof(scratch->status);
 | 
						|
+
 | 
						|
+		dev_dbg(&spi->dev, "    mmc_spi: STOP_TRAN\n");
 | 
						|
+
 | 
						|
+		/* Tweak the per-block message we set up earlier by morphing
 | 
						|
+		 * it to hold single buffer with the token followed by some
 | 
						|
+		 * all-ones bytes ... skip N(BR) (0..1), scan the rest for
 | 
						|
+		 * "not busy any longer" status, and leave chip selected.
 | 
						|
+		 */
 | 
						|
+		INIT_LIST_HEAD(&host->m.transfers);
 | 
						|
+		list_add(&host->early_status.transfer_list,
 | 
						|
+				&host->m.transfers);
 | 
						|
+
 | 
						|
+		memset(scratch->status, 0xff, statlen);
 | 
						|
+		scratch->status[0] = SPI_TOKEN_STOP_TRAN;
 | 
						|
+
 | 
						|
+		host->early_status.tx_buf = host->early_status.rx_buf;
 | 
						|
+		host->early_status.tx_dma = host->early_status.rx_dma;
 | 
						|
+		host->early_status.len = statlen;
 | 
						|
+
 | 
						|
+		if (host->dma_dev)
 | 
						|
+			dma_sync_single_for_device(host->dma_dev,
 | 
						|
+					host->data_dma, sizeof(*scratch),
 | 
						|
+					DMA_BIDIRECTIONAL);
 | 
						|
+
 | 
						|
+		tmp = spi_sync(spi, &host->m);
 | 
						|
+
 | 
						|
+		if (host->dma_dev)
 | 
						|
+			dma_sync_single_for_cpu(host->dma_dev,
 | 
						|
+					host->data_dma, sizeof(*scratch),
 | 
						|
+					DMA_BIDIRECTIONAL);
 | 
						|
+
 | 
						|
+		if (tmp < 0) {
 | 
						|
+			if (!data->error)
 | 
						|
+				data->error = tmp;
 | 
						|
+			return;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/* Ideally we collected "not busy" status with one I/O,
 | 
						|
+		 * avoiding wasteful byte-at-a-time scanning... but more
 | 
						|
+		 * I/O is often needed.
 | 
						|
+		 */
 | 
						|
+		for (tmp = 2; tmp < statlen; tmp++) {
 | 
						|
+			if (scratch->status[tmp] != 0)
 | 
						|
+				return;
 | 
						|
+		}
 | 
						|
+		tmp = mmc_spi_wait_unbusy(host, writeblock_timeout);
 | 
						|
+		if (tmp < 0 && !data->error)
 | 
						|
+			data->error = tmp;
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+/****************************************************************************/
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * MMC driver implementation -- the interface to the MMC stack
 | 
						|
+ */
 | 
						|
+
 | 
						|
+static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq)
 | 
						|
+{
 | 
						|
+	struct mmc_spi_host	*host = mmc_priv(mmc);
 | 
						|
+	int			status = -EINVAL;
 | 
						|
+
 | 
						|
+#ifdef DEBUG
 | 
						|
+	/* MMC core and layered drivers *MUST* issue SPI-aware commands */
 | 
						|
+	{
 | 
						|
+		struct mmc_command	*cmd;
 | 
						|
+		int			invalid = 0;
 | 
						|
+
 | 
						|
+		cmd = mrq->cmd;
 | 
						|
+		if (!mmc_spi_resp_type(cmd)) {
 | 
						|
+			dev_dbg(&host->spi->dev, "bogus command\n");
 | 
						|
+			cmd->error = -EINVAL;
 | 
						|
+			invalid = 1;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		cmd = mrq->stop;
 | 
						|
+		if (cmd && !mmc_spi_resp_type(cmd)) {
 | 
						|
+			dev_dbg(&host->spi->dev, "bogus STOP command\n");
 | 
						|
+			cmd->error = -EINVAL;
 | 
						|
+			invalid = 1;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		if (invalid) {
 | 
						|
+			dump_stack();
 | 
						|
+			mmc_request_done(host->mmc, mrq);
 | 
						|
+			return;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	/* issue command; then optionally data and stop */
 | 
						|
+	status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL);
 | 
						|
+	if (status == 0 && mrq->data) {
 | 
						|
+		mmc_spi_data_do(host, mrq->cmd, mrq->data, mrq->data->blksz);
 | 
						|
+		if (mrq->stop)
 | 
						|
+			status = mmc_spi_command_send(host, mrq, mrq->stop, 0);
 | 
						|
+		else
 | 
						|
+			mmc_cs_off(host);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mmc_request_done(host->mmc, mrq);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* See Section 6.4.1, in SD "Simplified Physical Layer Specification 2.0"
 | 
						|
+ *
 | 
						|
+ * NOTE that here we can't know that the card has just been powered up;
 | 
						|
+ * not all MMC/SD sockets support power switching.
 | 
						|
+ *
 | 
						|
+ * FIXME when the card is still in SPI mode, e.g. from a previous kernel,
 | 
						|
+ * this doesn't seem to do the right thing at all...
 | 
						|
+ */
 | 
						|
+static void mmc_spi_initsequence(struct mmc_spi_host *host)
 | 
						|
+{
 | 
						|
+	/* Try to be very sure any previous command has completed;
 | 
						|
+	 * wait till not-busy, skip debris from any old commands.
 | 
						|
+	 */
 | 
						|
+	mmc_spi_wait_unbusy(host, r1b_timeout);
 | 
						|
+	mmc_spi_readbytes(host, 10);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Do a burst with chipselect active-high.  We need to do this to
 | 
						|
+	 * meet the requirement of 74 clock cycles with both chipselect
 | 
						|
+	 * and CMD (MOSI) high before CMD0 ... after the card has been
 | 
						|
+	 * powered up to Vdd(min), and so is ready to take commands.
 | 
						|
+	 *
 | 
						|
+	 * Some cards are particularly needy of this (e.g. Viking "SD256")
 | 
						|
+	 * while most others don't seem to care.
 | 
						|
+	 *
 | 
						|
+	 * Note that this is one of the places MMC/SD plays games with the
 | 
						|
+	 * SPI protocol.  Another is that when chipselect is released while
 | 
						|
+	 * the card returns BUSY status, the clock must issue several cycles
 | 
						|
+	 * with chipselect high before the card will stop driving its output.
 | 
						|
+	 */
 | 
						|
+	host->spi->mode |= SPI_CS_HIGH;
 | 
						|
+	if (spi_setup(host->spi) != 0) {
 | 
						|
+		/* Just warn; most cards work without it. */
 | 
						|
+		dev_warn(&host->spi->dev,
 | 
						|
+				"can't change chip-select polarity\n");
 | 
						|
+		host->spi->mode &= ~SPI_CS_HIGH;
 | 
						|
+	} else {
 | 
						|
+		mmc_spi_readbytes(host, 18);
 | 
						|
+
 | 
						|
+		host->spi->mode &= ~SPI_CS_HIGH;
 | 
						|
+		if (spi_setup(host->spi) != 0) {
 | 
						|
+			/* Wot, we can't get the same setup we had before? */
 | 
						|
+			dev_err(&host->spi->dev,
 | 
						|
+					"can't restore chip-select polarity\n");
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static char *mmc_powerstring(u8 power_mode)
 | 
						|
+{
 | 
						|
+	switch (power_mode) {
 | 
						|
+	case MMC_POWER_OFF: return "off";
 | 
						|
+	case MMC_POWER_UP:  return "up";
 | 
						|
+	case MMC_POWER_ON:  return "on";
 | 
						|
+	}
 | 
						|
+	return "?";
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 | 
						|
+{
 | 
						|
+	struct mmc_spi_host *host = mmc_priv(mmc);
 | 
						|
+
 | 
						|
+	if (host->power_mode != ios->power_mode) {
 | 
						|
+		int		canpower;
 | 
						|
+
 | 
						|
+		canpower = host->pdata && host->pdata->setpower;
 | 
						|
+
 | 
						|
+		dev_dbg(&host->spi->dev, "mmc_spi: power %s (%d)%s\n",
 | 
						|
+				mmc_powerstring(ios->power_mode),
 | 
						|
+				ios->vdd,
 | 
						|
+				canpower ? ", can switch" : "");
 | 
						|
+
 | 
						|
+		/* switch power on/off if possible, accounting for
 | 
						|
+		 * max 250msec powerup time if needed.
 | 
						|
+		 */
 | 
						|
+		if (canpower) {
 | 
						|
+			switch (ios->power_mode) {
 | 
						|
+			case MMC_POWER_OFF:
 | 
						|
+			case MMC_POWER_UP:
 | 
						|
+				host->pdata->setpower(&host->spi->dev,
 | 
						|
+						ios->vdd);
 | 
						|
+				if (ios->power_mode == MMC_POWER_UP)
 | 
						|
+					msleep(host->powerup_msecs);
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/* See 6.4.1 in the simplified SD card physical spec 2.0 */
 | 
						|
+		if (ios->power_mode == MMC_POWER_ON)
 | 
						|
+			mmc_spi_initsequence(host);
 | 
						|
+
 | 
						|
+		/* If powering down, ground all card inputs to avoid power
 | 
						|
+		 * delivery from data lines!  On a shared SPI bus, this
 | 
						|
+		 * will probably be temporary; 6.4.2 of the simplified SD
 | 
						|
+		 * spec says this must last at least 1msec.
 | 
						|
+		 *
 | 
						|
+		 *   - Clock low means CPOL 0, e.g. mode 0
 | 
						|
+		 *   - MOSI low comes from writing zero
 | 
						|
+		 *   - Chipselect is usually active low...
 | 
						|
+		 */
 | 
						|
+		if (canpower && ios->power_mode == MMC_POWER_OFF) {
 | 
						|
+			int mres;
 | 
						|
+
 | 
						|
+			host->spi->mode &= ~(SPI_CPOL|SPI_CPHA);
 | 
						|
+			mres = spi_setup(host->spi);
 | 
						|
+			if (mres < 0)
 | 
						|
+				dev_dbg(&host->spi->dev,
 | 
						|
+					"switch to SPI mode 0 failed\n");
 | 
						|
+
 | 
						|
+			if (spi_w8r8(host->spi, 0x00) < 0)
 | 
						|
+				dev_dbg(&host->spi->dev,
 | 
						|
+					"put spi signals to low failed\n");
 | 
						|
+
 | 
						|
+			/*
 | 
						|
+			 * Now clock should be low due to spi mode 0;
 | 
						|
+			 * MOSI should be low because of written 0x00;
 | 
						|
+			 * chipselect should be low (it is active low)
 | 
						|
+			 * power supply is off, so now MMC is off too!
 | 
						|
+			 *
 | 
						|
+			 * FIXME no, chipselect can be high since the
 | 
						|
+			 * device is inactive and SPI_CS_HIGH is clear...
 | 
						|
+			 */
 | 
						|
+			msleep(10);
 | 
						|
+			if (mres == 0) {
 | 
						|
+				host->spi->mode |= (SPI_CPOL|SPI_CPHA);
 | 
						|
+				mres = spi_setup(host->spi);
 | 
						|
+				if (mres < 0)
 | 
						|
+					dev_dbg(&host->spi->dev,
 | 
						|
+						"switch back to SPI mode 3"
 | 
						|
+						" failed\n");
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		host->power_mode = ios->power_mode;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (host->spi->max_speed_hz != ios->clock && ios->clock != 0) {
 | 
						|
+		int		status;
 | 
						|
+
 | 
						|
+		host->spi->max_speed_hz = ios->clock;
 | 
						|
+		status = spi_setup(host->spi);
 | 
						|
+		dev_dbg(&host->spi->dev,
 | 
						|
+			"mmc_spi:  clock to %d Hz, %d\n",
 | 
						|
+			host->spi->max_speed_hz, status);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int mmc_spi_get_ro(struct mmc_host *mmc)
 | 
						|
+{
 | 
						|
+	struct mmc_spi_host *host = mmc_priv(mmc);
 | 
						|
+
 | 
						|
+	if (host->pdata && host->pdata->get_ro)
 | 
						|
+		return host->pdata->get_ro(mmc->parent);
 | 
						|
+	/* board doesn't support read only detection; assume writeable */
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static const struct mmc_host_ops mmc_spi_ops = {
 | 
						|
+	.request	= mmc_spi_request,
 | 
						|
+	.set_ios	= mmc_spi_set_ios,
 | 
						|
+	.get_ro		= mmc_spi_get_ro,
 | 
						|
+};
 | 
						|
+
 | 
						|
+
 | 
						|
+/****************************************************************************/
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * SPI driver implementation
 | 
						|
+ */
 | 
						|
+
 | 
						|
+static irqreturn_t
 | 
						|
+mmc_spi_detect_irq(int irq, void *mmc)
 | 
						|
+{
 | 
						|
+	struct mmc_spi_host *host = mmc_priv(mmc);
 | 
						|
+	u16 delay_msec = max(host->pdata->detect_delay, (u16)100);
 | 
						|
+
 | 
						|
+	mmc_detect_change(mmc, msecs_to_jiffies(delay_msec));
 | 
						|
+	return IRQ_HANDLED;
 | 
						|
+}
 | 
						|
+
 | 
						|
+struct count_children {
 | 
						|
+	unsigned	n;
 | 
						|
+	struct bus_type	*bus;
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int maybe_count_child(struct device *dev, void *c)
 | 
						|
+{
 | 
						|
+	struct count_children *ccp = c;
 | 
						|
+
 | 
						|
+	if (dev->bus == ccp->bus) {
 | 
						|
+		if (ccp->n)
 | 
						|
+			return -EBUSY;
 | 
						|
+		ccp->n++;
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int mmc_spi_probe(struct spi_device *spi)
 | 
						|
+{
 | 
						|
+	void			*ones;
 | 
						|
+	struct mmc_host		*mmc;
 | 
						|
+	struct mmc_spi_host	*host;
 | 
						|
+	int			status;
 | 
						|
+
 | 
						|
+	/* MMC and SD specs only seem to care that sampling is on the
 | 
						|
+	 * rising edge ... meaning SPI modes 0 or 3.  So either SPI mode
 | 
						|
+	 * should be legit.  We'll use mode 0 since it seems to be a
 | 
						|
+	 * bit less troublesome on some hardware ... unclear why.
 | 
						|
+	 */
 | 
						|
+	spi->mode = SPI_MODE_0;
 | 
						|
+	spi->bits_per_word = 8;
 | 
						|
+
 | 
						|
+	status = spi_setup(spi);
 | 
						|
+	if (status < 0) {
 | 
						|
+		dev_dbg(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n",
 | 
						|
+				spi->mode, spi->max_speed_hz / 1000,
 | 
						|
+				status);
 | 
						|
+		return status;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* We can use the bus safely iff nobody else will interfere with us.
 | 
						|
+	 * Most commands consist of one SPI message to issue a command, then
 | 
						|
+	 * several more to collect its response, then possibly more for data
 | 
						|
+	 * transfer.  Clocking access to other devices during that period will
 | 
						|
+	 * corrupt the command execution.
 | 
						|
+	 *
 | 
						|
+	 * Until we have software primitives which guarantee non-interference,
 | 
						|
+	 * we'll aim for a hardware-level guarantee.
 | 
						|
+	 *
 | 
						|
+	 * REVISIT we can't guarantee another device won't be added later...
 | 
						|
+	 */
 | 
						|
+	if (spi->master->num_chipselect > 1) {
 | 
						|
+		struct count_children cc;
 | 
						|
+
 | 
						|
+		cc.n = 0;
 | 
						|
+		cc.bus = spi->dev.bus;
 | 
						|
+		status = device_for_each_child(spi->dev.parent, &cc,
 | 
						|
+				maybe_count_child);
 | 
						|
+		if (status < 0) {
 | 
						|
+			dev_err(&spi->dev, "can't share SPI bus\n");
 | 
						|
+			return status;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		dev_warn(&spi->dev, "ASSUMING SPI bus stays unshared!\n");
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* We need a supply of ones to transmit.  This is the only time
 | 
						|
+	 * the CPU touches these, so cache coherency isn't a concern.
 | 
						|
+	 *
 | 
						|
+	 * NOTE if many systems use more than one MMC-over-SPI connector
 | 
						|
+	 * it'd save some memory to share this.  That's evidently rare.
 | 
						|
+	 */
 | 
						|
+	status = -ENOMEM;
 | 
						|
+	ones = kmalloc(MMC_SPI_BLOCKSIZE, GFP_KERNEL);
 | 
						|
+	if (!ones)
 | 
						|
+		goto nomem;
 | 
						|
+	memset(ones, 0xff, MMC_SPI_BLOCKSIZE);
 | 
						|
+
 | 
						|
+	mmc = mmc_alloc_host(sizeof(*host), &spi->dev);
 | 
						|
+	if (!mmc)
 | 
						|
+		goto nomem;
 | 
						|
+
 | 
						|
+	mmc->ops = &mmc_spi_ops;
 | 
						|
+	mmc->max_blk_size = MMC_SPI_BLOCKSIZE;
 | 
						|
+
 | 
						|
+	/* As long as we keep track of the number of successfully
 | 
						|
+	 * transmitted blocks, we're good for multiwrite.
 | 
						|
+	 */
 | 
						|
+	mmc->caps = MMC_CAP_SPI | MMC_CAP_MULTIWRITE;
 | 
						|
+
 | 
						|
+	/* SPI doesn't need the lowspeed device identification thing for
 | 
						|
+	 * MMC or SD cards, since it never comes up in open drain mode.
 | 
						|
+	 * That's good; some SPI masters can't handle very low speeds!
 | 
						|
+	 *
 | 
						|
+	 * However, low speed SDIO cards need not handle over 400 KHz;
 | 
						|
+	 * that's the only reason not to use a few MHz for f_min (until
 | 
						|
+	 * the upper layer reads the target frequency from the CSD).
 | 
						|
+	 */
 | 
						|
+	mmc->f_min = 400000;
 | 
						|
+	mmc->f_max = spi->max_speed_hz;
 | 
						|
+
 | 
						|
+	host = mmc_priv(mmc);
 | 
						|
+	host->mmc = mmc;
 | 
						|
+	host->spi = spi;
 | 
						|
+
 | 
						|
+	host->ones = ones;
 | 
						|
+
 | 
						|
+	/* Platform data is used to hook up things like card sensing
 | 
						|
+	 * and power switching gpios.
 | 
						|
+	 */
 | 
						|
+	host->pdata = spi->dev.platform_data;
 | 
						|
+	if (host->pdata)
 | 
						|
+		mmc->ocr_avail = host->pdata->ocr_mask;
 | 
						|
+	if (!mmc->ocr_avail) {
 | 
						|
+		dev_warn(&spi->dev, "ASSUMING 3.2-3.4 V slot power\n");
 | 
						|
+		mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
 | 
						|
+	}
 | 
						|
+	if (host->pdata && host->pdata->setpower) {
 | 
						|
+		host->powerup_msecs = host->pdata->powerup_msecs;
 | 
						|
+		if (!host->powerup_msecs || host->powerup_msecs > 250)
 | 
						|
+			host->powerup_msecs = 250;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	dev_set_drvdata(&spi->dev, mmc);
 | 
						|
+
 | 
						|
+	/* preallocate dma buffers */
 | 
						|
+	host->data = kmalloc(sizeof(*host->data), GFP_KERNEL);
 | 
						|
+	if (!host->data)
 | 
						|
+		goto fail_nobuf1;
 | 
						|
+
 | 
						|
+//FIXME
 | 
						|
+#if 0
 | 
						|
+	if (spi->master->dev.parent->dma_mask) {
 | 
						|
+		struct device	*dev = spi->master->dev.parent;
 | 
						|
+
 | 
						|
+		host->dma_dev = dev;
 | 
						|
+		host->ones_dma = dma_map_single(dev, ones,
 | 
						|
+				MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
 | 
						|
+		host->data_dma = dma_map_single(dev, host->data,
 | 
						|
+				sizeof(*host->data), DMA_BIDIRECTIONAL);
 | 
						|
+
 | 
						|
+		/* REVISIT in theory those map operations can fail... */
 | 
						|
+
 | 
						|
+		dma_sync_single_for_cpu(host->dma_dev,
 | 
						|
+				host->data_dma, sizeof(*host->data),
 | 
						|
+				DMA_BIDIRECTIONAL);
 | 
						|
+	}
 | 
						|
+#endif
 | 
						|
+
 | 
						|
+	/* setup message for status/busy readback */
 | 
						|
+	spi_message_init(&host->readback);
 | 
						|
+	host->readback.is_dma_mapped = (host->dma_dev != NULL);
 | 
						|
+
 | 
						|
+	spi_message_add_tail(&host->status, &host->readback);
 | 
						|
+	host->status.tx_buf = host->ones;
 | 
						|
+	host->status.tx_dma = host->ones_dma;
 | 
						|
+	host->status.rx_buf = &host->data->status;
 | 
						|
+	host->status.rx_dma = host->data_dma + offsetof(struct scratch, status);
 | 
						|
+	host->status.cs_change = 1;
 | 
						|
+
 | 
						|
+	/* register card detect irq */
 | 
						|
+	if (host->pdata && host->pdata->init) {
 | 
						|
+		status = host->pdata->init(&spi->dev, mmc_spi_detect_irq, mmc);
 | 
						|
+		if (status != 0)
 | 
						|
+			goto fail_glue_init;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	status = mmc_add_host(mmc);
 | 
						|
+	if (status != 0)
 | 
						|
+		goto fail_add_host;
 | 
						|
+
 | 
						|
+	dev_info(&spi->dev, "SD/MMC host %s%s%s%s\n",
 | 
						|
+			mmc->class_dev.bus_id,
 | 
						|
+			host->dma_dev ? "" : ", no DMA",
 | 
						|
+			(host->pdata && host->pdata->get_ro)
 | 
						|
+				? "" : ", no WP",
 | 
						|
+			(host->pdata && host->pdata->setpower)
 | 
						|
+				? "" : ", no poweroff");
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+fail_add_host:
 | 
						|
+	mmc_remove_host (mmc);
 | 
						|
+fail_glue_init:
 | 
						|
+	if (host->dma_dev)
 | 
						|
+		dma_unmap_single(host->dma_dev, host->data_dma,
 | 
						|
+				sizeof(*host->data), DMA_BIDIRECTIONAL);
 | 
						|
+	kfree(host->data);
 | 
						|
+
 | 
						|
+fail_nobuf1:
 | 
						|
+	mmc_free_host(mmc);
 | 
						|
+	dev_set_drvdata(&spi->dev, NULL);
 | 
						|
+
 | 
						|
+nomem:
 | 
						|
+	kfree(ones);
 | 
						|
+	return status;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static int __devexit mmc_spi_remove(struct spi_device *spi)
 | 
						|
+{
 | 
						|
+	struct mmc_host		*mmc = dev_get_drvdata(&spi->dev);
 | 
						|
+	struct mmc_spi_host	*host;
 | 
						|
+
 | 
						|
+	if (mmc) {
 | 
						|
+		host = mmc_priv(mmc);
 | 
						|
+
 | 
						|
+		/* prevent new mmc_detect_change() calls */
 | 
						|
+		if (host->pdata && host->pdata->exit)
 | 
						|
+			host->pdata->exit(&spi->dev, mmc);
 | 
						|
+
 | 
						|
+		mmc_remove_host(mmc);
 | 
						|
+
 | 
						|
+		if (host->dma_dev) {
 | 
						|
+			dma_unmap_single(host->dma_dev, host->ones_dma,
 | 
						|
+				MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
 | 
						|
+			dma_unmap_single(host->dma_dev, host->data_dma,
 | 
						|
+				sizeof(*host->data), DMA_BIDIRECTIONAL);
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		kfree(host->data);
 | 
						|
+		kfree(host->ones);
 | 
						|
+
 | 
						|
+		spi->max_speed_hz = mmc->f_max;
 | 
						|
+		mmc_free_host(mmc);
 | 
						|
+		dev_set_drvdata(&spi->dev, NULL);
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static struct spi_driver mmc_spi_driver = {
 | 
						|
+	.driver = {
 | 
						|
+		.name =		"mmc_spi",
 | 
						|
+		.bus =		&spi_bus_type,
 | 
						|
+		.owner =	THIS_MODULE,
 | 
						|
+	},
 | 
						|
+	.probe =	mmc_spi_probe,
 | 
						|
+	.remove =	__devexit_p(mmc_spi_remove),
 | 
						|
+};
 | 
						|
+
 | 
						|
+
 | 
						|
+static int __init mmc_spi_init(void)
 | 
						|
+{
 | 
						|
+	return spi_register_driver(&mmc_spi_driver);
 | 
						|
+}
 | 
						|
+module_init(mmc_spi_init);
 | 
						|
+
 | 
						|
+
 | 
						|
+static void __exit mmc_spi_exit(void)
 | 
						|
+{
 | 
						|
+	spi_unregister_driver(&mmc_spi_driver);
 | 
						|
+}
 | 
						|
+module_exit(mmc_spi_exit);
 | 
						|
+
 | 
						|
+
 | 
						|
+MODULE_AUTHOR("Mike Lavender, David Brownell, "
 | 
						|
+		"Hans-Peter Nilsson, Jan Nikitenko");
 | 
						|
+MODULE_DESCRIPTION("SPI SD/MMC host driver");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
--- /dev/null
 | 
						|
+++ b/include/linux/spi/mmc_spi.h
 | 
						|
@@ -0,0 +1,33 @@
 | 
						|
+#ifndef __LINUX_SPI_MMC_SPI_H
 | 
						|
+#define __LINUX_SPI_MMC_SPI_H
 | 
						|
+
 | 
						|
+struct device;
 | 
						|
+struct mmc_host;
 | 
						|
+
 | 
						|
+/* Put this in platform_data of a device being used to manage an MMC/SD
 | 
						|
+ * card slot.  (Modeled after PXA mmc glue; see that for usage examples.)
 | 
						|
+ *
 | 
						|
+ * REVISIT This is not a spi-specific notion.  Any card slot should be
 | 
						|
+ * able to handle it.  If the MMC core doesn't adopt this kind of notion,
 | 
						|
+ * switch the "struct device *" parameters over to "struct spi_device *".
 | 
						|
+ */
 | 
						|
+struct mmc_spi_platform_data {
 | 
						|
+	/* driver activation and (optional) card detect irq hookup */
 | 
						|
+	int (*init)(struct device *,
 | 
						|
+		irqreturn_t (*)(int, void *),
 | 
						|
+		void *);
 | 
						|
+	void (*exit)(struct device *, void *);
 | 
						|
+
 | 
						|
+	/* sense switch on sd cards */
 | 
						|
+	int (*get_ro)(struct device *);
 | 
						|
+
 | 
						|
+	/* how long to debounce card detect, in msecs */
 | 
						|
+	u16 detect_delay;
 | 
						|
+
 | 
						|
+	/* power management */
 | 
						|
+	u16 powerup_msecs;		/* delay of up to 250 msec */
 | 
						|
+	u32 ocr_mask;			/* available voltages */
 | 
						|
+	void (*setpower)(struct device *, unsigned int maskval);
 | 
						|
+};
 | 
						|
+
 | 
						|
+#endif /* __LINUX_SPI_MMC_SPI_H */
 | 
						|
--- a/drivers/mmc/core/bus.c
 | 
						|
+++ b/drivers/mmc/core/bus.c
 | 
						|
@@ -19,6 +19,7 @@
 | 
						|
 
 | 
						|
 #include "sysfs.h"
 | 
						|
 #include "core.h"
 | 
						|
+#include "sdio_cis.h"
 | 
						|
 #include "bus.h"
 | 
						|
 
 | 
						|
 #define dev_to_mmc_card(d)	container_of(d, struct mmc_card, dev)
 | 
						|
@@ -34,6 +35,8 @@
 | 
						|
 		return sprintf(buf, "MMC\n");
 | 
						|
 	case MMC_TYPE_SD:
 | 
						|
 		return sprintf(buf, "SD\n");
 | 
						|
+	case MMC_TYPE_SDIO:
 | 
						|
+		return sprintf(buf, "SDIO\n");
 | 
						|
 	default:
 | 
						|
 		return -EFAULT;
 | 
						|
 	}
 | 
						|
@@ -55,36 +58,37 @@
 | 
						|
 }
 | 
						|
 
 | 
						|
 static int
 | 
						|
-mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
 | 
						|
-		int buf_size)
 | 
						|
+mmc_bus_uevent(struct device *dev, char **envp,
 | 
						|
+		int num_envp, char *buffer, int buffer_size)
 | 
						|
 {
 | 
						|
 	struct mmc_card *card = dev_to_mmc_card(dev);
 | 
						|
-	int retval = 0, i = 0, length = 0;
 | 
						|
-
 | 
						|
-#define add_env(fmt,val) do {					\
 | 
						|
-	retval = add_uevent_var(envp, num_envp, &i,		\
 | 
						|
-				buf, buf_size, &length,		\
 | 
						|
-				fmt, val);			\
 | 
						|
-	if (retval)						\
 | 
						|
-		return retval;					\
 | 
						|
-} while (0);
 | 
						|
+	const char *type;
 | 
						|
+	int retval = 0;
 | 
						|
+	int i = 0, len = 0;
 | 
						|
 
 | 
						|
 	switch (card->type) {
 | 
						|
 	case MMC_TYPE_MMC:
 | 
						|
-		add_env("MMC_TYPE=%s", "MMC");
 | 
						|
+		type = "MMC";
 | 
						|
 		break;
 | 
						|
 	case MMC_TYPE_SD:
 | 
						|
-		add_env("MMC_TYPE=%s", "SD");
 | 
						|
+		type = "SD";
 | 
						|
 		break;
 | 
						|
+	case MMC_TYPE_SDIO:
 | 
						|
+		type = "SDIO";
 | 
						|
+		break;
 | 
						|
+	default:
 | 
						|
+		type = NULL;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	add_env("MMC_NAME=%s", mmc_card_name(card));
 | 
						|
-
 | 
						|
-#undef add_env
 | 
						|
+	if (type) {
 | 
						|
+		retval = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, "MMC_TYPE=%s", type);
 | 
						|
+		if (retval)
 | 
						|
+			return retval;
 | 
						|
+	}
 | 
						|
 
 | 
						|
-	envp[i] = NULL;
 | 
						|
+	retval = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, "MMC_NAME=%s", mmc_card_name(card));
 | 
						|
 
 | 
						|
-	return 0;
 | 
						|
+	return retval;
 | 
						|
 }
 | 
						|
 
 | 
						|
 static int mmc_bus_probe(struct device *dev)
 | 
						|
@@ -176,6 +180,11 @@
 | 
						|
 {
 | 
						|
 	struct mmc_card *card = dev_to_mmc_card(dev);
 | 
						|
 
 | 
						|
+	sdio_free_common_cis(card);
 | 
						|
+
 | 
						|
+	if (card->info)
 | 
						|
+		kfree(card->info);
 | 
						|
+
 | 
						|
 	kfree(card);
 | 
						|
 }
 | 
						|
 
 | 
						|
@@ -221,15 +230,25 @@
 | 
						|
 		if (mmc_card_blockaddr(card))
 | 
						|
 			type = "SDHC";
 | 
						|
 		break;
 | 
						|
+	case MMC_TYPE_SDIO:
 | 
						|
+		type = "SDIO";
 | 
						|
+		break;
 | 
						|
 	default:
 | 
						|
 		type = "?";
 | 
						|
 		break;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	printk(KERN_INFO "%s: new %s%s card at address %04x\n",
 | 
						|
-		mmc_hostname(card->host),
 | 
						|
-		mmc_card_highspeed(card) ? "high speed " : "",
 | 
						|
-		type, card->rca);
 | 
						|
+	if (mmc_host_is_spi(card->host)) {
 | 
						|
+		printk(KERN_INFO "%s: new %s%s card on SPI\n",
 | 
						|
+			mmc_hostname(card->host),
 | 
						|
+			mmc_card_highspeed(card) ? "high speed " : "",
 | 
						|
+			type);
 | 
						|
+	} else {
 | 
						|
+		printk(KERN_INFO "%s: new %s%s card at address %04x\n",
 | 
						|
+			mmc_hostname(card->host),
 | 
						|
+			mmc_card_highspeed(card) ? "high speed " : "",
 | 
						|
+			type, card->rca);
 | 
						|
+	}
 | 
						|
 
 | 
						|
 	card->dev.uevent_suppress = 1;
 | 
						|
 
 | 
						|
@@ -261,8 +280,13 @@
 | 
						|
 void mmc_remove_card(struct mmc_card *card)
 | 
						|
 {
 | 
						|
 	if (mmc_card_present(card)) {
 | 
						|
-		printk(KERN_INFO "%s: card %04x removed\n",
 | 
						|
-			mmc_hostname(card->host), card->rca);
 | 
						|
+		if (mmc_host_is_spi(card->host)) {
 | 
						|
+			printk(KERN_INFO "%s: SPI card removed\n",
 | 
						|
+				mmc_hostname(card->host));
 | 
						|
+		} else {
 | 
						|
+			printk(KERN_INFO "%s: card %04x removed\n",
 | 
						|
+				mmc_hostname(card->host), card->rca);
 | 
						|
+		}
 | 
						|
 
 | 
						|
 		if (card->host->bus_ops->sysfs_remove)
 | 
						|
 			card->host->bus_ops->sysfs_remove(card->host, card);
 | 
						|
--- a/drivers/mmc/core/core.c
 | 
						|
+++ b/drivers/mmc/core/core.c
 | 
						|
@@ -18,7 +18,7 @@
 | 
						|
 #include <linux/delay.h>
 | 
						|
 #include <linux/pagemap.h>
 | 
						|
 #include <linux/err.h>
 | 
						|
-#include <asm/scatterlist.h>
 | 
						|
+#include <linux/leds.h>
 | 
						|
 #include <linux/scatterlist.h>
 | 
						|
 
 | 
						|
 #include <linux/mmc/card.h>
 | 
						|
@@ -29,16 +29,27 @@
 | 
						|
 #include "core.h"
 | 
						|
 #include "bus.h"
 | 
						|
 #include "host.h"
 | 
						|
+#include "sdio_bus.h"
 | 
						|
 
 | 
						|
 #include "mmc_ops.h"
 | 
						|
 #include "sd_ops.h"
 | 
						|
+#include "sdio_ops.h"
 | 
						|
 
 | 
						|
 extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
 | 
						|
 extern int mmc_attach_sd(struct mmc_host *host, u32 ocr);
 | 
						|
+extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
 | 
						|
 
 | 
						|
 static struct workqueue_struct *workqueue;
 | 
						|
 
 | 
						|
 /*
 | 
						|
+ * Enabling software CRCs on the data blocks can be a significant (30%)
 | 
						|
+ * performance cost, and for other reasons may not always be desired.
 | 
						|
+ * So we allow it it to be disabled.
 | 
						|
+ */
 | 
						|
+int use_spi_crc = 1;
 | 
						|
+module_param(use_spi_crc, bool, 0);
 | 
						|
+
 | 
						|
+/*
 | 
						|
  * Internal function. Schedule delayed work in the MMC work queue.
 | 
						|
  */
 | 
						|
 static int mmc_schedule_delayed_work(struct delayed_work *work,
 | 
						|
@@ -68,6 +79,11 @@
 | 
						|
 	struct mmc_command *cmd = mrq->cmd;
 | 
						|
 	int err = cmd->error;
 | 
						|
 
 | 
						|
+	if (err && cmd->retries && mmc_host_is_spi(host)) {
 | 
						|
+		if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
 | 
						|
+			cmd->retries = 0;
 | 
						|
+	}
 | 
						|
+
 | 
						|
 	if (err && cmd->retries) {
 | 
						|
 		pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
 | 
						|
 			mmc_hostname(host), cmd->opcode, err);
 | 
						|
@@ -76,6 +92,8 @@
 | 
						|
 		cmd->error = 0;
 | 
						|
 		host->ops->request(host, mrq);
 | 
						|
 	} else {
 | 
						|
+		led_trigger_event(host->led, LED_OFF);
 | 
						|
+
 | 
						|
 		pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
 | 
						|
 			mmc_hostname(host), cmd->opcode, err,
 | 
						|
 			cmd->resp[0], cmd->resp[1],
 | 
						|
@@ -118,7 +136,7 @@
 | 
						|
 			"tsac %d ms nsac %d\n",
 | 
						|
 			mmc_hostname(host), mrq->data->blksz,
 | 
						|
 			mrq->data->blocks, mrq->data->flags,
 | 
						|
-			mrq->data->timeout_ns / 10000000,
 | 
						|
+			mrq->data->timeout_ns / 1000000,
 | 
						|
 			mrq->data->timeout_clks);
 | 
						|
 	}
 | 
						|
 
 | 
						|
@@ -130,6 +148,8 @@
 | 
						|
 
 | 
						|
 	WARN_ON(!host->claimed);
 | 
						|
 
 | 
						|
+	led_trigger_event(host->led, LED_FULL);
 | 
						|
+
 | 
						|
 	mrq->cmd->error = 0;
 | 
						|
 	mrq->cmd->mrq = mrq;
 | 
						|
 	if (mrq->data) {
 | 
						|
@@ -199,7 +219,7 @@
 | 
						|
 {
 | 
						|
 	struct mmc_request mrq;
 | 
						|
 
 | 
						|
-	BUG_ON(!host->claimed);
 | 
						|
+	WARN_ON(!host->claimed);
 | 
						|
 
 | 
						|
 	memset(&mrq, 0, sizeof(struct mmc_request));
 | 
						|
 
 | 
						|
@@ -220,17 +240,24 @@
 | 
						|
  *	mmc_set_data_timeout - set the timeout for a data command
 | 
						|
  *	@data: data phase for command
 | 
						|
  *	@card: the MMC card associated with the data transfer
 | 
						|
- *	@write: flag to differentiate reads from writes
 | 
						|
  *
 | 
						|
  *	Computes the data timeout parameters according to the
 | 
						|
  *	correct algorithm given the card type.
 | 
						|
  */
 | 
						|
-void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
 | 
						|
-			  int write)
 | 
						|
+void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
 | 
						|
 {
 | 
						|
 	unsigned int mult;
 | 
						|
 
 | 
						|
 	/*
 | 
						|
+	 * SDIO cards only define an upper 1 s limit on access.
 | 
						|
+	 */
 | 
						|
+	if (mmc_card_sdio(card)) {
 | 
						|
+		data->timeout_ns = 1000000000;
 | 
						|
+		data->timeout_clks = 0;
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
 	 * SD cards use a 100 multiplier rather than 10
 | 
						|
 	 */
 | 
						|
 	mult = mmc_card_sd(card) ? 100 : 10;
 | 
						|
@@ -239,7 +266,7 @@
 | 
						|
 	 * Scale up the multiplier (and therefore the timeout) by
 | 
						|
 	 * the r2w factor for writes.
 | 
						|
 	 */
 | 
						|
-	if (write)
 | 
						|
+	if (data->flags & MMC_DATA_WRITE)
 | 
						|
 		mult <<= card->csd.r2w_factor;
 | 
						|
 
 | 
						|
 	data->timeout_ns = card->csd.tacc_ns * mult;
 | 
						|
@@ -255,7 +282,7 @@
 | 
						|
 		timeout_us += data->timeout_clks * 1000 /
 | 
						|
 			(card->host->ios.clock / 1000);
 | 
						|
 
 | 
						|
-		if (write)
 | 
						|
+		if (data->flags & MMC_DATA_WRITE)
 | 
						|
 			limit_us = 250000;
 | 
						|
 		else
 | 
						|
 			limit_us = 100000;
 | 
						|
@@ -272,15 +299,20 @@
 | 
						|
 EXPORT_SYMBOL(mmc_set_data_timeout);
 | 
						|
 
 | 
						|
 /**
 | 
						|
- *	mmc_claim_host - exclusively claim a host
 | 
						|
+ *	__mmc_claim_host - exclusively claim a host
 | 
						|
  *	@host: mmc host to claim
 | 
						|
+ *	@abort: whether or not the operation should be aborted
 | 
						|
  *
 | 
						|
- *	Claim a host for a set of operations.
 | 
						|
+ *	Claim a host for a set of operations.  If @abort is non null and
 | 
						|
+ *	dereference a non-zero value then this will return prematurely with
 | 
						|
+ *	that non-zero value without acquiring the lock.  Returns zero
 | 
						|
+ *	with the lock held otherwise.
 | 
						|
  */
 | 
						|
-void mmc_claim_host(struct mmc_host *host)
 | 
						|
+int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
 | 
						|
 {
 | 
						|
 	DECLARE_WAITQUEUE(wait, current);
 | 
						|
 	unsigned long flags;
 | 
						|
+	int stop;
 | 
						|
 
 | 
						|
 	might_sleep();
 | 
						|
 
 | 
						|
@@ -288,19 +320,24 @@
 | 
						|
 	spin_lock_irqsave(&host->lock, flags);
 | 
						|
 	while (1) {
 | 
						|
 		set_current_state(TASK_UNINTERRUPTIBLE);
 | 
						|
-		if (!host->claimed)
 | 
						|
+		stop = abort ? atomic_read(abort) : 0;
 | 
						|
+		if (stop || !host->claimed)
 | 
						|
 			break;
 | 
						|
 		spin_unlock_irqrestore(&host->lock, flags);
 | 
						|
 		schedule();
 | 
						|
 		spin_lock_irqsave(&host->lock, flags);
 | 
						|
 	}
 | 
						|
 	set_current_state(TASK_RUNNING);
 | 
						|
-	host->claimed = 1;
 | 
						|
+	if (!stop)
 | 
						|
+		host->claimed = 1;
 | 
						|
+	else
 | 
						|
+		wake_up(&host->wq);
 | 
						|
 	spin_unlock_irqrestore(&host->lock, flags);
 | 
						|
 	remove_wait_queue(&host->wq, &wait);
 | 
						|
+	return stop;
 | 
						|
 }
 | 
						|
 
 | 
						|
-EXPORT_SYMBOL(mmc_claim_host);
 | 
						|
+EXPORT_SYMBOL(__mmc_claim_host);
 | 
						|
 
 | 
						|
 /**
 | 
						|
  *	mmc_release_host - release a host
 | 
						|
@@ -313,7 +350,7 @@
 | 
						|
 {
 | 
						|
 	unsigned long flags;
 | 
						|
 
 | 
						|
-	BUG_ON(!host->claimed);
 | 
						|
+	WARN_ON(!host->claimed);
 | 
						|
 
 | 
						|
 	spin_lock_irqsave(&host->lock, flags);
 | 
						|
 	host->claimed = 0;
 | 
						|
@@ -433,19 +470,32 @@
 | 
						|
 	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;
 | 
						|
 	mmc_set_ios(host);
 | 
						|
 
 | 
						|
-	mmc_delay(1);
 | 
						|
+	/*
 | 
						|
+	 * This delay should be sufficient to allow the power supply
 | 
						|
+	 * to reach the minimum voltage.
 | 
						|
+	 */
 | 
						|
+	mmc_delay(2);
 | 
						|
 
 | 
						|
 	host->ios.clock = host->f_min;
 | 
						|
 	host->ios.power_mode = MMC_POWER_ON;
 | 
						|
 	mmc_set_ios(host);
 | 
						|
 
 | 
						|
+	/*
 | 
						|
+	 * This delay must be at least 74 clock sizes, or 1 ms, or the
 | 
						|
+	 * time required to reach a stable voltage.
 | 
						|
+	 */
 | 
						|
 	mmc_delay(2);
 | 
						|
 }
 | 
						|
 
 | 
						|
@@ -453,8 +503,10 @@
 | 
						|
 {
 | 
						|
 	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;
 | 
						|
@@ -511,7 +563,7 @@
 | 
						|
 	BUG_ON(!host);
 | 
						|
 	BUG_ON(!ops);
 | 
						|
 
 | 
						|
-	BUG_ON(!host->claimed);
 | 
						|
+	WARN_ON(!host->claimed);
 | 
						|
 
 | 
						|
 	spin_lock_irqsave(&host->lock, flags);
 | 
						|
 
 | 
						|
@@ -535,8 +587,8 @@
 | 
						|
 
 | 
						|
 	BUG_ON(!host);
 | 
						|
 
 | 
						|
-	BUG_ON(!host->claimed);
 | 
						|
-	BUG_ON(!host->bus_ops);
 | 
						|
+	WARN_ON(!host->claimed);
 | 
						|
+	WARN_ON(!host->bus_ops);
 | 
						|
 
 | 
						|
 	spin_lock_irqsave(&host->lock, flags);
 | 
						|
 
 | 
						|
@@ -564,7 +616,7 @@
 | 
						|
 #ifdef CONFIG_MMC_DEBUG
 | 
						|
 	unsigned long flags;
 | 
						|
 	spin_lock_irqsave(&host->lock, flags);
 | 
						|
-	BUG_ON(host->removed);
 | 
						|
+	WARN_ON(host->removed);
 | 
						|
 	spin_unlock_irqrestore(&host->lock, flags);
 | 
						|
 #endif
 | 
						|
 
 | 
						|
@@ -597,24 +649,38 @@
 | 
						|
 
 | 
						|
 		mmc_send_if_cond(host, host->ocr_avail);
 | 
						|
 
 | 
						|
+		/*
 | 
						|
+		 * First we search for SDIO...
 | 
						|
+		 */
 | 
						|
+		err = mmc_send_io_op_cond(host, 0, &ocr);
 | 
						|
+		if (!err) {
 | 
						|
+			if (mmc_attach_sdio(host, ocr))
 | 
						|
+				mmc_power_off(host);
 | 
						|
+			return;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/*
 | 
						|
+		 * ...then normal SD...
 | 
						|
+		 */
 | 
						|
 		err = mmc_send_app_op_cond(host, 0, &ocr);
 | 
						|
-		if (err == MMC_ERR_NONE) {
 | 
						|
+		if (!err) {
 | 
						|
 			if (mmc_attach_sd(host, ocr))
 | 
						|
 				mmc_power_off(host);
 | 
						|
-		} else {
 | 
						|
-			/*
 | 
						|
-			 * If we fail to detect any SD cards then try
 | 
						|
-			 * searching for MMC cards.
 | 
						|
-			 */
 | 
						|
-			err = mmc_send_op_cond(host, 0, &ocr);
 | 
						|
-			if (err == MMC_ERR_NONE) {
 | 
						|
-				if (mmc_attach_mmc(host, ocr))
 | 
						|
-					mmc_power_off(host);
 | 
						|
-			} else {
 | 
						|
+			return;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/*
 | 
						|
+		 * ...and finally MMC.
 | 
						|
+		 */
 | 
						|
+		err = mmc_send_op_cond(host, 0, &ocr);
 | 
						|
+		if (!err) {
 | 
						|
+			if (mmc_attach_mmc(host, ocr))
 | 
						|
 				mmc_power_off(host);
 | 
						|
-				mmc_release_host(host);
 | 
						|
-			}
 | 
						|
+			return;
 | 
						|
 		}
 | 
						|
+
 | 
						|
+		mmc_release_host(host);
 | 
						|
+		mmc_power_off(host);
 | 
						|
 	} else {
 | 
						|
 		if (host->bus_ops->detect && !host->bus_dead)
 | 
						|
 			host->bus_ops->detect(host);
 | 
						|
@@ -725,22 +791,38 @@
 | 
						|
 		return -ENOMEM;
 | 
						|
 
 | 
						|
 	ret = mmc_register_bus();
 | 
						|
-	if (ret == 0) {
 | 
						|
-		ret = mmc_register_host_class();
 | 
						|
-		if (ret)
 | 
						|
-			mmc_unregister_bus();
 | 
						|
-	}
 | 
						|
+	if (ret)
 | 
						|
+		goto destroy_workqueue;
 | 
						|
+
 | 
						|
+	ret = mmc_register_host_class();
 | 
						|
+	if (ret)
 | 
						|
+		goto unregister_bus;
 | 
						|
+
 | 
						|
+	ret = sdio_register_bus();
 | 
						|
+	if (ret)
 | 
						|
+		goto unregister_host_class;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+unregister_host_class:
 | 
						|
+	mmc_unregister_host_class();
 | 
						|
+unregister_bus:
 | 
						|
+	mmc_unregister_bus();
 | 
						|
+destroy_workqueue:
 | 
						|
+	destroy_workqueue(workqueue);
 | 
						|
+
 | 
						|
 	return ret;
 | 
						|
 }
 | 
						|
 
 | 
						|
 static void __exit mmc_exit(void)
 | 
						|
 {
 | 
						|
+	sdio_unregister_bus();
 | 
						|
 	mmc_unregister_host_class();
 | 
						|
 	mmc_unregister_bus();
 | 
						|
 	destroy_workqueue(workqueue);
 | 
						|
 }
 | 
						|
 
 | 
						|
-module_init(mmc_init);
 | 
						|
+subsys_initcall(mmc_init);
 | 
						|
 module_exit(mmc_exit);
 | 
						|
 
 | 
						|
 MODULE_LICENSE("GPL");
 | 
						|
--- a/drivers/mmc/core/core.h
 | 
						|
+++ b/drivers/mmc/core/core.h
 | 
						|
@@ -48,5 +48,7 @@
 | 
						|
 void mmc_start_host(struct mmc_host *host);
 | 
						|
 void mmc_stop_host(struct mmc_host *host);
 | 
						|
 
 | 
						|
+extern int use_spi_crc;
 | 
						|
+
 | 
						|
 #endif
 | 
						|
 
 | 
						|
--- a/drivers/mmc/core/host.c
 | 
						|
+++ b/drivers/mmc/core/host.c
 | 
						|
@@ -15,6 +15,7 @@
 | 
						|
 #include <linux/err.h>
 | 
						|
 #include <linux/idr.h>
 | 
						|
 #include <linux/pagemap.h>
 | 
						|
+#include <linux/leds.h>
 | 
						|
 
 | 
						|
 #include <linux/mmc/host.h>
 | 
						|
 
 | 
						|
@@ -100,6 +101,9 @@
 | 
						|
 {
 | 
						|
 	int err;
 | 
						|
 
 | 
						|
+	WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
 | 
						|
+		!host->ops->enable_sdio_irq);
 | 
						|
+
 | 
						|
 	if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
 | 
						|
 		return -ENOMEM;
 | 
						|
 
 | 
						|
@@ -112,6 +116,8 @@
 | 
						|
 	snprintf(host->class_dev.bus_id, BUS_ID_SIZE,
 | 
						|
 		 "mmc%d", host->index);
 | 
						|
 
 | 
						|
+	led_trigger_register_simple(host->class_dev.bus_id, &host->led);
 | 
						|
+
 | 
						|
 	err = device_add(&host->class_dev);
 | 
						|
 	if (err)
 | 
						|
 		return err;
 | 
						|
@@ -137,6 +143,8 @@
 | 
						|
 
 | 
						|
 	device_del(&host->class_dev);
 | 
						|
 
 | 
						|
+	led_trigger_unregister_simple(host->led);
 | 
						|
+
 | 
						|
 	spin_lock(&mmc_host_lock);
 | 
						|
 	idr_remove(&mmc_host_idr, host->index);
 | 
						|
 	spin_unlock(&mmc_host_lock);
 | 
						|
--- a/drivers/mmc/core/mmc.c
 | 
						|
+++ b/drivers/mmc/core/mmc.c
 | 
						|
@@ -161,13 +161,12 @@
 | 
						|
 {
 | 
						|
 	int err;
 | 
						|
 	u8 *ext_csd;
 | 
						|
+	unsigned int ext_csd_struct;
 | 
						|
 
 | 
						|
 	BUG_ON(!card);
 | 
						|
 
 | 
						|
-	err = MMC_ERR_FAILED;
 | 
						|
-
 | 
						|
 	if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
 | 
						|
-		return MMC_ERR_NONE;
 | 
						|
+		return 0;
 | 
						|
 
 | 
						|
 	/*
 | 
						|
 	 * As the ext_csd is so large and mostly unused, we don't store the
 | 
						|
@@ -176,13 +175,19 @@
 | 
						|
 	ext_csd = kmalloc(512, GFP_KERNEL);
 | 
						|
 	if (!ext_csd) {
 | 
						|
 		printk(KERN_ERR "%s: could not allocate a buffer to "
 | 
						|
-			"receive the ext_csd. mmc v4 cards will be "
 | 
						|
-			"treated as v3.\n", mmc_hostname(card->host));
 | 
						|
-		return MMC_ERR_FAILED;
 | 
						|
+			"receive the ext_csd.\n", mmc_hostname(card->host));
 | 
						|
+		return -ENOMEM;
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	err = mmc_send_ext_csd(card, ext_csd);
 | 
						|
-	if (err != MMC_ERR_NONE) {
 | 
						|
+	if (err) {
 | 
						|
+		/*
 | 
						|
+		 * We all hosts that cannot perform the command
 | 
						|
+		 * to fail more gracefully
 | 
						|
+		 */
 | 
						|
+		if (err != -EINVAL)
 | 
						|
+			goto out;
 | 
						|
+
 | 
						|
 		/*
 | 
						|
 		 * High capacity cards should have this "magic" size
 | 
						|
 		 * stored in their CSD.
 | 
						|
@@ -197,18 +202,30 @@
 | 
						|
 				"EXT_CSD, performance might "
 | 
						|
 				"suffer.\n",
 | 
						|
 				mmc_hostname(card->host));
 | 
						|
-			err = MMC_ERR_NONE;
 | 
						|
+			err = 0;
 | 
						|
 		}
 | 
						|
+
 | 
						|
 		goto out;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	card->ext_csd.sectors =
 | 
						|
-		ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
 | 
						|
-		ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
 | 
						|
-		ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
 | 
						|
-		ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
 | 
						|
-	if (card->ext_csd.sectors)
 | 
						|
-		mmc_card_set_blockaddr(card);
 | 
						|
+	ext_csd_struct = ext_csd[EXT_CSD_REV];
 | 
						|
+	if (ext_csd_struct > 2) {
 | 
						|
+		printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
 | 
						|
+			"version %d\n", mmc_hostname(card->host),
 | 
						|
+			ext_csd_struct);
 | 
						|
+		err = -EINVAL;
 | 
						|
+		goto out;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (ext_csd_struct >= 2) {
 | 
						|
+		card->ext_csd.sectors =
 | 
						|
+			ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
 | 
						|
+			ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
 | 
						|
+			ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
 | 
						|
+			ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
 | 
						|
+		if (card->ext_csd.sectors)
 | 
						|
+			mmc_card_set_blockaddr(card);
 | 
						|
+	}
 | 
						|
 
 | 
						|
 	switch (ext_csd[EXT_CSD_CARD_TYPE]) {
 | 
						|
 	case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
 | 
						|
@@ -246,7 +263,7 @@
 | 
						|
 	unsigned int max_dtr;
 | 
						|
 
 | 
						|
 	BUG_ON(!host);
 | 
						|
-	BUG_ON(!host->claimed);
 | 
						|
+	WARN_ON(!host->claimed);
 | 
						|
 
 | 
						|
 	/*
 | 
						|
 	 * Since we're changing the OCR value, we seem to
 | 
						|
@@ -258,19 +275,33 @@
 | 
						|
 
 | 
						|
 	/* The extra bit indicates that we support high capacity */
 | 
						|
 	err = mmc_send_op_cond(host, ocr | (1 << 30), NULL);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		goto err;
 | 
						|
 
 | 
						|
 	/*
 | 
						|
+	 * For SPI, enable CRC as appropriate.
 | 
						|
+	 */
 | 
						|
+	if (mmc_host_is_spi(host)) {
 | 
						|
+		err = mmc_spi_set_crc(host, use_spi_crc);
 | 
						|
+		if (err)
 | 
						|
+			goto err;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
 	 * Fetch CID from card.
 | 
						|
 	 */
 | 
						|
-	err = mmc_all_send_cid(host, cid);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (mmc_host_is_spi(host))
 | 
						|
+		err = mmc_send_cid(host, cid);
 | 
						|
+	else
 | 
						|
+		err = mmc_all_send_cid(host, cid);
 | 
						|
+	if (err)
 | 
						|
 		goto err;
 | 
						|
 
 | 
						|
 	if (oldcard) {
 | 
						|
-		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
 | 
						|
+		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
 | 
						|
+			err = -ENOENT;
 | 
						|
 			goto err;
 | 
						|
+		}
 | 
						|
 
 | 
						|
 		card = oldcard;
 | 
						|
 	} else {
 | 
						|
@@ -278,8 +309,10 @@
 | 
						|
 		 * Allocate card structure.
 | 
						|
 		 */
 | 
						|
 		card = mmc_alloc_card(host);
 | 
						|
-		if (IS_ERR(card))
 | 
						|
+		if (IS_ERR(card)) {
 | 
						|
+			err = PTR_ERR(card);
 | 
						|
 			goto err;
 | 
						|
+		}
 | 
						|
 
 | 
						|
 		card->type = MMC_TYPE_MMC;
 | 
						|
 		card->rca = 1;
 | 
						|
@@ -287,43 +320,47 @@
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	/*
 | 
						|
-	 * Set card RCA.
 | 
						|
+	 * For native busses:  set card RCA and quit 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)
 | 
						|
+			goto free_card;
 | 
						|
 
 | 
						|
-	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
 | 
						|
+		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
 | 
						|
+	}
 | 
						|
 
 | 
						|
 	if (!oldcard) {
 | 
						|
 		/*
 | 
						|
 		 * Fetch CSD from card.
 | 
						|
 		 */
 | 
						|
 		err = mmc_send_csd(card, card->raw_csd);
 | 
						|
-		if (err != MMC_ERR_NONE)
 | 
						|
+		if (err)
 | 
						|
 			goto free_card;
 | 
						|
 
 | 
						|
 		err = mmc_decode_csd(card);
 | 
						|
-		if (err < 0)
 | 
						|
+		if (err)
 | 
						|
 			goto free_card;
 | 
						|
 		err = mmc_decode_cid(card);
 | 
						|
-		if (err < 0)
 | 
						|
+		if (err)
 | 
						|
 			goto free_card;
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	/*
 | 
						|
 	 * Select card, as all following commands rely on that.
 | 
						|
 	 */
 | 
						|
-	err = mmc_select_card(card);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
-		goto free_card;
 | 
						|
+	if (!mmc_host_is_spi(host)) {
 | 
						|
+		err = mmc_select_card(card);
 | 
						|
+		if (err)
 | 
						|
+			goto free_card;
 | 
						|
+	}
 | 
						|
 
 | 
						|
 	if (!oldcard) {
 | 
						|
 		/*
 | 
						|
-		 * Fetch and process extened CSD.
 | 
						|
+		 * Fetch and process extended CSD.
 | 
						|
 		 */
 | 
						|
 		err = mmc_read_ext_csd(card);
 | 
						|
-		if (err != MMC_ERR_NONE)
 | 
						|
+		if (err)
 | 
						|
 			goto free_card;
 | 
						|
 	}
 | 
						|
 
 | 
						|
@@ -334,7 +371,7 @@
 | 
						|
 		(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
 | 
						|
 		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 | 
						|
 			EXT_CSD_HS_TIMING, 1);
 | 
						|
-		if (err != MMC_ERR_NONE)
 | 
						|
+		if (err)
 | 
						|
 			goto free_card;
 | 
						|
 
 | 
						|
 		mmc_card_set_highspeed(card);
 | 
						|
@@ -363,7 +400,7 @@
 | 
						|
 		(host->caps & MMC_CAP_4_BIT_DATA)) {
 | 
						|
 		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 | 
						|
 			EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
 | 
						|
-		if (err != MMC_ERR_NONE)
 | 
						|
+		if (err)
 | 
						|
 			goto free_card;
 | 
						|
 
 | 
						|
 		mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
 | 
						|
@@ -372,14 +409,14 @@
 | 
						|
 	if (!oldcard)
 | 
						|
 		host->card = card;
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 
 | 
						|
 free_card:
 | 
						|
 	if (!oldcard)
 | 
						|
 		mmc_remove_card(card);
 | 
						|
 err:
 | 
						|
 
 | 
						|
-	return MMC_ERR_FAILED;
 | 
						|
+	return err;
 | 
						|
 }
 | 
						|
 
 | 
						|
 /*
 | 
						|
@@ -413,7 +450,7 @@
 | 
						|
 
 | 
						|
 	mmc_release_host(host);
 | 
						|
 
 | 
						|
-	if (err != MMC_ERR_NONE) {
 | 
						|
+	if (err) {
 | 
						|
 		mmc_remove(host);
 | 
						|
 
 | 
						|
 		mmc_claim_host(host);
 | 
						|
@@ -480,7 +517,8 @@
 | 
						|
 	BUG_ON(!host->card);
 | 
						|
 
 | 
						|
 	mmc_claim_host(host);
 | 
						|
-	mmc_deselect_cards(host);
 | 
						|
+	if (!mmc_host_is_spi(host))
 | 
						|
+		mmc_deselect_cards(host);
 | 
						|
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 | 
						|
 	mmc_release_host(host);
 | 
						|
 }
 | 
						|
@@ -502,7 +540,7 @@
 | 
						|
 	err = mmc_init_card(host, host->ocr, host->card);
 | 
						|
 	mmc_release_host(host);
 | 
						|
 
 | 
						|
-	if (err != MMC_ERR_NONE) {
 | 
						|
+	if (err) {
 | 
						|
 		mmc_remove(host);
 | 
						|
 
 | 
						|
 		mmc_claim_host(host);
 | 
						|
@@ -536,11 +574,20 @@
 | 
						|
 	int err;
 | 
						|
 
 | 
						|
 	BUG_ON(!host);
 | 
						|
-	BUG_ON(!host->claimed);
 | 
						|
+	WARN_ON(!host->claimed);
 | 
						|
 
 | 
						|
 	mmc_attach_bus(host, &mmc_ops);
 | 
						|
 
 | 
						|
 	/*
 | 
						|
+	 * We need to get OCR a different way for SPI.
 | 
						|
+	 */
 | 
						|
+	if (mmc_host_is_spi(host)) {
 | 
						|
+		err = mmc_spi_read_ocr(host, 1, &ocr);
 | 
						|
+		if (err)
 | 
						|
+			goto err;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
 	 * Sanity check the voltages that the card claims to
 | 
						|
 	 * support.
 | 
						|
 	 */
 | 
						|
@@ -565,7 +612,7 @@
 | 
						|
 	 * Detect and init the card.
 | 
						|
 	 */
 | 
						|
 	err = mmc_init_card(host, host->ocr, NULL);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		goto err;
 | 
						|
 
 | 
						|
 	mmc_release_host(host);
 | 
						|
@@ -587,6 +634,6 @@
 | 
						|
 	printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
 | 
						|
 		mmc_hostname(host), err);
 | 
						|
 
 | 
						|
-	return 0;
 | 
						|
+	return err;
 | 
						|
 }
 | 
						|
 
 | 
						|
--- a/drivers/mmc/core/mmc_ops.c
 | 
						|
+++ b/drivers/mmc/core/mmc_ops.c
 | 
						|
@@ -10,7 +10,6 @@
 | 
						|
  */
 | 
						|
 
 | 
						|
 #include <linux/types.h>
 | 
						|
-#include <asm/scatterlist.h>
 | 
						|
 #include <linux/scatterlist.h>
 | 
						|
 
 | 
						|
 #include <linux/mmc/host.h>
 | 
						|
@@ -40,10 +39,10 @@
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		return err;
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
 int mmc_select_card(struct mmc_card *card)
 | 
						|
@@ -63,23 +62,36 @@
 | 
						|
 	int err;
 | 
						|
 	struct mmc_command cmd;
 | 
						|
 
 | 
						|
-	mmc_set_chip_select(host, MMC_CS_HIGH);
 | 
						|
-
 | 
						|
-	mmc_delay(1);
 | 
						|
+	/*
 | 
						|
+	 * Non-SPI hosts need to prevent chipselect going active during
 | 
						|
+	 * GO_IDLE; that would put chips into SPI mode.  Remind them of
 | 
						|
+	 * that in case of hardware that won't pull up DAT3/nCS otherwise.
 | 
						|
+	 *
 | 
						|
+	 * SPI hosts ignore ios.chip_select; it's managed according to
 | 
						|
+	 * rules that must accomodate non-MMC slaves which this layer
 | 
						|
+	 * won't even know about.
 | 
						|
+	 */
 | 
						|
+	if (!mmc_host_is_spi(host)) {
 | 
						|
+		mmc_set_chip_select(host, MMC_CS_HIGH);
 | 
						|
+		mmc_delay(1);
 | 
						|
+	}
 | 
						|
 
 | 
						|
 	memset(&cmd, 0, sizeof(struct mmc_command));
 | 
						|
 
 | 
						|
 	cmd.opcode = MMC_GO_IDLE_STATE;
 | 
						|
 	cmd.arg = 0;
 | 
						|
-	cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
 | 
						|
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
 | 
						|
 
 | 
						|
 	err = mmc_wait_for_cmd(host, &cmd, 0);
 | 
						|
 
 | 
						|
 	mmc_delay(1);
 | 
						|
 
 | 
						|
-	mmc_set_chip_select(host, MMC_CS_DONTCARE);
 | 
						|
+	if (!mmc_host_is_spi(host)) {
 | 
						|
+		mmc_set_chip_select(host, MMC_CS_DONTCARE);
 | 
						|
+		mmc_delay(1);
 | 
						|
+	}
 | 
						|
 
 | 
						|
-	mmc_delay(1);
 | 
						|
+	host->use_spi_crc = 0;
 | 
						|
 
 | 
						|
 	return err;
 | 
						|
 }
 | 
						|
@@ -94,23 +106,33 @@
 | 
						|
 	memset(&cmd, 0, sizeof(struct mmc_command));
 | 
						|
 
 | 
						|
 	cmd.opcode = MMC_SEND_OP_COND;
 | 
						|
-	cmd.arg = ocr;
 | 
						|
-	cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
 | 
						|
+	cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
 | 
						|
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
 | 
						|
 
 | 
						|
 	for (i = 100; i; i--) {
 | 
						|
 		err = mmc_wait_for_cmd(host, &cmd, 0);
 | 
						|
-		if (err != MMC_ERR_NONE)
 | 
						|
+		if (err)
 | 
						|
 			break;
 | 
						|
 
 | 
						|
-		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
 | 
						|
+		/* if we're just probing, do a single pass */
 | 
						|
+		if (ocr == 0)
 | 
						|
 			break;
 | 
						|
 
 | 
						|
-		err = MMC_ERR_TIMEOUT;
 | 
						|
+		/* otherwise wait until reset completes */
 | 
						|
+		if (mmc_host_is_spi(host)) {
 | 
						|
+			if (!(cmd.resp[0] & R1_SPI_IDLE))
 | 
						|
+				break;
 | 
						|
+		} else {
 | 
						|
+			if (cmd.resp[0] & MMC_CARD_BUSY)
 | 
						|
+				break;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		err = -ETIMEDOUT;
 | 
						|
 
 | 
						|
 		mmc_delay(10);
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	if (rocr)
 | 
						|
+	if (rocr && !mmc_host_is_spi(host))
 | 
						|
 		*rocr = cmd.resp[0];
 | 
						|
 
 | 
						|
 	return err;
 | 
						|
@@ -131,12 +153,12 @@
 | 
						|
 	cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
 | 
						|
 
 | 
						|
 	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		return err;
 | 
						|
 
 | 
						|
 	memcpy(cid, cmd.resp, sizeof(u32) * 4);
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
 int mmc_set_relative_addr(struct mmc_card *card)
 | 
						|
@@ -154,46 +176,52 @@
 | 
						|
 	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
 | 
						|
 
 | 
						|
 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		return err;
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
-int mmc_send_csd(struct mmc_card *card, u32 *csd)
 | 
						|
+static int
 | 
						|
+mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
 | 
						|
 {
 | 
						|
 	int err;
 | 
						|
 	struct mmc_command cmd;
 | 
						|
 
 | 
						|
-	BUG_ON(!card);
 | 
						|
-	BUG_ON(!card->host);
 | 
						|
-	BUG_ON(!csd);
 | 
						|
+	BUG_ON(!host);
 | 
						|
+	BUG_ON(!cxd);
 | 
						|
 
 | 
						|
 	memset(&cmd, 0, sizeof(struct mmc_command));
 | 
						|
 
 | 
						|
-	cmd.opcode = MMC_SEND_CSD;
 | 
						|
-	cmd.arg = card->rca << 16;
 | 
						|
+	cmd.opcode = opcode;
 | 
						|
+	cmd.arg = arg;
 | 
						|
 	cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
 | 
						|
 
 | 
						|
-	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
 | 
						|
+	if (err)
 | 
						|
 		return err;
 | 
						|
 
 | 
						|
-	memcpy(csd, cmd.resp, sizeof(u32) * 4);
 | 
						|
+	memcpy(cxd, cmd.resp, sizeof(u32) * 4);
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
-int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
 | 
						|
+static int
 | 
						|
+mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
 | 
						|
+		u32 opcode, void *buf, unsigned len)
 | 
						|
 {
 | 
						|
 	struct mmc_request mrq;
 | 
						|
 	struct mmc_command cmd;
 | 
						|
 	struct mmc_data data;
 | 
						|
 	struct scatterlist sg;
 | 
						|
+	void *data_buf;
 | 
						|
 
 | 
						|
-	BUG_ON(!card);
 | 
						|
-	BUG_ON(!card->host);
 | 
						|
-	BUG_ON(!ext_csd);
 | 
						|
+	/* dma onto stack is unsafe/nonportable, but callers to this
 | 
						|
+	 * routine normally provide temporary on-stack buffers ...
 | 
						|
+	 */
 | 
						|
+	data_buf = kmalloc(len, GFP_KERNEL);
 | 
						|
+	if (data_buf == NULL)
 | 
						|
+		return -ENOMEM;
 | 
						|
 
 | 
						|
 	memset(&mrq, 0, sizeof(struct mmc_request));
 | 
						|
 	memset(&cmd, 0, sizeof(struct mmc_command));
 | 
						|
@@ -202,28 +230,117 @@
 | 
						|
 	mrq.cmd = &cmd;
 | 
						|
 	mrq.data = &data;
 | 
						|
 
 | 
						|
-	cmd.opcode = MMC_SEND_EXT_CSD;
 | 
						|
+	cmd.opcode = opcode;
 | 
						|
 	cmd.arg = 0;
 | 
						|
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
 | 
						|
 
 | 
						|
-	data.blksz = 512;
 | 
						|
+	/* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
 | 
						|
+	 * rely on callers to never use this with "native" calls for reading
 | 
						|
+	 * CSD or CID.  Native versions of those commands use the R2 type,
 | 
						|
+	 * not R1 plus a data block.
 | 
						|
+	 */
 | 
						|
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 | 
						|
+
 | 
						|
+	data.blksz = len;
 | 
						|
 	data.blocks = 1;
 | 
						|
 	data.flags = MMC_DATA_READ;
 | 
						|
 	data.sg = &sg;
 | 
						|
 	data.sg_len = 1;
 | 
						|
 
 | 
						|
-	sg_init_one(&sg, ext_csd, 512);
 | 
						|
+	sg_init_one(&sg, data_buf, len);
 | 
						|
+
 | 
						|
+	if (card)
 | 
						|
+		mmc_set_data_timeout(&data, card);
 | 
						|
 
 | 
						|
-	mmc_set_data_timeout(&data, card, 0);
 | 
						|
+	mmc_wait_for_req(host, &mrq);
 | 
						|
 
 | 
						|
-	mmc_wait_for_req(card->host, &mrq);
 | 
						|
+	memcpy(buf, data_buf, len);
 | 
						|
+	kfree(data_buf);
 | 
						|
 
 | 
						|
-	if (cmd.error != MMC_ERR_NONE)
 | 
						|
+	if (cmd.error)
 | 
						|
 		return cmd.error;
 | 
						|
-	if (data.error != MMC_ERR_NONE)
 | 
						|
+	if (data.error)
 | 
						|
 		return data.error;
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+int mmc_send_csd(struct mmc_card *card, u32 *csd)
 | 
						|
+{
 | 
						|
+	int ret, i;
 | 
						|
+
 | 
						|
+	if (!mmc_host_is_spi(card->host))
 | 
						|
+		return mmc_send_cxd_native(card->host, card->rca << 16,
 | 
						|
+				csd, MMC_SEND_CSD);
 | 
						|
+
 | 
						|
+	ret = mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd, 16);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	for (i = 0;i < 4;i++)
 | 
						|
+		csd[i] = be32_to_cpu(csd[i]);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+int mmc_send_cid(struct mmc_host *host, u32 *cid)
 | 
						|
+{
 | 
						|
+	int ret, i;
 | 
						|
+
 | 
						|
+	if (!mmc_host_is_spi(host)) {
 | 
						|
+		if (!host->card)
 | 
						|
+			return -EINVAL;
 | 
						|
+		return mmc_send_cxd_native(host, host->card->rca << 16,
 | 
						|
+				cid, MMC_SEND_CID);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid, 16);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	for (i = 0;i < 4;i++)
 | 
						|
+		cid[i] = be32_to_cpu(cid[i]);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
 | 
						|
+{
 | 
						|
+	return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
 | 
						|
+			ext_csd, 512);
 | 
						|
+}
 | 
						|
+
 | 
						|
+int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
 | 
						|
+{
 | 
						|
+	struct mmc_command cmd;
 | 
						|
+	int err;
 | 
						|
+
 | 
						|
+	memset(&cmd, 0, sizeof(struct mmc_command));
 | 
						|
+
 | 
						|
+	cmd.opcode = MMC_SPI_READ_OCR;
 | 
						|
+	cmd.arg = highcap ? (1 << 30) : 0;
 | 
						|
+	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, int use_crc)
 | 
						|
+{
 | 
						|
+	struct mmc_command cmd;
 | 
						|
+	int err;
 | 
						|
+
 | 
						|
+	memset(&cmd, 0, sizeof(struct mmc_command));
 | 
						|
+
 | 
						|
+	cmd.opcode = MMC_SPI_CRC_ON_OFF;
 | 
						|
+	cmd.flags = MMC_RSP_SPI_R1;
 | 
						|
+	cmd.arg = use_crc;
 | 
						|
+
 | 
						|
+	err = mmc_wait_for_cmd(host, &cmd, 0);
 | 
						|
+	if (!err)
 | 
						|
+		host->use_spi_crc = use_crc;
 | 
						|
+	return err;
 | 
						|
 }
 | 
						|
 
 | 
						|
 int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
 | 
						|
@@ -241,13 +358,13 @@
 | 
						|
 		  (index << 16) |
 | 
						|
 		  (value << 8) |
 | 
						|
 		  set;
 | 
						|
-	cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
 | 
						|
+	cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
 | 
						|
 
 | 
						|
 	err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		return err;
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
 int mmc_send_status(struct mmc_card *card, u32 *status)
 | 
						|
@@ -261,16 +378,20 @@
 | 
						|
 	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)
 | 
						|
+	if (err)
 | 
						|
 		return err;
 | 
						|
 
 | 
						|
+	/* NOTE: callers are required to understand the difference
 | 
						|
+	 * between "native" and SPI format status words!
 | 
						|
+	 */
 | 
						|
 	if (status)
 | 
						|
 		*status = cmd.resp[0];
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
--- a/drivers/mmc/core/mmc_ops.h
 | 
						|
+++ b/drivers/mmc/core/mmc_ops.h
 | 
						|
@@ -22,6 +22,9 @@
 | 
						|
 int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
 | 
						|
 int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
 | 
						|
 int mmc_send_status(struct mmc_card *card, u32 *status);
 | 
						|
+int mmc_send_cid(struct mmc_host *host, u32 *cid);
 | 
						|
+int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
 | 
						|
+int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
 | 
						|
 
 | 
						|
 #endif
 | 
						|
 
 | 
						|
--- a/drivers/mmc/core/sd.c
 | 
						|
+++ b/drivers/mmc/core/sd.c
 | 
						|
@@ -166,8 +166,6 @@
 | 
						|
 	unsigned int scr_struct;
 | 
						|
 	u32 resp[4];
 | 
						|
 
 | 
						|
-	BUG_ON(!mmc_card_sd(card));
 | 
						|
-
 | 
						|
 	resp[3] = card->raw_scr[1];
 | 
						|
 	resp[2] = card->raw_scr[0];
 | 
						|
 
 | 
						|
@@ -193,30 +191,38 @@
 | 
						|
 	u8 *status;
 | 
						|
 
 | 
						|
 	if (card->scr.sda_vsn < SCR_SPEC_VER_1)
 | 
						|
-		return MMC_ERR_NONE;
 | 
						|
+		return 0;
 | 
						|
 
 | 
						|
 	if (!(card->csd.cmdclass & CCC_SWITCH)) {
 | 
						|
 		printk(KERN_WARNING "%s: card lacks mandatory switch "
 | 
						|
 			"function, performance might suffer.\n",
 | 
						|
 			mmc_hostname(card->host));
 | 
						|
-		return MMC_ERR_NONE;
 | 
						|
+		return 0;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	err = MMC_ERR_FAILED;
 | 
						|
+	err = -EIO;
 | 
						|
 
 | 
						|
 	status = kmalloc(64, GFP_KERNEL);
 | 
						|
 	if (!status) {
 | 
						|
 		printk(KERN_ERR "%s: could not allocate a buffer for "
 | 
						|
 			"switch capabilities.\n", mmc_hostname(card->host));
 | 
						|
-		return err;
 | 
						|
+		return -ENOMEM;
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	err = mmc_sd_switch(card, 0, 0, 1, status);
 | 
						|
-	if (err != MMC_ERR_NONE) {
 | 
						|
+	if (err) {
 | 
						|
+		/*
 | 
						|
+		 * We all hosts that cannot perform the command
 | 
						|
+		 * to fail more gracefully
 | 
						|
+		 */
 | 
						|
+		if (err != -EINVAL)
 | 
						|
+			goto out;
 | 
						|
+
 | 
						|
 		printk(KERN_WARNING "%s: problem reading switch "
 | 
						|
 			"capabilities, performance might suffer.\n",
 | 
						|
 			mmc_hostname(card->host));
 | 
						|
-		err = MMC_ERR_NONE;
 | 
						|
+		err = 0;
 | 
						|
+
 | 
						|
 		goto out;
 | 
						|
 	}
 | 
						|
 
 | 
						|
@@ -238,28 +244,28 @@
 | 
						|
 	u8 *status;
 | 
						|
 
 | 
						|
 	if (card->scr.sda_vsn < SCR_SPEC_VER_1)
 | 
						|
-		return MMC_ERR_NONE;
 | 
						|
+		return 0;
 | 
						|
 
 | 
						|
 	if (!(card->csd.cmdclass & CCC_SWITCH))
 | 
						|
-		return MMC_ERR_NONE;
 | 
						|
+		return 0;
 | 
						|
 
 | 
						|
 	if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
 | 
						|
-		return MMC_ERR_NONE;
 | 
						|
+		return 0;
 | 
						|
 
 | 
						|
 	if (card->sw_caps.hs_max_dtr == 0)
 | 
						|
-		return MMC_ERR_NONE;
 | 
						|
+		return 0;
 | 
						|
 
 | 
						|
-	err = MMC_ERR_FAILED;
 | 
						|
+	err = -EIO;
 | 
						|
 
 | 
						|
 	status = kmalloc(64, GFP_KERNEL);
 | 
						|
 	if (!status) {
 | 
						|
 		printk(KERN_ERR "%s: could not allocate a buffer for "
 | 
						|
 			"switch capabilities.\n", mmc_hostname(card->host));
 | 
						|
-		return err;
 | 
						|
+		return -ENOMEM;
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	err = mmc_sd_switch(card, 1, 0, 1, status);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		goto out;
 | 
						|
 
 | 
						|
 	if ((status[16] & 0xF) != 1) {
 | 
						|
@@ -292,7 +298,7 @@
 | 
						|
 	unsigned int max_dtr;
 | 
						|
 
 | 
						|
 	BUG_ON(!host);
 | 
						|
-	BUG_ON(!host->claimed);
 | 
						|
+	WARN_ON(!host->claimed);
 | 
						|
 
 | 
						|
 	/*
 | 
						|
 	 * Since we're changing the OCR value, we seem to
 | 
						|
@@ -309,23 +315,37 @@
 | 
						|
 	 * block-addressed SDHC cards.
 | 
						|
 	 */
 | 
						|
 	err = mmc_send_if_cond(host, ocr);
 | 
						|
-	if (err == MMC_ERR_NONE)
 | 
						|
+	if (!err)
 | 
						|
 		ocr |= 1 << 30;
 | 
						|
 
 | 
						|
 	err = mmc_send_app_op_cond(host, ocr, NULL);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		goto err;
 | 
						|
 
 | 
						|
 	/*
 | 
						|
+	 * For SPI, enable CRC as appropriate.
 | 
						|
+	 */
 | 
						|
+	if (mmc_host_is_spi(host)) {
 | 
						|
+		err = mmc_spi_set_crc(host, use_spi_crc);
 | 
						|
+		if (err)
 | 
						|
+			goto err;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
 	 * Fetch CID from card.
 | 
						|
 	 */
 | 
						|
-	err = mmc_all_send_cid(host, cid);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (mmc_host_is_spi(host))
 | 
						|
+		err = mmc_send_cid(host, cid);
 | 
						|
+	else
 | 
						|
+		err = mmc_all_send_cid(host, cid);
 | 
						|
+	if (err)
 | 
						|
 		goto err;
 | 
						|
 
 | 
						|
 	if (oldcard) {
 | 
						|
-		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
 | 
						|
+		if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
 | 
						|
+			err = -ENOENT;
 | 
						|
 			goto err;
 | 
						|
+		}
 | 
						|
 
 | 
						|
 		card = oldcard;
 | 
						|
 	} else {
 | 
						|
@@ -333,32 +353,36 @@
 | 
						|
 		 * Allocate card structure.
 | 
						|
 		 */
 | 
						|
 		card = mmc_alloc_card(host);
 | 
						|
-		if (IS_ERR(card))
 | 
						|
+		if (IS_ERR(card)) {
 | 
						|
+			err = PTR_ERR(card);
 | 
						|
 			goto err;
 | 
						|
+		}
 | 
						|
 
 | 
						|
 		card->type = MMC_TYPE_SD;
 | 
						|
 		memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	/*
 | 
						|
-	 * Set card RCA.
 | 
						|
+	 * For native busses:  get card RCA and quit 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)
 | 
						|
+			goto free_card;
 | 
						|
 
 | 
						|
-	mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
 | 
						|
+		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
 | 
						|
+	}
 | 
						|
 
 | 
						|
 	if (!oldcard) {
 | 
						|
 		/*
 | 
						|
 		 * Fetch CSD from card.
 | 
						|
 		 */
 | 
						|
 		err = mmc_send_csd(card, card->raw_csd);
 | 
						|
-		if (err != MMC_ERR_NONE)
 | 
						|
+		if (err)
 | 
						|
 			goto free_card;
 | 
						|
 
 | 
						|
 		err = mmc_decode_csd(card);
 | 
						|
-		if (err < 0)
 | 
						|
+		if (err)
 | 
						|
 			goto free_card;
 | 
						|
 
 | 
						|
 		mmc_decode_cid(card);
 | 
						|
@@ -367,16 +391,18 @@
 | 
						|
 	/*
 | 
						|
 	 * Select card, as all following commands rely on that.
 | 
						|
 	 */
 | 
						|
-	err = mmc_select_card(card);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
-		goto free_card;
 | 
						|
+	if (!mmc_host_is_spi(host)) {
 | 
						|
+		err = mmc_select_card(card);
 | 
						|
+		if (err)
 | 
						|
+			goto free_card;
 | 
						|
+	}
 | 
						|
 
 | 
						|
 	if (!oldcard) {
 | 
						|
 		/*
 | 
						|
 		 * Fetch SCR from card.
 | 
						|
 		 */
 | 
						|
 		err = mmc_app_send_scr(card, card->raw_scr);
 | 
						|
-		if (err != MMC_ERR_NONE)
 | 
						|
+		if (err)
 | 
						|
 			goto free_card;
 | 
						|
 
 | 
						|
 		err = mmc_decode_scr(card);
 | 
						|
@@ -387,7 +413,7 @@
 | 
						|
 		 * Fetch switch information from card.
 | 
						|
 		 */
 | 
						|
 		err = mmc_read_switch(card);
 | 
						|
-		if (err != MMC_ERR_NONE)
 | 
						|
+		if (err)
 | 
						|
 			goto free_card;
 | 
						|
 	}
 | 
						|
 
 | 
						|
@@ -395,7 +421,7 @@
 | 
						|
 	 * Attempt to change to high-speed (if supported)
 | 
						|
 	 */
 | 
						|
 	err = mmc_switch_hs(card);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		goto free_card;
 | 
						|
 
 | 
						|
 	/*
 | 
						|
@@ -418,7 +444,7 @@
 | 
						|
 	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)
 | 
						|
+		if (err)
 | 
						|
 			goto free_card;
 | 
						|
 
 | 
						|
 		mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
 | 
						|
@@ -442,14 +468,14 @@
 | 
						|
 	if (!oldcard)
 | 
						|
 		host->card = card;
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 
 | 
						|
 free_card:
 | 
						|
 	if (!oldcard)
 | 
						|
 		mmc_remove_card(card);
 | 
						|
 err:
 | 
						|
 
 | 
						|
-	return MMC_ERR_FAILED;
 | 
						|
+	return err;
 | 
						|
 }
 | 
						|
 
 | 
						|
 /*
 | 
						|
@@ -483,7 +509,7 @@
 | 
						|
 
 | 
						|
 	mmc_release_host(host);
 | 
						|
 
 | 
						|
-	if (err != MMC_ERR_NONE) {
 | 
						|
+	if (err) {
 | 
						|
 		mmc_sd_remove(host);
 | 
						|
 
 | 
						|
 		mmc_claim_host(host);
 | 
						|
@@ -552,7 +578,8 @@
 | 
						|
 	BUG_ON(!host->card);
 | 
						|
 
 | 
						|
 	mmc_claim_host(host);
 | 
						|
-	mmc_deselect_cards(host);
 | 
						|
+	if (!mmc_host_is_spi(host))
 | 
						|
+		mmc_deselect_cards(host);
 | 
						|
 	host->card->state &= ~MMC_STATE_HIGHSPEED;
 | 
						|
 	mmc_release_host(host);
 | 
						|
 }
 | 
						|
@@ -574,7 +601,7 @@
 | 
						|
 	err = mmc_sd_init_card(host, host->ocr, host->card);
 | 
						|
 	mmc_release_host(host);
 | 
						|
 
 | 
						|
-	if (err != MMC_ERR_NONE) {
 | 
						|
+	if (err) {
 | 
						|
 		mmc_sd_remove(host);
 | 
						|
 
 | 
						|
 		mmc_claim_host(host);
 | 
						|
@@ -608,11 +635,22 @@
 | 
						|
 	int err;
 | 
						|
 
 | 
						|
 	BUG_ON(!host);
 | 
						|
-	BUG_ON(!host->claimed);
 | 
						|
+	WARN_ON(!host->claimed);
 | 
						|
 
 | 
						|
 	mmc_attach_bus(host, &mmc_sd_ops);
 | 
						|
 
 | 
						|
 	/*
 | 
						|
+	 * We need to get OCR a different way for SPI.
 | 
						|
+	 */
 | 
						|
+	if (mmc_host_is_spi(host)) {
 | 
						|
+		mmc_go_idle(host);
 | 
						|
+
 | 
						|
+		err = mmc_spi_read_ocr(host, 0, &ocr);
 | 
						|
+		if (err)
 | 
						|
+			goto err;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
 	 * Sanity check the voltages that the card claims to
 | 
						|
 	 * support.
 | 
						|
 	 */
 | 
						|
@@ -644,7 +682,7 @@
 | 
						|
 	 * Detect and init the card.
 | 
						|
 	 */
 | 
						|
 	err = mmc_sd_init_card(host, host->ocr, NULL);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		goto err;
 | 
						|
 
 | 
						|
 	mmc_release_host(host);
 | 
						|
@@ -666,6 +704,6 @@
 | 
						|
 	printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
 | 
						|
 		mmc_hostname(host), err);
 | 
						|
 
 | 
						|
-	return 0;
 | 
						|
+	return err;
 | 
						|
 }
 | 
						|
 
 | 
						|
--- a/drivers/mmc/core/sd_ops.c
 | 
						|
+++ b/drivers/mmc/core/sd_ops.c
 | 
						|
@@ -10,7 +10,6 @@
 | 
						|
  */
 | 
						|
 
 | 
						|
 #include <linux/types.h>
 | 
						|
-#include <asm/scatterlist.h>
 | 
						|
 #include <linux/scatterlist.h>
 | 
						|
 
 | 
						|
 #include <linux/mmc/host.h>
 | 
						|
@@ -33,21 +32,21 @@
 | 
						|
 
 | 
						|
 	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);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		return err;
 | 
						|
 
 | 
						|
 	/* Check that card supported application commands */
 | 
						|
-	if (!(cmd.resp[0] & R1_APP_CMD))
 | 
						|
-		return MMC_ERR_FAILED;
 | 
						|
+	if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
 | 
						|
+		return -EOPNOTSUPP;
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
 /**
 | 
						|
@@ -73,7 +72,7 @@
 | 
						|
 	BUG_ON(!cmd);
 | 
						|
 	BUG_ON(retries < 0);
 | 
						|
 
 | 
						|
-	err = MMC_ERR_INVALID;
 | 
						|
+	err = -EIO;
 | 
						|
 
 | 
						|
 	/*
 | 
						|
 	 * We have to resend MMC_APP_CMD for each attempt so
 | 
						|
@@ -83,8 +82,14 @@
 | 
						|
 		memset(&mrq, 0, sizeof(struct mmc_request));
 | 
						|
 
 | 
						|
 		err = mmc_app_cmd(host, card);
 | 
						|
-		if (err != MMC_ERR_NONE)
 | 
						|
+		if (err) {
 | 
						|
+			/* no point in retrying; no APP commands allowed */
 | 
						|
+			if (mmc_host_is_spi(host)) {
 | 
						|
+				if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
 | 
						|
+					break;
 | 
						|
+			}
 | 
						|
 			continue;
 | 
						|
+		}
 | 
						|
 
 | 
						|
 		memset(&mrq, 0, sizeof(struct mmc_request));
 | 
						|
 
 | 
						|
@@ -97,8 +102,14 @@
 | 
						|
 		mmc_wait_for_req(host, &mrq);
 | 
						|
 
 | 
						|
 		err = cmd->error;
 | 
						|
-		if (cmd->error == MMC_ERR_NONE)
 | 
						|
+		if (!cmd->error)
 | 
						|
 			break;
 | 
						|
+
 | 
						|
+		/* no point in retrying illegal APP commands */
 | 
						|
+		if (mmc_host_is_spi(host)) {
 | 
						|
+			if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
 | 
						|
+				break;
 | 
						|
+		}
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	return err;
 | 
						|
@@ -127,14 +138,14 @@
 | 
						|
 		cmd.arg = SD_BUS_WIDTH_4;
 | 
						|
 		break;
 | 
						|
 	default:
 | 
						|
-		return MMC_ERR_INVALID;
 | 
						|
+		return -EINVAL;
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		return err;
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
 int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
 | 
						|
@@ -147,23 +158,36 @@
 | 
						|
 	memset(&cmd, 0, sizeof(struct mmc_command));
 | 
						|
 
 | 
						|
 	cmd.opcode = SD_APP_OP_COND;
 | 
						|
-	cmd.arg = ocr;
 | 
						|
-	cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
 | 
						|
+	if (mmc_host_is_spi(host))
 | 
						|
+		cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
 | 
						|
+	else
 | 
						|
+		cmd.arg = ocr;
 | 
						|
+	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)
 | 
						|
+		if (err)
 | 
						|
 			break;
 | 
						|
 
 | 
						|
-		if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
 | 
						|
+		/* if we're just probing, do a single pass */
 | 
						|
+		if (ocr == 0)
 | 
						|
 			break;
 | 
						|
 
 | 
						|
-		err = MMC_ERR_TIMEOUT;
 | 
						|
+		/* otherwise wait until reset completes */
 | 
						|
+		if (mmc_host_is_spi(host)) {
 | 
						|
+			if (!(cmd.resp[0] & R1_SPI_IDLE))
 | 
						|
+				break;
 | 
						|
+		} else {
 | 
						|
+			if (cmd.resp[0] & MMC_CARD_BUSY)
 | 
						|
+				break;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		err = -ETIMEDOUT;
 | 
						|
 
 | 
						|
 		mmc_delay(10);
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	if (rocr)
 | 
						|
+	if (rocr && !mmc_host_is_spi(host))
 | 
						|
 		*rocr = cmd.resp[0];
 | 
						|
 
 | 
						|
 	return err;
 | 
						|
@@ -174,6 +198,7 @@
 | 
						|
 	struct mmc_command cmd;
 | 
						|
 	int err;
 | 
						|
 	static const u8 test_pattern = 0xAA;
 | 
						|
+	u8 result_pattern;
 | 
						|
 
 | 
						|
 	/*
 | 
						|
 	 * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
 | 
						|
@@ -182,16 +207,21 @@
 | 
						|
 	 */
 | 
						|
 	cmd.opcode = SD_SEND_IF_COND;
 | 
						|
 	cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
 | 
						|
-	cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
 | 
						|
+	cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;
 | 
						|
 
 | 
						|
 	err = mmc_wait_for_cmd(host, &cmd, 0);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		return err;
 | 
						|
 
 | 
						|
-	if ((cmd.resp[0] & 0xFF) != test_pattern)
 | 
						|
-		return MMC_ERR_FAILED;
 | 
						|
+	if (mmc_host_is_spi(host))
 | 
						|
+		result_pattern = cmd.resp[1] & 0xFF;
 | 
						|
+	else
 | 
						|
+		result_pattern = cmd.resp[0] & 0xFF;
 | 
						|
+
 | 
						|
+	if (result_pattern != test_pattern)
 | 
						|
+		return -EIO;
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
 int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
 | 
						|
@@ -209,12 +239,12 @@
 | 
						|
 	cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
 | 
						|
 
 | 
						|
 	err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		return err;
 | 
						|
 
 | 
						|
 	*rca = cmd.resp[0] >> 16;
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
 int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
 | 
						|
@@ -229,8 +259,10 @@
 | 
						|
 	BUG_ON(!card->host);
 | 
						|
 	BUG_ON(!scr);
 | 
						|
 
 | 
						|
+	/* NOTE: caller guarantees scr is heap-allocated */
 | 
						|
+
 | 
						|
 	err = mmc_app_cmd(card->host, card);
 | 
						|
-	if (err != MMC_ERR_NONE)
 | 
						|
+	if (err)
 | 
						|
 		return err;
 | 
						|
 
 | 
						|
 	memset(&mrq, 0, sizeof(struct mmc_request));
 | 
						|
@@ -242,7 +274,7 @@
 | 
						|
 
 | 
						|
 	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;
 | 
						|
@@ -252,19 +284,19 @@
 | 
						|
 
 | 
						|
 	sg_init_one(&sg, scr, 8);
 | 
						|
 
 | 
						|
-	mmc_set_data_timeout(&data, card, 0);
 | 
						|
+	mmc_set_data_timeout(&data, card);
 | 
						|
 
 | 
						|
 	mmc_wait_for_req(card->host, &mrq);
 | 
						|
 
 | 
						|
-	if (cmd.error != MMC_ERR_NONE)
 | 
						|
+	if (cmd.error)
 | 
						|
 		return cmd.error;
 | 
						|
-	if (data.error != MMC_ERR_NONE)
 | 
						|
+	if (data.error)
 | 
						|
 		return data.error;
 | 
						|
 
 | 
						|
-	scr[0] = ntohl(scr[0]);
 | 
						|
-	scr[1] = ntohl(scr[1]);
 | 
						|
+	scr[0] = be32_to_cpu(scr[0]);
 | 
						|
+	scr[1] = be32_to_cpu(scr[1]);
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
 int mmc_sd_switch(struct mmc_card *card, int mode, int group,
 | 
						|
@@ -278,6 +310,8 @@
 | 
						|
 	BUG_ON(!card);
 | 
						|
 	BUG_ON(!card->host);
 | 
						|
 
 | 
						|
+	/* NOTE: caller guarantees resp is heap-allocated */
 | 
						|
+
 | 
						|
 	mode = !!mode;
 | 
						|
 	value &= 0xF;
 | 
						|
 
 | 
						|
@@ -292,7 +326,7 @@
 | 
						|
 	cmd.arg = mode << 31 | 0x00FFFFFF;
 | 
						|
 	cmd.arg &= ~(0xF << (group * 4));
 | 
						|
 	cmd.arg |= value << (group * 4);
 | 
						|
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
 | 
						|
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 | 
						|
 
 | 
						|
 	data.blksz = 64;
 | 
						|
 	data.blocks = 1;
 | 
						|
@@ -302,15 +336,15 @@
 | 
						|
 
 | 
						|
 	sg_init_one(&sg, resp, 64);
 | 
						|
 
 | 
						|
-	mmc_set_data_timeout(&data, card, 0);
 | 
						|
+	mmc_set_data_timeout(&data, card);
 | 
						|
 
 | 
						|
 	mmc_wait_for_req(card->host, &mrq);
 | 
						|
 
 | 
						|
-	if (cmd.error != MMC_ERR_NONE)
 | 
						|
+	if (cmd.error)
 | 
						|
 		return cmd.error;
 | 
						|
-	if (data.error != MMC_ERR_NONE)
 | 
						|
+	if (data.error)
 | 
						|
 		return data.error;
 | 
						|
 
 | 
						|
-	return MMC_ERR_NONE;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/mmc/core/sdio.c
 | 
						|
@@ -0,0 +1,395 @@
 | 
						|
+/*
 | 
						|
+ *  linux/drivers/mmc/sdio.c
 | 
						|
+ *
 | 
						|
+ *  Copyright 2006-2007 Pierre Ossman
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or modify
 | 
						|
+ * it under the terms of the GNU General Public License as published by
 | 
						|
+ * the Free Software Foundation; either version 2 of the License, or (at
 | 
						|
+ * your option) any later version.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/err.h>
 | 
						|
+
 | 
						|
+#include <linux/mmc/host.h>
 | 
						|
+#include <linux/mmc/card.h>
 | 
						|
+#include <linux/mmc/sdio.h>
 | 
						|
+#include <linux/mmc/sdio_func.h>
 | 
						|
+
 | 
						|
+#include "core.h"
 | 
						|
+#include "bus.h"
 | 
						|
+#include "sdio_bus.h"
 | 
						|
+#include "mmc_ops.h"
 | 
						|
+#include "sd_ops.h"
 | 
						|
+#include "sdio_ops.h"
 | 
						|
+#include "sdio_cis.h"
 | 
						|
+
 | 
						|
+static int sdio_read_fbr(struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	unsigned char data;
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 0, 0,
 | 
						|
+		SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data);
 | 
						|
+	if (ret)
 | 
						|
+		goto out;
 | 
						|
+
 | 
						|
+	data &= 0x0f;
 | 
						|
+
 | 
						|
+	if (data == 0x0f) {
 | 
						|
+		ret = mmc_io_rw_direct(func->card, 0, 0,
 | 
						|
+			SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF_EXT, 0, &data);
 | 
						|
+		if (ret)
 | 
						|
+			goto out;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	func->class = data;
 | 
						|
+
 | 
						|
+out:
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_init_func(struct mmc_card *card, unsigned int fn)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	struct sdio_func *func;
 | 
						|
+
 | 
						|
+	BUG_ON(fn > SDIO_MAX_FUNCS);
 | 
						|
+
 | 
						|
+	func = sdio_alloc_func(card);
 | 
						|
+	if (IS_ERR(func))
 | 
						|
+		return PTR_ERR(func);
 | 
						|
+
 | 
						|
+	func->num = fn;
 | 
						|
+
 | 
						|
+	ret = sdio_read_fbr(func);
 | 
						|
+	if (ret)
 | 
						|
+		goto fail;
 | 
						|
+
 | 
						|
+	ret = sdio_read_func_cis(func);
 | 
						|
+	if (ret)
 | 
						|
+		goto fail;
 | 
						|
+
 | 
						|
+	card->sdio_func[fn - 1] = func;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+fail:
 | 
						|
+	/*
 | 
						|
+	 * It is okay to remove the function here even though we hold
 | 
						|
+	 * the host lock as we haven't registered the device yet.
 | 
						|
+	 */
 | 
						|
+	sdio_remove_func(func);
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_read_cccr(struct mmc_card *card)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	int cccr_vsn;
 | 
						|
+	unsigned char data;
 | 
						|
+
 | 
						|
+	memset(&card->cccr, 0, sizeof(struct sdio_cccr));
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data);
 | 
						|
+	if (ret)
 | 
						|
+		goto out;
 | 
						|
+
 | 
						|
+	cccr_vsn = data & 0x0f;
 | 
						|
+
 | 
						|
+	if (cccr_vsn > SDIO_CCCR_REV_1_20) {
 | 
						|
+		printk(KERN_ERR "%s: unrecognised CCCR structure version %d\n",
 | 
						|
+			mmc_hostname(card->host), cccr_vsn);
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	card->cccr.sdio_vsn = (data & 0xf0) >> 4;
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CAPS, 0, &data);
 | 
						|
+	if (ret)
 | 
						|
+		goto out;
 | 
						|
+
 | 
						|
+	if (data & SDIO_CCCR_CAP_SMB)
 | 
						|
+		card->cccr.multi_block = 1;
 | 
						|
+	if (data & SDIO_CCCR_CAP_LSC)
 | 
						|
+		card->cccr.low_speed = 1;
 | 
						|
+	if (data & SDIO_CCCR_CAP_4BLS)
 | 
						|
+		card->cccr.wide_bus = 1;
 | 
						|
+
 | 
						|
+	if (cccr_vsn >= SDIO_CCCR_REV_1_10) {
 | 
						|
+		ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_POWER, 0, &data);
 | 
						|
+		if (ret)
 | 
						|
+			goto out;
 | 
						|
+
 | 
						|
+		if (data & SDIO_POWER_SMPC)
 | 
						|
+			card->cccr.high_power = 1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
 | 
						|
+		ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data);
 | 
						|
+		if (ret)
 | 
						|
+			goto out;
 | 
						|
+
 | 
						|
+		if (data & SDIO_SPEED_SHS)
 | 
						|
+			card->cccr.high_speed = 1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+out:
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_enable_wide(struct mmc_card *card)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	u8 ctrl;
 | 
						|
+
 | 
						|
+	if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	if (card->cccr.low_speed && !card->cccr.wide_bus)
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	ctrl |= SDIO_BUS_WIDTH_4BIT;
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Host is being removed. Free up the current card.
 | 
						|
+ */
 | 
						|
+static void mmc_sdio_remove(struct mmc_host *host)
 | 
						|
+{
 | 
						|
+	int i;
 | 
						|
+
 | 
						|
+	BUG_ON(!host);
 | 
						|
+	BUG_ON(!host->card);
 | 
						|
+
 | 
						|
+	for (i = 0;i < host->card->sdio_funcs;i++) {
 | 
						|
+		if (host->card->sdio_func[i]) {
 | 
						|
+			sdio_remove_func(host->card->sdio_func[i]);
 | 
						|
+			host->card->sdio_func[i] = NULL;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mmc_remove_card(host->card);
 | 
						|
+	host->card = NULL;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Card detection callback from host.
 | 
						|
+ */
 | 
						|
+static void mmc_sdio_detect(struct mmc_host *host)
 | 
						|
+{
 | 
						|
+	int err;
 | 
						|
+
 | 
						|
+	BUG_ON(!host);
 | 
						|
+	BUG_ON(!host->card);
 | 
						|
+
 | 
						|
+	mmc_claim_host(host);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Just check if our card has been removed.
 | 
						|
+	 */
 | 
						|
+	err = mmc_select_card(host->card);
 | 
						|
+
 | 
						|
+	mmc_release_host(host);
 | 
						|
+
 | 
						|
+	if (err) {
 | 
						|
+		mmc_sdio_remove(host);
 | 
						|
+
 | 
						|
+		mmc_claim_host(host);
 | 
						|
+		mmc_detach_bus(host);
 | 
						|
+		mmc_release_host(host);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+
 | 
						|
+static const struct mmc_bus_ops mmc_sdio_ops = {
 | 
						|
+	.remove = mmc_sdio_remove,
 | 
						|
+	.detect = mmc_sdio_detect,
 | 
						|
+};
 | 
						|
+
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Starting point for SDIO card init.
 | 
						|
+ */
 | 
						|
+int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
 | 
						|
+{
 | 
						|
+	int err;
 | 
						|
+	int i, funcs;
 | 
						|
+	struct mmc_card *card;
 | 
						|
+
 | 
						|
+	BUG_ON(!host);
 | 
						|
+	WARN_ON(!host->claimed);
 | 
						|
+
 | 
						|
+	mmc_attach_bus(host, &mmc_sdio_ops);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Sanity check the voltages that the card claims to
 | 
						|
+	 * support.
 | 
						|
+	 */
 | 
						|
+	if (ocr & 0x7F) {
 | 
						|
+		printk(KERN_WARNING "%s: card claims to support voltages "
 | 
						|
+		       "below the defined range. These will be ignored.\n",
 | 
						|
+		       mmc_hostname(host));
 | 
						|
+		ocr &= ~0x7F;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (ocr & MMC_VDD_165_195) {
 | 
						|
+		printk(KERN_WARNING "%s: SDIO card claims to support the "
 | 
						|
+		       "incompletely defined 'low voltage range'. This "
 | 
						|
+		       "will be ignored.\n", mmc_hostname(host));
 | 
						|
+		ocr &= ~MMC_VDD_165_195;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	host->ocr = mmc_select_voltage(host, ocr);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Can we support the voltage(s) of the card(s)?
 | 
						|
+	 */
 | 
						|
+	if (!host->ocr) {
 | 
						|
+		err = -EINVAL;
 | 
						|
+		goto err;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Inform the card of the voltage
 | 
						|
+	 */
 | 
						|
+	err = mmc_send_io_op_cond(host, host->ocr, &ocr);
 | 
						|
+	if (err)
 | 
						|
+		goto err;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * For SPI, enable CRC as appropriate.
 | 
						|
+	 */
 | 
						|
+	if (mmc_host_is_spi(host)) {
 | 
						|
+		err = mmc_spi_set_crc(host, use_spi_crc);
 | 
						|
+		if (err)
 | 
						|
+			goto err;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * The number of functions on the card is encoded inside
 | 
						|
+	 * the ocr.
 | 
						|
+	 */
 | 
						|
+	funcs = (ocr & 0x70000000) >> 28;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Allocate card structure.
 | 
						|
+	 */
 | 
						|
+	card = mmc_alloc_card(host);
 | 
						|
+	if (IS_ERR(card)) {
 | 
						|
+		err = PTR_ERR(card);
 | 
						|
+		goto err;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	card->type = MMC_TYPE_SDIO;
 | 
						|
+	card->sdio_funcs = funcs;
 | 
						|
+
 | 
						|
+	host->card = card;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * For native busses:  set card RCA and quit open drain mode.
 | 
						|
+	 */
 | 
						|
+	if (!mmc_host_is_spi(host)) {
 | 
						|
+		err = mmc_send_relative_addr(host, &card->rca);
 | 
						|
+		if (err)
 | 
						|
+			goto remove;
 | 
						|
+
 | 
						|
+		mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Select card, as all following commands rely on that.
 | 
						|
+	 */
 | 
						|
+	if (!mmc_host_is_spi(host)) {
 | 
						|
+		err = mmc_select_card(card);
 | 
						|
+		if (err)
 | 
						|
+			goto remove;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Read the common registers.
 | 
						|
+	 */
 | 
						|
+	err = sdio_read_cccr(card);
 | 
						|
+	if (err)
 | 
						|
+		goto remove;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Read the common CIS tuples.
 | 
						|
+	 */
 | 
						|
+	err = sdio_read_common_cis(card);
 | 
						|
+	if (err)
 | 
						|
+		goto remove;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * No support for high-speed yet, so just set
 | 
						|
+	 * the card's maximum speed.
 | 
						|
+	 */
 | 
						|
+	mmc_set_clock(host, card->cis.max_dtr);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Switch to wider bus (if supported).
 | 
						|
+	 */
 | 
						|
+	err = sdio_enable_wide(card);
 | 
						|
+	if (err)
 | 
						|
+		goto remove;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Initialize (but don't add) all present functions.
 | 
						|
+	 */
 | 
						|
+	for (i = 0;i < funcs;i++) {
 | 
						|
+		err = sdio_init_func(host->card, i + 1);
 | 
						|
+		if (err)
 | 
						|
+			goto remove;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	mmc_release_host(host);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * First add the card to the driver model...
 | 
						|
+	 */
 | 
						|
+	err = mmc_add_card(host->card);
 | 
						|
+	if (err)
 | 
						|
+		goto remove_added;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * ...then the SDIO functions.
 | 
						|
+	 */
 | 
						|
+	for (i = 0;i < funcs;i++) {
 | 
						|
+		err = sdio_add_func(host->card->sdio_func[i]);
 | 
						|
+		if (err)
 | 
						|
+			goto remove_added;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+
 | 
						|
+remove_added:
 | 
						|
+	/* Remove without lock if the device has been added. */
 | 
						|
+	mmc_sdio_remove(host);
 | 
						|
+	mmc_claim_host(host);
 | 
						|
+remove:
 | 
						|
+	/* And with lock if it hasn't been added. */
 | 
						|
+	if (host->card)
 | 
						|
+		mmc_sdio_remove(host);
 | 
						|
+err:
 | 
						|
+	mmc_detach_bus(host);
 | 
						|
+	mmc_release_host(host);
 | 
						|
+
 | 
						|
+	printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
 | 
						|
+		mmc_hostname(host), err);
 | 
						|
+
 | 
						|
+	return err;
 | 
						|
+}
 | 
						|
+
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/mmc/core/sdio_bus.c
 | 
						|
@@ -0,0 +1,265 @@
 | 
						|
+/*
 | 
						|
+ *  linux/drivers/mmc/core/sdio_bus.c
 | 
						|
+ *
 | 
						|
+ *  Copyright 2007 Pierre Ossman
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or modify
 | 
						|
+ * it under the terms of the GNU General Public License as published by
 | 
						|
+ * the Free Software Foundation; either version 2 of the License, or (at
 | 
						|
+ * your option) any later version.
 | 
						|
+ *
 | 
						|
+ * SDIO function driver model
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/device.h>
 | 
						|
+#include <linux/err.h>
 | 
						|
+
 | 
						|
+#include <linux/mmc/card.h>
 | 
						|
+#include <linux/mmc/sdio_func.h>
 | 
						|
+
 | 
						|
+#include "sdio_cis.h"
 | 
						|
+#include "sdio_bus.h"
 | 
						|
+
 | 
						|
+#define dev_to_sdio_func(d)	container_of(d, struct sdio_func, dev)
 | 
						|
+#define to_sdio_driver(d)      container_of(d, struct sdio_driver, drv)
 | 
						|
+
 | 
						|
+/* show configuration fields */
 | 
						|
+#define sdio_config_attr(field, format_string)				\
 | 
						|
+static ssize_t								\
 | 
						|
+field##_show(struct device *dev, struct device_attribute *attr, char *buf)				\
 | 
						|
+{									\
 | 
						|
+	struct sdio_func *func;						\
 | 
						|
+									\
 | 
						|
+	func = dev_to_sdio_func (dev);					\
 | 
						|
+	return sprintf (buf, format_string, func->field);		\
 | 
						|
+}
 | 
						|
+
 | 
						|
+sdio_config_attr(class, "0x%02x\n");
 | 
						|
+sdio_config_attr(vendor, "0x%04x\n");
 | 
						|
+sdio_config_attr(device, "0x%04x\n");
 | 
						|
+
 | 
						|
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
 | 
						|
+{
 | 
						|
+	struct sdio_func *func = dev_to_sdio_func (dev);
 | 
						|
+
 | 
						|
+	return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n",
 | 
						|
+			func->class, func->vendor, func->device);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct device_attribute sdio_dev_attrs[] = {
 | 
						|
+	__ATTR_RO(class),
 | 
						|
+	__ATTR_RO(vendor),
 | 
						|
+	__ATTR_RO(device),
 | 
						|
+	__ATTR_RO(modalias),
 | 
						|
+	__ATTR_NULL,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
 | 
						|
+	const struct sdio_device_id *id)
 | 
						|
+{
 | 
						|
+	if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)
 | 
						|
+		return NULL;
 | 
						|
+	if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)
 | 
						|
+		return NULL;
 | 
						|
+	if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)
 | 
						|
+		return NULL;
 | 
						|
+	return id;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
 | 
						|
+	struct sdio_driver *sdrv)
 | 
						|
+{
 | 
						|
+	const struct sdio_device_id *ids;
 | 
						|
+
 | 
						|
+	ids = sdrv->id_table;
 | 
						|
+
 | 
						|
+	if (ids) {
 | 
						|
+		while (ids->class || ids->vendor || ids->device) {
 | 
						|
+			if (sdio_match_one(func, ids))
 | 
						|
+				return ids;
 | 
						|
+			ids++;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return NULL;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_bus_match(struct device *dev, struct device_driver *drv)
 | 
						|
+{
 | 
						|
+	struct sdio_func *func = dev_to_sdio_func(dev);
 | 
						|
+	struct sdio_driver *sdrv = to_sdio_driver(drv);
 | 
						|
+
 | 
						|
+	if (sdio_match_device(func, sdrv))
 | 
						|
+		return 1;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int
 | 
						|
+sdio_bus_uevent(struct device *dev, char **envp,
 | 
						|
+		int num_envp, char *buffer, int buffer_size)
 | 
						|
+{
 | 
						|
+	struct sdio_func *func = dev_to_sdio_func(dev);
 | 
						|
+	int i = 0, len = 0;
 | 
						|
+
 | 
						|
+	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
 | 
						|
+			"SDIO_CLASS=%02X", func->class))
 | 
						|
+		return -ENOMEM;
 | 
						|
+
 | 
						|
+	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
 | 
						|
+			"SDIO_ID=%04X:%04X", func->vendor, func->device))
 | 
						|
+		return -ENOMEM;
 | 
						|
+
 | 
						|
+	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
 | 
						|
+			"MODALIAS=sdio:c%02Xv%04Xd%04X",
 | 
						|
+			func->class, func->vendor, func->device))
 | 
						|
+		return -ENOMEM;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_bus_probe(struct device *dev)
 | 
						|
+{
 | 
						|
+	struct sdio_driver *drv = to_sdio_driver(dev->driver);
 | 
						|
+	struct sdio_func *func = dev_to_sdio_func(dev);
 | 
						|
+	const struct sdio_device_id *id;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	id = sdio_match_device(func, drv);
 | 
						|
+	if (!id)
 | 
						|
+		return -ENODEV;
 | 
						|
+
 | 
						|
+	/* Set the default block size so the driver is sure it's something
 | 
						|
+	 * sensible. */
 | 
						|
+	sdio_claim_host(func);
 | 
						|
+	ret = sdio_set_block_size(func, 0);
 | 
						|
+	sdio_release_host(func);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	return drv->probe(func, id);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_bus_remove(struct device *dev)
 | 
						|
+{
 | 
						|
+	struct sdio_driver *drv = to_sdio_driver(dev->driver);
 | 
						|
+	struct sdio_func *func = dev_to_sdio_func(dev);
 | 
						|
+
 | 
						|
+	drv->remove(func);
 | 
						|
+
 | 
						|
+	if (func->irq_handler) {
 | 
						|
+		printk(KERN_WARNING "WARNING: driver %s did not remove "
 | 
						|
+			"its interrupt handler!\n", drv->name);
 | 
						|
+		sdio_claim_host(func);
 | 
						|
+		sdio_release_irq(func);
 | 
						|
+		sdio_release_host(func);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct bus_type sdio_bus_type = {
 | 
						|
+	.name		= "sdio",
 | 
						|
+	.dev_attrs	= sdio_dev_attrs,
 | 
						|
+	.match		= sdio_bus_match,
 | 
						|
+	.uevent		= sdio_bus_uevent,
 | 
						|
+	.probe		= sdio_bus_probe,
 | 
						|
+	.remove		= sdio_bus_remove,
 | 
						|
+};
 | 
						|
+
 | 
						|
+int sdio_register_bus(void)
 | 
						|
+{
 | 
						|
+	return bus_register(&sdio_bus_type);
 | 
						|
+}
 | 
						|
+
 | 
						|
+void sdio_unregister_bus(void)
 | 
						|
+{
 | 
						|
+	bus_unregister(&sdio_bus_type);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_register_driver - register a function driver
 | 
						|
+ *	@drv: SDIO function driver
 | 
						|
+ */
 | 
						|
+int sdio_register_driver(struct sdio_driver *drv)
 | 
						|
+{
 | 
						|
+	drv->drv.name = drv->name;
 | 
						|
+	drv->drv.bus = &sdio_bus_type;
 | 
						|
+	return driver_register(&drv->drv);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_register_driver);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_unregister_driver - unregister a function driver
 | 
						|
+ *	@drv: SDIO function driver
 | 
						|
+ */
 | 
						|
+void sdio_unregister_driver(struct sdio_driver *drv)
 | 
						|
+{
 | 
						|
+	drv->drv.bus = &sdio_bus_type;
 | 
						|
+	driver_unregister(&drv->drv);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_unregister_driver);
 | 
						|
+
 | 
						|
+static void sdio_release_func(struct device *dev)
 | 
						|
+{
 | 
						|
+	struct sdio_func *func = dev_to_sdio_func(dev);
 | 
						|
+
 | 
						|
+	sdio_free_func_cis(func);
 | 
						|
+
 | 
						|
+	if (func->info)
 | 
						|
+		kfree(func->info);
 | 
						|
+
 | 
						|
+	kfree(func);
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Allocate and initialise a new SDIO function structure.
 | 
						|
+ */
 | 
						|
+struct sdio_func *sdio_alloc_func(struct mmc_card *card)
 | 
						|
+{
 | 
						|
+	struct sdio_func *func;
 | 
						|
+
 | 
						|
+	func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL);
 | 
						|
+	if (!func)
 | 
						|
+		return ERR_PTR(-ENOMEM);
 | 
						|
+
 | 
						|
+	func->card = card;
 | 
						|
+
 | 
						|
+	device_initialize(&func->dev);
 | 
						|
+
 | 
						|
+	func->dev.parent = &card->dev;
 | 
						|
+	func->dev.bus = &sdio_bus_type;
 | 
						|
+	func->dev.release = sdio_release_func;
 | 
						|
+
 | 
						|
+	return func;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Register a new SDIO function with the driver model.
 | 
						|
+ */
 | 
						|
+int sdio_add_func(struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	snprintf(func->dev.bus_id, sizeof(func->dev.bus_id),
 | 
						|
+		 "%s:%d", mmc_card_id(func->card), func->num);
 | 
						|
+
 | 
						|
+	ret = device_add(&func->dev);
 | 
						|
+	if (ret == 0)
 | 
						|
+		sdio_func_set_present(func);
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Unregister a SDIO function with the driver model, and
 | 
						|
+ * (eventually) free it.
 | 
						|
+ */
 | 
						|
+void sdio_remove_func(struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	if (sdio_func_present(func))
 | 
						|
+		device_del(&func->dev);
 | 
						|
+
 | 
						|
+	put_device(&func->dev);
 | 
						|
+}
 | 
						|
+
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/mmc/core/sdio_bus.h
 | 
						|
@@ -0,0 +1,22 @@
 | 
						|
+/*
 | 
						|
+ *  linux/drivers/mmc/core/sdio_bus.h
 | 
						|
+ *
 | 
						|
+ *  Copyright 2007 Pierre Ossman
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or modify
 | 
						|
+ * it under the terms of the GNU General Public License as published by
 | 
						|
+ * the Free Software Foundation; either version 2 of the License, or (at
 | 
						|
+ * your option) any later version.
 | 
						|
+ */
 | 
						|
+#ifndef _MMC_CORE_SDIO_BUS_H
 | 
						|
+#define _MMC_CORE_SDIO_BUS_H
 | 
						|
+
 | 
						|
+struct sdio_func *sdio_alloc_func(struct mmc_card *card);
 | 
						|
+int sdio_add_func(struct sdio_func *func);
 | 
						|
+void sdio_remove_func(struct sdio_func *func);
 | 
						|
+
 | 
						|
+int sdio_register_bus(void);
 | 
						|
+void sdio_unregister_bus(void);
 | 
						|
+
 | 
						|
+#endif
 | 
						|
+
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/mmc/core/sdio_cis.c
 | 
						|
@@ -0,0 +1,346 @@
 | 
						|
+/*
 | 
						|
+ * linux/drivers/mmc/core/sdio_cis.c
 | 
						|
+ *
 | 
						|
+ * Author:	Nicolas Pitre
 | 
						|
+ * Created:	June 11, 2007
 | 
						|
+ * Copyright:	MontaVista Software Inc.
 | 
						|
+ *
 | 
						|
+ * Copyright 2007 Pierre Ossman
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or modify
 | 
						|
+ * it under the terms of the GNU General Public License as published by
 | 
						|
+ * the Free Software Foundation; either version 2 of the License, or (at
 | 
						|
+ * your option) any later version.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+
 | 
						|
+#include <linux/mmc/host.h>
 | 
						|
+#include <linux/mmc/card.h>
 | 
						|
+#include <linux/mmc/sdio.h>
 | 
						|
+#include <linux/mmc/sdio_func.h>
 | 
						|
+
 | 
						|
+#include "sdio_cis.h"
 | 
						|
+#include "sdio_ops.h"
 | 
						|
+
 | 
						|
+static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func,
 | 
						|
+			 const unsigned char *buf, unsigned size)
 | 
						|
+{
 | 
						|
+	unsigned i, nr_strings;
 | 
						|
+	char **buffer, *string;
 | 
						|
+
 | 
						|
+	buf += 2;
 | 
						|
+	size -= 2;
 | 
						|
+
 | 
						|
+	nr_strings = 0;
 | 
						|
+	for (i = 0; i < size; i++) {
 | 
						|
+		if (buf[i] == 0xff)
 | 
						|
+			break;
 | 
						|
+		if (buf[i] == 0)
 | 
						|
+			nr_strings++;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (buf[i-1] != '\0') {
 | 
						|
+		printk(KERN_WARNING "SDIO: ignoring broken CISTPL_VERS_1\n");
 | 
						|
+		return 0;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	size = i;
 | 
						|
+
 | 
						|
+	buffer = kzalloc(sizeof(char*) * nr_strings + size, GFP_KERNEL);
 | 
						|
+	if (!buffer)
 | 
						|
+		return -ENOMEM;
 | 
						|
+
 | 
						|
+	string = (char*)(buffer + nr_strings);
 | 
						|
+
 | 
						|
+	for (i = 0; i < nr_strings; i++) {
 | 
						|
+		buffer[i] = string;
 | 
						|
+		strcpy(string, buf);
 | 
						|
+		string += strlen(string) + 1;
 | 
						|
+		buf += strlen(buf) + 1;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (func) {
 | 
						|
+		func->num_info = nr_strings;
 | 
						|
+		func->info = (const char**)buffer;
 | 
						|
+	} else {
 | 
						|
+		card->num_info = nr_strings;
 | 
						|
+		card->info = (const char**)buffer;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int cistpl_manfid(struct mmc_card *card, struct sdio_func *func,
 | 
						|
+			 const unsigned char *buf, unsigned size)
 | 
						|
+{
 | 
						|
+	unsigned int vendor, device;
 | 
						|
+
 | 
						|
+	/* TPLMID_MANF */
 | 
						|
+	vendor = buf[0] | (buf[1] << 8);
 | 
						|
+
 | 
						|
+	/* TPLMID_CARD */
 | 
						|
+	device = buf[2] | (buf[3] << 8);
 | 
						|
+
 | 
						|
+	if (func) {
 | 
						|
+		func->vendor = vendor;
 | 
						|
+		func->device = device;
 | 
						|
+	} else {
 | 
						|
+		card->cis.vendor = vendor;
 | 
						|
+		card->cis.device = device;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static const unsigned char speed_val[16] =
 | 
						|
+	{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
 | 
						|
+static const unsigned int speed_unit[8] =
 | 
						|
+	{ 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
 | 
						|
+
 | 
						|
+static int cistpl_funce_common(struct mmc_card *card,
 | 
						|
+			       const unsigned char *buf, unsigned size)
 | 
						|
+{
 | 
						|
+	if (size < 0x04 || buf[0] != 0)
 | 
						|
+		return -EINVAL;
 | 
						|
+
 | 
						|
+	/* TPLFE_FN0_BLK_SIZE */
 | 
						|
+	card->cis.blksize = buf[1] | (buf[2] << 8);
 | 
						|
+
 | 
						|
+	/* TPLFE_MAX_TRAN_SPEED */
 | 
						|
+	card->cis.max_dtr = speed_val[(buf[3] >> 3) & 15] *
 | 
						|
+			    speed_unit[buf[3] & 7];
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int cistpl_funce_func(struct sdio_func *func,
 | 
						|
+			     const unsigned char *buf, unsigned size)
 | 
						|
+{
 | 
						|
+	unsigned vsn;
 | 
						|
+	unsigned min_size;
 | 
						|
+
 | 
						|
+	vsn = func->card->cccr.sdio_vsn;
 | 
						|
+	min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;
 | 
						|
+
 | 
						|
+	if (size < min_size || buf[0] != 1)
 | 
						|
+		return -EINVAL;
 | 
						|
+
 | 
						|
+	/* TPLFE_MAX_BLK_SIZE */
 | 
						|
+	func->max_blksize = buf[12] | (buf[13] << 8);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,
 | 
						|
+			const unsigned char *buf, unsigned size)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * There should be two versions of the CISTPL_FUNCE tuple,
 | 
						|
+	 * one for the common CIS (function 0) and a version used by
 | 
						|
+	 * the individual function's CIS (1-7). Yet, the later has a
 | 
						|
+	 * different length depending on the SDIO spec version.
 | 
						|
+	 */
 | 
						|
+	if (func)
 | 
						|
+		ret = cistpl_funce_func(func, buf, size);
 | 
						|
+	else
 | 
						|
+		ret = cistpl_funce_common(card, buf, size);
 | 
						|
+
 | 
						|
+	if (ret) {
 | 
						|
+		printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u "
 | 
						|
+		       "type %u\n", mmc_hostname(card->host), size, buf[0]);
 | 
						|
+		return ret;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
 | 
						|
+			   const unsigned char *, unsigned);
 | 
						|
+
 | 
						|
+struct cis_tpl {
 | 
						|
+	unsigned char code;
 | 
						|
+	unsigned char min_size;
 | 
						|
+	tpl_parse_t *parse;
 | 
						|
+};
 | 
						|
+
 | 
						|
+static const struct cis_tpl cis_tpl_list[] = {
 | 
						|
+	{	0x15,	3,	cistpl_vers_1		},
 | 
						|
+	{	0x20,	4,	cistpl_manfid		},
 | 
						|
+	{	0x21,	2,	/* cistpl_funcid */	},
 | 
						|
+	{	0x22,	0,	cistpl_funce		},
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	struct sdio_func_tuple *this, **prev;
 | 
						|
+	unsigned i, ptr = 0;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Note that this works for the common CIS (function number 0) as
 | 
						|
+	 * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS
 | 
						|
+	 * have the same offset.
 | 
						|
+	 */
 | 
						|
+	for (i = 0; i < 3; i++) {
 | 
						|
+		unsigned char x, fn;
 | 
						|
+
 | 
						|
+		if (func)
 | 
						|
+			fn = func->num;
 | 
						|
+		else
 | 
						|
+			fn = 0;
 | 
						|
+
 | 
						|
+		ret = mmc_io_rw_direct(card, 0, 0,
 | 
						|
+			SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x);
 | 
						|
+		if (ret)
 | 
						|
+			return ret;
 | 
						|
+		ptr |= x << (i * 8);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (func)
 | 
						|
+		prev = &func->tuples;
 | 
						|
+	else
 | 
						|
+		prev = &card->tuples;
 | 
						|
+
 | 
						|
+	BUG_ON(*prev);
 | 
						|
+
 | 
						|
+	do {
 | 
						|
+		unsigned char tpl_code, tpl_link;
 | 
						|
+
 | 
						|
+		ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code);
 | 
						|
+		if (ret)
 | 
						|
+			break;
 | 
						|
+
 | 
						|
+		/* 0xff means we're done */
 | 
						|
+		if (tpl_code == 0xff)
 | 
						|
+			break;
 | 
						|
+
 | 
						|
+		ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
 | 
						|
+		if (ret)
 | 
						|
+			break;
 | 
						|
+
 | 
						|
+		this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
 | 
						|
+		if (!this)
 | 
						|
+			return -ENOMEM;
 | 
						|
+
 | 
						|
+		for (i = 0; i < tpl_link; i++) {
 | 
						|
+			ret = mmc_io_rw_direct(card, 0, 0,
 | 
						|
+					       ptr + i, 0, &this->data[i]);
 | 
						|
+			if (ret)
 | 
						|
+				break;
 | 
						|
+		}
 | 
						|
+		if (ret) {
 | 
						|
+			kfree(this);
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
 | 
						|
+			if (cis_tpl_list[i].code == tpl_code)
 | 
						|
+				break;
 | 
						|
+		if (i >= ARRAY_SIZE(cis_tpl_list)) {
 | 
						|
+			/* this tuple is unknown to the core */
 | 
						|
+			this->next = NULL;
 | 
						|
+			this->code = tpl_code;
 | 
						|
+			this->size = tpl_link;
 | 
						|
+			*prev = this;
 | 
						|
+			prev = &this->next;
 | 
						|
+			printk(KERN_DEBUG
 | 
						|
+			       "%s: queuing CIS tuple 0x%02x length %u\n",
 | 
						|
+			       mmc_hostname(card->host), tpl_code, tpl_link);
 | 
						|
+		} else {
 | 
						|
+			const struct cis_tpl *tpl = cis_tpl_list + i;
 | 
						|
+			if (tpl_link < tpl->min_size) {
 | 
						|
+				printk(KERN_ERR
 | 
						|
+				       "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n",
 | 
						|
+				       mmc_hostname(card->host),
 | 
						|
+				       tpl_code, tpl_link, tpl->min_size);
 | 
						|
+				ret = -EINVAL;
 | 
						|
+			} else if (tpl->parse) {
 | 
						|
+				ret = tpl->parse(card, func,
 | 
						|
+						 this->data, tpl_link);
 | 
						|
+			}
 | 
						|
+			kfree(this);
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		ptr += tpl_link;
 | 
						|
+	} while (!ret);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Link in all unknown tuples found in the common CIS so that
 | 
						|
+	 * drivers don't have to go digging in two places.
 | 
						|
+	 */
 | 
						|
+	if (func)
 | 
						|
+		*prev = card->tuples;
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+int sdio_read_common_cis(struct mmc_card *card)
 | 
						|
+{
 | 
						|
+	return sdio_read_cis(card, NULL);
 | 
						|
+}
 | 
						|
+
 | 
						|
+void sdio_free_common_cis(struct mmc_card *card)
 | 
						|
+{
 | 
						|
+	struct sdio_func_tuple *tuple, *victim;
 | 
						|
+
 | 
						|
+	tuple = card->tuples;
 | 
						|
+
 | 
						|
+	while (tuple) {
 | 
						|
+		victim = tuple;
 | 
						|
+		tuple = tuple->next;
 | 
						|
+		kfree(victim);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	card->tuples = NULL;
 | 
						|
+}
 | 
						|
+
 | 
						|
+int sdio_read_func_cis(struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	ret = sdio_read_cis(func->card, func);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Since we've linked to tuples in the card structure,
 | 
						|
+	 * we must make sure we have a reference to it.
 | 
						|
+	 */
 | 
						|
+	get_device(&func->card->dev);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Vendor/device id is optional for function CIS, so
 | 
						|
+	 * copy it from the card structure as needed.
 | 
						|
+	 */
 | 
						|
+	if (func->vendor == 0) {
 | 
						|
+		func->vendor = func->card->cis.vendor;
 | 
						|
+		func->device = func->card->cis.device;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+void sdio_free_func_cis(struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	struct sdio_func_tuple *tuple, *victim;
 | 
						|
+
 | 
						|
+	tuple = func->tuples;
 | 
						|
+
 | 
						|
+	while (tuple && tuple != func->card->tuples) {
 | 
						|
+		victim = tuple;
 | 
						|
+		tuple = tuple->next;
 | 
						|
+		kfree(victim);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	func->tuples = NULL;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * We have now removed the link to the tuples in the
 | 
						|
+	 * card structure, so remove the reference.
 | 
						|
+	 */
 | 
						|
+	put_device(&func->card->dev);
 | 
						|
+}
 | 
						|
+
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/mmc/core/sdio_cis.h
 | 
						|
@@ -0,0 +1,23 @@
 | 
						|
+/*
 | 
						|
+ * linux/drivers/mmc/core/sdio_cis.h
 | 
						|
+ *
 | 
						|
+ * Author:	Nicolas Pitre
 | 
						|
+ * Created:	June 11, 2007
 | 
						|
+ * Copyright:	MontaVista Software Inc.
 | 
						|
+ *
 | 
						|
+ * 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.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#ifndef _MMC_SDIO_CIS_H
 | 
						|
+#define _MMC_SDIO_CIS_H
 | 
						|
+
 | 
						|
+int sdio_read_common_cis(struct mmc_card *card);
 | 
						|
+void sdio_free_common_cis(struct mmc_card *card);
 | 
						|
+
 | 
						|
+int sdio_read_func_cis(struct sdio_func *func);
 | 
						|
+void sdio_free_func_cis(struct sdio_func *func);
 | 
						|
+
 | 
						|
+#endif
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/mmc/core/sdio_io.c
 | 
						|
@@ -0,0 +1,548 @@
 | 
						|
+/*
 | 
						|
+ *  linux/drivers/mmc/core/sdio_io.c
 | 
						|
+ *
 | 
						|
+ *  Copyright 2007 Pierre Ossman
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or modify
 | 
						|
+ * it under the terms of the GNU General Public License as published by
 | 
						|
+ * the Free Software Foundation; either version 2 of the License, or (at
 | 
						|
+ * your option) any later version.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/mmc/host.h>
 | 
						|
+#include <linux/mmc/card.h>
 | 
						|
+#include <linux/mmc/sdio.h>
 | 
						|
+#include <linux/mmc/sdio_func.h>
 | 
						|
+
 | 
						|
+#include "sdio_ops.h"
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_claim_host - exclusively claim a bus for a certain SDIO function
 | 
						|
+ *	@func: SDIO function that will be accessed
 | 
						|
+ *
 | 
						|
+ *	Claim a bus for a set of operations. The SDIO function given
 | 
						|
+ *	is used to figure out which bus is relevant.
 | 
						|
+ */
 | 
						|
+void sdio_claim_host(struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	BUG_ON(!func);
 | 
						|
+	BUG_ON(!func->card);
 | 
						|
+
 | 
						|
+	mmc_claim_host(func->card->host);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_claim_host);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_release_host - release a bus for a certain SDIO function
 | 
						|
+ *	@func: SDIO function that was accessed
 | 
						|
+ *
 | 
						|
+ *	Release a bus, allowing others to claim the bus for their
 | 
						|
+ *	operations.
 | 
						|
+ */
 | 
						|
+void sdio_release_host(struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	BUG_ON(!func);
 | 
						|
+	BUG_ON(!func->card);
 | 
						|
+
 | 
						|
+	mmc_release_host(func->card->host);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_release_host);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_enable_func - enables a SDIO function for usage
 | 
						|
+ *	@func: SDIO function to enable
 | 
						|
+ *
 | 
						|
+ *	Powers up and activates a SDIO function so that register
 | 
						|
+ *	access is possible.
 | 
						|
+ */
 | 
						|
+int sdio_enable_func(struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	unsigned char reg;
 | 
						|
+	unsigned long timeout;
 | 
						|
+
 | 
						|
+	BUG_ON(!func);
 | 
						|
+	BUG_ON(!func->card);
 | 
						|
+
 | 
						|
+	pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func));
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®);
 | 
						|
+	if (ret)
 | 
						|
+		goto err;
 | 
						|
+
 | 
						|
+	reg |= 1 << func->num;
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
 | 
						|
+	if (ret)
 | 
						|
+		goto err;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * FIXME: This should timeout based on information in the CIS,
 | 
						|
+	 * but we don't have card to parse that yet.
 | 
						|
+	 */
 | 
						|
+	timeout = jiffies + HZ;
 | 
						|
+
 | 
						|
+	while (1) {
 | 
						|
+		ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, ®);
 | 
						|
+		if (ret)
 | 
						|
+			goto err;
 | 
						|
+		if (reg & (1 << func->num))
 | 
						|
+			break;
 | 
						|
+		ret = -ETIME;
 | 
						|
+		if (time_after(jiffies, timeout))
 | 
						|
+			goto err;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func));
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+err:
 | 
						|
+	pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func));
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_enable_func);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_disable_func - disable a SDIO function
 | 
						|
+ *	@func: SDIO function to disable
 | 
						|
+ *
 | 
						|
+ *	Powers down and deactivates a SDIO function. Register access
 | 
						|
+ *	to this function will fail until the function is reenabled.
 | 
						|
+ */
 | 
						|
+int sdio_disable_func(struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	unsigned char reg;
 | 
						|
+
 | 
						|
+	BUG_ON(!func);
 | 
						|
+	BUG_ON(!func->card);
 | 
						|
+
 | 
						|
+	pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func));
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®);
 | 
						|
+	if (ret)
 | 
						|
+		goto err;
 | 
						|
+
 | 
						|
+	reg &= ~(1 << func->num);
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
 | 
						|
+	if (ret)
 | 
						|
+		goto err;
 | 
						|
+
 | 
						|
+	pr_debug("SDIO: Disabled device %s\n", sdio_func_id(func));
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+err:
 | 
						|
+	pr_debug("SDIO: Failed to disable device %s\n", sdio_func_id(func));
 | 
						|
+	return -EIO;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_disable_func);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_set_block_size - set the block size of an SDIO function
 | 
						|
+ *	@func: SDIO function to change
 | 
						|
+ *	@blksz: new block size or 0 to use the default.
 | 
						|
+ *
 | 
						|
+ *	The default block size is the largest supported by both the function
 | 
						|
+ *	and the host, with a maximum of 512 to ensure that arbitrarily sized
 | 
						|
+ *	data transfer use the optimal (least) number of commands.
 | 
						|
+ *
 | 
						|
+ *	A driver may call this to override the default block size set by the
 | 
						|
+ *	core. This can be used to set a block size greater than the maximum
 | 
						|
+ *	that reported by the card; it is the driver's responsibility to ensure
 | 
						|
+ *	it uses a value that the card supports.
 | 
						|
+ *
 | 
						|
+ *	Returns 0 on success, -EINVAL if the host does not support the
 | 
						|
+ *	requested block size, or -EIO (etc.) if one of the resultant FBR block
 | 
						|
+ *	size register writes failed.
 | 
						|
+ *
 | 
						|
+ */
 | 
						|
+int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	if (blksz > func->card->host->max_blk_size)
 | 
						|
+		return -EINVAL;
 | 
						|
+
 | 
						|
+	if (blksz == 0) {
 | 
						|
+		blksz = min(min(
 | 
						|
+			func->max_blksize,
 | 
						|
+			func->card->host->max_blk_size),
 | 
						|
+			512u);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 1, 0,
 | 
						|
+		SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
 | 
						|
+		blksz & 0xff, NULL);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 1, 0,
 | 
						|
+		SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1,
 | 
						|
+		(blksz >> 8) & 0xff, NULL);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+	func->cur_blksize = blksz;
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_set_block_size);
 | 
						|
+
 | 
						|
+/* Split an arbitrarily sized data transfer into several
 | 
						|
+ * IO_RW_EXTENDED commands. */
 | 
						|
+static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
 | 
						|
+	unsigned addr, int incr_addr, u8 *buf, unsigned size)
 | 
						|
+{
 | 
						|
+	unsigned remainder = size;
 | 
						|
+	unsigned max_blocks;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	/* Do the bulk of the transfer using block mode (if supported). */
 | 
						|
+	if (func->card->cccr.multi_block) {
 | 
						|
+		/* Blocks per command is limited by host count, host transfer
 | 
						|
+		 * size (we only use a single sg entry) and the maximum for
 | 
						|
+		 * IO_RW_EXTENDED of 511 blocks. */
 | 
						|
+		max_blocks = min(min(
 | 
						|
+			func->card->host->max_blk_count,
 | 
						|
+			func->card->host->max_seg_size / func->cur_blksize),
 | 
						|
+			511u);
 | 
						|
+
 | 
						|
+		while (remainder > func->cur_blksize) {
 | 
						|
+			unsigned blocks;
 | 
						|
+
 | 
						|
+			blocks = remainder / func->cur_blksize;
 | 
						|
+			if (blocks > max_blocks)
 | 
						|
+				blocks = max_blocks;
 | 
						|
+			size = blocks * func->cur_blksize;
 | 
						|
+
 | 
						|
+			ret = mmc_io_rw_extended(func->card, write,
 | 
						|
+				func->num, addr, incr_addr, buf,
 | 
						|
+				blocks, func->cur_blksize);
 | 
						|
+			if (ret)
 | 
						|
+				return ret;
 | 
						|
+
 | 
						|
+			remainder -= size;
 | 
						|
+			buf += size;
 | 
						|
+			if (incr_addr)
 | 
						|
+				addr += size;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Write the remainder using byte mode. */
 | 
						|
+	while (remainder > 0) {
 | 
						|
+		size = remainder;
 | 
						|
+		if (size > func->cur_blksize)
 | 
						|
+			size = func->cur_blksize;
 | 
						|
+		if (size > 512)
 | 
						|
+			size = 512; /* maximum size for byte mode */
 | 
						|
+
 | 
						|
+		ret = mmc_io_rw_extended(func->card, write, func->num, addr,
 | 
						|
+			 incr_addr, buf, 1, size);
 | 
						|
+		if (ret)
 | 
						|
+			return ret;
 | 
						|
+
 | 
						|
+		remainder -= size;
 | 
						|
+		buf += size;
 | 
						|
+		if (incr_addr)
 | 
						|
+			addr += size;
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_readb - read a single byte from a SDIO function
 | 
						|
+ *	@func: SDIO function to access
 | 
						|
+ *	@addr: address to read
 | 
						|
+ *	@err_ret: optional status value from transfer
 | 
						|
+ *
 | 
						|
+ *	Reads a single byte from the address space of a given SDIO
 | 
						|
+ *	function. If there is a problem reading the address, 0xff
 | 
						|
+ *	is returned and @err_ret will contain the error code.
 | 
						|
+ */
 | 
						|
+unsigned char sdio_readb(struct sdio_func *func, unsigned int addr,
 | 
						|
+	int *err_ret)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	unsigned char val;
 | 
						|
+
 | 
						|
+	BUG_ON(!func);
 | 
						|
+
 | 
						|
+	if (err_ret)
 | 
						|
+		*err_ret = 0;
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val);
 | 
						|
+	if (ret) {
 | 
						|
+		if (err_ret)
 | 
						|
+			*err_ret = ret;
 | 
						|
+		return 0xFF;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return val;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_readb);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_writeb - write a single byte to a SDIO function
 | 
						|
+ *	@func: SDIO function to access
 | 
						|
+ *	@b: byte to write
 | 
						|
+ *	@addr: address to write to
 | 
						|
+ *	@err_ret: optional status value from transfer
 | 
						|
+ *
 | 
						|
+ *	Writes a single byte to the address space of a given SDIO
 | 
						|
+ *	function. @err_ret will contain the status of the actual
 | 
						|
+ *	transfer.
 | 
						|
+ */
 | 
						|
+void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
 | 
						|
+	int *err_ret)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	BUG_ON(!func);
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL);
 | 
						|
+	if (err_ret)
 | 
						|
+		*err_ret = ret;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_writeb);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_memcpy_fromio - read a chunk of memory from a SDIO function
 | 
						|
+ *	@func: SDIO function to access
 | 
						|
+ *	@dst: buffer to store the data
 | 
						|
+ *	@addr: address to begin reading from
 | 
						|
+ *	@count: number of bytes to read
 | 
						|
+ *
 | 
						|
+ *	Reads from the address space of a given SDIO function. Return
 | 
						|
+ *	value indicates if the transfer succeeded or not.
 | 
						|
+ */
 | 
						|
+int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
 | 
						|
+	unsigned int addr, int count)
 | 
						|
+{
 | 
						|
+	return sdio_io_rw_ext_helper(func, 0, addr, 1, dst, count);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_memcpy_fromio);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_memcpy_toio - write a chunk of memory to a SDIO function
 | 
						|
+ *	@func: SDIO function to access
 | 
						|
+ *	@addr: address to start writing to
 | 
						|
+ *	@src: buffer that contains the data to write
 | 
						|
+ *	@count: number of bytes to write
 | 
						|
+ *
 | 
						|
+ *	Writes to the address space of a given SDIO function. Return
 | 
						|
+ *	value indicates if the transfer succeeded or not.
 | 
						|
+ */
 | 
						|
+int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
 | 
						|
+	void *src, int count)
 | 
						|
+{
 | 
						|
+	return sdio_io_rw_ext_helper(func, 1, addr, 1, src, count);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_memcpy_toio);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_readsb - read from a FIFO on a SDIO function
 | 
						|
+ *	@func: SDIO function to access
 | 
						|
+ *	@dst: buffer to store the data
 | 
						|
+ *	@addr: address of (single byte) FIFO
 | 
						|
+ *	@count: number of bytes to read
 | 
						|
+ *
 | 
						|
+ *	Reads from the specified FIFO of a given SDIO function. Return
 | 
						|
+ *	value indicates if the transfer succeeded or not.
 | 
						|
+ */
 | 
						|
+int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr,
 | 
						|
+	int count)
 | 
						|
+{
 | 
						|
+	return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count);
 | 
						|
+}
 | 
						|
+
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_readsb);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_writesb - write to a FIFO of a SDIO function
 | 
						|
+ *	@func: SDIO function to access
 | 
						|
+ *	@addr: address of (single byte) FIFO
 | 
						|
+ *	@src: buffer that contains the data to write
 | 
						|
+ *	@count: number of bytes to write
 | 
						|
+ *
 | 
						|
+ *	Writes to the specified FIFO of a given SDIO function. Return
 | 
						|
+ *	value indicates if the transfer succeeded or not.
 | 
						|
+ */
 | 
						|
+int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src,
 | 
						|
+	int count)
 | 
						|
+{
 | 
						|
+	return sdio_io_rw_ext_helper(func, 1, addr, 0, src, count);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_writesb);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_readw - read a 16 bit integer from a SDIO function
 | 
						|
+ *	@func: SDIO function to access
 | 
						|
+ *	@addr: address to read
 | 
						|
+ *	@err_ret: optional status value from transfer
 | 
						|
+ *
 | 
						|
+ *	Reads a 16 bit integer from the address space of a given SDIO
 | 
						|
+ *	function. If there is a problem reading the address, 0xffff
 | 
						|
+ *	is returned and @err_ret will contain the error code.
 | 
						|
+ */
 | 
						|
+unsigned short sdio_readw(struct sdio_func *func, unsigned int addr,
 | 
						|
+	int *err_ret)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	if (err_ret)
 | 
						|
+		*err_ret = 0;
 | 
						|
+
 | 
						|
+	ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 2);
 | 
						|
+	if (ret) {
 | 
						|
+		if (err_ret)
 | 
						|
+			*err_ret = ret;
 | 
						|
+		return 0xFFFF;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return le16_to_cpu(*(u16*)func->tmpbuf);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_readw);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_writew - write a 16 bit integer to a SDIO function
 | 
						|
+ *	@func: SDIO function to access
 | 
						|
+ *	@b: integer to write
 | 
						|
+ *	@addr: address to write to
 | 
						|
+ *	@err_ret: optional status value from transfer
 | 
						|
+ *
 | 
						|
+ *	Writes a 16 bit integer to the address space of a given SDIO
 | 
						|
+ *	function. @err_ret will contain the status of the actual
 | 
						|
+ *	transfer.
 | 
						|
+ */
 | 
						|
+void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr,
 | 
						|
+	int *err_ret)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	*(u16*)func->tmpbuf = cpu_to_le16(b);
 | 
						|
+
 | 
						|
+	ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2);
 | 
						|
+	if (err_ret)
 | 
						|
+		*err_ret = ret;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_writew);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_readl - read a 32 bit integer from a SDIO function
 | 
						|
+ *	@func: SDIO function to access
 | 
						|
+ *	@addr: address to read
 | 
						|
+ *	@err_ret: optional status value from transfer
 | 
						|
+ *
 | 
						|
+ *	Reads a 32 bit integer from the address space of a given SDIO
 | 
						|
+ *	function. If there is a problem reading the address,
 | 
						|
+ *	0xffffffff is returned and @err_ret will contain the error
 | 
						|
+ *	code.
 | 
						|
+ */
 | 
						|
+unsigned long sdio_readl(struct sdio_func *func, unsigned int addr,
 | 
						|
+	int *err_ret)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	if (err_ret)
 | 
						|
+		*err_ret = 0;
 | 
						|
+
 | 
						|
+	ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 4);
 | 
						|
+	if (ret) {
 | 
						|
+		if (err_ret)
 | 
						|
+			*err_ret = ret;
 | 
						|
+		return 0xFFFFFFFF;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return le32_to_cpu(*(u32*)func->tmpbuf);
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_readl);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_writel - write a 32 bit integer to a SDIO function
 | 
						|
+ *	@func: SDIO function to access
 | 
						|
+ *	@b: integer to write
 | 
						|
+ *	@addr: address to write to
 | 
						|
+ *	@err_ret: optional status value from transfer
 | 
						|
+ *
 | 
						|
+ *	Writes a 32 bit integer to the address space of a given SDIO
 | 
						|
+ *	function. @err_ret will contain the status of the actual
 | 
						|
+ *	transfer.
 | 
						|
+ */
 | 
						|
+void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr,
 | 
						|
+	int *err_ret)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	*(u32*)func->tmpbuf = cpu_to_le32(b);
 | 
						|
+
 | 
						|
+	ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4);
 | 
						|
+	if (err_ret)
 | 
						|
+		*err_ret = ret;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_writel);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_f0_readb - read a single byte from SDIO function 0
 | 
						|
+ *	@func: an SDIO function of the card
 | 
						|
+ *	@addr: address to read
 | 
						|
+ *	@err_ret: optional status value from transfer
 | 
						|
+ *
 | 
						|
+ *	Reads a single byte from the address space of SDIO function 0.
 | 
						|
+ *	If there is a problem reading the address, 0xff is returned
 | 
						|
+ *	and @err_ret will contain the error code.
 | 
						|
+ */
 | 
						|
+unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr,
 | 
						|
+	int *err_ret)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	unsigned char val;
 | 
						|
+
 | 
						|
+	BUG_ON(!func);
 | 
						|
+
 | 
						|
+	if (err_ret)
 | 
						|
+		*err_ret = 0;
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 0, 0, addr, 0, &val);
 | 
						|
+	if (ret) {
 | 
						|
+		if (err_ret)
 | 
						|
+			*err_ret = ret;
 | 
						|
+		return 0xFF;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return val;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_f0_readb);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_f0_writeb - write a single byte to SDIO function 0
 | 
						|
+ *	@func: an SDIO function of the card
 | 
						|
+ *	@b: byte to write
 | 
						|
+ *	@addr: address to write to
 | 
						|
+ *	@err_ret: optional status value from transfer
 | 
						|
+ *
 | 
						|
+ *	Writes a single byte to the address space of SDIO function 0.
 | 
						|
+ *	@err_ret will contain the status of the actual transfer.
 | 
						|
+ *
 | 
						|
+ *	Only writes to the vendor specific CCCR registers (0xF0 -
 | 
						|
+ *	0xFF) are permiited; @err_ret will be set to -EINVAL for *
 | 
						|
+ *	writes outside this range.
 | 
						|
+ */
 | 
						|
+void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
 | 
						|
+	int *err_ret)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	BUG_ON(!func);
 | 
						|
+
 | 
						|
+	if (addr < 0xF0 || addr > 0xFF) {
 | 
						|
+		if (err_ret)
 | 
						|
+			*err_ret = -EINVAL;
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL);
 | 
						|
+	if (err_ret)
 | 
						|
+		*err_ret = ret;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_f0_writeb);
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/mmc/core/sdio_irq.c
 | 
						|
@@ -0,0 +1,267 @@
 | 
						|
+/*
 | 
						|
+ * linux/drivers/mmc/core/sdio_irq.c
 | 
						|
+ *
 | 
						|
+ * Author:      Nicolas Pitre
 | 
						|
+ * Created:     June 18, 2007
 | 
						|
+ * Copyright:   MontaVista Software Inc.
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or modify
 | 
						|
+ * it under the terms of the GNU General Public License as published by
 | 
						|
+ * the Free Software Foundation; either version 2 of the License, or (at
 | 
						|
+ * your option) any later version.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+#include <linux/sched.h>
 | 
						|
+#include <linux/kthread.h>
 | 
						|
+#include <linux/wait.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+
 | 
						|
+#include <linux/mmc/core.h>
 | 
						|
+#include <linux/mmc/host.h>
 | 
						|
+#include <linux/mmc/card.h>
 | 
						|
+#include <linux/mmc/sdio.h>
 | 
						|
+#include <linux/mmc/sdio_func.h>
 | 
						|
+
 | 
						|
+#include "sdio_ops.h"
 | 
						|
+
 | 
						|
+static int process_sdio_pending_irqs(struct mmc_card *card)
 | 
						|
+{
 | 
						|
+	int i, ret, count;
 | 
						|
+	unsigned char pending;
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
 | 
						|
+	if (ret) {
 | 
						|
+		printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n",
 | 
						|
+		       mmc_card_id(card), ret);
 | 
						|
+		return ret;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	count = 0;
 | 
						|
+	for (i = 1; i <= 7; i++) {
 | 
						|
+		if (pending & (1 << i)) {
 | 
						|
+			struct sdio_func *func = card->sdio_func[i - 1];
 | 
						|
+			if (!func) {
 | 
						|
+				printk(KERN_WARNING "%s: pending IRQ for "
 | 
						|
+					"non-existant function\n",
 | 
						|
+					mmc_card_id(card));
 | 
						|
+				ret = -EINVAL;
 | 
						|
+			} else if (func->irq_handler) {
 | 
						|
+				func->irq_handler(func);
 | 
						|
+				count++;
 | 
						|
+			} else {
 | 
						|
+				printk(KERN_WARNING "%s: pending IRQ with no handler\n",
 | 
						|
+				       sdio_func_id(func));
 | 
						|
+				ret = -EINVAL;
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (count)
 | 
						|
+		return count;
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_irq_thread(void *_host)
 | 
						|
+{
 | 
						|
+	struct mmc_host *host = _host;
 | 
						|
+	struct sched_param param = { .sched_priority = 1 };
 | 
						|
+	unsigned long period, idle_period;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	sched_setscheduler(current, SCHED_FIFO, ¶m);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * We want to allow for SDIO cards to work even on non SDIO
 | 
						|
+	 * aware hosts.  One thing that non SDIO host cannot do is
 | 
						|
+	 * asynchronous notification of pending SDIO card interrupts
 | 
						|
+	 * hence we poll for them in that case.
 | 
						|
+	 */
 | 
						|
+	idle_period = msecs_to_jiffies(10);
 | 
						|
+	period = (host->caps & MMC_CAP_SDIO_IRQ) ?
 | 
						|
+		MAX_SCHEDULE_TIMEOUT : idle_period;
 | 
						|
+
 | 
						|
+	pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n",
 | 
						|
+		 mmc_hostname(host), period);
 | 
						|
+
 | 
						|
+	do {
 | 
						|
+		/*
 | 
						|
+		 * We claim the host here on drivers behalf for a couple
 | 
						|
+		 * reasons:
 | 
						|
+		 *
 | 
						|
+		 * 1) it is already needed to retrieve the CCCR_INTx;
 | 
						|
+		 * 2) we want the driver(s) to clear the IRQ condition ASAP;
 | 
						|
+		 * 3) we need to control the abort condition locally.
 | 
						|
+		 *
 | 
						|
+		 * Just like traditional hard IRQ handlers, we expect SDIO
 | 
						|
+		 * IRQ handlers to be quick and to the point, so that the
 | 
						|
+		 * holding of the host lock does not cover too much work
 | 
						|
+		 * that doesn't require that lock to be held.
 | 
						|
+		 */
 | 
						|
+		ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
 | 
						|
+		if (ret)
 | 
						|
+			break;
 | 
						|
+		ret = process_sdio_pending_irqs(host->card);
 | 
						|
+		mmc_release_host(host);
 | 
						|
+
 | 
						|
+		/*
 | 
						|
+		 * Give other threads a chance to run in the presence of
 | 
						|
+		 * errors.  FIXME: determine if due to card removal and
 | 
						|
+		 * possibly exit this thread if so.
 | 
						|
+		 */
 | 
						|
+		if (ret < 0)
 | 
						|
+			ssleep(1);
 | 
						|
+
 | 
						|
+		/*
 | 
						|
+		 * Adaptive polling frequency based on the assumption
 | 
						|
+		 * that an interrupt will be closely followed by more.
 | 
						|
+		 * This has a substantial benefit for network devices.
 | 
						|
+		 */
 | 
						|
+		if (!(host->caps & MMC_CAP_SDIO_IRQ)) {
 | 
						|
+			if (ret > 0)
 | 
						|
+				period /= 2;
 | 
						|
+			else {
 | 
						|
+				period++;
 | 
						|
+				if (period > idle_period)
 | 
						|
+					period = idle_period;
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		set_task_state(current, TASK_INTERRUPTIBLE);
 | 
						|
+		if (host->caps & MMC_CAP_SDIO_IRQ)
 | 
						|
+			host->ops->enable_sdio_irq(host, 1);
 | 
						|
+		if (!kthread_should_stop())
 | 
						|
+			schedule_timeout(period);
 | 
						|
+		set_task_state(current, TASK_RUNNING);
 | 
						|
+	} while (!kthread_should_stop());
 | 
						|
+
 | 
						|
+	if (host->caps & MMC_CAP_SDIO_IRQ)
 | 
						|
+		host->ops->enable_sdio_irq(host, 0);
 | 
						|
+
 | 
						|
+	pr_debug("%s: IRQ thread exiting with code %d\n",
 | 
						|
+		 mmc_hostname(host), ret);
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_card_irq_get(struct mmc_card *card)
 | 
						|
+{
 | 
						|
+	struct mmc_host *host = card->host;
 | 
						|
+
 | 
						|
+	WARN_ON(!host->claimed);
 | 
						|
+
 | 
						|
+	if (!host->sdio_irqs++) {
 | 
						|
+		atomic_set(&host->sdio_irq_thread_abort, 0);
 | 
						|
+		host->sdio_irq_thread =
 | 
						|
+			kthread_run(sdio_irq_thread, host, "ksdiorqd");
 | 
						|
+		if (IS_ERR(host->sdio_irq_thread)) {
 | 
						|
+			int err = PTR_ERR(host->sdio_irq_thread);
 | 
						|
+			host->sdio_irqs--;
 | 
						|
+			return err;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_card_irq_put(struct mmc_card *card)
 | 
						|
+{
 | 
						|
+	struct mmc_host *host = card->host;
 | 
						|
+
 | 
						|
+	WARN_ON(!host->claimed);
 | 
						|
+	BUG_ON(host->sdio_irqs < 1);
 | 
						|
+
 | 
						|
+	if (!--host->sdio_irqs) {
 | 
						|
+		atomic_set(&host->sdio_irq_thread_abort, 1);
 | 
						|
+		kthread_stop(host->sdio_irq_thread);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_claim_irq - claim the IRQ for a SDIO function
 | 
						|
+ *	@func: SDIO function
 | 
						|
+ *	@handler: IRQ handler callback
 | 
						|
+ *
 | 
						|
+ *	Claim and activate the IRQ for the given SDIO function. The provided
 | 
						|
+ *	handler will be called when that IRQ is asserted.  The host is always
 | 
						|
+ *	claimed already when the handler is called so the handler must not
 | 
						|
+ *	call sdio_claim_host() nor sdio_release_host().
 | 
						|
+ */
 | 
						|
+int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	unsigned char reg;
 | 
						|
+
 | 
						|
+	BUG_ON(!func);
 | 
						|
+	BUG_ON(!func->card);
 | 
						|
+
 | 
						|
+	pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));
 | 
						|
+
 | 
						|
+	if (func->irq_handler) {
 | 
						|
+		pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func));
 | 
						|
+		return -EBUSY;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	reg |= 1 << func->num;
 | 
						|
+
 | 
						|
+	reg |= 1; /* Master interrupt enable */
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	func->irq_handler = handler;
 | 
						|
+	ret = sdio_card_irq_get(func->card);
 | 
						|
+	if (ret)
 | 
						|
+		func->irq_handler = NULL;
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_claim_irq);
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ *	sdio_release_irq - release the IRQ for a SDIO function
 | 
						|
+ *	@func: SDIO function
 | 
						|
+ *
 | 
						|
+ *	Disable and release the IRQ for the given SDIO function.
 | 
						|
+ */
 | 
						|
+int sdio_release_irq(struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	unsigned char reg;
 | 
						|
+
 | 
						|
+	BUG_ON(!func);
 | 
						|
+	BUG_ON(!func->card);
 | 
						|
+
 | 
						|
+	pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));
 | 
						|
+
 | 
						|
+	if (func->irq_handler) {
 | 
						|
+		func->irq_handler = NULL;
 | 
						|
+		sdio_card_irq_put(func->card);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	reg &= ~(1 << func->num);
 | 
						|
+
 | 
						|
+	/* Disable master interrupt with the last function interrupt */
 | 
						|
+	if (!(reg & 0xFE))
 | 
						|
+		reg = 0;
 | 
						|
+
 | 
						|
+	ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
 | 
						|
+	if (ret)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+EXPORT_SYMBOL_GPL(sdio_release_irq);
 | 
						|
+
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/mmc/core/sdio_ops.c
 | 
						|
@@ -0,0 +1,175 @@
 | 
						|
+/*
 | 
						|
+ *  linux/drivers/mmc/sdio_ops.c
 | 
						|
+ *
 | 
						|
+ *  Copyright 2006-2007 Pierre Ossman
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or modify
 | 
						|
+ * it under the terms of the GNU General Public License as published by
 | 
						|
+ * the Free Software Foundation; either version 2 of the License, or (at
 | 
						|
+ * your option) any later version.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/scatterlist.h>
 | 
						|
+
 | 
						|
+#include <linux/mmc/host.h>
 | 
						|
+#include <linux/mmc/card.h>
 | 
						|
+#include <linux/mmc/mmc.h>
 | 
						|
+#include <linux/mmc/sdio.h>
 | 
						|
+
 | 
						|
+#include "core.h"
 | 
						|
+
 | 
						|
+int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
 | 
						|
+{
 | 
						|
+	struct mmc_command cmd;
 | 
						|
+	int i, err = 0;
 | 
						|
+
 | 
						|
+	BUG_ON(!host);
 | 
						|
+
 | 
						|
+	memset(&cmd, 0, sizeof(struct mmc_command));
 | 
						|
+
 | 
						|
+	cmd.opcode = SD_IO_SEND_OP_COND;
 | 
						|
+	cmd.arg = ocr;
 | 
						|
+	cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR;
 | 
						|
+
 | 
						|
+	for (i = 100; i; i--) {
 | 
						|
+		err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
 | 
						|
+		if (err)
 | 
						|
+			break;
 | 
						|
+
 | 
						|
+		/* if we're just probing, do a single pass */
 | 
						|
+		if (ocr == 0)
 | 
						|
+			break;
 | 
						|
+
 | 
						|
+		/* otherwise wait until reset completes */
 | 
						|
+		if (mmc_host_is_spi(host)) {
 | 
						|
+			/*
 | 
						|
+			 * Both R1_SPI_IDLE and MMC_CARD_BUSY indicate
 | 
						|
+			 * an initialized card under SPI, but some cards
 | 
						|
+			 * (Marvell's) only behave when looking at this
 | 
						|
+			 * one.
 | 
						|
+			 */
 | 
						|
+			if (cmd.resp[1] & MMC_CARD_BUSY)
 | 
						|
+				break;
 | 
						|
+		} else {
 | 
						|
+			if (cmd.resp[0] & MMC_CARD_BUSY)
 | 
						|
+				break;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		err = -ETIMEDOUT;
 | 
						|
+
 | 
						|
+		mmc_delay(10);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (rocr)
 | 
						|
+		*rocr = cmd.resp[mmc_host_is_spi(host) ? 1 : 0];
 | 
						|
+
 | 
						|
+	return err;
 | 
						|
+}
 | 
						|
+
 | 
						|
+int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
 | 
						|
+	unsigned addr, u8 in, u8* out)
 | 
						|
+{
 | 
						|
+	struct mmc_command cmd;
 | 
						|
+	int err;
 | 
						|
+
 | 
						|
+	BUG_ON(!card);
 | 
						|
+	BUG_ON(fn > 7);
 | 
						|
+
 | 
						|
+	memset(&cmd, 0, sizeof(struct mmc_command));
 | 
						|
+
 | 
						|
+	cmd.opcode = SD_IO_RW_DIRECT;
 | 
						|
+	cmd.arg = write ? 0x80000000 : 0x00000000;
 | 
						|
+	cmd.arg |= fn << 28;
 | 
						|
+	cmd.arg |= (write && out) ? 0x08000000 : 0x00000000;
 | 
						|
+	cmd.arg |= addr << 9;
 | 
						|
+	cmd.arg |= in;
 | 
						|
+	cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
 | 
						|
+
 | 
						|
+	err = mmc_wait_for_cmd(card->host, &cmd, 0);
 | 
						|
+	if (err)
 | 
						|
+		return err;
 | 
						|
+
 | 
						|
+	if (mmc_host_is_spi(card->host)) {
 | 
						|
+		/* host driver already reported errors */
 | 
						|
+	} else {
 | 
						|
+		if (cmd.resp[0] & R5_ERROR)
 | 
						|
+			return -EIO;
 | 
						|
+		if (cmd.resp[0] & R5_FUNCTION_NUMBER)
 | 
						|
+			return -EINVAL;
 | 
						|
+		if (cmd.resp[0] & R5_OUT_OF_RANGE)
 | 
						|
+			return -ERANGE;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (out) {
 | 
						|
+		if (mmc_host_is_spi(card->host))
 | 
						|
+			*out = (cmd.resp[0] >> 8) & 0xFF;
 | 
						|
+		else
 | 
						|
+			*out = cmd.resp[0] & 0xFF;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
 | 
						|
+	unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
 | 
						|
+{
 | 
						|
+	struct mmc_request mrq;
 | 
						|
+	struct mmc_command cmd;
 | 
						|
+	struct mmc_data data;
 | 
						|
+	struct scatterlist sg;
 | 
						|
+
 | 
						|
+	BUG_ON(!card);
 | 
						|
+	BUG_ON(fn > 7);
 | 
						|
+	BUG_ON(blocks == 1 && blksz > 512);
 | 
						|
+	WARN_ON(blocks == 0);
 | 
						|
+	WARN_ON(blksz == 0);
 | 
						|
+
 | 
						|
+	memset(&mrq, 0, sizeof(struct mmc_request));
 | 
						|
+	memset(&cmd, 0, sizeof(struct mmc_command));
 | 
						|
+	memset(&data, 0, sizeof(struct mmc_data));
 | 
						|
+
 | 
						|
+	mrq.cmd = &cmd;
 | 
						|
+	mrq.data = &data;
 | 
						|
+
 | 
						|
+	cmd.opcode = SD_IO_RW_EXTENDED;
 | 
						|
+	cmd.arg = write ? 0x80000000 : 0x00000000;
 | 
						|
+	cmd.arg |= fn << 28;
 | 
						|
+	cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
 | 
						|
+	cmd.arg |= addr << 9;
 | 
						|
+	if (blocks == 1 && blksz <= 512)
 | 
						|
+		cmd.arg |= (blksz == 512) ? 0 : blksz;	/* byte mode */
 | 
						|
+	else
 | 
						|
+		cmd.arg |= 0x08000000 | blocks;		/* block mode */
 | 
						|
+	cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
 | 
						|
+
 | 
						|
+	data.blksz = blksz;
 | 
						|
+	data.blocks = blocks;
 | 
						|
+	data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
 | 
						|
+	data.sg = &sg;
 | 
						|
+	data.sg_len = 1;
 | 
						|
+
 | 
						|
+	sg_init_one(&sg, buf, blksz * blocks);
 | 
						|
+
 | 
						|
+	mmc_set_data_timeout(&data, card);
 | 
						|
+
 | 
						|
+	mmc_wait_for_req(card->host, &mrq);
 | 
						|
+
 | 
						|
+	if (cmd.error)
 | 
						|
+		return cmd.error;
 | 
						|
+	if (data.error)
 | 
						|
+		return data.error;
 | 
						|
+
 | 
						|
+	if (mmc_host_is_spi(card->host)) {
 | 
						|
+		/* host driver already reported errors */
 | 
						|
+	} else {
 | 
						|
+		if (cmd.resp[0] & R5_ERROR)
 | 
						|
+			return -EIO;
 | 
						|
+		if (cmd.resp[0] & R5_FUNCTION_NUMBER)
 | 
						|
+			return -EINVAL;
 | 
						|
+		if (cmd.resp[0] & R5_OUT_OF_RANGE)
 | 
						|
+			return -ERANGE;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/mmc/core/sdio_ops.h
 | 
						|
@@ -0,0 +1,22 @@
 | 
						|
+/*
 | 
						|
+ *  linux/drivers/mmc/sdio_ops.c
 | 
						|
+ *
 | 
						|
+ *  Copyright 2006-2007 Pierre Ossman
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or modify
 | 
						|
+ * it under the terms of the GNU General Public License as published by
 | 
						|
+ * the Free Software Foundation; either version 2 of the License, or (at
 | 
						|
+ * your option) any later version.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#ifndef _MMC_SDIO_OPS_H
 | 
						|
+#define _MMC_SDIO_OPS_H
 | 
						|
+
 | 
						|
+int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
 | 
						|
+int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
 | 
						|
+	unsigned addr, u8 in, u8* out);
 | 
						|
+int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
 | 
						|
+	unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz);
 | 
						|
+
 | 
						|
+#endif
 | 
						|
+
 | 
						|
--- a/include/linux/mmc/card.h
 | 
						|
+++ b/include/linux/mmc/card.h
 | 
						|
@@ -55,7 +55,28 @@
 | 
						|
 	unsigned int		hs_max_dtr;
 | 
						|
 };
 | 
						|
 
 | 
						|
+struct sdio_cccr {
 | 
						|
+	unsigned int		sdio_vsn;
 | 
						|
+	unsigned int		sd_vsn;
 | 
						|
+	unsigned int		multi_block:1,
 | 
						|
+				low_speed:1,
 | 
						|
+				wide_bus:1,
 | 
						|
+				high_power:1,
 | 
						|
+				high_speed:1;
 | 
						|
+};
 | 
						|
+
 | 
						|
+struct sdio_cis {
 | 
						|
+	unsigned short		vendor;
 | 
						|
+	unsigned short		device;
 | 
						|
+	unsigned short		blksize;
 | 
						|
+	unsigned int		max_dtr;
 | 
						|
+};
 | 
						|
+
 | 
						|
 struct mmc_host;
 | 
						|
+struct sdio_func;
 | 
						|
+struct sdio_func_tuple;
 | 
						|
+
 | 
						|
+#define SDIO_MAX_FUNCS		7
 | 
						|
 
 | 
						|
 /*
 | 
						|
  * MMC device
 | 
						|
@@ -67,11 +88,13 @@
 | 
						|
 	unsigned int		type;		/* card type */
 | 
						|
 #define MMC_TYPE_MMC		0		/* MMC card */
 | 
						|
 #define MMC_TYPE_SD		1		/* SD card */
 | 
						|
+#define MMC_TYPE_SDIO		2		/* SDIO card */
 | 
						|
 	unsigned int		state;		/* (our) card state */
 | 
						|
 #define MMC_STATE_PRESENT	(1<<0)		/* present in sysfs */
 | 
						|
 #define MMC_STATE_READONLY	(1<<1)		/* card is read-only */
 | 
						|
 #define MMC_STATE_HIGHSPEED	(1<<2)		/* card is in high speed mode */
 | 
						|
 #define MMC_STATE_BLOCKADDR	(1<<3)		/* card uses block-addressing */
 | 
						|
+
 | 
						|
 	u32			raw_cid[4];	/* raw card CID */
 | 
						|
 	u32			raw_csd[4];	/* raw card CSD */
 | 
						|
 	u32			raw_scr[2];	/* raw card SCR */
 | 
						|
@@ -80,10 +103,19 @@
 | 
						|
 	struct mmc_ext_csd	ext_csd;	/* mmc v4 extended card specific */
 | 
						|
 	struct sd_scr		scr;		/* extra SD information */
 | 
						|
 	struct sd_switch_caps	sw_caps;	/* switch (CMD6) caps */
 | 
						|
+
 | 
						|
+	unsigned int		sdio_funcs;	/* number of SDIO functions */
 | 
						|
+	struct sdio_cccr	cccr;		/* common card info */
 | 
						|
+	struct sdio_cis		cis;		/* common tuple info */
 | 
						|
+	struct sdio_func	*sdio_func[SDIO_MAX_FUNCS]; /* SDIO functions (devices) */
 | 
						|
+	unsigned		num_info;	/* number of info strings */
 | 
						|
+	const char		**info;		/* info strings */
 | 
						|
+	struct sdio_func_tuple	*tuples;	/* unknown common tuples */
 | 
						|
 };
 | 
						|
 
 | 
						|
 #define mmc_card_mmc(c)		((c)->type == MMC_TYPE_MMC)
 | 
						|
 #define mmc_card_sd(c)		((c)->type == MMC_TYPE_SD)
 | 
						|
+#define mmc_card_sdio(c)	((c)->type == MMC_TYPE_SDIO)
 | 
						|
 
 | 
						|
 #define mmc_card_present(c)	((c)->state & MMC_STATE_PRESENT)
 | 
						|
 #define mmc_card_readonly(c)	((c)->state & MMC_STATE_READONLY)
 | 
						|
--- a/include/linux/mmc/core.h
 | 
						|
+++ b/include/linux/mmc/core.h
 | 
						|
@@ -25,14 +25,20 @@
 | 
						|
 #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 byte */
 | 
						|
+#define MMC_RSP_SPI_B4	(1 << 9)		/* four data bytes */
 | 
						|
+#define MMC_RSP_SPI_BUSY (1 << 10)		/* card may send busy */
 | 
						|
+
 | 
						|
 /*
 | 
						|
- * These are the response types, and correspond to valid bit
 | 
						|
+ * These are the native response types, and correspond to valid bit
 | 
						|
  * patterns of the above flags.  One additional valid pattern
 | 
						|
  * is all zeros, which means we don't expect a response.
 | 
						|
  */
 | 
						|
@@ -41,12 +47,30 @@
 | 
						|
 #define MMC_RSP_R1B	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
 | 
						|
 #define MMC_RSP_R2	(MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
 | 
						|
 #define MMC_RSP_R3	(MMC_RSP_PRESENT)
 | 
						|
+#define MMC_RSP_R4	(MMC_RSP_PRESENT)
 | 
						|
+#define MMC_RSP_R5	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
 | 
						|
 #define MMC_RSP_R6	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
 | 
						|
 #define MMC_RSP_R7	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
 | 
						|
 
 | 
						|
 #define mmc_resp_type(cmd)	((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
 | 
						|
 
 | 
						|
 /*
 | 
						|
+ * These are the SPI response types for MMC, SD, and SDIO cards.
 | 
						|
+ * Commands return R1, with maybe more info.  Zero is an error type;
 | 
						|
+ * callers must always provide the appropriate MMC_RSP_SPI_Rx flags.
 | 
						|
+ */
 | 
						|
+#define MMC_RSP_SPI_R1	(MMC_RSP_SPI_S1)
 | 
						|
+#define MMC_RSP_SPI_R1B	(MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY)
 | 
						|
+#define MMC_RSP_SPI_R2	(MMC_RSP_SPI_S1|MMC_RSP_SPI_S2)
 | 
						|
+#define MMC_RSP_SPI_R3	(MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
 | 
						|
+#define MMC_RSP_SPI_R4	(MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
 | 
						|
+#define MMC_RSP_SPI_R5	(MMC_RSP_SPI_S1|MMC_RSP_SPI_S2)
 | 
						|
+#define MMC_RSP_SPI_R7	(MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
 | 
						|
+
 | 
						|
+#define mmc_spi_resp_type(cmd)	((cmd)->flags & \
 | 
						|
+		(MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY|MMC_RSP_SPI_S2|MMC_RSP_SPI_B4))
 | 
						|
+
 | 
						|
+/*
 | 
						|
  * These are the command types.
 | 
						|
  */
 | 
						|
 #define mmc_cmd_type(cmd)	((cmd)->flags & MMC_CMD_MASK)
 | 
						|
@@ -54,12 +78,19 @@
 | 
						|
 	unsigned int		retries;	/* max number of retries */
 | 
						|
 	unsigned int		error;		/* command error */
 | 
						|
 
 | 
						|
-#define MMC_ERR_NONE	0
 | 
						|
-#define MMC_ERR_TIMEOUT	1
 | 
						|
-#define MMC_ERR_BADCRC	2
 | 
						|
-#define MMC_ERR_FIFO	3
 | 
						|
-#define MMC_ERR_FAILED	4
 | 
						|
-#define MMC_ERR_INVALID	5
 | 
						|
+/*
 | 
						|
+ * Standard errno values are used for errors, but some have specific
 | 
						|
+ * meaning in the MMC layer:
 | 
						|
+ *
 | 
						|
+ * ETIMEDOUT    Card took too long to respond
 | 
						|
+ * EILSEQ       Basic format problem with the received or sent data
 | 
						|
+ *              (e.g. CRC check failed, incorrect opcode in response
 | 
						|
+ *              or bad end bit)
 | 
						|
+ * EINVAL       Request cannot be performed because of restrictions
 | 
						|
+ *              in hardware and/or the driver
 | 
						|
+ * ENOMEDIUM    Host can determine that the slot is empty and is
 | 
						|
+ *              actively failing requests
 | 
						|
+ */
 | 
						|
 
 | 
						|
 	struct mmc_data		*data;		/* data segment associated with cmd */
 | 
						|
 	struct mmc_request	*mrq;		/* associated request */
 | 
						|
@@ -76,7 +107,6 @@
 | 
						|
 #define MMC_DATA_WRITE	(1 << 8)
 | 
						|
 #define MMC_DATA_READ	(1 << 9)
 | 
						|
 #define MMC_DATA_STREAM	(1 << 10)
 | 
						|
-#define MMC_DATA_MULTI	(1 << 11)
 | 
						|
 
 | 
						|
 	unsigned int		bytes_xfered;
 | 
						|
 
 | 
						|
@@ -104,9 +134,20 @@
 | 
						|
 extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
 | 
						|
 	struct mmc_command *, int);
 | 
						|
 
 | 
						|
-extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int);
 | 
						|
+extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *);
 | 
						|
 
 | 
						|
-extern void mmc_claim_host(struct mmc_host *host);
 | 
						|
+extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
 | 
						|
 extern void mmc_release_host(struct mmc_host *host);
 | 
						|
 
 | 
						|
+/**
 | 
						|
+ *	mmc_claim_host - exclusively claim a host
 | 
						|
+ *	@host: mmc host to claim
 | 
						|
+ *
 | 
						|
+ *	Claim a host for a set of operations.
 | 
						|
+ */
 | 
						|
+static inline void mmc_claim_host(struct mmc_host *host)
 | 
						|
+{
 | 
						|
+	__mmc_claim_host(host, NULL);
 | 
						|
+}
 | 
						|
+
 | 
						|
 #endif
 | 
						|
--- a/include/linux/mmc/host.h
 | 
						|
+++ b/include/linux/mmc/host.h
 | 
						|
@@ -10,6 +10,8 @@
 | 
						|
 #ifndef LINUX_MMC_HOST_H
 | 
						|
 #define LINUX_MMC_HOST_H
 | 
						|
 
 | 
						|
+#include <linux/leds.h>
 | 
						|
+
 | 
						|
 #include <linux/mmc/core.h>
 | 
						|
 
 | 
						|
 struct mmc_ios {
 | 
						|
@@ -51,6 +53,7 @@
 | 
						|
 	void	(*request)(struct mmc_host *host, struct mmc_request *req);
 | 
						|
 	void	(*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
 | 
						|
 	int	(*get_ro)(struct mmc_host *host);
 | 
						|
+	void	(*enable_sdio_irq)(struct mmc_host *host, int enable);
 | 
						|
 };
 | 
						|
 
 | 
						|
 struct mmc_card;
 | 
						|
@@ -87,9 +90,10 @@
 | 
						|
 
 | 
						|
 #define MMC_CAP_4_BIT_DATA	(1 << 0)	/* Can the host do 4 bit transfers */
 | 
						|
 #define MMC_CAP_MULTIWRITE	(1 << 1)	/* Can accurately report bytes sent to card on error */
 | 
						|
-#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_MMC_HIGHSPEED	(1 << 2)	/* Can do MMC high-speed timing */
 | 
						|
+#define MMC_CAP_SD_HIGHSPEED	(1 << 3)	/* Can do SD high-speed timing */
 | 
						|
+#define MMC_CAP_SDIO_IRQ	(1 << 4)	/* Can signal pending SDIO IRQs */
 | 
						|
+#define MMC_CAP_SPI		(1 << 5)	/* Talks only SPI protocols */
 | 
						|
 
 | 
						|
 	/* host specific block data */
 | 
						|
 	unsigned int		max_seg_size;	/* see blk_queue_max_segment_size */
 | 
						|
@@ -106,23 +110,30 @@
 | 
						|
 	struct mmc_ios		ios;		/* current io bus settings */
 | 
						|
 	u32			ocr;		/* the current OCR setting */
 | 
						|
 
 | 
						|
-	unsigned int		mode;		/* current card mode of host */
 | 
						|
-#define MMC_MODE_MMC		0
 | 
						|
-#define MMC_MODE_SD		1
 | 
						|
+	/* group bitfields together to minimize padding */
 | 
						|
+	unsigned int		use_spi_crc:1;
 | 
						|
+	unsigned int		claimed:1;	/* host exclusively claimed */
 | 
						|
+	unsigned int		bus_dead:1;	/* bus has been released */
 | 
						|
+#ifdef CONFIG_MMC_DEBUG
 | 
						|
+	unsigned int		removed:1;	/* host is being removed */
 | 
						|
+#endif
 | 
						|
 
 | 
						|
 	struct mmc_card		*card;		/* device attached to this host */
 | 
						|
 
 | 
						|
 	wait_queue_head_t	wq;
 | 
						|
-	unsigned int		claimed:1;	/* host exclusively claimed */
 | 
						|
 
 | 
						|
 	struct delayed_work	detect;
 | 
						|
-#ifdef CONFIG_MMC_DEBUG
 | 
						|
-	unsigned int		removed:1;	/* host is being removed */
 | 
						|
-#endif
 | 
						|
 
 | 
						|
 	const struct mmc_bus_ops *bus_ops;	/* current bus driver */
 | 
						|
 	unsigned int		bus_refs;	/* reference counter */
 | 
						|
-	unsigned int		bus_dead:1;	/* bus has been released */
 | 
						|
+
 | 
						|
+	unsigned int		sdio_irqs;
 | 
						|
+	struct task_struct	*sdio_irq_thread;
 | 
						|
+	atomic_t		sdio_irq_thread_abort;
 | 
						|
+
 | 
						|
+#ifdef CONFIG_LEDS_TRIGGERS
 | 
						|
+	struct led_trigger	*led;		/* activity led */
 | 
						|
+#endif
 | 
						|
 
 | 
						|
 	unsigned long		private[0] ____cacheline_aligned;
 | 
						|
 };
 | 
						|
@@ -137,6 +148,8 @@
 | 
						|
 	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)
 | 
						|
@@ -147,5 +160,11 @@
 | 
						|
 extern void mmc_detect_change(struct mmc_host *, unsigned long delay);
 | 
						|
 extern void mmc_request_done(struct mmc_host *, struct mmc_request *);
 | 
						|
 
 | 
						|
+static inline void mmc_signal_sdio_irq(struct mmc_host *host)
 | 
						|
+{
 | 
						|
+	host->ops->enable_sdio_irq(host, 0);
 | 
						|
+	wake_up_process(host->sdio_irq_thread);
 | 
						|
+}
 | 
						|
+
 | 
						|
 #endif
 | 
						|
 
 | 
						|
--- a/include/linux/mmc/mmc.h
 | 
						|
+++ b/include/linux/mmc/mmc.h
 | 
						|
@@ -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,33 @@
 | 
						|
 #define R1_CARD_ECC_DISABLED	(1 << 14)	/* sx, a */
 | 
						|
 #define R1_ERASE_RESET		(1 << 13)	/* sr, c */
 | 
						|
 #define R1_STATUS(x)            (x & 0xFFFFE000)
 | 
						|
-#define R1_CURRENT_STATE(x)    	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
 | 
						|
+#define R1_CURRENT_STATE(x)	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
 | 
						|
 #define R1_READY_FOR_DATA	(1 << 8)	/* sx, a */
 | 
						|
 #define R1_APP_CMD		(1 << 5)	/* sr, c */
 | 
						|
 
 | 
						|
+/*
 | 
						|
+ * MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS
 | 
						|
+ * R1 is the low order byte; R2 is the next highest byte, when present.
 | 
						|
+ */
 | 
						|
+#define R1_SPI_IDLE		(1 << 0)
 | 
						|
+#define R1_SPI_ERASE_RESET	(1 << 1)
 | 
						|
+#define R1_SPI_ILLEGAL_COMMAND	(1 << 2)
 | 
						|
+#define R1_SPI_COM_CRC		(1 << 3)
 | 
						|
+#define R1_SPI_ERASE_SEQ	(1 << 4)
 | 
						|
+#define R1_SPI_ADDRESS		(1 << 5)
 | 
						|
+#define R1_SPI_PARAMETER	(1 << 6)
 | 
						|
+/* R1 bit 7 is always zero */
 | 
						|
+#define R2_SPI_CARD_LOCKED	(1 << 8)
 | 
						|
+#define R2_SPI_WP_ERASE_SKIP	(1 << 9)	/* or lock/unlock fail */
 | 
						|
+#define R2_SPI_LOCK_UNLOCK_FAIL	R2_SPI_WP_ERASE_SKIP
 | 
						|
+#define R2_SPI_ERROR		(1 << 10)
 | 
						|
+#define R2_SPI_CC_ERROR		(1 << 11)
 | 
						|
+#define R2_SPI_CARD_ECC_ERROR	(1 << 12)
 | 
						|
+#define R2_SPI_WP_VIOLATION	(1 << 13)
 | 
						|
+#define R2_SPI_ERASE_PARAM	(1 << 14)
 | 
						|
+#define R2_SPI_OUT_OF_RANGE	(1 << 15)	/* or CSD overwrite */
 | 
						|
+#define R2_SPI_CSD_OVERWRITE	R2_SPI_OUT_OF_RANGE
 | 
						|
+
 | 
						|
 /* These are unpacked versions of the actual responses */
 | 
						|
 
 | 
						|
 struct _mmc_csd {
 | 
						|
@@ -182,6 +207,7 @@
 | 
						|
  */
 | 
						|
 #define CCC_BASIC		(1<<0)	/* (0) Basic protocol functions */
 | 
						|
 					/* (CMD0,1,2,3,4,7,9,10,12,13,15) */
 | 
						|
+					/* (and for SPI, CMD58,59) */
 | 
						|
 #define CCC_STREAM_READ		(1<<1)	/* (1) Stream read commands */
 | 
						|
 					/* (CMD11) */
 | 
						|
 #define CCC_BLOCK_READ		(1<<2)	/* (2) Block read commands */
 | 
						|
@@ -227,6 +253,7 @@
 | 
						|
 #define EXT_CSD_BUS_WIDTH	183	/* R/W */
 | 
						|
 #define EXT_CSD_HS_TIMING	185	/* R/W */
 | 
						|
 #define EXT_CSD_CARD_TYPE	196	/* RO */
 | 
						|
+#define EXT_CSD_REV		192	/* RO */
 | 
						|
 #define EXT_CSD_SEC_CNT		212	/* RO, 4 bytes */
 | 
						|
 
 | 
						|
 /*
 | 
						|
--- /dev/null
 | 
						|
+++ b/include/linux/mmc/sdio.h
 | 
						|
@@ -0,0 +1,159 @@
 | 
						|
+/*
 | 
						|
+ *  include/linux/mmc/sdio.h
 | 
						|
+ *
 | 
						|
+ *  Copyright 2006-2007 Pierre Ossman
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or modify
 | 
						|
+ * it under the terms of the GNU General Public License as published by
 | 
						|
+ * the Free Software Foundation; either version 2 of the License, or (at
 | 
						|
+ * your option) any later version.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#ifndef MMC_SDIO_H
 | 
						|
+#define MMC_SDIO_H
 | 
						|
+
 | 
						|
+/* SDIO commands                         type  argument     response */
 | 
						|
+#define SD_IO_SEND_OP_COND          5 /* bcr  [23:0] OCR         R4  */
 | 
						|
+#define SD_IO_RW_DIRECT            52 /* ac   [31:0] See below   R5  */
 | 
						|
+#define SD_IO_RW_EXTENDED          53 /* adtc [31:0] See below   R5  */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * SD_IO_RW_DIRECT argument format:
 | 
						|
+ *
 | 
						|
+ *      [31] R/W flag
 | 
						|
+ *      [30:28] Function number
 | 
						|
+ *      [27] RAW flag
 | 
						|
+ *      [25:9] Register address
 | 
						|
+ *      [7:0] Data
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * SD_IO_RW_EXTENDED argument format:
 | 
						|
+ *
 | 
						|
+ *      [31] R/W flag
 | 
						|
+ *      [30:28] Function number
 | 
						|
+ *      [27] Block mode
 | 
						|
+ *      [26] Increment address
 | 
						|
+ *      [25:9] Register address
 | 
						|
+ *      [8:0] Byte/block count
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+  SDIO status in R5
 | 
						|
+  Type
 | 
						|
+	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
 | 
						|
+	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
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define R5_COM_CRC_ERROR	(1 << 15)	/* er, b */
 | 
						|
+#define R5_ILLEGAL_COMMAND	(1 << 14)	/* er, b */
 | 
						|
+#define R5_ERROR		(1 << 11)	/* erx, c */
 | 
						|
+#define R5_FUNCTION_NUMBER	(1 << 9)	/* er, c */
 | 
						|
+#define R5_OUT_OF_RANGE		(1 << 8)	/* er, c */
 | 
						|
+#define R5_STATUS(x)		(x & 0xCB00)
 | 
						|
+#define R5_IO_CURRENT_STATE(x)	((x & 0x3000) >> 12) /* s, b */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Card Common Control Registers (CCCR)
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define SDIO_CCCR_CCCR		0x00
 | 
						|
+
 | 
						|
+#define  SDIO_CCCR_REV_1_00	0	/* CCCR/FBR Version 1.00 */
 | 
						|
+#define  SDIO_CCCR_REV_1_10	1	/* CCCR/FBR Version 1.10 */
 | 
						|
+#define  SDIO_CCCR_REV_1_20	2	/* CCCR/FBR Version 1.20 */
 | 
						|
+
 | 
						|
+#define  SDIO_SDIO_REV_1_00	0	/* SDIO Spec Version 1.00 */
 | 
						|
+#define  SDIO_SDIO_REV_1_10	1	/* SDIO Spec Version 1.10 */
 | 
						|
+#define  SDIO_SDIO_REV_1_20	2	/* SDIO Spec Version 1.20 */
 | 
						|
+#define  SDIO_SDIO_REV_2_00	3	/* SDIO Spec Version 2.00 */
 | 
						|
+
 | 
						|
+#define SDIO_CCCR_SD		0x01
 | 
						|
+
 | 
						|
+#define  SDIO_SD_REV_1_01	0	/* SD Physical Spec Version 1.01 */
 | 
						|
+#define  SDIO_SD_REV_1_10	1	/* SD Physical Spec Version 1.10 */
 | 
						|
+#define  SDIO_SD_REV_2_00	2	/* SD Physical Spec Version 2.00 */
 | 
						|
+
 | 
						|
+#define SDIO_CCCR_IOEx		0x02
 | 
						|
+#define SDIO_CCCR_IORx		0x03
 | 
						|
+
 | 
						|
+#define SDIO_CCCR_IENx		0x04	/* Function/Master Interrupt Enable */
 | 
						|
+#define SDIO_CCCR_INTx		0x05	/* Function Interrupt Pending */
 | 
						|
+
 | 
						|
+#define SDIO_CCCR_ABORT		0x06	/* function abort/card reset */
 | 
						|
+
 | 
						|
+#define SDIO_CCCR_IF		0x07	/* bus interface controls */
 | 
						|
+
 | 
						|
+#define  SDIO_BUS_WIDTH_1BIT	0x00
 | 
						|
+#define  SDIO_BUS_WIDTH_4BIT	0x02
 | 
						|
+
 | 
						|
+#define  SDIO_BUS_CD_DISABLE     0x80	/* disable pull-up on DAT3 (pin 1) */
 | 
						|
+
 | 
						|
+#define SDIO_CCCR_CAPS		0x08
 | 
						|
+
 | 
						|
+#define  SDIO_CCCR_CAP_SDC	0x01	/* can do CMD52 while data transfer */
 | 
						|
+#define  SDIO_CCCR_CAP_SMB	0x02	/* can do multi-block xfers (CMD53) */
 | 
						|
+#define  SDIO_CCCR_CAP_SRW	0x04	/* supports read-wait protocol */
 | 
						|
+#define  SDIO_CCCR_CAP_SBS	0x08	/* supports suspend/resume */
 | 
						|
+#define  SDIO_CCCR_CAP_S4MI	0x10	/* interrupt during 4-bit CMD53 */
 | 
						|
+#define  SDIO_CCCR_CAP_E4MI	0x20	/* enable ints during 4-bit CMD53 */
 | 
						|
+#define  SDIO_CCCR_CAP_LSC	0x40	/* low speed card */
 | 
						|
+#define  SDIO_CCCR_CAP_4BLS	0x80	/* 4 bit low speed card */
 | 
						|
+
 | 
						|
+#define SDIO_CCCR_CIS		0x09	/* common CIS pointer (3 bytes) */
 | 
						|
+
 | 
						|
+/* Following 4 regs are valid only if SBS is set */
 | 
						|
+#define SDIO_CCCR_SUSPEND	0x0c
 | 
						|
+#define SDIO_CCCR_SELx		0x0d
 | 
						|
+#define SDIO_CCCR_EXECx		0x0e
 | 
						|
+#define SDIO_CCCR_READYx	0x0f
 | 
						|
+
 | 
						|
+#define SDIO_CCCR_BLKSIZE	0x10
 | 
						|
+
 | 
						|
+#define SDIO_CCCR_POWER		0x12
 | 
						|
+
 | 
						|
+#define  SDIO_POWER_SMPC	0x01	/* Supports Master Power Control */
 | 
						|
+#define  SDIO_POWER_EMPC	0x02	/* Enable Master Power Control */
 | 
						|
+
 | 
						|
+#define SDIO_CCCR_SPEED		0x13
 | 
						|
+
 | 
						|
+#define  SDIO_SPEED_SHS		0x01	/* Supports High-Speed mode */
 | 
						|
+#define  SDIO_SPEED_EHS		0x02	/* Enable High-Speed mode */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Function Basic Registers (FBR)
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define SDIO_FBR_BASE(f)	((f) * 0x100) /* base of function f's FBRs */
 | 
						|
+
 | 
						|
+#define SDIO_FBR_STD_IF		0x00
 | 
						|
+
 | 
						|
+#define  SDIO_FBR_SUPPORTS_CSA	0x40	/* supports Code Storage Area */
 | 
						|
+#define  SDIO_FBR_ENABLE_CSA	0x80	/* enable Code Storage Area */
 | 
						|
+
 | 
						|
+#define SDIO_FBR_STD_IF_EXT	0x01
 | 
						|
+
 | 
						|
+#define SDIO_FBR_POWER		0x02
 | 
						|
+
 | 
						|
+#define  SDIO_FBR_POWER_SPS	0x01	/* Supports Power Selection */
 | 
						|
+#define  SDIO_FBR_POWER_EPS	0x02	/* Enable (low) Power Selection */
 | 
						|
+
 | 
						|
+#define SDIO_FBR_CIS		0x09	/* CIS pointer (3 bytes) */
 | 
						|
+
 | 
						|
+
 | 
						|
+#define SDIO_FBR_CSA		0x0C	/* CSA pointer (3 bytes) */
 | 
						|
+
 | 
						|
+#define SDIO_FBR_CSA_DATA	0x0F
 | 
						|
+
 | 
						|
+#define SDIO_FBR_BLKSIZE	0x10	/* block size (2 bytes) */
 | 
						|
+
 | 
						|
+#endif
 | 
						|
+
 | 
						|
--- /dev/null
 | 
						|
+++ b/include/linux/mmc/sdio_func.h
 | 
						|
@@ -0,0 +1,153 @@
 | 
						|
+/*
 | 
						|
+ *  include/linux/mmc/sdio_func.h
 | 
						|
+ *
 | 
						|
+ *  Copyright 2007 Pierre Ossman
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or modify
 | 
						|
+ * it under the terms of the GNU General Public License as published by
 | 
						|
+ * the Free Software Foundation; either version 2 of the License, or (at
 | 
						|
+ * your option) any later version.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#ifndef MMC_SDIO_FUNC_H
 | 
						|
+#define MMC_SDIO_FUNC_H
 | 
						|
+
 | 
						|
+#include <linux/device.h>
 | 
						|
+#include <linux/mod_devicetable.h>
 | 
						|
+
 | 
						|
+struct mmc_card;
 | 
						|
+struct sdio_func;
 | 
						|
+
 | 
						|
+typedef void (sdio_irq_handler_t)(struct sdio_func *);
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * SDIO function CIS tuple (unknown to the core)
 | 
						|
+ */
 | 
						|
+struct sdio_func_tuple {
 | 
						|
+	struct sdio_func_tuple *next;
 | 
						|
+	unsigned char code;
 | 
						|
+	unsigned char size;
 | 
						|
+	unsigned char data[0];
 | 
						|
+};
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * SDIO function devices
 | 
						|
+ */
 | 
						|
+struct sdio_func {
 | 
						|
+	struct mmc_card		*card;		/* the card this device belongs to */
 | 
						|
+	struct device		dev;		/* the device */
 | 
						|
+	sdio_irq_handler_t	*irq_handler;	/* IRQ callback */
 | 
						|
+	unsigned int		num;		/* function number */
 | 
						|
+
 | 
						|
+	unsigned char		class;		/* standard interface class */
 | 
						|
+	unsigned short		vendor;		/* vendor id */
 | 
						|
+	unsigned short		device;		/* device id */
 | 
						|
+
 | 
						|
+	unsigned		max_blksize;	/* maximum block size */
 | 
						|
+	unsigned		cur_blksize;	/* current block size */
 | 
						|
+
 | 
						|
+	unsigned int		state;		/* function state */
 | 
						|
+#define SDIO_STATE_PRESENT	(1<<0)		/* present in sysfs */
 | 
						|
+
 | 
						|
+	u8			tmpbuf[4];	/* DMA:able scratch buffer */
 | 
						|
+
 | 
						|
+	unsigned		num_info;	/* number of info strings */
 | 
						|
+	const char		**info;		/* info strings */
 | 
						|
+
 | 
						|
+	struct sdio_func_tuple *tuples;
 | 
						|
+};
 | 
						|
+
 | 
						|
+#define sdio_func_present(f)	((f)->state & SDIO_STATE_PRESENT)
 | 
						|
+
 | 
						|
+#define sdio_func_set_present(f) ((f)->state |= SDIO_STATE_PRESENT)
 | 
						|
+
 | 
						|
+#define sdio_func_id(f)		((f)->dev.bus_id)
 | 
						|
+
 | 
						|
+#define sdio_get_drvdata(f)	dev_get_drvdata(&(f)->dev)
 | 
						|
+#define sdio_set_drvdata(f,d)	dev_set_drvdata(&(f)->dev, d)
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * SDIO function device driver
 | 
						|
+ */
 | 
						|
+struct sdio_driver {
 | 
						|
+	char *name;
 | 
						|
+	const struct sdio_device_id *id_table;
 | 
						|
+
 | 
						|
+	int (*probe)(struct sdio_func *, const struct sdio_device_id *);
 | 
						|
+	void (*remove)(struct sdio_func *);
 | 
						|
+
 | 
						|
+	struct device_driver drv;
 | 
						|
+};
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * SDIO_DEVICE - macro used to describe a specific SDIO device
 | 
						|
+ * @vend: the 16 bit manufacturer code
 | 
						|
+ * @dev: the 16 bit function id
 | 
						|
+ *
 | 
						|
+ * This macro is used to create a struct sdio_device_id that matches a
 | 
						|
+ * specific device. The class field will be set to SDIO_ANY_ID.
 | 
						|
+ */
 | 
						|
+#define SDIO_DEVICE(vend,dev) \
 | 
						|
+	.class = SDIO_ANY_ID, \
 | 
						|
+	.vendor = (vend), .device = (dev)
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * SDIO_DEVICE_CLASS - macro used to describe a specific SDIO device class
 | 
						|
+ * @dev_class: the 8 bit standard interface code
 | 
						|
+ *
 | 
						|
+ * This macro is used to create a struct sdio_device_id that matches a
 | 
						|
+ * specific standard SDIO function type.  The vendor and device fields will
 | 
						|
+ * be set to SDIO_ANY_ID.
 | 
						|
+ */
 | 
						|
+#define SDIO_DEVICE_CLASS(dev_class) \
 | 
						|
+	.class = (dev_class), \
 | 
						|
+	.vendor = SDIO_ANY_ID, .device = SDIO_ANY_ID
 | 
						|
+
 | 
						|
+extern int sdio_register_driver(struct sdio_driver *);
 | 
						|
+extern void sdio_unregister_driver(struct sdio_driver *);
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * SDIO I/O operations
 | 
						|
+ */
 | 
						|
+extern void sdio_claim_host(struct sdio_func *func);
 | 
						|
+extern void sdio_release_host(struct sdio_func *func);
 | 
						|
+
 | 
						|
+extern int sdio_enable_func(struct sdio_func *func);
 | 
						|
+extern int sdio_disable_func(struct sdio_func *func);
 | 
						|
+
 | 
						|
+extern int sdio_set_block_size(struct sdio_func *func, unsigned blksz);
 | 
						|
+
 | 
						|
+extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler);
 | 
						|
+extern int sdio_release_irq(struct sdio_func *func);
 | 
						|
+
 | 
						|
+extern unsigned char sdio_readb(struct sdio_func *func,
 | 
						|
+	unsigned int addr, int *err_ret);
 | 
						|
+extern unsigned short sdio_readw(struct sdio_func *func,
 | 
						|
+	unsigned int addr, int *err_ret);
 | 
						|
+extern unsigned long sdio_readl(struct sdio_func *func,
 | 
						|
+	unsigned int addr, int *err_ret);
 | 
						|
+
 | 
						|
+extern int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
 | 
						|
+	unsigned int addr, int count);
 | 
						|
+extern int sdio_readsb(struct sdio_func *func, void *dst,
 | 
						|
+	unsigned int addr, int count);
 | 
						|
+
 | 
						|
+extern void sdio_writeb(struct sdio_func *func, unsigned char b,
 | 
						|
+	unsigned int addr, int *err_ret);
 | 
						|
+extern void sdio_writew(struct sdio_func *func, unsigned short b,
 | 
						|
+	unsigned int addr, int *err_ret);
 | 
						|
+extern void sdio_writel(struct sdio_func *func, unsigned long b,
 | 
						|
+	unsigned int addr, int *err_ret);
 | 
						|
+
 | 
						|
+extern int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
 | 
						|
+	void *src, int count);
 | 
						|
+extern int sdio_writesb(struct sdio_func *func, unsigned int addr,
 | 
						|
+	void *src, int count);
 | 
						|
+
 | 
						|
+extern unsigned char sdio_f0_readb(struct sdio_func *func,
 | 
						|
+	unsigned int addr, int *err_ret);
 | 
						|
+extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b,
 | 
						|
+	unsigned int addr, int *err_ret);
 | 
						|
+
 | 
						|
+#endif
 | 
						|
+
 | 
						|
--- /dev/null
 | 
						|
+++ b/include/linux/mmc/sdio_ids.h
 | 
						|
@@ -0,0 +1,29 @@
 | 
						|
+/*
 | 
						|
+ * SDIO Classes, Interface Types, Manufacturer IDs, etc.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#ifndef MMC_SDIO_IDS_H
 | 
						|
+#define MMC_SDIO_IDS_H
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Standard SDIO Function Interfaces
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define SDIO_CLASS_NONE		0x00	/* Not a SDIO standard interface */
 | 
						|
+#define SDIO_CLASS_UART		0x01	/* standard UART interface */
 | 
						|
+#define SDIO_CLASS_BT_A		0x02	/* Type-A BlueTooth std interface */
 | 
						|
+#define SDIO_CLASS_BT_B		0x03	/* Type-B BlueTooth std interface */
 | 
						|
+#define SDIO_CLASS_GPS		0x04	/* GPS standard interface */
 | 
						|
+#define SDIO_CLASS_CAMERA	0x05	/* Camera standard interface */
 | 
						|
+#define SDIO_CLASS_PHS		0x06	/* PHS standard interface */
 | 
						|
+#define SDIO_CLASS_WLAN		0x07	/* WLAN interface */
 | 
						|
+#define SDIO_CLASS_ATA		0x08	/* Embedded SDIO-ATA std interface */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Vendors and devices.  Sort key: vendor first, device next.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define SDIO_VENDOR_ID_MARVELL			0x02df
 | 
						|
+#define SDIO_DEVICE_ID_MARVELL_LIBERTAS		0x9103
 | 
						|
+
 | 
						|
+#endif
 | 
						|
--- a/include/linux/mod_devicetable.h
 | 
						|
+++ b/include/linux/mod_devicetable.h
 | 
						|
@@ -22,6 +22,18 @@
 | 
						|
 };
 | 
						|
 
 | 
						|
 
 | 
						|
+/* SDIO */
 | 
						|
+
 | 
						|
+#define SDIO_ANY_ID (~0)
 | 
						|
+
 | 
						|
+struct sdio_device_id {
 | 
						|
+	__u8	class;			/* Standard interface or SDIO_ANY_ID */
 | 
						|
+	__u16	vendor;			/* Vendor or SDIO_ANY_ID */
 | 
						|
+	__u16	device;			/* Device ID or SDIO_ANY_ID */
 | 
						|
+	kernel_ulong_t driver_data;	/* Data private to the driver */
 | 
						|
+};
 | 
						|
+
 | 
						|
+
 | 
						|
 #define IEEE1394_MATCH_VENDOR_ID	0x0001
 | 
						|
 #define IEEE1394_MATCH_MODEL_ID		0x0002
 | 
						|
 #define IEEE1394_MATCH_SPECIFIER_ID	0x0004
 | 
						|
--- a/drivers/mmc/card/Kconfig
 | 
						|
+++ b/drivers/mmc/card/Kconfig
 | 
						|
@@ -32,3 +32,10 @@
 | 
						|
 
 | 
						|
 	  If unsure, say Y here.
 | 
						|
 
 | 
						|
+config SDIO_UART
 | 
						|
+	tristate "SDIO UART/GPS class support"
 | 
						|
+	depends on MMC
 | 
						|
+	help
 | 
						|
+	  SDIO function driver for SDIO cards that implements the UART
 | 
						|
+	  class, as well as the GPS class which appears like a UART.
 | 
						|
+
 | 
						|
--- a/drivers/mmc/card/Makefile
 | 
						|
+++ b/drivers/mmc/card/Makefile
 | 
						|
@@ -9,3 +9,5 @@
 | 
						|
 obj-$(CONFIG_MMC_BLOCK)		+= mmc_block.o
 | 
						|
 mmc_block-objs			:= block.o queue.o
 | 
						|
 
 | 
						|
+obj-$(CONFIG_SDIO_UART)		+= sdio_uart.o
 | 
						|
+
 | 
						|
--- a/drivers/mmc/card/block.c
 | 
						|
+++ b/drivers/mmc/card/block.c
 | 
						|
@@ -44,6 +44,9 @@
 | 
						|
  * max 8 partitions per card
 | 
						|
  */
 | 
						|
 #define MMC_SHIFT	3
 | 
						|
+#define MMC_NUM_MINORS	(256 >> MMC_SHIFT)
 | 
						|
+
 | 
						|
+static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))];
 | 
						|
 
 | 
						|
 /*
 | 
						|
  * There is one mmc_blk_data per slot.
 | 
						|
@@ -80,6 +83,9 @@
 | 
						|
 	mutex_lock(&open_lock);
 | 
						|
 	md->usage--;
 | 
						|
 	if (md->usage == 0) {
 | 
						|
+		int devidx = md->disk->first_minor >> MMC_SHIFT;
 | 
						|
+		__clear_bit(devidx, dev_use);
 | 
						|
+
 | 
						|
 		put_disk(md->disk);
 | 
						|
 		kfree(md);
 | 
						|
 	}
 | 
						|
@@ -151,17 +157,19 @@
 | 
						|
 
 | 
						|
 	cmd.opcode = MMC_APP_CMD;
 | 
						|
 	cmd.arg = card->rca << 16;
 | 
						|
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
 | 
						|
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
 | 
						|
 
 | 
						|
 	err = mmc_wait_for_cmd(card->host, &cmd, 0);
 | 
						|
-	if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD))
 | 
						|
+	if (err)
 | 
						|
+		return (u32)-1;
 | 
						|
+	if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD))
 | 
						|
 		return (u32)-1;
 | 
						|
 
 | 
						|
 	memset(&cmd, 0, sizeof(struct mmc_command));
 | 
						|
 
 | 
						|
 	cmd.opcode = SD_APP_SEND_NUM_WR_BLKS;
 | 
						|
 	cmd.arg = 0;
 | 
						|
-	cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
 | 
						|
+	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
 | 
						|
 
 | 
						|
 	memset(&data, 0, sizeof(struct mmc_data));
 | 
						|
 
 | 
						|
@@ -192,7 +200,7 @@
 | 
						|
 
 | 
						|
 	mmc_wait_for_req(card->host, &mrq);
 | 
						|
 
 | 
						|
-	if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE)
 | 
						|
+	if (cmd.error || data.error)
 | 
						|
 		return (u32)-1;
 | 
						|
 
 | 
						|
 	blocks = ntohl(blocks);
 | 
						|
@@ -220,17 +228,15 @@
 | 
						|
 		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;
 | 
						|
 
 | 
						|
-		mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ);
 | 
						|
-
 | 
						|
 		/*
 | 
						|
 		 * If the host doesn't support multiple block writes, force
 | 
						|
 		 * block writes to single block. SD cards are excepted from
 | 
						|
@@ -243,8 +249,12 @@
 | 
						|
 			brq.data.blocks = 1;
 | 
						|
 
 | 
						|
 		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 {
 | 
						|
@@ -261,6 +271,8 @@
 | 
						|
 			brq.data.flags |= MMC_DATA_WRITE;
 | 
						|
 		}
 | 
						|
 
 | 
						|
+		mmc_set_data_timeout(&brq.data, card);
 | 
						|
+
 | 
						|
 		brq.data.sg = mq->sg;
 | 
						|
 		brq.data.sg_len = mmc_queue_map_sg(mq);
 | 
						|
 
 | 
						|
@@ -302,7 +314,7 @@
 | 
						|
 			goto cmd_err;
 | 
						|
 		}
 | 
						|
 
 | 
						|
-		if (rq_data_dir(req) != READ) {
 | 
						|
+		if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
 | 
						|
 			do {
 | 
						|
 				int err;
 | 
						|
 
 | 
						|
@@ -315,7 +327,13 @@
 | 
						|
 					       req->rq_disk->disk_name, err);
 | 
						|
 					goto cmd_err;
 | 
						|
 				}
 | 
						|
-			} while (!(cmd.resp[0] & R1_READY_FOR_DATA));
 | 
						|
+				/*
 | 
						|
+				 * Some cards mishandle the status bits,
 | 
						|
+				 * so make sure to check both the busy
 | 
						|
+				 * indication and the card state.
 | 
						|
+				 */
 | 
						|
+			} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
 | 
						|
+				(R1_CURRENT_STATE(cmd.resp[0]) == 7));
 | 
						|
 
 | 
						|
 #if 0
 | 
						|
 			if (cmd.resp[0] & ~0x00000900)
 | 
						|
@@ -394,9 +412,6 @@
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
-#define MMC_NUM_MINORS	(256 >> MMC_SHIFT)
 | 
						|
-
 | 
						|
-static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))];
 | 
						|
 
 | 
						|
 static inline int mmc_blk_readonly(struct mmc_card *card)
 | 
						|
 {
 | 
						|
@@ -510,7 +525,7 @@
 | 
						|
 	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);
 | 
						|
 
 | 
						|
@@ -562,17 +577,12 @@
 | 
						|
 	struct mmc_blk_data *md = mmc_get_drvdata(card);
 | 
						|
 
 | 
						|
 	if (md) {
 | 
						|
-		int devidx;
 | 
						|
-
 | 
						|
 		/* Stop new requests from getting into the queue */
 | 
						|
 		del_gendisk(md->disk);
 | 
						|
 
 | 
						|
 		/* Then flush out any already in there */
 | 
						|
 		mmc_cleanup_queue(&md->queue);
 | 
						|
 
 | 
						|
-		devidx = md->disk->first_minor >> MMC_SHIFT;
 | 
						|
-		__clear_bit(devidx, dev_use);
 | 
						|
-
 | 
						|
 		mmc_blk_put(md);
 | 
						|
 	}
 | 
						|
 	mmc_set_drvdata(card, NULL);
 | 
						|
--- a/drivers/mmc/card/queue.c
 | 
						|
+++ b/drivers/mmc/card/queue.c
 | 
						|
@@ -13,6 +13,7 @@
 | 
						|
 #include <linux/blkdev.h>
 | 
						|
 #include <linux/freezer.h>
 | 
						|
 #include <linux/kthread.h>
 | 
						|
+#include <linux/scatterlist.h>
 | 
						|
 
 | 
						|
 #include <linux/mmc/card.h>
 | 
						|
 #include <linux/mmc/host.h>
 | 
						|
@@ -22,6 +23,12 @@
 | 
						|
 
 | 
						|
 #define MMC_QUEUE_SUSPENDED	(1 << 0)
 | 
						|
 
 | 
						|
+#define sg_init_table(sg, n) do { \
 | 
						|
+	memset(sg, 0, sizeof(*(sg)) * (n)); \
 | 
						|
+} while (0)
 | 
						|
+
 | 
						|
+#define sg_virt(sg)	(page_address((sg)->page) + (sg)->offset)
 | 
						|
+
 | 
						|
 /*
 | 
						|
  * Prepare a MMC request. This just filters out odd stuff.
 | 
						|
  */
 | 
						|
@@ -159,6 +166,7 @@
 | 
						|
 				ret = -ENOMEM;
 | 
						|
 				goto cleanup_queue;
 | 
						|
 			}
 | 
						|
+			sg_init_table(mq->sg, 1);
 | 
						|
 
 | 
						|
 			mq->bounce_sg = kmalloc(sizeof(struct scatterlist) *
 | 
						|
 				bouncesz / 512, GFP_KERNEL);
 | 
						|
@@ -166,6 +174,7 @@
 | 
						|
 				ret = -ENOMEM;
 | 
						|
 				goto cleanup_queue;
 | 
						|
 			}
 | 
						|
+			sg_init_table(mq->bounce_sg, bouncesz / 512);
 | 
						|
 		}
 | 
						|
 	}
 | 
						|
 #endif
 | 
						|
@@ -183,6 +192,7 @@
 | 
						|
 			ret = -ENOMEM;
 | 
						|
 			goto cleanup_queue;
 | 
						|
 		}
 | 
						|
+		sg_init_table(mq->sg, host->max_phys_segs);
 | 
						|
 	}
 | 
						|
 
 | 
						|
 	init_MUTEX(&mq->thread_sem);
 | 
						|
@@ -302,12 +312,12 @@
 | 
						|
 		BUG_ON(dst_len == 0);
 | 
						|
 
 | 
						|
 		if (dst_size == 0) {
 | 
						|
-			dst_buf = page_address(dst->page) + dst->offset;
 | 
						|
+			dst_buf = sg_virt(dst);
 | 
						|
 			dst_size = dst->length;
 | 
						|
 		}
 | 
						|
 
 | 
						|
 		if (src_size == 0) {
 | 
						|
-			src_buf = page_address(src->page) + src->offset;
 | 
						|
+			src_buf = sg_virt(src);
 | 
						|
 			src_size = src->length;
 | 
						|
 		}
 | 
						|
 
 | 
						|
@@ -353,9 +363,7 @@
 | 
						|
 		return 1;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	mq->sg[0].page = virt_to_page(mq->bounce_buf);
 | 
						|
-	mq->sg[0].offset = offset_in_page(mq->bounce_buf);
 | 
						|
-	mq->sg[0].length = 0;
 | 
						|
+	sg_init_one(mq->sg, mq->bounce_buf, 0);
 | 
						|
 
 | 
						|
 	while (sg_len) {
 | 
						|
 		mq->sg[0].length += mq->bounce_sg[sg_len - 1].length;
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/mmc/card/sdio_uart.c
 | 
						|
@@ -0,0 +1,1158 @@
 | 
						|
+/*
 | 
						|
+ * linux/drivers/mmc/card/sdio_uart.c - SDIO UART/GPS driver
 | 
						|
+ *
 | 
						|
+ * Based on drivers/serial/8250.c and drivers/serial/serial_core.c
 | 
						|
+ * by Russell King.
 | 
						|
+ *
 | 
						|
+ * Author:	Nicolas Pitre
 | 
						|
+ * Created:	June 15, 2007
 | 
						|
+ * Copyright:	MontaVista Software, Inc.
 | 
						|
+ *
 | 
						|
+ * 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.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * Note: Although this driver assumes a 16550A-like UART implementation,
 | 
						|
+ * it is not possible to leverage the common 8250/16550 driver, nor the
 | 
						|
+ * core UART infrastructure, as they assumes direct access to the hardware
 | 
						|
+ * registers, often under a spinlock.  This is not possible in the SDIO
 | 
						|
+ * context as SDIO access functions must be able to sleep.
 | 
						|
+ *
 | 
						|
+ * Because we need to lock the SDIO host to ensure an exclusive access to
 | 
						|
+ * the card, we simply rely on that lock to also prevent and serialize
 | 
						|
+ * concurrent access to the same port.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/init.h>
 | 
						|
+#include <linux/kernel.h>
 | 
						|
+#include <linux/mutex.h>
 | 
						|
+#include <linux/serial_reg.h>
 | 
						|
+#include <linux/circ_buf.h>
 | 
						|
+#include <linux/gfp.h>
 | 
						|
+#include <linux/tty.h>
 | 
						|
+#include <linux/tty_flip.h>
 | 
						|
+
 | 
						|
+#include <linux/mmc/core.h>
 | 
						|
+#include <linux/mmc/card.h>
 | 
						|
+#include <linux/mmc/sdio_func.h>
 | 
						|
+#include <linux/mmc/sdio_ids.h>
 | 
						|
+
 | 
						|
+
 | 
						|
+#define UART_NR		8	/* Number of UARTs this driver can handle */
 | 
						|
+
 | 
						|
+
 | 
						|
+#define UART_XMIT_SIZE	PAGE_SIZE
 | 
						|
+#define WAKEUP_CHARS	256
 | 
						|
+
 | 
						|
+#define circ_empty(circ)	((circ)->head == (circ)->tail)
 | 
						|
+#define circ_clear(circ)	((circ)->head = (circ)->tail = 0)
 | 
						|
+
 | 
						|
+#define circ_chars_pending(circ) \
 | 
						|
+		(CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
 | 
						|
+
 | 
						|
+#define circ_chars_free(circ) \
 | 
						|
+		(CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
 | 
						|
+
 | 
						|
+
 | 
						|
+struct uart_icount {
 | 
						|
+	__u32	cts;
 | 
						|
+	__u32	dsr;
 | 
						|
+	__u32	rng;
 | 
						|
+	__u32	dcd;
 | 
						|
+	__u32	rx;
 | 
						|
+	__u32	tx;
 | 
						|
+	__u32	frame;
 | 
						|
+	__u32	overrun;
 | 
						|
+	__u32	parity;
 | 
						|
+	__u32	brk;
 | 
						|
+};
 | 
						|
+
 | 
						|
+struct sdio_uart_port {
 | 
						|
+	struct kref		kref;
 | 
						|
+	struct tty_struct	*tty;
 | 
						|
+	unsigned int		index;
 | 
						|
+	unsigned int		opened;
 | 
						|
+	struct mutex		open_lock;
 | 
						|
+	struct sdio_func	*func;
 | 
						|
+	struct mutex		func_lock;
 | 
						|
+	struct task_struct	*in_sdio_uart_irq;
 | 
						|
+	unsigned int		regs_offset;
 | 
						|
+	struct circ_buf		xmit;
 | 
						|
+	spinlock_t		write_lock;
 | 
						|
+	struct uart_icount	icount;
 | 
						|
+	unsigned int		uartclk;
 | 
						|
+	unsigned int		mctrl;
 | 
						|
+	unsigned int		read_status_mask;
 | 
						|
+	unsigned int		ignore_status_mask;
 | 
						|
+	unsigned char		x_char;
 | 
						|
+	unsigned char           ier;
 | 
						|
+	unsigned char           lcr;
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct sdio_uart_port *sdio_uart_table[UART_NR];
 | 
						|
+static DEFINE_SPINLOCK(sdio_uart_table_lock);
 | 
						|
+
 | 
						|
+static int sdio_uart_add_port(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	int index, ret = -EBUSY;
 | 
						|
+
 | 
						|
+	kref_init(&port->kref);
 | 
						|
+	mutex_init(&port->open_lock);
 | 
						|
+	mutex_init(&port->func_lock);
 | 
						|
+	spin_lock_init(&port->write_lock);
 | 
						|
+
 | 
						|
+	spin_lock(&sdio_uart_table_lock);
 | 
						|
+	for (index = 0; index < UART_NR; index++) {
 | 
						|
+		if (!sdio_uart_table[index]) {
 | 
						|
+			port->index = index;
 | 
						|
+			sdio_uart_table[index] = port;
 | 
						|
+			ret = 0;
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	spin_unlock(&sdio_uart_table_lock);
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static struct sdio_uart_port *sdio_uart_port_get(unsigned index)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port;
 | 
						|
+
 | 
						|
+	if (index >= UART_NR)
 | 
						|
+		return NULL;
 | 
						|
+
 | 
						|
+	spin_lock(&sdio_uart_table_lock);
 | 
						|
+	port = sdio_uart_table[index];
 | 
						|
+	if (port)
 | 
						|
+		kref_get(&port->kref);
 | 
						|
+	spin_unlock(&sdio_uart_table_lock);
 | 
						|
+
 | 
						|
+	return port;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_port_destroy(struct kref *kref)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port =
 | 
						|
+		container_of(kref, struct sdio_uart_port, kref);
 | 
						|
+	kfree(port);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_port_put(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	kref_put(&port->kref, sdio_uart_port_destroy);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_port_remove(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	struct sdio_func *func;
 | 
						|
+
 | 
						|
+	BUG_ON(sdio_uart_table[port->index] != port);
 | 
						|
+
 | 
						|
+	spin_lock(&sdio_uart_table_lock);
 | 
						|
+	sdio_uart_table[port->index] = NULL;
 | 
						|
+	spin_unlock(&sdio_uart_table_lock);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * We're killing a port that potentially still is in use by
 | 
						|
+	 * the tty layer. Be careful to prevent any further access
 | 
						|
+	 * to the SDIO function and arrange for the tty layer to
 | 
						|
+	 * give up on that port ASAP.
 | 
						|
+	 * Beware: the lock ordering is critical.
 | 
						|
+	 */
 | 
						|
+	mutex_lock(&port->open_lock);
 | 
						|
+	mutex_lock(&port->func_lock);
 | 
						|
+	func = port->func;
 | 
						|
+	sdio_claim_host(func);
 | 
						|
+	port->func = NULL;
 | 
						|
+	mutex_unlock(&port->func_lock);
 | 
						|
+	if (port->opened)
 | 
						|
+		tty_hangup(port->tty);
 | 
						|
+	mutex_unlock(&port->open_lock);
 | 
						|
+	sdio_release_irq(func);
 | 
						|
+	sdio_disable_func(func);
 | 
						|
+	sdio_release_host(func);
 | 
						|
+
 | 
						|
+	sdio_uart_port_put(port);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_uart_claim_func(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	mutex_lock(&port->func_lock);
 | 
						|
+	if (unlikely(!port->func)) {
 | 
						|
+		mutex_unlock(&port->func_lock);
 | 
						|
+		return -ENODEV;
 | 
						|
+	}
 | 
						|
+	if (likely(port->in_sdio_uart_irq != current))
 | 
						|
+		sdio_claim_host(port->func);
 | 
						|
+	mutex_unlock(&port->func_lock);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static inline void sdio_uart_release_func(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	if (likely(port->in_sdio_uart_irq != current))
 | 
						|
+		sdio_release_host(port->func);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset)
 | 
						|
+{
 | 
						|
+	unsigned char c;
 | 
						|
+	c = sdio_readb(port->func, port->regs_offset + offset, NULL);
 | 
						|
+	return c;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static inline void sdio_out(struct sdio_uart_port *port, int offset, int value)
 | 
						|
+{
 | 
						|
+	sdio_writeb(port->func, value, port->regs_offset + offset, NULL);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	unsigned char status;
 | 
						|
+	unsigned int ret;
 | 
						|
+
 | 
						|
+	status = sdio_in(port, UART_MSR);
 | 
						|
+
 | 
						|
+	ret = 0;
 | 
						|
+	if (status & UART_MSR_DCD)
 | 
						|
+		ret |= TIOCM_CAR;
 | 
						|
+	if (status & UART_MSR_RI)
 | 
						|
+		ret |= TIOCM_RNG;
 | 
						|
+	if (status & UART_MSR_DSR)
 | 
						|
+		ret |= TIOCM_DSR;
 | 
						|
+	if (status & UART_MSR_CTS)
 | 
						|
+		ret |= TIOCM_CTS;
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_write_mctrl(struct sdio_uart_port *port, unsigned int mctrl)
 | 
						|
+{
 | 
						|
+	unsigned char mcr = 0;
 | 
						|
+
 | 
						|
+	if (mctrl & TIOCM_RTS)
 | 
						|
+		mcr |= UART_MCR_RTS;
 | 
						|
+	if (mctrl & TIOCM_DTR)
 | 
						|
+		mcr |= UART_MCR_DTR;
 | 
						|
+	if (mctrl & TIOCM_OUT1)
 | 
						|
+		mcr |= UART_MCR_OUT1;
 | 
						|
+	if (mctrl & TIOCM_OUT2)
 | 
						|
+		mcr |= UART_MCR_OUT2;
 | 
						|
+	if (mctrl & TIOCM_LOOP)
 | 
						|
+		mcr |= UART_MCR_LOOP;
 | 
						|
+
 | 
						|
+	sdio_out(port, UART_MCR, mcr);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static inline void sdio_uart_update_mctrl(struct sdio_uart_port *port,
 | 
						|
+					  unsigned int set, unsigned int clear)
 | 
						|
+{
 | 
						|
+	unsigned int old;
 | 
						|
+
 | 
						|
+	old = port->mctrl;
 | 
						|
+	port->mctrl = (old & ~clear) | set;
 | 
						|
+	if (old != port->mctrl)
 | 
						|
+		sdio_uart_write_mctrl(port, port->mctrl);
 | 
						|
+}
 | 
						|
+
 | 
						|
+#define sdio_uart_set_mctrl(port, x)	sdio_uart_update_mctrl(port, x, 0)
 | 
						|
+#define sdio_uart_clear_mctrl(port, x)	sdio_uart_update_mctrl(port, 0, x)
 | 
						|
+
 | 
						|
+static void sdio_uart_change_speed(struct sdio_uart_port *port,
 | 
						|
+				   struct ktermios *termios,
 | 
						|
+				   struct ktermios *old)
 | 
						|
+{
 | 
						|
+	unsigned char cval, fcr = 0;
 | 
						|
+	unsigned int baud, quot;
 | 
						|
+
 | 
						|
+	switch (termios->c_cflag & CSIZE) {
 | 
						|
+	case CS5:
 | 
						|
+		cval = UART_LCR_WLEN5;
 | 
						|
+		break;
 | 
						|
+	case CS6:
 | 
						|
+		cval = UART_LCR_WLEN6;
 | 
						|
+		break;
 | 
						|
+	case CS7:
 | 
						|
+		cval = UART_LCR_WLEN7;
 | 
						|
+		break;
 | 
						|
+	default:
 | 
						|
+	case CS8:
 | 
						|
+		cval = UART_LCR_WLEN8;
 | 
						|
+		break;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (termios->c_cflag & CSTOPB)
 | 
						|
+		cval |= UART_LCR_STOP;
 | 
						|
+	if (termios->c_cflag & PARENB)
 | 
						|
+		cval |= UART_LCR_PARITY;
 | 
						|
+	if (!(termios->c_cflag & PARODD))
 | 
						|
+		cval |= UART_LCR_EPAR;
 | 
						|
+
 | 
						|
+	for (;;) {
 | 
						|
+		baud = tty_termios_baud_rate(termios);
 | 
						|
+		if (baud == 0)
 | 
						|
+			baud = 9600;  /* Special case: B0 rate. */
 | 
						|
+		if (baud <= port->uartclk)
 | 
						|
+			break;
 | 
						|
+		/*
 | 
						|
+		 * Oops, the quotient was zero.  Try again with the old
 | 
						|
+		 * baud rate if possible, otherwise default to 9600.
 | 
						|
+		 */
 | 
						|
+		termios->c_cflag &= ~CBAUD;
 | 
						|
+		if (old) {
 | 
						|
+			termios->c_cflag |= old->c_cflag & CBAUD;
 | 
						|
+			old = NULL;
 | 
						|
+		} else
 | 
						|
+			termios->c_cflag |= B9600;
 | 
						|
+	}
 | 
						|
+	quot = (2 * port->uartclk + baud) / (2 * baud);
 | 
						|
+
 | 
						|
+	if (baud < 2400)
 | 
						|
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
 | 
						|
+	else
 | 
						|
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10;
 | 
						|
+
 | 
						|
+	port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
 | 
						|
+	if (termios->c_iflag & INPCK)
 | 
						|
+		port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
 | 
						|
+	if (termios->c_iflag & (BRKINT | PARMRK))
 | 
						|
+		port->read_status_mask |= UART_LSR_BI;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Characters to ignore
 | 
						|
+	 */
 | 
						|
+	port->ignore_status_mask = 0;
 | 
						|
+	if (termios->c_iflag & IGNPAR)
 | 
						|
+		port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
 | 
						|
+	if (termios->c_iflag & IGNBRK) {
 | 
						|
+		port->ignore_status_mask |= UART_LSR_BI;
 | 
						|
+		/*
 | 
						|
+		 * If we're ignoring parity and break indicators,
 | 
						|
+		 * ignore overruns too (for real raw support).
 | 
						|
+		 */
 | 
						|
+		if (termios->c_iflag & IGNPAR)
 | 
						|
+			port->ignore_status_mask |= UART_LSR_OE;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * ignore all characters if CREAD is not set
 | 
						|
+	 */
 | 
						|
+	if ((termios->c_cflag & CREAD) == 0)
 | 
						|
+		port->ignore_status_mask |= UART_LSR_DR;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * CTS flow control flag and modem status interrupts
 | 
						|
+	 */
 | 
						|
+	port->ier &= ~UART_IER_MSI;
 | 
						|
+	if ((termios->c_cflag & CRTSCTS) || !(termios->c_cflag & CLOCAL))
 | 
						|
+		port->ier |= UART_IER_MSI;
 | 
						|
+
 | 
						|
+	port->lcr = cval;
 | 
						|
+
 | 
						|
+	sdio_out(port, UART_IER, port->ier);
 | 
						|
+	sdio_out(port, UART_LCR, cval | UART_LCR_DLAB);
 | 
						|
+	sdio_out(port, UART_DLL, quot & 0xff);
 | 
						|
+	sdio_out(port, UART_DLM, quot >> 8);
 | 
						|
+	sdio_out(port, UART_LCR, cval);
 | 
						|
+	sdio_out(port, UART_FCR, fcr);
 | 
						|
+
 | 
						|
+	sdio_uart_write_mctrl(port, port->mctrl);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_start_tx(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	if (!(port->ier & UART_IER_THRI)) {
 | 
						|
+		port->ier |= UART_IER_THRI;
 | 
						|
+		sdio_out(port, UART_IER, port->ier);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_stop_tx(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	if (port->ier & UART_IER_THRI) {
 | 
						|
+		port->ier &= ~UART_IER_THRI;
 | 
						|
+		sdio_out(port, UART_IER, port->ier);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_stop_rx(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	port->ier &= ~UART_IER_RLSI;
 | 
						|
+	port->read_status_mask &= ~UART_LSR_DR;
 | 
						|
+	sdio_out(port, UART_IER, port->ier);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *status)
 | 
						|
+{
 | 
						|
+	struct tty_struct *tty = port->tty;
 | 
						|
+	unsigned int ch, flag;
 | 
						|
+	int max_count = 256;
 | 
						|
+
 | 
						|
+	do {
 | 
						|
+		ch = sdio_in(port, UART_RX);
 | 
						|
+		flag = TTY_NORMAL;
 | 
						|
+		port->icount.rx++;
 | 
						|
+
 | 
						|
+		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
 | 
						|
+				        UART_LSR_FE | UART_LSR_OE))) {
 | 
						|
+			/*
 | 
						|
+			 * For statistics only
 | 
						|
+			 */
 | 
						|
+			if (*status & UART_LSR_BI) {
 | 
						|
+				*status &= ~(UART_LSR_FE | UART_LSR_PE);
 | 
						|
+				port->icount.brk++;
 | 
						|
+			} else if (*status & UART_LSR_PE)
 | 
						|
+				port->icount.parity++;
 | 
						|
+			else if (*status & UART_LSR_FE)
 | 
						|
+				port->icount.frame++;
 | 
						|
+			if (*status & UART_LSR_OE)
 | 
						|
+				port->icount.overrun++;
 | 
						|
+
 | 
						|
+			/*
 | 
						|
+			 * Mask off conditions which should be ignored.
 | 
						|
+			 */
 | 
						|
+			*status &= port->read_status_mask;
 | 
						|
+			if (*status & UART_LSR_BI) {
 | 
						|
+				flag = TTY_BREAK;
 | 
						|
+			} else if (*status & UART_LSR_PE)
 | 
						|
+				flag = TTY_PARITY;
 | 
						|
+			else if (*status & UART_LSR_FE)
 | 
						|
+				flag = TTY_FRAME;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		if ((*status & port->ignore_status_mask & ~UART_LSR_OE) == 0)
 | 
						|
+			tty_insert_flip_char(tty, ch, flag);
 | 
						|
+
 | 
						|
+		/*
 | 
						|
+		 * Overrun is special.  Since it's reported immediately,
 | 
						|
+		 * it doesn't affect the current character.
 | 
						|
+		 */
 | 
						|
+		if (*status & ~port->ignore_status_mask & UART_LSR_OE)
 | 
						|
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
 | 
						|
+
 | 
						|
+		*status = sdio_in(port, UART_LSR);
 | 
						|
+	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
 | 
						|
+	tty_flip_buffer_push(tty);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	struct circ_buf *xmit = &port->xmit;
 | 
						|
+	int count;
 | 
						|
+
 | 
						|
+	if (port->x_char) {
 | 
						|
+		sdio_out(port, UART_TX, port->x_char);
 | 
						|
+		port->icount.tx++;
 | 
						|
+		port->x_char = 0;
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+	if (circ_empty(xmit) || port->tty->stopped || port->tty->hw_stopped) {
 | 
						|
+		sdio_uart_stop_tx(port);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	count = 16;
 | 
						|
+	do {
 | 
						|
+		sdio_out(port, UART_TX, xmit->buf[xmit->tail]);
 | 
						|
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
 | 
						|
+		port->icount.tx++;
 | 
						|
+		if (circ_empty(xmit))
 | 
						|
+			break;
 | 
						|
+	} while (--count > 0);
 | 
						|
+
 | 
						|
+	if (circ_chars_pending(xmit) < WAKEUP_CHARS)
 | 
						|
+		tty_wakeup(port->tty);
 | 
						|
+
 | 
						|
+	if (circ_empty(xmit))
 | 
						|
+		sdio_uart_stop_tx(port);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	int status;
 | 
						|
+
 | 
						|
+	status = sdio_in(port, UART_MSR);
 | 
						|
+
 | 
						|
+	if ((status & UART_MSR_ANY_DELTA) == 0)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	if (status & UART_MSR_TERI)
 | 
						|
+		port->icount.rng++;
 | 
						|
+	if (status & UART_MSR_DDSR)
 | 
						|
+		port->icount.dsr++;
 | 
						|
+	if (status & UART_MSR_DDCD)
 | 
						|
+		port->icount.dcd++;
 | 
						|
+	if (status & UART_MSR_DCTS) {
 | 
						|
+		port->icount.cts++;
 | 
						|
+		if (port->tty->termios->c_cflag & CRTSCTS) {
 | 
						|
+			int cts = (status & UART_MSR_CTS);
 | 
						|
+			if (port->tty->hw_stopped) {
 | 
						|
+				if (cts) {
 | 
						|
+					port->tty->hw_stopped = 0;
 | 
						|
+					sdio_uart_start_tx(port);
 | 
						|
+					tty_wakeup(port->tty);
 | 
						|
+				}
 | 
						|
+			} else {
 | 
						|
+				if (!cts) {
 | 
						|
+					port->tty->hw_stopped = 1;
 | 
						|
+					sdio_uart_stop_tx(port);
 | 
						|
+				}
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+/*
 | 
						|
+ * This handles the interrupt from one port.
 | 
						|
+ */
 | 
						|
+static void sdio_uart_irq(struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = sdio_get_drvdata(func);
 | 
						|
+	unsigned int iir, lsr;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * In a few places sdio_uart_irq() is called directly instead of
 | 
						|
+	 * waiting for the actual interrupt to be raised and the SDIO IRQ
 | 
						|
+	 * thread scheduled in order to reduce latency.  However, some
 | 
						|
+	 * interaction with the tty core may end up calling us back
 | 
						|
+	 * (serial echo, flow control, etc.) through those same places
 | 
						|
+	 * causing undesirable effects.  Let's stop the recursion here.
 | 
						|
+	 */
 | 
						|
+	if (unlikely(port->in_sdio_uart_irq == current))
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	iir = sdio_in(port, UART_IIR);
 | 
						|
+	if (iir & UART_IIR_NO_INT)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	port->in_sdio_uart_irq = current;
 | 
						|
+	lsr = sdio_in(port, UART_LSR);
 | 
						|
+	if (lsr & UART_LSR_DR)
 | 
						|
+		sdio_uart_receive_chars(port, &lsr);
 | 
						|
+	sdio_uart_check_modem_status(port);
 | 
						|
+	if (lsr & UART_LSR_THRE)
 | 
						|
+		sdio_uart_transmit_chars(port);
 | 
						|
+	port->in_sdio_uart_irq = NULL;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_uart_startup(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	unsigned long page;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Set the TTY IO error marker - we will only clear this
 | 
						|
+	 * once we have successfully opened the port.
 | 
						|
+	 */
 | 
						|
+	set_bit(TTY_IO_ERROR, &port->tty->flags);
 | 
						|
+
 | 
						|
+	/* Initialise and allocate the transmit buffer. */
 | 
						|
+	page = __get_free_page(GFP_KERNEL);
 | 
						|
+	if (!page)
 | 
						|
+		return -ENOMEM;
 | 
						|
+	port->xmit.buf = (unsigned char *)page;
 | 
						|
+	circ_clear(&port->xmit);
 | 
						|
+
 | 
						|
+	ret = sdio_uart_claim_func(port);
 | 
						|
+	if (ret)
 | 
						|
+		goto err1;
 | 
						|
+	ret = sdio_enable_func(port->func);
 | 
						|
+	if (ret)
 | 
						|
+		goto err2;
 | 
						|
+	ret = sdio_claim_irq(port->func, sdio_uart_irq);
 | 
						|
+	if (ret)
 | 
						|
+		goto err3;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Clear the FIFO buffers and disable them.
 | 
						|
+	 * (they will be reenabled in sdio_change_speed())
 | 
						|
+	 */
 | 
						|
+	sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO);
 | 
						|
+	sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
 | 
						|
+			UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
 | 
						|
+	sdio_out(port, UART_FCR, 0);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Clear the interrupt registers.
 | 
						|
+	 */
 | 
						|
+	(void) sdio_in(port, UART_LSR);
 | 
						|
+	(void) sdio_in(port, UART_RX);
 | 
						|
+	(void) sdio_in(port, UART_IIR);
 | 
						|
+	(void) sdio_in(port, UART_MSR);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Now, initialize the UART
 | 
						|
+	 */
 | 
						|
+	sdio_out(port, UART_LCR, UART_LCR_WLEN8);
 | 
						|
+
 | 
						|
+	port->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
 | 
						|
+	port->mctrl = TIOCM_OUT2;
 | 
						|
+
 | 
						|
+	sdio_uart_change_speed(port, port->tty->termios, NULL);
 | 
						|
+
 | 
						|
+	if (port->tty->termios->c_cflag & CBAUD)
 | 
						|
+		sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
 | 
						|
+
 | 
						|
+	if (port->tty->termios->c_cflag & CRTSCTS)
 | 
						|
+		if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS))
 | 
						|
+			port->tty->hw_stopped = 1;
 | 
						|
+
 | 
						|
+	clear_bit(TTY_IO_ERROR, &port->tty->flags);
 | 
						|
+
 | 
						|
+	/* Kick the IRQ handler once while we're still holding the host lock */
 | 
						|
+	sdio_uart_irq(port->func);
 | 
						|
+
 | 
						|
+	sdio_uart_release_func(port);
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+err3:
 | 
						|
+	sdio_disable_func(port->func);
 | 
						|
+err2:
 | 
						|
+	sdio_uart_release_func(port);
 | 
						|
+err1:
 | 
						|
+	free_page((unsigned long)port->xmit.buf);
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_shutdown(struct sdio_uart_port *port)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	ret = sdio_uart_claim_func(port);
 | 
						|
+	if (ret)
 | 
						|
+		goto skip;
 | 
						|
+
 | 
						|
+	sdio_uart_stop_rx(port);
 | 
						|
+
 | 
						|
+	/* TODO: wait here for TX FIFO to drain */
 | 
						|
+
 | 
						|
+	/* Turn off DTR and RTS early. */
 | 
						|
+	if (port->tty->termios->c_cflag & HUPCL)
 | 
						|
+		sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
 | 
						|
+
 | 
						|
+	 /* Disable interrupts from this port */
 | 
						|
+	sdio_release_irq(port->func);
 | 
						|
+	port->ier = 0;
 | 
						|
+	sdio_out(port, UART_IER, 0);
 | 
						|
+
 | 
						|
+	sdio_uart_clear_mctrl(port, TIOCM_OUT2);
 | 
						|
+
 | 
						|
+	/* Disable break condition and FIFOs. */
 | 
						|
+	port->lcr &= ~UART_LCR_SBC;
 | 
						|
+	sdio_out(port, UART_LCR, port->lcr);
 | 
						|
+	sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
 | 
						|
+				 UART_FCR_CLEAR_RCVR |
 | 
						|
+				 UART_FCR_CLEAR_XMIT);
 | 
						|
+	sdio_out(port, UART_FCR, 0);
 | 
						|
+
 | 
						|
+	sdio_disable_func(port->func);
 | 
						|
+
 | 
						|
+	sdio_uart_release_func(port);
 | 
						|
+
 | 
						|
+skip:
 | 
						|
+	/* Free the transmit buffer page. */
 | 
						|
+	free_page((unsigned long)port->xmit.buf);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_uart_open (struct tty_struct *tty, struct file * filp)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	port = sdio_uart_port_get(tty->index);
 | 
						|
+	if (!port)
 | 
						|
+		return -ENODEV;
 | 
						|
+
 | 
						|
+	mutex_lock(&port->open_lock);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * Make sure not to mess up with a dead port
 | 
						|
+	 * which has not been closed yet.
 | 
						|
+	 */
 | 
						|
+	if (tty->driver_data && tty->driver_data != port) {
 | 
						|
+		mutex_unlock(&port->open_lock);
 | 
						|
+		sdio_uart_port_put(port);
 | 
						|
+		return -EBUSY;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (!port->opened) {
 | 
						|
+		tty->driver_data = port;
 | 
						|
+		port->tty = tty;
 | 
						|
+		ret = sdio_uart_startup(port);
 | 
						|
+		if (ret) {
 | 
						|
+			tty->driver_data = NULL;
 | 
						|
+			port->tty = NULL;
 | 
						|
+			mutex_unlock(&port->open_lock);
 | 
						|
+			sdio_uart_port_put(port);
 | 
						|
+			return ret;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	port->opened++;
 | 
						|
+	mutex_unlock(&port->open_lock);
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = tty->driver_data;
 | 
						|
+
 | 
						|
+	if (!port)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	mutex_lock(&port->open_lock);
 | 
						|
+	BUG_ON(!port->opened);
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * This is messy.  The tty layer calls us even when open()
 | 
						|
+	 * returned an error.  Ignore this close request if tty->count
 | 
						|
+	 * is larger than port->count.
 | 
						|
+	 */
 | 
						|
+	if (tty->count > port->opened) {
 | 
						|
+		mutex_unlock(&port->open_lock);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (--port->opened == 0) {
 | 
						|
+		tty->closing = 1;
 | 
						|
+		sdio_uart_shutdown(port);
 | 
						|
+		tty_ldisc_flush(tty);
 | 
						|
+		port->tty = NULL;
 | 
						|
+		tty->driver_data = NULL;
 | 
						|
+		tty->closing = 0;
 | 
						|
+	}
 | 
						|
+	mutex_unlock(&port->open_lock);
 | 
						|
+	sdio_uart_port_put(port);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,
 | 
						|
+			   int count)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = tty->driver_data;
 | 
						|
+	struct circ_buf *circ = &port->xmit;
 | 
						|
+	int c, ret = 0;
 | 
						|
+
 | 
						|
+	if (!port->func)
 | 
						|
+		return -ENODEV;
 | 
						|
+
 | 
						|
+	spin_lock(&port->write_lock);
 | 
						|
+	while (1) {
 | 
						|
+		c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
 | 
						|
+		if (count < c)
 | 
						|
+			c = count;
 | 
						|
+		if (c <= 0)
 | 
						|
+			break;
 | 
						|
+		memcpy(circ->buf + circ->head, buf, c);
 | 
						|
+		circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
 | 
						|
+		buf += c;
 | 
						|
+		count -= c;
 | 
						|
+		ret += c;
 | 
						|
+	}
 | 
						|
+	spin_unlock(&port->write_lock);
 | 
						|
+
 | 
						|
+	if ( !(port->ier & UART_IER_THRI)) {
 | 
						|
+		int err = sdio_uart_claim_func(port);
 | 
						|
+		if (!err) {
 | 
						|
+			sdio_uart_start_tx(port);
 | 
						|
+			sdio_uart_irq(port->func);
 | 
						|
+			sdio_uart_release_func(port);
 | 
						|
+		} else
 | 
						|
+			ret = err;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_uart_write_room(struct tty_struct *tty)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = tty->driver_data;
 | 
						|
+	return port ? circ_chars_free(&port->xmit) : 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_uart_chars_in_buffer(struct tty_struct *tty)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = tty->driver_data;
 | 
						|
+	return port ? circ_chars_pending(&port->xmit) : 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_send_xchar(struct tty_struct *tty, char ch)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = tty->driver_data;
 | 
						|
+
 | 
						|
+	port->x_char = ch;
 | 
						|
+	if (ch && !(port->ier & UART_IER_THRI)) {
 | 
						|
+		if (sdio_uart_claim_func(port) != 0)
 | 
						|
+			return;
 | 
						|
+		sdio_uart_start_tx(port);
 | 
						|
+		sdio_uart_irq(port->func);
 | 
						|
+		sdio_uart_release_func(port);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_throttle(struct tty_struct *tty)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = tty->driver_data;
 | 
						|
+
 | 
						|
+	if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	if (sdio_uart_claim_func(port) != 0)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	if (I_IXOFF(tty)) {
 | 
						|
+		port->x_char = STOP_CHAR(tty);
 | 
						|
+		sdio_uart_start_tx(port);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (tty->termios->c_cflag & CRTSCTS)
 | 
						|
+		sdio_uart_clear_mctrl(port, TIOCM_RTS);
 | 
						|
+
 | 
						|
+	sdio_uart_irq(port->func);
 | 
						|
+	sdio_uart_release_func(port);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_unthrottle(struct tty_struct *tty)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = tty->driver_data;
 | 
						|
+
 | 
						|
+	if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	if (sdio_uart_claim_func(port) != 0)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	if (I_IXOFF(tty)) {
 | 
						|
+		if (port->x_char) {
 | 
						|
+			port->x_char = 0;
 | 
						|
+		} else {
 | 
						|
+			port->x_char = START_CHAR(tty);
 | 
						|
+			sdio_uart_start_tx(port);
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (tty->termios->c_cflag & CRTSCTS)
 | 
						|
+		sdio_uart_set_mctrl(port, TIOCM_RTS);
 | 
						|
+
 | 
						|
+	sdio_uart_irq(port->func);
 | 
						|
+	sdio_uart_release_func(port);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = tty->driver_data;
 | 
						|
+	unsigned int cflag = tty->termios->c_cflag;
 | 
						|
+
 | 
						|
+#define RELEVANT_IFLAG(iflag)   ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
 | 
						|
+
 | 
						|
+	if ((cflag ^ old_termios->c_cflag) == 0 &&
 | 
						|
+	    RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	if (sdio_uart_claim_func(port) != 0)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	sdio_uart_change_speed(port, tty->termios, old_termios);
 | 
						|
+
 | 
						|
+	/* Handle transition to B0 status */
 | 
						|
+	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
 | 
						|
+		sdio_uart_clear_mctrl(port, TIOCM_RTS | TIOCM_DTR);
 | 
						|
+
 | 
						|
+	/* Handle transition away from B0 status */
 | 
						|
+	if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
 | 
						|
+		unsigned int mask = TIOCM_DTR;
 | 
						|
+		if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags))
 | 
						|
+			mask |= TIOCM_RTS;
 | 
						|
+		sdio_uart_set_mctrl(port, mask);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Handle turning off CRTSCTS */
 | 
						|
+	if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
 | 
						|
+		tty->hw_stopped = 0;
 | 
						|
+		sdio_uart_start_tx(port);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Handle turning on CRTSCTS */
 | 
						|
+	if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
 | 
						|
+		if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) {
 | 
						|
+			tty->hw_stopped = 1;
 | 
						|
+			sdio_uart_stop_tx(port);
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	sdio_uart_release_func(port);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = tty->driver_data;
 | 
						|
+
 | 
						|
+	if (sdio_uart_claim_func(port) != 0)
 | 
						|
+		return;
 | 
						|
+
 | 
						|
+	if (break_state == -1)
 | 
						|
+		port->lcr |= UART_LCR_SBC;
 | 
						|
+	else
 | 
						|
+		port->lcr &= ~UART_LCR_SBC;
 | 
						|
+	sdio_out(port, UART_LCR, port->lcr);
 | 
						|
+
 | 
						|
+	sdio_uart_release_func(port);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = tty->driver_data;
 | 
						|
+	int result;
 | 
						|
+
 | 
						|
+	result = sdio_uart_claim_func(port);
 | 
						|
+	if (!result) {
 | 
						|
+		result = port->mctrl | sdio_uart_get_mctrl(port);
 | 
						|
+		sdio_uart_release_func(port);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return result;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file,
 | 
						|
+			      unsigned int set, unsigned int clear)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = tty->driver_data;
 | 
						|
+	int result;
 | 
						|
+
 | 
						|
+	result =sdio_uart_claim_func(port);
 | 
						|
+	if(!result) {
 | 
						|
+		sdio_uart_update_mctrl(port, set, clear);
 | 
						|
+		sdio_uart_release_func(port);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return result;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int sdio_uart_read_proc(char *page, char **start, off_t off,
 | 
						|
+			       int count, int *eof, void *data)
 | 
						|
+{
 | 
						|
+	int i, len = 0;
 | 
						|
+	off_t begin = 0;
 | 
						|
+
 | 
						|
+	len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n",
 | 
						|
+		       "", "", "");
 | 
						|
+	for (i = 0; i < UART_NR && len < PAGE_SIZE - 96; i++) {
 | 
						|
+		struct sdio_uart_port *port = sdio_uart_port_get(i);
 | 
						|
+		if (port) {
 | 
						|
+			len += sprintf(page+len, "%d: uart:SDIO", i);
 | 
						|
+			if(capable(CAP_SYS_ADMIN)) {
 | 
						|
+				len += sprintf(page + len, " tx:%d rx:%d",
 | 
						|
+					       port->icount.tx, port->icount.rx);
 | 
						|
+				if (port->icount.frame)
 | 
						|
+					len += sprintf(page + len, " fe:%d",
 | 
						|
+						       port->icount.frame);
 | 
						|
+				if (port->icount.parity)
 | 
						|
+					len += sprintf(page + len, " pe:%d",
 | 
						|
+						       port->icount.parity);
 | 
						|
+				if (port->icount.brk)
 | 
						|
+					len += sprintf(page + len, " brk:%d",
 | 
						|
+						       port->icount.brk);
 | 
						|
+				if (port->icount.overrun)
 | 
						|
+					len += sprintf(page + len, " oe:%d",
 | 
						|
+						       port->icount.overrun);
 | 
						|
+				if (port->icount.cts)
 | 
						|
+					len += sprintf(page + len, " cts:%d",
 | 
						|
+						       port->icount.cts);
 | 
						|
+				if (port->icount.dsr)
 | 
						|
+					len += sprintf(page + len, " dsr:%d",
 | 
						|
+						       port->icount.dsr);
 | 
						|
+				if (port->icount.rng)
 | 
						|
+					len += sprintf(page + len, " rng:%d",
 | 
						|
+						       port->icount.rng);
 | 
						|
+				if (port->icount.dcd)
 | 
						|
+					len += sprintf(page + len, " dcd:%d",
 | 
						|
+						       port->icount.dcd);
 | 
						|
+			}
 | 
						|
+			strcat(page, "\n");
 | 
						|
+			len++;
 | 
						|
+			sdio_uart_port_put(port);
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		if (len + begin > off + count)
 | 
						|
+			goto done;
 | 
						|
+		if (len + begin < off) {
 | 
						|
+			begin += len;
 | 
						|
+			len = 0;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+	*eof = 1;
 | 
						|
+
 | 
						|
+done:
 | 
						|
+	if (off >= len + begin)
 | 
						|
+		return 0;
 | 
						|
+	*start = page + (off - begin);
 | 
						|
+	return (count < begin + len - off) ? count : (begin + len - off);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static const struct tty_operations sdio_uart_ops = {
 | 
						|
+	.open			= sdio_uart_open,
 | 
						|
+	.close			= sdio_uart_close,
 | 
						|
+	.write			= sdio_uart_write,
 | 
						|
+	.write_room		= sdio_uart_write_room,
 | 
						|
+	.chars_in_buffer	= sdio_uart_chars_in_buffer,
 | 
						|
+	.send_xchar		= sdio_uart_send_xchar,
 | 
						|
+	.throttle		= sdio_uart_throttle,
 | 
						|
+	.unthrottle		= sdio_uart_unthrottle,
 | 
						|
+	.set_termios		= sdio_uart_set_termios,
 | 
						|
+	.break_ctl		= sdio_uart_break_ctl,
 | 
						|
+	.tiocmget		= sdio_uart_tiocmget,
 | 
						|
+	.tiocmset		= sdio_uart_tiocmset,
 | 
						|
+	.read_proc		= sdio_uart_read_proc,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct tty_driver *sdio_uart_tty_driver;
 | 
						|
+
 | 
						|
+static int sdio_uart_probe(struct sdio_func *func,
 | 
						|
+			   const struct sdio_device_id *id)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port;
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	port = kzalloc(sizeof(struct sdio_uart_port), GFP_KERNEL);
 | 
						|
+	if (!port)
 | 
						|
+		return -ENOMEM;
 | 
						|
+
 | 
						|
+	if (func->class == SDIO_CLASS_UART) {
 | 
						|
+		printk(KERN_WARNING "%s: need info on UART class basic setup\n",
 | 
						|
+		       sdio_func_id(func));
 | 
						|
+		kfree(port);
 | 
						|
+		return -ENOSYS;
 | 
						|
+	} else if (func->class == SDIO_CLASS_GPS) {
 | 
						|
+		/*
 | 
						|
+		 * We need tuple 0x91.  It contains SUBTPL_SIOREG
 | 
						|
+		 * and SUBTPL_RCVCAPS.
 | 
						|
+		 */
 | 
						|
+		struct sdio_func_tuple *tpl;
 | 
						|
+		for (tpl = func->tuples; tpl; tpl = tpl->next) {
 | 
						|
+			if (tpl->code != 0x91)
 | 
						|
+				continue;
 | 
						|
+			if (tpl->size < 10)
 | 
						|
+				continue;
 | 
						|
+			if (tpl->data[1] == 0)  /* SUBTPL_SIOREG */
 | 
						|
+				break;
 | 
						|
+		}
 | 
						|
+		if (!tpl) {
 | 
						|
+			printk(KERN_WARNING
 | 
						|
+			       "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
 | 
						|
+			       sdio_func_id(func));
 | 
						|
+			kfree(port);
 | 
						|
+			return -EINVAL;
 | 
						|
+		}
 | 
						|
+		printk(KERN_DEBUG "%s: Register ID = 0x%02x, Exp ID = 0x%02x\n",
 | 
						|
+		       sdio_func_id(func), tpl->data[2], tpl->data[3]);
 | 
						|
+		port->regs_offset = (tpl->data[4] << 0) |
 | 
						|
+				    (tpl->data[5] << 8) |
 | 
						|
+				    (tpl->data[6] << 16);
 | 
						|
+		printk(KERN_DEBUG "%s: regs offset = 0x%x\n",
 | 
						|
+		       sdio_func_id(func), port->regs_offset);
 | 
						|
+		port->uartclk = tpl->data[7] * 115200;
 | 
						|
+		if (port->uartclk == 0)
 | 
						|
+			port->uartclk = 115200;
 | 
						|
+		printk(KERN_DEBUG "%s: clk %d baudcode %u 4800-div %u\n",
 | 
						|
+		       sdio_func_id(func), port->uartclk,
 | 
						|
+		       tpl->data[7], tpl->data[8] | (tpl->data[9] << 8));
 | 
						|
+	} else {
 | 
						|
+		kfree(port);
 | 
						|
+		return -EINVAL;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	port->func = func;
 | 
						|
+	sdio_set_drvdata(func, port);
 | 
						|
+
 | 
						|
+	ret = sdio_uart_add_port(port);
 | 
						|
+	if (ret) {
 | 
						|
+		kfree(port);
 | 
						|
+	} else {
 | 
						|
+		struct device *dev;
 | 
						|
+		dev = tty_register_device(sdio_uart_tty_driver, port->index, &func->dev);
 | 
						|
+		if (IS_ERR(dev)) {
 | 
						|
+			sdio_uart_port_remove(port);
 | 
						|
+			ret = PTR_ERR(dev);
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void sdio_uart_remove(struct sdio_func *func)
 | 
						|
+{
 | 
						|
+	struct sdio_uart_port *port = sdio_get_drvdata(func);
 | 
						|
+
 | 
						|
+	tty_unregister_device(sdio_uart_tty_driver, port->index);
 | 
						|
+	sdio_uart_port_remove(port);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static const struct sdio_device_id sdio_uart_ids[] = {
 | 
						|
+	{ SDIO_DEVICE_CLASS(SDIO_CLASS_UART)		},
 | 
						|
+	{ SDIO_DEVICE_CLASS(SDIO_CLASS_GPS)		},
 | 
						|
+	{ /* end: all zeroes */				},
 | 
						|
+};
 | 
						|
+
 | 
						|
+MODULE_DEVICE_TABLE(sdio, sdio_uart_ids);
 | 
						|
+
 | 
						|
+static struct sdio_driver sdio_uart_driver = {
 | 
						|
+	.probe		= sdio_uart_probe,
 | 
						|
+	.remove		= sdio_uart_remove,
 | 
						|
+	.name		= "sdio_uart",
 | 
						|
+	.id_table	= sdio_uart_ids,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int __init sdio_uart_init(void)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+	struct tty_driver *tty_drv;
 | 
						|
+
 | 
						|
+	sdio_uart_tty_driver = tty_drv = alloc_tty_driver(UART_NR);
 | 
						|
+	if (!tty_drv)
 | 
						|
+		return -ENOMEM;
 | 
						|
+
 | 
						|
+	tty_drv->owner = THIS_MODULE;
 | 
						|
+	tty_drv->driver_name = "sdio_uart";
 | 
						|
+	tty_drv->name =   "ttySDIO";
 | 
						|
+	tty_drv->major = 0;  /* dynamically allocated */
 | 
						|
+	tty_drv->minor_start = 0;
 | 
						|
+	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
 | 
						|
+	tty_drv->subtype = SERIAL_TYPE_NORMAL;
 | 
						|
+	tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
 | 
						|
+	tty_drv->init_termios = tty_std_termios;
 | 
						|
+	tty_drv->init_termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL;
 | 
						|
+	tty_drv->init_termios.c_ispeed = 4800;
 | 
						|
+	tty_drv->init_termios.c_ospeed = 4800;
 | 
						|
+	tty_set_operations(tty_drv, &sdio_uart_ops);
 | 
						|
+
 | 
						|
+	ret = tty_register_driver(tty_drv);
 | 
						|
+	if (ret)
 | 
						|
+		goto err1;
 | 
						|
+
 | 
						|
+	ret = sdio_register_driver(&sdio_uart_driver);
 | 
						|
+	if (ret)
 | 
						|
+		goto err2;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+
 | 
						|
+err2:
 | 
						|
+	tty_unregister_driver(tty_drv);
 | 
						|
+err1:
 | 
						|
+	put_tty_driver(tty_drv);
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void __exit sdio_uart_exit(void)
 | 
						|
+{
 | 
						|
+	sdio_unregister_driver(&sdio_uart_driver);
 | 
						|
+	tty_unregister_driver(sdio_uart_tty_driver);
 | 
						|
+	put_tty_driver(sdio_uart_tty_driver);
 | 
						|
+}
 | 
						|
+
 | 
						|
+module_init(sdio_uart_init);
 | 
						|
+module_exit(sdio_uart_exit);
 | 
						|
+
 | 
						|
+MODULE_AUTHOR("Nicolas Pitre");
 | 
						|
+MODULE_LICENSE("GPL");
 | 
						|
--- a/drivers/mmc/core/Makefile
 | 
						|
+++ b/drivers/mmc/core/Makefile
 | 
						|
@@ -8,5 +8,7 @@
 | 
						|
 
 | 
						|
 obj-$(CONFIG_MMC)		+= mmc_core.o
 | 
						|
 mmc_core-y			:= core.o sysfs.o bus.o host.o \
 | 
						|
-				   mmc.o mmc_ops.o sd.o sd_ops.o
 | 
						|
+				   mmc.o mmc_ops.o sd.o sd_ops.o \
 | 
						|
+				   sdio.o sdio_ops.o sdio_bus.o \
 | 
						|
+				   sdio_cis.o sdio_io.o sdio_irq.o
 | 
						|
 
 |