]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc
authorLinus Torvalds <torvalds@woody.linux-foundation.org>
Fri, 12 Oct 2007 01:57:31 +0000 (18:57 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Fri, 12 Oct 2007 01:57:31 +0000 (18:57 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc: (67 commits)
  mmc: don't use weight32()
  pxamci: support arbitrary block size
  sdio: make the IRQ thread more resilient in the presence of bad states
  sdio: fix IRQ diagnostic message
  sdhci: remove old dma module params
  sdhci: add SDHCI_QUIRK_BROKEN_DMA quirk
  sdhci: remove DMA capability check from controller's PCI Class reg
  sdhci: fix a typo
  mmc: Disabler for Ricoh MMC controller
  sdio: adaptive interrupt polling
  mmc: pxamci: add SDIO card interrupt reporting capability
  mmc: pxamci: set proper buswidth capabilities according to PXA flavor
  mmc: pxamci: set proper block capabilities according to PXA flavor
  mmc: pxamci: better pending IRQ determination
  arm: i.MX/MX1 SDHC implements SD cards read-only switch read-back
  mmc: add led trigger
  mmc_spi host driver
  MMC core learns about SPI
  MMC/SD card driver learns SPI
  MMC headers learn about SPI
  ...

52 files changed:
MAINTAINERS
arch/arm/mach-imx/mx1ads.c
drivers/mmc/card/Kconfig
drivers/mmc/card/Makefile
drivers/mmc/card/block.c
drivers/mmc/card/sdio_uart.c [new file with mode: 0644]
drivers/mmc/core/Makefile
drivers/mmc/core/bus.c
drivers/mmc/core/core.c
drivers/mmc/core/core.h
drivers/mmc/core/host.c
drivers/mmc/core/mmc.c
drivers/mmc/core/mmc_ops.c
drivers/mmc/core/mmc_ops.h
drivers/mmc/core/sd.c
drivers/mmc/core/sd_ops.c
drivers/mmc/core/sdio.c [new file with mode: 0644]
drivers/mmc/core/sdio_bus.c [new file with mode: 0644]
drivers/mmc/core/sdio_bus.h [new file with mode: 0644]
drivers/mmc/core/sdio_cis.c [new file with mode: 0644]
drivers/mmc/core/sdio_cis.h [new file with mode: 0644]
drivers/mmc/core/sdio_io.c [new file with mode: 0644]
drivers/mmc/core/sdio_irq.c [new file with mode: 0644]
drivers/mmc/core/sdio_ops.c [new file with mode: 0644]
drivers/mmc/core/sdio_ops.h [new file with mode: 0644]
drivers/mmc/host/Kconfig
drivers/mmc/host/Makefile
drivers/mmc/host/at91_mci.c
drivers/mmc/host/au1xmmc.c
drivers/mmc/host/imxmmc.c
drivers/mmc/host/mmc_spi.c [new file with mode: 0644]
drivers/mmc/host/mmci.c
drivers/mmc/host/omap.c
drivers/mmc/host/pxamci.c
drivers/mmc/host/pxamci.h
drivers/mmc/host/ricoh_mmc.c [new file with mode: 0644]
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
drivers/mmc/host/tifm_sd.c
drivers/mmc/host/wbsd.c
include/asm-arm/arch-imx/mmc.h
include/linux/mmc/card.h
include/linux/mmc/core.h
include/linux/mmc/host.h
include/linux/mmc/mmc.h
include/linux/mmc/sdio.h [new file with mode: 0644]
include/linux/mmc/sdio_func.h [new file with mode: 0644]
include/linux/mmc/sdio_ids.h [new file with mode: 0644]
include/linux/mod_devicetable.h
include/linux/pci_ids.h
include/linux/spi/mmc_spi.h [new file with mode: 0644]
scripts/mod/file2alias.c

index 9a91d9e3f1f25c2e0aaded950fae19e4d6fa7e08..0df29ce05e5a5bcb7098f1868ce3796ced881e7c 100644 (file)
@@ -2561,12 +2561,18 @@ L:      linux-kernel@vger.kernel.org
 W:     http://www.atnf.csiro.au/~rgooch/linux/kernel-patches.html
 S:     Maintained
 
-MULTIMEDIA CARD (MMC) AND SECURE DIGITAL (SD) SUBSYSTEM
+MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
 P:     Pierre Ossman
 M:     drzeus-mmc@drzeus.cx
 L:     linux-kernel@vger.kernel.org
 S:     Maintained
 
+MULTIMEDIA CARD (MMC) ETC. OVER SPI
+P:     David Brownell
+M:     dbrownell@users.sourceforge.net
+L:     linux-kernel@vger.kernel.org
+S:     Odd fixes
+
 MULTISOUND SOUND DRIVER
 P:     Andrew Veliath
 M:     andrewtv@usa.net
index da893c80d471550c59a51acbeeb31d97437c730b..a9778c1587abcede102e26aa304e854d4649c4a9 100644 (file)
@@ -116,7 +116,7 @@ static struct platform_device *devices[] __initdata = {
 };
 
 #ifdef CONFIG_MMC_IMX
-static int mx1ads_mmc_card_present(void)
+static int mx1ads_mmc_card_present(struct device *dev)
 {
        /* MMC/SD Card Detect is PB 20 on MX1ADS V1.0.7 */
        return (SSR(1) & (1 << 20) ? 0 : 1);
index a49cb9737cd858c8f36679249b29f86b565426f2..aa8a4e461942801db89ec3963c7dabf605dfb006 100644 (file)
@@ -32,3 +32,10 @@ config MMC_BLOCK_BOUNCE
 
          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.
+
index cf8c939867f531f0f20b252daf928551f3366055..fc5a784cfa1a7410f2025fdfcdddd08f06e8ec4b 100644 (file)
@@ -9,3 +9,5 @@ endif
 obj-$(CONFIG_MMC_BLOCK)                += mmc_block.o
 mmc_block-objs                 := block.o queue.o
 
+obj-$(CONFIG_SDIO_UART)                += sdio_uart.o
+
index 93fe2e5dd6162eb12e3e1bc63adee1cfc5483f9d..e38d5a3b2a899d6f9988e12ad294bf47862959c6 100644 (file)
@@ -151,17 +151,19 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
 
        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 +194,7 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
 
        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 +222,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
                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 +243,12 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
                        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 +265,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
                        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 +308,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
                        goto cmd_err;
                }
 
-               if (rq_data_dir(req) != READ) {
+               if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
                        do {
                                int err;
 
@@ -510,7 +516,7 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
        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);
 
diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c
new file mode 100644 (file)
index 0000000..d552de6
--- /dev/null
@@ -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, 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");
index 3fdd08c7f143c8f874db806a948e54790f299efc..4985807257a8f0c621ec855add57103ce6b042ef 100644 (file)
@@ -8,5 +8,7 @@ endif
 
 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
 
index 817a79462b3d5747e75057ac7f6819d925ae4d4e..8d6f6014870f2cebfa8030fca45549b1cfc7fe30 100644 (file)
@@ -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 @@ static ssize_t mmc_type_show(struct device *dev,
                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;
        }
@@ -59,28 +62,34 @@ mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
                int buf_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 i = 0, length = 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));
+       if (type) {
+               if (add_uevent_var(envp, num_envp, &i,
+                               buf, buf_size, &length,
+                               "MMC_TYPE=%s", type))
+                       return -ENOMEM;
+       }
 
-#undef add_env
+       if (add_uevent_var(envp, num_envp, &i,
+                       buf, buf_size, &length,
+                       "MMC_NAME=%s", mmc_card_name(card)))
+               return -ENOMEM;
 
        envp[i] = NULL;
 
@@ -176,6 +185,11 @@ static void mmc_release_card(struct device *dev)
 {
        struct mmc_card *card = dev_to_mmc_card(dev);
 
+       sdio_free_common_cis(card);
+
+       if (card->info)
+               kfree(card->info);
+
        kfree(card);
 }
 
@@ -221,15 +235,25 @@ int mmc_add_card(struct mmc_card *card)
                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 +285,13 @@ int mmc_add_card(struct mmc_card *card)
 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);
index bfd2ae5bd669fe2a61307995832f2fd2bcb0c4c2..09435e0ec6806fa4771fb616227413bbc5c2dd33 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/delay.h>
 #include <linux/pagemap.h>
 #include <linux/err.h>
+#include <linux/leds.h>
 #include <asm/scatterlist.h>
 #include <linux/scatterlist.h>
 
 #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.
  */
@@ -68,6 +80,11 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
        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 +93,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
                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 +137,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
                        "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 +149,8 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
 
        WARN_ON(!host->claimed);
 
+       led_trigger_event(host->led, LED_FULL);
+
        mrq->cmd->error = 0;
        mrq->cmd->mrq = mrq;
        if (mrq->data) {
@@ -199,7 +220,7 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
 {
        struct mmc_request mrq;
 
-       BUG_ON(!host->claimed);
+       WARN_ON(!host->claimed);
 
        memset(&mrq, 0, sizeof(struct mmc_request));
 
@@ -220,16 +241,23 @@ EXPORT_SYMBOL(mmc_wait_for_cmd);
  *     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
         */
@@ -239,7 +267,7 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
         * 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 +283,7 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
                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 +300,20 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
 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 +321,24 @@ void mmc_claim_host(struct mmc_host *host)
        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 +351,7 @@ void mmc_release_host(struct mmc_host *host)
 {
        unsigned long flags;
 
-       BUG_ON(!host->claimed);
+       WARN_ON(!host->claimed);
 
        spin_lock_irqsave(&host->lock, flags);
        host->claimed = 0;
@@ -433,19 +471,32 @@ static void mmc_power_up(struct mmc_host *host)
        int bit = fls(host->ocr_avail) - 1;
 
        host->ios.vdd = bit;
-       host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
-       host->ios.chip_select = MMC_CS_DONTCARE;
+       if (mmc_host_is_spi(host)) {
+               host->ios.chip_select = MMC_CS_HIGH;
+               host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
+       } else {
+               host->ios.chip_select = MMC_CS_DONTCARE;
+               host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+       }
        host->ios.power_mode = MMC_POWER_UP;
        host->ios.bus_width = MMC_BUS_WIDTH_1;
        host->ios.timing = MMC_TIMING_LEGACY;
        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 +504,10 @@ static void mmc_power_off(struct mmc_host *host)
 {
        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 +564,7 @@ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
        BUG_ON(!host);
        BUG_ON(!ops);
 
-       BUG_ON(!host->claimed);
+       WARN_ON(!host->claimed);
 
        spin_lock_irqsave(&host->lock, flags);
 
@@ -535,8 +588,8 @@ void mmc_detach_bus(struct mmc_host *host)
 
        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 +617,7 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
 #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 +650,38 @@ void mmc_rescan(struct work_struct *work)
 
                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 +792,38 @@ static int __init mmc_init(void)
                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");
index bb2774af9ea91d9891b3b4ba752b4c7221266e8e..39daf2fb5dc46c67691a50e8c72987f797f1c488 100644 (file)
@@ -48,5 +48,7 @@ void mmc_rescan(struct work_struct *work);
 void mmc_start_host(struct mmc_host *host);
 void mmc_stop_host(struct mmc_host *host);
 
+extern int use_spi_crc;
+
 #endif
 
index 2c7ce8f43a9a60367a6f75397a7da831c3afe8d3..64fbc9759a30757426de0270b0afa25d5c31ea94 100644 (file)
@@ -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 mmc_add_host(struct mmc_host *host)
 {
        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 @@ int mmc_add_host(struct mmc_host *host)
        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 @@ void mmc_remove_host(struct mmc_host *host)
 
        device_del(&host->class_dev);
 
+       led_trigger_unregister(host->led);
+
        spin_lock(&mmc_host_lock);
        idr_remove(&mmc_host_idr, host->index);
        spin_unlock(&mmc_host_lock);
index 21d7f48e1d4e67c33ff7f59a46955bc7e2e70df0..65fe28860f54431ed77e5069526c566e5376521a 100644 (file)
@@ -161,13 +161,12 @@ static int mmc_read_ext_csd(struct mmc_card *card)
 {
        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 @@ static int mmc_read_ext_csd(struct mmc_card *card)
        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,29 @@ static int mmc_read_ext_csd(struct mmc_card *card)
                                "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);
+               return -EINVAL;
+       }
+
+       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 +262,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
        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 +274,33 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
 
        /* 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 +308,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                 * 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 +319,47 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
        }
 
        /*
-        * 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 +370,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                (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 +399,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
                (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 +408,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
        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 +449,7 @@ static void mmc_detect(struct mmc_host *host)
 
        mmc_release_host(host);
 
-       if (err != MMC_ERR_NONE) {
+       if (err) {
                mmc_remove(host);
 
                mmc_claim_host(host);
@@ -480,7 +516,8 @@ static void mmc_suspend(struct mmc_host *host)
        BUG_ON(!host->card);
 
        mmc_claim_host(host);
-       mmc_deselect_cards(host);
+       if (!mmc_host_is_spi(host))
+               mmc_deselect_cards(host);
        host->card->state &= ~MMC_STATE_HIGHSPEED;
        mmc_release_host(host);
 }
@@ -502,7 +539,7 @@ static void mmc_resume(struct mmc_host *host)
        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,10 +573,19 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
        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 +611,7 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
         * 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 +633,6 @@ err:
        printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
                mmc_hostname(host), err);
 
-       return 0;
+       return err;
 }
 
index 913e75f008434179e45780ffb7e57ba7c6ff3d9e..bf4bc6adcfef8fbed951a274ee2cc4ca12dae36f 100644 (file)
@@ -40,10 +40,10 @@ static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
        }
 
        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 +63,36 @@ int mmc_go_idle(struct mmc_host *host)
        int err;
        struct mmc_command cmd;
 
-       mmc_set_chip_select(host, MMC_CS_HIGH);
-
-       mmc_delay(1);
+       /*
+        * Non-SPI hosts need to prevent chipselect going active during
+        * GO_IDLE; that would put chips into SPI mode.  Remind them of
+        * that in case of hardware that won't pull up DAT3/nCS otherwise.
+        *
+        * SPI hosts ignore ios.chip_select; it's managed according to
+        * rules that must accomodate non-MMC slaves which this layer
+        * won't even know about.
+        */
+       if (!mmc_host_is_spi(host)) {
+               mmc_set_chip_select(host, MMC_CS_HIGH);
+               mmc_delay(1);
+       }
 
        memset(&cmd, 0, sizeof(struct mmc_command));
 
        cmd.opcode = MMC_GO_IDLE_STATE;
        cmd.arg = 0;
-       cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
+       cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
 
        err = mmc_wait_for_cmd(host, &cmd, 0);
 
        mmc_delay(1);
 
-       mmc_set_chip_select(host, MMC_CS_DONTCARE);
+       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 +107,33 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
        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 +154,12 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
        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 +177,52 @@ int mmc_set_relative_addr(struct mmc_card *card)
        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 +231,99 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
        mrq.cmd = &cmd;
        mrq.data = &data;
 
-       cmd.opcode = MMC_SEND_EXT_CSD;
+       cmd.opcode = opcode;
        cmd.arg = 0;
-       cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
 
-       data.blksz = 512;
+       /* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
+        * rely on callers to never use this with "native" calls for reading
+        * CSD or CID.  Native versions of those commands use the R2 type,
+        * not R1 plus a data block.
+        */
+       cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+
+       data.blksz = len;
        data.blocks = 1;
        data.flags = MMC_DATA_READ;
        data.sg = &sg;
        data.sg_len = 1;
 
-       sg_init_one(&sg, ext_csd, 512);
+       sg_init_one(&sg, data_buf, len);
 
-       mmc_set_data_timeout(&data, card, 0);
+       if (card)
+               mmc_set_data_timeout(&data, card);
 
-       mmc_wait_for_req(card->host, &mrq);
+       mmc_wait_for_req(host, &mrq);
 
-       if (cmd.error != MMC_ERR_NONE)
+       memcpy(buf, data_buf, len);
+       kfree(data_buf);
+
+       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)
+{
+       if (!mmc_host_is_spi(card->host))
+               return mmc_send_cxd_native(card->host, card->rca << 16,
+                               csd, MMC_SEND_CSD);
+
+       return mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd, 16);
+}
+
+int mmc_send_cid(struct mmc_host *host, u32 *cid)
+{
+       if (!mmc_host_is_spi(host)) {
+               if (!host->card)
+                       return -EINVAL;
+               return mmc_send_cxd_native(host, host->card->rca << 16,
+                               cid, MMC_SEND_CID);
+       }
+
+       return mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid, 16);
+}
+
+int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
+{
+       return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
+                       ext_csd, 512);
+}
+
+int mmc_spi_read_ocr(struct mmc_host *host, 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 +341,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
                  (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 +361,20 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
        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;
 }
 
index 76d09a93c5d6c991cbe394889e12428845bacff4..17854bf7cf0d1e36601389d118e178b89f74363e 100644 (file)
@@ -22,6 +22,9 @@ int mmc_send_csd(struct mmc_card *card, u32 *csd);
 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
 
index 1edc62b1e5c65d4625daf17ee0d0429c34a95d1c..d1c1e0f592f19d56f1b200aa4233f1c95e125054 100644 (file)
@@ -166,8 +166,6 @@ static int mmc_decode_scr(struct mmc_card *card)
        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 @@ static int mmc_read_switch(struct mmc_card *card)
        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 @@ static int mmc_switch_hs(struct mmc_card *card)
        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 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
        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 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
         * 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 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
                 * 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 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
        /*
         * 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 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
                 * Fetch switch information from card.
                 */
                err = mmc_read_switch(card);
-               if (err != MMC_ERR_NONE)
+               if (err)
                        goto free_card;
        }
 
@@ -395,7 +421,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
         * 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 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
        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 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
        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 @@ static void mmc_sd_detect(struct mmc_host *host)
 
        mmc_release_host(host);
 
-       if (err != MMC_ERR_NONE) {
+       if (err) {
                mmc_sd_remove(host);
 
                mmc_claim_host(host);
@@ -552,7 +578,8 @@ static void mmc_sd_suspend(struct mmc_host *host)
        BUG_ON(!host->card);
 
        mmc_claim_host(host);
-       mmc_deselect_cards(host);
+       if (!mmc_host_is_spi(host))
+               mmc_deselect_cards(host);
        host->card->state &= ~MMC_STATE_HIGHSPEED;
        mmc_release_host(host);
 }
@@ -574,7 +601,7 @@ static void mmc_sd_resume(struct mmc_host *host)
        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,10 +635,21 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
        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 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
         * 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 @@ err:
        printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
                mmc_hostname(host), err);
 
-       return 0;
+       return err;
 }
 
index 342f340ebc2579d8c81dd231f526b140f87552d5..ee4029a24efdb59b87e213e0c431c58f6afdd46b 100644 (file)
@@ -33,21 +33,21 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
 
        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 +73,7 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
        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 +83,14 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
                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 +103,14 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
                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 +139,14 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
                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 +159,36 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
        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 +199,7 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
        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 +208,21 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
         */
        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 +240,12 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
        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 +260,10 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
        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 +275,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
 
        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 +285,19 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
 
        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]);
 
-       return MMC_ERR_NONE;
+       return 0;
 }
 
 int mmc_sd_switch(struct mmc_card *card, int mode, int group,
@@ -278,6 +311,8 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
        BUG_ON(!card);
        BUG_ON(!card->host);
 
+       /* NOTE: caller guarantees resp is heap-allocated */
+
        mode = !!mode;
        value &= 0xF;
 
@@ -292,7 +327,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
        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 +337,15 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
 
        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;
 }
 
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
new file mode 100644 (file)
index 0000000..87a50f4
--- /dev/null
@@ -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;
+}
+
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
new file mode 100644 (file)
index 0000000..0713a8c
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ *  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 *buf,
+               int buf_size)
+{
+       struct sdio_func *func = dev_to_sdio_func(dev);
+       int i = 0, length = 0;
+
+       if (add_uevent_var(envp, num_envp, &i,
+                       buf, buf_size, &length,
+                       "SDIO_CLASS=%02X", func->class))
+               return -ENOMEM;
+
+       if (add_uevent_var(envp, num_envp, &i,
+                       buf, buf_size, &length,
+                       "SDIO_ID=%04X:%04X", func->vendor, func->device))
+               return -ENOMEM;
+
+       if (add_uevent_var(envp, num_envp, &i,
+                       buf, buf_size, &length,
+                       "MODALIAS=sdio:c%02Xv%04Xd%04X",
+                       func->class, func->vendor, func->device))
+               return -ENOMEM;
+
+       envp[i] = NULL;
+
+       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);
+}
+
diff --git a/drivers/mmc/core/sdio_bus.h b/drivers/mmc/core/sdio_bus.h
new file mode 100644 (file)
index 0000000..567a768
--- /dev/null
@@ -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
+
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
new file mode 100644 (file)
index 0000000..d5e51b1
--- /dev/null
@@ -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);
+}
+
diff --git a/drivers/mmc/core/sdio_cis.h b/drivers/mmc/core/sdio_cis.h
new file mode 100644 (file)
index 0000000..4d903c2
--- /dev/null
@@ -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
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
new file mode 100644 (file)
index 0000000..625b92c
--- /dev/null
@@ -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, &reg);
+       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, &reg);
+               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, &reg);
+       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);
diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
new file mode 100644 (file)
index 0000000..3bd3021
--- /dev/null
@@ -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, &param);
+
+       /*
+        * 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, &reg);
+       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, &reg);
+       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);
+
diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c
new file mode 100644 (file)
index 0000000..4d289b2
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ *  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 <asm/scatterlist.h>
+#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;
+}
+
diff --git a/drivers/mmc/core/sdio_ops.h b/drivers/mmc/core/sdio_ops.h
new file mode 100644 (file)
index 0000000..e2e74b0
--- /dev/null
@@ -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
+
index e23082fe88d0ddebac4921b54631fb75bed935e0..5fef6783c7161fc8cf95b836e49dcc13259ff655 100644 (file)
@@ -35,6 +35,23 @@ config MMC_SDHCI
 
          If unsure, say N.
 
+config MMC_RICOH_MMC
+       tristate "Ricoh MMC Controller Disabler  (EXPERIMENTAL)"
+       depends on PCI && EXPERIMENTAL && MMC_SDHCI
+       help
+         This selects the disabler for the Ricoh MMC Controller. This
+         proprietary controller is unnecessary because the SDHCI driver
+         supports MMC cards on the SD controller, but if it is not
+         disabled, it will steal the MMC cards away - rendering them
+         useless. It is safe to select this driver even if you don't
+         have a Ricoh based card reader.
+
+
+         To compile this driver as a module, choose M here:
+         the module will be called ricoh_mmc.
+
+         If unsure, say Y.
+
 config MMC_OMAP
        tristate "TI OMAP Multimedia Card Interface support"
        depends on ARCH_OMAP
@@ -100,3 +117,16 @@ config MMC_TIFM_SD
           To compile this driver as a module, choose M here: the
          module will be called tifm_sd.
 
+config MMC_SPI
+       tristate "MMC/SD over SPI (EXPERIMENTAL)"
+       depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL
+       select CRC7
+       select CRC_ITU_T
+       help
+         Some systems accss MMC/SD cards using a SPI controller instead of
+         using a "native" MMC/SD controller.  This has a disadvantage of
+         being relatively high overhead, but a compensating advantage of
+         working on many systems without dedicated MMC/SD controllers.
+
+         If unsure, or if your system has no SPI master driver, say N.
+
index 6685f64345b4d649b16113dae53ec6bb00e21dcb..3877c87e6da26e699a730959104dc61ce5992290 100644 (file)
@@ -10,9 +10,11 @@ obj-$(CONFIG_MMC_ARMMMCI)    += mmci.o
 obj-$(CONFIG_MMC_PXA)          += pxamci.o
 obj-$(CONFIG_MMC_IMX)          += imxmmc.o
 obj-$(CONFIG_MMC_SDHCI)                += sdhci.o
+obj-$(CONFIG_MMC_RICOH_MMC)    += ricoh_mmc.o
 obj-$(CONFIG_MMC_WBSD)         += wbsd.o
 obj-$(CONFIG_MMC_AU1X)         += au1xmmc.o
 obj-$(CONFIG_MMC_OMAP)         += omap.o
 obj-$(CONFIG_MMC_AT91)         += at91_mci.o
 obj-$(CONFIG_MMC_TIFM_SD)      += tifm_sd.o
+obj-$(CONFIG_MMC_SPI)          += mmc_spi.o
 
index 955ea60583b5f5c77438c0ea166d6be060f37dcd..6ba98a49612d4c27ce4dc39aaff749cea6295942 100644 (file)
@@ -328,7 +328,7 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host)
        data = cmd->data;
        if (!data) return;
 
-       if (cmd->data->flags & MMC_DATA_MULTI) {
+       if (cmd->data->blocks > 1) {
                pr_debug("multiple write : wait for BLKE...\n");
                at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
        } else
@@ -428,6 +428,14 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
        }
 
        if (data) {
+
+               if ( data->blksz & 0x3 ) {
+                       pr_debug("Unsupported block size\n");
+                       cmd->error = -EINVAL;
+                       mmc_request_done(host->mmc, host->request);
+                       return;
+               }
+
                block_length = data->blksz;
                blocks = data->blocks;
 
@@ -439,7 +447,7 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
 
                if (data->flags & MMC_DATA_STREAM)
                        cmdr |= AT91_MCI_TRTYP_STREAM;
-               if (data->flags & MMC_DATA_MULTI)
+               if (data->blocks > 1)
                        cmdr |= AT91_MCI_TRTYP_MULTIPLE;
        }
        else {
@@ -577,24 +585,22 @@ static void at91_mci_completed_command(struct at91mci_host *host)
                        AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE |
                        AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)) {
                if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) {
-                       cmd->error = MMC_ERR_NONE;
+                       cmd->error = 0;
                }
                else {
                        if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE))
-                               cmd->error = MMC_ERR_TIMEOUT;
+                               cmd->error = -ETIMEDOUT;
                        else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE))
-                               cmd->error = MMC_ERR_BADCRC;
-                       else if (status & (AT91_MCI_OVRE | AT91_MCI_UNRE))
-                               cmd->error = MMC_ERR_FIFO;
+                               cmd->error = -EILSEQ;
                        else
-                               cmd->error = MMC_ERR_FAILED;
+                               cmd->error = -EIO;
 
                        pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n",
                                 cmd->error, cmd->opcode, cmd->retries);
                }
        }
        else
-               cmd->error = MMC_ERR_NONE;
+               cmd->error = 0;
 
        at91_mci_process_next(host);
 }
@@ -836,7 +842,6 @@ static int __init at91_mci_probe(struct platform_device *pdev)
        mmc->f_min = 375000;
        mmc->f_max = 25000000;
        mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
-       mmc->caps = MMC_CAP_BYTEBLOCK;
 
        mmc->max_blk_size = 4095;
        mmc->max_blk_count = mmc->max_req_size;
index 34c99d4ea0415885e3bff95df2f6cc60a8aa564f..92c4d0dfee434d4d09eb5183314b2a1c3d09d9ed 100644 (file)
@@ -186,7 +186,7 @@ static void au1xmmc_tasklet_finish(unsigned long param)
 }
 
 static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
-                               struct mmc_command *cmd, unsigned int flags)
+                               struct mmc_command *cmd, struct mmc_data *data)
 {
        u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT);
 
@@ -208,19 +208,21 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
        default:
                printk(KERN_INFO "au1xmmc: unhandled response type %02x\n",
                        mmc_resp_type(cmd));
-               return MMC_ERR_INVALID;
+               return -EINVAL;
        }
 
-       if (flags & MMC_DATA_READ) {
-               if (flags & MMC_DATA_MULTI)
-                       mmccmd |= SD_CMD_CT_4;
-               else
-                       mmccmd |= SD_CMD_CT_2;
-       } else if (flags & MMC_DATA_WRITE) {
-               if (flags & MMC_DATA_MULTI)
-                       mmccmd |= SD_CMD_CT_3;
-               else
-                       mmccmd |= SD_CMD_CT_1;
+       if (data) {
+               if (flags & MMC_DATA_READ) {
+                       if (data->blocks > 1)
+                               mmccmd |= SD_CMD_CT_4;
+                       else
+                               mmccmd |= SD_CMD_CT_2;
+               } else if (flags & MMC_DATA_WRITE) {
+                       if (data->blocks > 1)
+                               mmccmd |= SD_CMD_CT_3;
+                       else
+                               mmccmd |= SD_CMD_CT_1;
+               }
        }
 
        au_writel(cmd->arg, HOST_CMDARG(host));
@@ -253,7 +255,7 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
                IRQ_ON(host, SD_CONFIG_CR);
        }
 
-       return MMC_ERR_NONE;
+       return 0;
 }
 
 static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
@@ -278,7 +280,7 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
        while((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB))
                status = au_readl(HOST_STATUS(host));
 
-       data->error = MMC_ERR_NONE;
+       data->error = 0;
        dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir);
 
         /* Process any errors */
@@ -288,14 +290,14 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
                crc |= ((status & 0x07) == 0x02) ? 0 : 1;
 
        if (crc)
-               data->error = MMC_ERR_BADCRC;
+               data->error = -EILSEQ;
 
        /* Clear the CRC bits */
        au_writel(SD_STATUS_WC | SD_STATUS_RC, HOST_STATUS(host));
 
        data->bytes_xfered = 0;
 
-       if (data->error == MMC_ERR_NONE) {
+       if (!data->error) {
                if (host->flags & HOST_F_DMA) {
                        u32 chan = DMA_CHANNEL(host);
 
@@ -475,7 +477,7 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
                return;
 
        cmd = mrq->cmd;
-       cmd->error = MMC_ERR_NONE;
+       cmd->error = 0;
 
        if (cmd->flags & MMC_RSP_PRESENT) {
                if (cmd->flags & MMC_RSP_136) {
@@ -512,11 +514,11 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
         /* Figure out errors */
 
        if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC))
-               cmd->error = MMC_ERR_BADCRC;
+               cmd->error = -EILSEQ;
 
        trans = host->flags & (HOST_F_XMIT | HOST_F_RECV);
 
-       if (!trans || cmd->error != MMC_ERR_NONE) {
+       if (!trans || cmd->error) {
 
                IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA|SD_CONFIG_RF);
                tasklet_schedule(&host->finish_task);
@@ -589,7 +591,7 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)
                                   data->sg_len, host->dma.dir);
 
        if (host->dma.len == 0)
-               return MMC_ERR_TIMEOUT;
+               return -ETIMEDOUT;
 
        au_writel(data->blksz - 1, HOST_BLKSIZE(host));
 
@@ -640,11 +642,11 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)
                        //IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF);
        }
 
-       return MMC_ERR_NONE;
+       return 0;
 
  dataerr:
        dma_unmap_sg(mmc_dev(host->mmc),data->sg,data->sg_len,host->dma.dir);
-       return MMC_ERR_TIMEOUT;
+       return -ETIMEDOUT;
 }
 
 /* static void au1xmmc_request
@@ -656,7 +658,7 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
 
        struct au1xmmc_host *host = mmc_priv(mmc);
        unsigned int flags = 0;
-       int ret = MMC_ERR_NONE;
+       int ret = 0;
 
        WARN_ON(irqs_disabled());
        WARN_ON(host->status != HOST_S_IDLE);
@@ -672,10 +674,10 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
                ret = au1xmmc_prepare_data(host, mrq->data);
        }
 
-       if (ret == MMC_ERR_NONE)
-               ret = au1xmmc_send_command(host, 0, mrq->cmd, flags);
+       if (!ret)
+               ret = au1xmmc_send_command(host, 0, mrq->cmd, mrq->data);
 
-       if (ret != MMC_ERR_NONE) {
+       if (ret) {
                mrq->cmd->error = ret;
                au1xmmc_finish_request(host);
        }
@@ -764,10 +766,10 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
 
                if (host->mrq && (status & STATUS_TIMEOUT)) {
                        if (status & SD_STATUS_RAT)
-                               host->mrq->cmd->error = MMC_ERR_TIMEOUT;
+                               host->mrq->cmd->error = -ETIMEDOUT;
 
                        else if (status & SD_STATUS_DT)
-                               host->mrq->data->error = MMC_ERR_TIMEOUT;
+                               host->mrq->data->error = -ETIMEDOUT;
 
                        /* In PIO mode, interrupts might still be enabled */
                        IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH);
index 54bfc9f255966329b6e01c290650f297e7859154..6ebc41e7592cefd131915f6d82b4aff87aca1496 100644 (file)
@@ -428,11 +428,11 @@ static int imxmci_finish_data(struct imxmci_host *host, unsigned int stat)
        if ( stat & STATUS_ERR_MASK ) {
                dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat);
                if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR))
-                       data->error = MMC_ERR_BADCRC;
+                       data->error = -EILSEQ;
                else if(stat & STATUS_TIME_OUT_READ)
-                       data->error = MMC_ERR_TIMEOUT;
+                       data->error = -ETIMEDOUT;
                else
-                       data->error = MMC_ERR_FAILED;
+                       data->error = -EIO;
        } else {
                data->bytes_xfered = host->dma_size;
        }
@@ -458,10 +458,10 @@ static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
 
        if (stat & STATUS_TIME_OUT_RESP) {
                dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n");
-               cmd->error = MMC_ERR_TIMEOUT;
+               cmd->error = -ETIMEDOUT;
        } else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
                dev_dbg(mmc_dev(host->mmc), "cmd crc error\n");
-               cmd->error = MMC_ERR_BADCRC;
+               cmd->error = -EILSEQ;
        }
 
        if(cmd->flags & MMC_RSP_PRESENT) {
@@ -482,7 +482,7 @@ static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
        dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n",
                cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error);
 
-       if (data && (cmd->error == MMC_ERR_NONE) && !(stat & STATUS_ERR_MASK)) {
+       if (data && !cmd->error && !(stat & STATUS_ERR_MASK)) {
                if (host->req->data->flags & MMC_DATA_WRITE) {
 
                        /* Wait for FIFO to be empty before starting DMA write */
@@ -491,7 +491,7 @@ static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
                        if(imxmci_busy_wait_for_status(host, &stat,
                                STATUS_APPL_BUFF_FE,
                                40, "imxmci_cmd_done DMA WR") < 0) {
-                               cmd->error = MMC_ERR_FIFO;
+                               cmd->error = -EIO;
                                imxmci_finish_data(host, stat);
                                if(host->req)
                                        imxmci_finish_request(host, host->req);
@@ -884,9 +884,21 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
        }
 }
 
+static int imxmci_get_ro(struct mmc_host *mmc)
+{
+       struct imxmci_host *host = mmc_priv(mmc);
+
+       if (host->pdata && host->pdata->get_ro)
+               return host->pdata->get_ro(mmc_dev(mmc));
+       /* Host doesn't support read only detection so assume writeable */
+       return 0;
+}
+
+
 static const struct mmc_host_ops imxmci_ops = {
        .request        = imxmci_request,
        .set_ios        = imxmci_set_ios,
+       .get_ro         = imxmci_get_ro,
 };
 
 static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr)
@@ -913,7 +925,7 @@ static void imxmci_check_status(unsigned long data)
 {
        struct imxmci_host *host = (struct imxmci_host *)data;
 
-       if( host->pdata->card_present() != host->present ) {
+       if( host->pdata->card_present(mmc_dev(host->mmc)) != host->present ) {
                host->present ^= 1;
                dev_info(mmc_dev(host->mmc), "card %s\n",
                      host->present ? "inserted" : "removed");
@@ -963,7 +975,7 @@ static int imxmci_probe(struct platform_device *pdev)
        mmc->f_min = 150000;
        mmc->f_max = CLK_RATE/2;
        mmc->ocr_avail = MMC_VDD_32_33;
-       mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_BYTEBLOCK;
+       mmc->caps = MMC_CAP_4_BIT_DATA;
 
        /* MMC core transfer sizes tunable parameters */
        mmc->max_hw_segs = 64;
@@ -1022,7 +1034,7 @@ static int imxmci_probe(struct platform_device *pdev)
        if (ret)
                goto out;
 
-       host->present = host->pdata->card_present();
+       host->present = host->pdata->card_present(mmc_dev(mmc));
        init_timer(&host->timer);
        host->timer.data = (unsigned long)host;
        host->timer.function = imxmci_check_status;
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
new file mode 100644 (file)
index 0000000..f30327b
--- /dev/null
@@ -0,0 +1,1408 @@
+/*
+ * mmc_spi.c - Access SD/MMC cards through SPI master controllers
+ *
+ * (C) Copyright 2005, Intec Automation,
+ *             Mike Lavender (mike@steroidmicros)
+ * (C) Copyright 2006-2007, David Brownell
+ * (C) Copyright 2007, Axis Communications,
+ *             Hans-Peter Nilsson (hp@axis.com)
+ * (C) Copyright 2007, ATRON electronic GmbH,
+ *             Jan Nikitenko <jan.nikitenko@gmail.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/crc7.h>
+#include <linux/crc-itu-t.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>             /* for R1_SPI_* bit values */
+
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
+
+#include <asm/unaligned.h>
+
+
+/* NOTES:
+ *
+ * - For now, we won't try to interoperate with a real mmc/sd/sdio
+ *   controller, although some of them do have hardware support for
+ *   SPI protocol.  The main reason for such configs would be mmc-ish
+ *   cards like DataFlash, which don't support that "native" protocol.
+ *
+ *   We don't have a "DataFlash/MMC/SD/SDIO card slot" abstraction to
+ *   switch between driver stacks, and in any case if "native" mode
+ *   is available, it will be faster and hence preferable.
+ *
+ * - MMC depends on a different chipselect management policy than the
+ *   SPI interface currently supports for shared bus segments:  it needs
+ *   to issue multiple spi_message requests with the chipselect active,
+ *   using the results of one message to decide the next one to issue.
+ *
+ *   Pending updates to the programming interface, this driver 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 (status == 0)
+               status = host->readback.status;
+
+       if (host->dma_dev)
+               dma_sync_single_for_cpu(host->dma_dev,
+                               host->data_dma, sizeof(*host->data),
+                               DMA_FROM_DEVICE);
+
+       return status;
+}
+
+static int
+mmc_spi_skip(struct mmc_spi_host *host, ktime_t timeout, unsigned n, u8 byte)
+{
+       u8              *cp = host->data->status;
+
+       timeout = ktime_add(timeout, ktime_get());
+
+       while (1) {
+               int             status;
+               unsigned        i;
+
+               status = mmc_spi_readbytes(host, n);
+               if (status < 0)
+                       return status;
+
+               for (i = 0; i < n; i++) {
+                       if (cp[i] != byte)
+                               return cp[i];
+               }
+
+               /* REVISIT investigate msleep() to avoid busy-wait I/O
+                * in at least some cases.
+                */
+               if (ktime_to_ns(ktime_sub(ktime_get(), timeout)) > 0)
+                       break;
+       }
+       return -ETIMEDOUT;
+}
+
+static inline int
+mmc_spi_wait_unbusy(struct mmc_spi_host *host, ktime_t timeout)
+{
+       return mmc_spi_skip(host, timeout, sizeof(host->data->status), 0);
+}
+
+static int mmc_spi_readtoken(struct mmc_spi_host *host)
+{
+       return mmc_spi_skip(host, readblock_timeout, 1, 0xff);
+}
+
+
+/*
+ * Note that for SPI, cmd->resp[0] is not the same data as "native" protocol
+ * hosts return!  The low byte holds R1_SPI bits.  The next byte may hold
+ * R2_SPI bits ... for SEND_STATUS, or after data read errors.
+ *
+ * cmd->resp[1] holds any four-byte response, for R3 (READ_OCR) and on
+ * newer cards R7 (IF_COND).
+ */
+
+static char *maptype(struct mmc_command *cmd)
+{
+       switch (mmc_spi_resp_type(cmd)) {
+       case MMC_RSP_SPI_R1:    return "R1";
+       case MMC_RSP_SPI_R1B:   return "R1B";
+       case MMC_RSP_SPI_R2:    return "R2/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 (status == 0)
+               status = host->m.status;
+
+       if (host->dma_dev)
+               dma_sync_single_for_cpu(host->dma_dev,
+                               host->data_dma, sizeof(*host->data),
+                               DMA_BIDIRECTIONAL);
+       if (status < 0) {
+               dev_dbg(&host->spi->dev, "  ... write returned %d\n", status);
+               cmd->error = 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)
+               status = host->m.status;
+
+       if (status != 0) {
+               dev_dbg(&spi->dev, "write error (%d)\n", status);
+               return status;
+       }
+
+       if (host->dma_dev)
+               dma_sync_single_for_cpu(host->dma_dev,
+                               host->data_dma, sizeof(*scratch),
+                               DMA_BIDIRECTIONAL);
+
+       /*
+        * Get the transmission data-response reply.  It must follow
+        * immediately after the data block we transferred.  This reply
+        * doesn't necessarily tell whether the write operation succeeded;
+        * it just says if the transmission was ok and whether *earlier*
+        * writes succeeded; see the standard.
+        */
+       switch (SPI_MMC_RESPONSE_CODE(scratch->status[0])) {
+       case SPI_RESPONSE_ACCEPTED:
+               status = 0;
+               break;
+       case SPI_RESPONSE_CRC_ERR:
+               /* host shall then issue MMC_STOP_TRANSMISSION */
+               status = -EILSEQ;
+               break;
+       case SPI_RESPONSE_WRITE_ERR:
+               /* host shall then issue MMC_STOP_TRANSMISSION,
+                * and should MMC_SEND_STATUS to sort it out
+                */
+               status = -EIO;
+               break;
+       default:
+               status = -EPROTO;
+               break;
+       }
+       if (status != 0) {
+               dev_dbg(&spi->dev, "write error %02x (%d)\n",
+                       scratch->status[0], status);
+               return status;
+       }
+
+       t->tx_buf += t->len;
+       if (host->dma_dev)
+               t->tx_dma += t->len;
+
+       /* Return when not busy.  If we didn't collect that status yet,
+        * we'll need some more I/O.
+        */
+       for (i = 1; i < sizeof(scratch->status); i++) {
+               if (scratch->status[i] != 0)
+                       return 0;
+       }
+       return mmc_spi_wait_unbusy(host, writeblock_timeout);
+}
+
+/*
+ * Read one block:
+ *  - skip leading all-ones bytes ... either
+ *      + N(AC) [1..f(clock,CSD)] usually, else
+ *      + N(CX) [0..8] when reading CSD or CID
+ *  - data block
+ *     + token ... if error token, no data or crc
+ *     + data bytes
+ *     + crc16
+ *
+ * After single block reads, we're done; N(EC) [0+] all-ones bytes follow
+ * before dropping chipselect.
+ *
+ * For multiblock reads, caller either reads the next block or issues a
+ * STOP_TRANSMISSION command.
+ */
+static int
+mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t)
+{
+       struct spi_device       *spi = host->spi;
+       int                     status;
+       struct scratch          *scratch = host->data;
+
+       /* At least one SD card sends an all-zeroes byte when N(CX)
+        * applies, before the all-ones bytes ... just cope with that.
+        */
+       status = mmc_spi_readbytes(host, 1);
+       if (status < 0)
+               return status;
+       status = scratch->status[0];
+       if (status == 0xff || status == 0)
+               status = mmc_spi_readtoken(host);
+
+       if (status == SPI_TOKEN_SINGLE) {
+               if (host->dma_dev) {
+                       dma_sync_single_for_device(host->dma_dev,
+                                       host->data_dma, sizeof(*scratch),
+                                       DMA_BIDIRECTIONAL);
+                       dma_sync_single_for_device(host->dma_dev,
+                                       t->rx_dma, t->len,
+                                       DMA_FROM_DEVICE);
+               }
+
+               status = spi_sync(spi, &host->m);
+               if (status == 0)
+                       status = host->m.status;
+
+               if (host->dma_dev) {
+                       dma_sync_single_for_cpu(host->dma_dev,
+                                       host->data_dma, sizeof(*scratch),
+                                       DMA_BIDIRECTIONAL);
+                       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, 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);
+               if (direction == DMA_TO_DEVICE)
+                       t->tx_buf = kmap_addr + sg->offset;
+               else
+                       t->rx_buf = kmap_addr + sg->offset;
+
+               /* transfer each block, and update request status */
+               while (length) {
+                       t->len = min(length, blk_size);
+
+                       dev_dbg(&host->spi->dev,
+                               "    mmc_spi: %s block, %d bytes\n",
+                               (direction == DMA_TO_DEVICE)
+                               ? "write"
+                               : "read",
+                               t->len);
+
+                       if (direction == DMA_TO_DEVICE)
+                               status = mmc_spi_writeblock(host, t);
+                       else
+                               status = mmc_spi_readblock(host, t);
+                       if (status < 0)
+                               break;
+
+                       data->bytes_xfered += t->len;
+                       length -= t->len;
+
+                       if (!multiple)
+                               break;
+               }
+
+               /* discard mappings */
+               if (direction == DMA_FROM_DEVICE)
+                       flush_kernel_dcache_page(sg->page);
+               kunmap(sg->page);
+               if (dma_dev)
+                       dma_unmap_page(dma_dev, dma_addr, PAGE_SIZE, 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 (tmp == 0)
+                       tmp = host->m.status;
+
+               if (host->dma_dev)
+                       dma_sync_single_for_cpu(host->dma_dev,
+                                       host->data_dma, sizeof(*scratch),
+                                       DMA_BIDIRECTIONAL);
+
+               if (tmp < 0) {
+                       if (!data->error)
+                               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;
+}
+
+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.  That is, either we have the experimental exclusive access
+        * primitives ... or else there's nobody to share it with.
+        */
+       if (spi->master->num_chipselect > 1) {
+               struct device   *parent = spi->dev.parent;
+
+               /* If there are multiple devices on this bus, we
+                * can't proceed.
+                */
+               spin_lock(&parent->klist_children.k_lock);
+               if (parent->klist_children.k_list.next
+                               != parent->klist_children.k_list.prev)
+                       status = -EMLINK;
+               else
+                       status = 0;
+               spin_unlock(&parent->klist_children.k_lock);
+               if (status < 0) {
+                       dev_err(&spi->dev, "can't share SPI bus\n");
+                       return status;
+               }
+
+               /* REVISIT we can't guarantee another device won't
+                * be added later.  It's uncommon though ... for now,
+                * work as if this is safe.
+                */
+               dev_warn(&spi->dev, "ASSUMING unshared SPI bus!\n");
+       }
+
+       /* We need a supply of ones to transmit.  This is the only time
+        * the CPU touches these, so cache coherency isn't a concern.
+        *
+        * NOTE if many systems use more than one MMC-over-SPI connector
+        * it'd save some memory to share this.  That's evidently rare.
+        */
+       status = -ENOMEM;
+       ones = kmalloc(MMC_SPI_BLOCKSIZE, GFP_KERNEL);
+       if (!ones)
+               goto nomem;
+       memset(ones, 0xff, MMC_SPI_BLOCKSIZE);
+
+       mmc = mmc_alloc_host(sizeof(*host), &spi->dev);
+       if (!mmc)
+               goto nomem;
+
+       mmc->ops = &mmc_spi_ops;
+       mmc->max_blk_size = MMC_SPI_BLOCKSIZE;
+
+       /* As long as we keep track of the number of successfully
+        * transmitted blocks, we're good for multiwrite.
+        */
+       mmc->caps = MMC_CAP_SPI | MMC_CAP_MULTIWRITE;
+
+       /* SPI doesn't need the lowspeed device identification thing for
+        * MMC or SD cards, since it never comes up in open drain mode.
+        * That's good; some SPI masters can't handle very low speeds!
+        *
+        * However, low speed SDIO cards need not handle over 400 KHz;
+        * that's the only reason not to use a few MHz for f_min (until
+        * the upper layer reads the target frequency from the CSD).
+        */
+       mmc->f_min = 400000;
+       mmc->f_max = spi->max_speed_hz;
+
+       host = mmc_priv(mmc);
+       host->mmc = mmc;
+       host->spi = spi;
+
+       host->ones = ones;
+
+       /* Platform data is used to hook up things like card sensing
+        * and power switching gpios.
+        */
+       host->pdata = spi->dev.platform_data;
+       if (host->pdata)
+               mmc->ocr_avail = host->pdata->ocr_mask;
+       if (!mmc->ocr_avail) {
+               dev_warn(&spi->dev, "ASSUMING 3.2-3.4 V slot power\n");
+               mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+       }
+       if (host->pdata && host->pdata->setpower) {
+               host->powerup_msecs = host->pdata->powerup_msecs;
+               if (!host->powerup_msecs || host->powerup_msecs > 250)
+                       host->powerup_msecs = 250;
+       }
+
+       dev_set_drvdata(&spi->dev, mmc);
+
+       /* preallocate dma buffers */
+       host->data = kmalloc(sizeof(*host->data), GFP_KERNEL);
+       if (!host->data)
+               goto fail_nobuf1;
+
+       if (spi->master->cdev.dev->dma_mask) {
+               struct device   *dev = spi->master->cdev.dev;
+
+               host->dma_dev = dev;
+               host->ones_dma = dma_map_single(dev, ones,
+                               MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
+               host->data_dma = dma_map_single(dev, host->data,
+                               sizeof(*host->data), DMA_BIDIRECTIONAL);
+
+               /* REVISIT in theory those map operations can fail... */
+
+               dma_sync_single_for_cpu(host->dma_dev,
+                               host->data_dma, sizeof(*host->data),
+                               DMA_BIDIRECTIONAL);
+       }
+
+       /* setup message for status/busy readback */
+       spi_message_init(&host->readback);
+       host->readback.is_dma_mapped = (host->dma_dev != NULL);
+
+       spi_message_add_tail(&host->status, &host->readback);
+       host->status.tx_buf = host->ones;
+       host->status.tx_dma = host->ones_dma;
+       host->status.rx_buf = &host->data->status;
+       host->status.rx_dma = host->data_dma + offsetof(struct scratch, status);
+       host->status.cs_change = 1;
+
+       /* register card detect irq */
+       if (host->pdata && host->pdata->init) {
+               status = host->pdata->init(&spi->dev, mmc_spi_detect_irq, mmc);
+               if (status != 0)
+                       goto fail_glue_init;
+       }
+
+       status = mmc_add_host(mmc);
+       if (status != 0)
+               goto fail_add_host;
+
+       dev_info(&spi->dev, "SD/MMC host %s%s%s%s\n",
+                       mmc->class_dev.bus_id,
+                       host->dma_dev ? "" : ", no DMA",
+                       (host->pdata && host->pdata->get_ro)
+                               ? "" : ", no WP",
+                       (host->pdata && host->pdata->setpower)
+                               ? "" : ", no poweroff");
+       return 0;
+
+fail_add_host:
+       mmc_remove_host (mmc);
+fail_glue_init:
+       if (host->dma_dev)
+               dma_unmap_single(host->dma_dev, host->data_dma,
+                               sizeof(*host->data), DMA_BIDIRECTIONAL);
+       kfree(host->data);
+
+fail_nobuf1:
+       mmc_free_host(mmc);
+       dev_set_drvdata(&spi->dev, NULL);
+
+nomem:
+       kfree(ones);
+       return status;
+}
+
+
+static int __devexit mmc_spi_remove(struct spi_device *spi)
+{
+       struct mmc_host         *mmc = dev_get_drvdata(&spi->dev);
+       struct mmc_spi_host     *host;
+
+       if (mmc) {
+               host = mmc_priv(mmc);
+
+               /* prevent new mmc_detect_change() calls */
+               if (host->pdata && host->pdata->exit)
+                       host->pdata->exit(&spi->dev, mmc);
+
+               mmc_remove_host(mmc);
+
+               if (host->dma_dev) {
+                       dma_unmap_single(host->dma_dev, host->ones_dma,
+                               MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
+                       dma_unmap_single(host->dma_dev, host->data_dma,
+                               sizeof(*host->data), DMA_BIDIRECTIONAL);
+               }
+
+               kfree(host->data);
+               kfree(host->ones);
+
+               spi->max_speed_hz = mmc->f_max;
+               mmc_free_host(mmc);
+               dev_set_drvdata(&spi->dev, NULL);
+       }
+       return 0;
+}
+
+
+static struct spi_driver mmc_spi_driver = {
+       .driver = {
+               .name =         "mmc_spi",
+               .bus =          &spi_bus_type,
+               .owner =        THIS_MODULE,
+       },
+       .probe =        mmc_spi_probe,
+       .remove =       __devexit_p(mmc_spi_remove),
+};
+
+
+static int __init mmc_spi_init(void)
+{
+       return spi_register_driver(&mmc_spi_driver);
+}
+module_init(mmc_spi_init);
+
+
+static void __exit mmc_spi_exit(void)
+{
+       spi_unregister_driver(&mmc_spi_driver);
+}
+module_exit(mmc_spi_exit);
+
+
+MODULE_AUTHOR("Mike Lavender, David Brownell, "
+               "Hans-Peter Nilsson, Jan Nikitenko");
+MODULE_DESCRIPTION("SPI SD/MMC host driver");
+MODULE_LICENSE("GPL");
index be730c0a0352a6f942fb001ad8866b42ab84d666..d0eb0a2abf4dd272484949e5a0a14c27a681d143 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/highmem.h>
+#include <linux/log2.h>
 #include <linux/mmc/host.h>
 #include <linux/amba/bus.h>
 #include <linux/clk.h>
@@ -154,11 +155,11 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
        }
        if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
                if (status & MCI_DATACRCFAIL)
-                       data->error = MMC_ERR_BADCRC;
+                       data->error = -EILSEQ;
                else if (status & MCI_DATATIMEOUT)
-                       data->error = MMC_ERR_TIMEOUT;
+                       data->error = -ETIMEDOUT;
                else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
-                       data->error = MMC_ERR_FIFO;
+                       data->error = -EIO;
                status |= MCI_DATAEND;
 
                /*
@@ -193,12 +194,12 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
        cmd->resp[3] = readl(base + MMCIRESPONSE3);
 
        if (status & MCI_CMDTIMEOUT) {
-               cmd->error = MMC_ERR_TIMEOUT;
+               cmd->error = -ETIMEDOUT;
        } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) {
-               cmd->error = MMC_ERR_BADCRC;
+               cmd->error = -EILSEQ;
        }
 
-       if (!cmd->data || cmd->error != MMC_ERR_NONE) {
+       if (!cmd->data || cmd->error) {
                if (host->data)
                        mmci_stop_data(host);
                mmci_request_end(host, cmd->mrq);
@@ -391,6 +392,14 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
        WARN_ON(host->mrq != NULL);
 
+       if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
+               printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n",
+                       mmc_hostname(mmc), mrq->data->blksz);
+               mrq->cmd->error = -EINVAL;
+               mmc_request_done(mmc, mrq);
+               return;
+       }
+
        spin_lock_irq(&host->lock);
 
        host->mrq = mrq;
index 0cf97edc5f58f978437f49da8cb1323c3599bc41..60a67dfcda6a86a0d786c33799124981f19f49db 100644 (file)
@@ -263,7 +263,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
                enum dma_data_direction dma_data_dir;
 
                BUG_ON(host->dma_ch < 0);
-               if (data->error != MMC_ERR_NONE)
+               if (data->error)
                        omap_stop_dma(host->dma_ch);
                /* Release DMA channel lazily */
                mod_timer(&host->dma_timer, jiffies + HZ);
@@ -368,7 +368,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
                }
        }
 
-       if (host->data == NULL || cmd->error != MMC_ERR_NONE) {
+       if (host->data == NULL || cmd->error) {
                host->mrq = NULL;
                clk_disable(host->fclk);
                mmc_request_done(host->mmc, cmd->mrq);
@@ -475,14 +475,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                if (status & OMAP_MMC_STAT_DATA_TOUT) {
                        dev_dbg(mmc_dev(host->mmc), "data timeout\n");
                        if (host->data) {
-                               host->data->error |= MMC_ERR_TIMEOUT;
+                               host->data->error = -ETIMEDOUT;
                                transfer_error = 1;
                        }
                }
 
                if (status & OMAP_MMC_STAT_DATA_CRC) {
                        if (host->data) {
-                               host->data->error |= MMC_ERR_BADCRC;
+                               host->data->error = -EILSEQ;
                                dev_dbg(mmc_dev(host->mmc),
                                         "data CRC error, bytes left %d\n",
                                        host->total_bytes_left);
@@ -504,7 +504,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                                        dev_err(mmc_dev(host->mmc),
                                                "command timeout, CMD %d\n",
                                                host->cmd->opcode);
-                               host->cmd->error = MMC_ERR_TIMEOUT;
+                               host->cmd->error = -ETIMEDOUT;
                                end_command = 1;
                        }
                }
@@ -514,7 +514,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
                                dev_err(mmc_dev(host->mmc),
                                        "command CRC error (CMD%d, arg 0x%08x)\n",
                                        host->cmd->opcode, host->cmd->arg);
-                               host->cmd->error = MMC_ERR_BADCRC;
+                               host->cmd->error = -EILSEQ;
                                end_command = 1;
                        } else
                                dev_err(mmc_dev(host->mmc),
index ff960334b337d814e72bd1fc5446d80aa4294ea0..657901eecfced1b6222d2f772a81415a5b836eaa 100644 (file)
@@ -142,6 +142,10 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
                                   host->dma_dir);
 
        for (i = 0; i < host->dma_len; i++) {
+               unsigned int length = sg_dma_len(&data->sg[i]);
+               host->sg_cpu[i].dcmd = dcmd | length;
+               if (length & 31 && !(data->flags & MMC_DATA_READ))
+                       host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN;
                if (data->flags & MMC_DATA_READ) {
                        host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
                        host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
@@ -149,7 +153,6 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
                        host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]);
                        host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO;
                }
-               host->sg_cpu[i].dcmd = dcmd | sg_dma_len(&data->sg[i]);
                host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
                                        sizeof(struct pxa_dma_desc);
        }
@@ -226,7 +229,7 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
        }
 
        if (stat & STAT_TIME_OUT_RESPONSE) {
-               cmd->error = MMC_ERR_TIMEOUT;
+               cmd->error = -ETIMEDOUT;
        } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
 #ifdef CONFIG_PXA27x
                /*
@@ -239,11 +242,11 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
                        pr_debug("ignoring CRC from command %d - *risky*\n", cmd->opcode);
                } else
 #endif
-               cmd->error = MMC_ERR_BADCRC;
+               cmd->error = -EILSEQ;
        }
 
        pxamci_disable_irq(host, END_CMD_RES);
-       if (host->data && cmd->error == MMC_ERR_NONE) {
+       if (host->data && !cmd->error) {
                pxamci_enable_irq(host, DATA_TRAN_DONE);
        } else {
                pxamci_finish_request(host, host->mrq);
@@ -264,9 +267,9 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
                     host->dma_dir);
 
        if (stat & STAT_READ_TIME_OUT)
-               data->error = MMC_ERR_TIMEOUT;
+               data->error = -ETIMEDOUT;
        else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR))
-               data->error = MMC_ERR_BADCRC;
+               data->error = -EILSEQ;
 
        /*
         * There appears to be a hardware design bug here.  There seems to
@@ -274,7 +277,7 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
         * This means that if there was an error on any block, we mark all
         * data blocks as being in error.
         */
-       if (data->error == MMC_ERR_NONE)
+       if (!data->error)
                data->bytes_xfered = data->blocks * data->blksz;
        else
                data->bytes_xfered = 0;
@@ -284,7 +287,7 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
        host->data = NULL;
        if (host->mrq->stop) {
                pxamci_stop_clock(host);
-               pxamci_start_cmd(host, host->mrq->stop, 0);
+               pxamci_start_cmd(host, host->mrq->stop, host->cmdat);
        } else {
                pxamci_finish_request(host, host->mrq);
        }
@@ -298,7 +301,7 @@ static irqreturn_t pxamci_irq(int irq, void *devid)
        unsigned int ireg;
        int handled = 0;
 
-       ireg = readl(host->base + MMC_I_REG);
+       ireg = readl(host->base + MMC_I_REG) & ~readl(host->base + MMC_I_MASK);
 
        if (ireg) {
                unsigned stat = readl(host->base + MMC_STAT);
@@ -309,6 +312,10 @@ static irqreturn_t pxamci_irq(int irq, void *devid)
                        handled |= pxamci_cmd_done(host, stat);
                if (ireg & DATA_TRAN_DONE)
                        handled |= pxamci_data_done(host, stat);
+               if (ireg & SDIO_INT) {
+                       mmc_signal_sdio_irq(host->mmc);
+                       handled = 1;
+               }
        }
 
        return IRQ_RETVAL(handled);
@@ -382,20 +389,46 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
                        host->cmdat |= CMDAT_INIT;
        }
 
+       if (ios->bus_width == MMC_BUS_WIDTH_4)
+               host->cmdat |= CMDAT_SD_4DAT;
+       else
+               host->cmdat &= ~CMDAT_SD_4DAT;
+
        pr_debug("PXAMCI: clkrt = %x cmdat = %x\n",
                 host->clkrt, host->cmdat);
 }
 
+static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable)
+{
+       struct pxamci_host *pxa_host = mmc_priv(host);
+
+       if (enable)
+               pxamci_enable_irq(pxa_host, SDIO_INT);
+       else
+               pxamci_disable_irq(pxa_host, SDIO_INT);
+}
+
 static const struct mmc_host_ops pxamci_ops = {
-       .request        = pxamci_request,
-       .get_ro         = pxamci_get_ro,
-       .set_ios        = pxamci_set_ios,
+       .request                = pxamci_request,
+       .get_ro                 = pxamci_get_ro,
+       .set_ios                = pxamci_set_ios,
+       .enable_sdio_irq        = pxamci_enable_sdio_irq,
 };
 
 static void pxamci_dma_irq(int dma, void *devid)
 {
-       printk(KERN_ERR "DMA%d: IRQ???\n", dma);
-       DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR;
+       struct pxamci_host *host = devid;
+       int dcsr = DCSR(dma);
+       DCSR(dma) = dcsr & ~DCSR_STOPIRQEN;
+
+       if (dcsr & DCSR_ENDINTR) {
+               writel(BUF_PART_FULL, host->base + MMC_PRTBUF);
+       } else {
+               printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
+                      mmc_hostname(host->mmc), dma, dcsr);
+               host->data->error = -EIO;
+               pxamci_data_done(host, 0);
+       }
 }
 
 static irqreturn_t pxamci_detect_irq(int irq, void *devid)
@@ -444,9 +477,9 @@ static int pxamci_probe(struct platform_device *pdev)
        mmc->max_seg_size = PAGE_SIZE;
 
        /*
-        * Block length register is 10 bits.
+        * Block length register is only 10 bits before PXA27x.
         */
-       mmc->max_blk_size = 1023;
+       mmc->max_blk_size = (cpu_is_pxa21x() || cpu_is_pxa25x()) ? 1023 : 2048;
 
        /*
         * Block count register is 16 bits.
@@ -460,6 +493,12 @@ static int pxamci_probe(struct platform_device *pdev)
        mmc->ocr_avail = host->pdata ?
                         host->pdata->ocr_mask :
                         MMC_VDD_32_33|MMC_VDD_33_34;
+       mmc->caps = 0;
+       host->cmdat = 0;
+       if (!cpu_is_pxa21x() && !cpu_is_pxa25x()) {
+               mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
+               host->cmdat |= CMDAT_SDIO_INT_EN;
+       }
 
        host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
        if (!host->sg_cpu) {
index df17c281278a68d272a21756b1be5eb1f1d9c82f..3153e779d46a9edbd481249871d2779cf988e432 100644 (file)
@@ -25,6 +25,8 @@
 #define SPI_EN                 (1 << 0)
 
 #define MMC_CMDAT      0x0010
+#define CMDAT_SDIO_INT_EN      (1 << 11)
+#define CMDAT_SD_4DAT          (1 << 8)
 #define CMDAT_DMAEN            (1 << 7)
 #define CMDAT_INIT             (1 << 6)
 #define CMDAT_BUSY             (1 << 5)
diff --git a/drivers/mmc/host/ricoh_mmc.c b/drivers/mmc/host/ricoh_mmc.c
new file mode 100644 (file)
index 0000000..1e87045
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ *  ricoh_mmc.c - Dummy driver to disable the Rioch MMC controller.
+ *
+ *  Copyright (C) 2007 Philip Langdale, All Rights Reserved.
+ *
+ * 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 is a conceptually ridiculous driver, but it is required by the way
+ * the Ricoh multi-function R5C832 works. This chip implements firewire
+ * and four different memory card controllers. Two of those controllers are
+ * an SDHCI controller and a proprietary MMC controller. The linux SDHCI
+ * driver supports MMC cards but the chip detects MMC cards in hardware
+ * and directs them to the MMC controller - so the SDHCI driver never sees
+ * them. To get around this, we must disable the useless MMC controller.
+ * At that point, the SDHCI controller will start seeing them. As a bonus,
+ * a detection event occurs immediately, even if the MMC card is already
+ * in the reader.
+ *
+ * The relevant registers live on the firewire function, so this is unavoidably
+ * ugly. Such is life.
+ */
+
+#include <linux/pci.h>
+
+#define DRIVER_NAME "ricoh-mmc"
+
+static const struct pci_device_id pci_ids[] __devinitdata = {
+       {
+               .vendor         = PCI_VENDOR_ID_RICOH,
+               .device         = PCI_DEVICE_ID_RICOH_R5C843,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+       },
+       { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
+                                    const struct pci_device_id *ent)
+{
+       u8 rev;
+
+       struct pci_dev *fw_dev = NULL;
+
+       BUG_ON(pdev == NULL);
+       BUG_ON(ent == NULL);
+
+       pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
+
+       printk(KERN_INFO DRIVER_NAME
+               ": Ricoh MMC controller found at %s [%04x:%04x] (rev %x)\n",
+               pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
+               (int)rev);
+
+       while ((fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
+               if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
+                   pdev->bus == fw_dev->bus) {
+                       u8 write_enable;
+                       u8 disable;
+
+                       pci_read_config_byte(fw_dev, 0xCB, &disable);
+                       if (disable & 0x02) {
+                               printk(KERN_INFO DRIVER_NAME
+                                      ": Controller already disabled. Nothing to do.\n");
+                               return -ENODEV;
+                       }
+
+                       pci_read_config_byte(fw_dev, 0xCA, &write_enable);
+                       pci_write_config_byte(fw_dev, 0xCA, 0x57);
+                       pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
+                       pci_write_config_byte(fw_dev, 0xCA, write_enable);
+
+                       pci_set_drvdata(pdev, fw_dev);
+
+                       printk(KERN_INFO DRIVER_NAME
+                              ": Controller is now disabled.\n");
+
+                       break;
+               }
+       }
+
+       if (pci_get_drvdata(pdev) == NULL) {
+               printk(KERN_WARNING DRIVER_NAME
+                      ": Main firewire function not found. Cannot disable controller.\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void __devexit ricoh_mmc_remove(struct pci_dev *pdev)
+{
+       u8 write_enable;
+       u8 disable;
+       struct pci_dev *fw_dev = NULL;
+
+       fw_dev = pci_get_drvdata(pdev);
+       BUG_ON(fw_dev == NULL);
+
+       pci_read_config_byte(fw_dev, 0xCA, &write_enable);
+       pci_read_config_byte(fw_dev, 0xCB, &disable);
+       pci_write_config_byte(fw_dev, 0xCA, 0x57);
+       pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
+       pci_write_config_byte(fw_dev, 0xCA, write_enable);
+
+       printk(KERN_INFO DRIVER_NAME
+              ": Controller is now re-enabled.\n");
+
+       pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_driver ricoh_mmc_driver = {
+       .name =         DRIVER_NAME,
+       .id_table =     pci_ids,
+       .probe =        ricoh_mmc_probe,
+       .remove =       __devexit_p(ricoh_mmc_remove),
+};
+
+/*****************************************************************************\
+ *                                                                           *
+ * Driver init/exit                                                          *
+ *                                                                           *
+\*****************************************************************************/
+
+static int __init ricoh_mmc_drv_init(void)
+{
+       printk(KERN_INFO DRIVER_NAME
+               ": Ricoh MMC Controller disabling driver\n");
+       printk(KERN_INFO DRIVER_NAME ": Copyright(c) Philip Langdale\n");
+
+       return pci_register_driver(&ricoh_mmc_driver);
+}
+
+static void __exit ricoh_mmc_drv_exit(void)
+{
+       pci_unregister_driver(&ricoh_mmc_driver);
+}
+
+module_init(ricoh_mmc_drv_init);
+module_exit(ricoh_mmc_drv_exit);
+
+MODULE_AUTHOR("Philip Langdale <philipl@alumni.utexas.net>");
+MODULE_DESCRIPTION("Ricoh MMC Controller disabling driver");
+MODULE_LICENSE("GPL");
+
index 20a7d89e01bac08032fa874ecc081fc7a44a322d..b397121b947d5acf78b764c06c486a30a8fe1797 100644 (file)
@@ -25,8 +25,6 @@
 #define DBG(f, x...) \
        pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x)
 
-static unsigned int debug_nodma = 0;
-static unsigned int debug_forcedma = 0;
 static unsigned int debug_quirks = 0;
 
 #define SDHCI_QUIRK_CLOCK_BEFORE_RESET                 (1<<0)
@@ -35,6 +33,7 @@ static unsigned int debug_quirks = 0;
 #define SDHCI_QUIRK_NO_CARD_NO_RESET                   (1<<2)
 #define SDHCI_QUIRK_SINGLE_POWER_WRITE                 (1<<3)
 #define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS              (1<<4)
+#define SDHCI_QUIRK_BROKEN_DMA                         (1<<5)
 
 static const struct pci_device_id pci_ids[] __devinitdata = {
        {
@@ -68,7 +67,8 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
                .device         = PCI_DEVICE_ID_ENE_CB712_SD,
                .subvendor      = PCI_ANY_ID,
                .subdevice      = PCI_ANY_ID,
-               .driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE,
+               .driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE |
+                                 SDHCI_QUIRK_BROKEN_DMA,
        },
 
        {
@@ -76,7 +76,8 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
                .device         = PCI_DEVICE_ID_ENE_CB712_SD_2,
                .subvendor      = PCI_ANY_ID,
                .subdevice      = PCI_ANY_ID,
-               .driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE,
+               .driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE |
+                                 SDHCI_QUIRK_BROKEN_DMA,
        },
 
        {
@@ -132,7 +133,7 @@ static void sdhci_dumpregs(struct sdhci_host *host)
                readb(host->ioaddr + SDHCI_POWER_CONTROL),
                readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL));
        printk(KERN_DEBUG DRIVER_NAME ": Wake-up:  0x%08x | Clock:    0x%08x\n",
-               readb(host->ioaddr + SDHCI_WALK_UP_CONTROL),
+               readb(host->ioaddr + SDHCI_WAKE_UP_CONTROL),
                readw(host->ioaddr + SDHCI_CLOCK_CONTROL));
        printk(KERN_DEBUG DRIVER_NAME ": Timeout:  0x%08x | Int stat: 0x%08x\n",
                readb(host->ioaddr + SDHCI_TIMEOUT_CONTROL),
@@ -481,16 +482,16 @@ static void sdhci_finish_data(struct sdhci_host *host)
         * Controller doesn't count down when in single block mode.
         */
        if (data->blocks == 1)
-               blocks = (data->error == MMC_ERR_NONE) ? 0 : 1;
+               blocks = (data->error == 0) ? 0 : 1;
        else
                blocks = readw(host->ioaddr + SDHCI_BLOCK_COUNT);
        data->bytes_xfered = data->blksz * (data->blocks - blocks);
 
-       if ((data->error == MMC_ERR_NONE) && blocks) {
+       if (!data->error && blocks) {
                printk(KERN_ERR "%s: Controller signalled completion even "
                        "though there were blocks left.\n",
                        mmc_hostname(host->mmc));
-               data->error = MMC_ERR_FAILED;
+               data->error = -EIO;
        }
 
        if (data->stop) {
@@ -498,7 +499,7 @@ static void sdhci_finish_data(struct sdhci_host *host)
                 * The controller needs a reset of internal state machines
                 * upon error conditions.
                 */
-               if (data->error != MMC_ERR_NONE) {
+               if (data->error) {
                        sdhci_reset(host, SDHCI_RESET_CMD);
                        sdhci_reset(host, SDHCI_RESET_DATA);
                }
@@ -533,7 +534,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
                        printk(KERN_ERR "%s: Controller never released "
                                "inhibit bit(s).\n", mmc_hostname(host->mmc));
                        sdhci_dumpregs(host);
-                       cmd->error = MMC_ERR_FAILED;
+                       cmd->error = -EIO;
                        tasklet_schedule(&host->finish_tasklet);
                        return;
                }
@@ -554,7 +555,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
        if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
                printk(KERN_ERR "%s: Unsupported response type!\n",
                        mmc_hostname(host->mmc));
-               cmd->error = MMC_ERR_INVALID;
+               cmd->error = -EINVAL;
                tasklet_schedule(&host->finish_tasklet);
                return;
        }
@@ -601,7 +602,7 @@ static void sdhci_finish_command(struct sdhci_host *host)
                }
        }
 
-       host->cmd->error = MMC_ERR_NONE;
+       host->cmd->error = 0;
 
        if (host->data && host->data_early)
                sdhci_finish_data(host);
@@ -722,7 +723,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
        host->mrq = mrq;
 
        if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
-               host->mrq->cmd->error = MMC_ERR_TIMEOUT;
+               host->mrq->cmd->error = -ENOMEDIUM;
                tasklet_schedule(&host->finish_tasklet);
        } else
                sdhci_send_command(host, mrq->cmd);
@@ -800,10 +801,35 @@ static int sdhci_get_ro(struct mmc_host *mmc)
        return !(present & SDHCI_WRITE_PROTECT);
 }
 
+static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+       struct sdhci_host *host;
+       unsigned long flags;
+       u32 ier;
+
+       host = mmc_priv(mmc);
+
+       spin_lock_irqsave(&host->lock, flags);
+
+       ier = readl(host->ioaddr + SDHCI_INT_ENABLE);
+
+       ier &= ~SDHCI_INT_CARD_INT;
+       if (enable)
+               ier |= SDHCI_INT_CARD_INT;
+
+       writel(ier, host->ioaddr + SDHCI_INT_ENABLE);
+       writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+
+       mmiowb();
+
+       spin_unlock_irqrestore(&host->lock, flags);
+}
+
 static const struct mmc_host_ops sdhci_ops = {
        .request        = sdhci_request,
        .set_ios        = sdhci_set_ios,
        .get_ro         = sdhci_get_ro,
+       .enable_sdio_irq = sdhci_enable_sdio_irq,
 };
 
 /*****************************************************************************\
@@ -831,7 +857,7 @@ static void sdhci_tasklet_card(unsigned long param)
                        sdhci_reset(host, SDHCI_RESET_CMD);
                        sdhci_reset(host, SDHCI_RESET_DATA);
 
-                       host->mrq->cmd->error = MMC_ERR_FAILED;
+                       host->mrq->cmd->error = -ENOMEDIUM;
                        tasklet_schedule(&host->finish_tasklet);
                }
        }
@@ -859,9 +885,9 @@ static void sdhci_tasklet_finish(unsigned long param)
         * The controller needs a reset of internal state machines
         * upon error conditions.
         */
-       if ((mrq->cmd->error != MMC_ERR_NONE) ||
-               (mrq->data && ((mrq->data->error != MMC_ERR_NONE) ||
-               (mrq->data->stop && (mrq->data->stop->error != MMC_ERR_NONE))))) {
+       if (mrq->cmd->error ||
+               (mrq->data && (mrq->data->error ||
+               (mrq->data->stop && mrq->data->stop->error)))) {
 
                /* Some controllers need this kick or reset won't work here */
                if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
@@ -906,13 +932,13 @@ static void sdhci_timeout_timer(unsigned long data)
                sdhci_dumpregs(host);
 
                if (host->data) {
-                       host->data->error = MMC_ERR_TIMEOUT;
+                       host->data->error = -ETIMEDOUT;
                        sdhci_finish_data(host);
                } else {
                        if (host->cmd)
-                               host->cmd->error = MMC_ERR_TIMEOUT;
+                               host->cmd->error = -ETIMEDOUT;
                        else
-                               host->mrq->cmd->error = MMC_ERR_TIMEOUT;
+                               host->mrq->cmd->error = -ETIMEDOUT;
 
                        tasklet_schedule(&host->finish_tasklet);
                }
@@ -941,13 +967,12 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
        }
 
        if (intmask & SDHCI_INT_TIMEOUT)
-               host->cmd->error = MMC_ERR_TIMEOUT;
-       else if (intmask & SDHCI_INT_CRC)
-               host->cmd->error = MMC_ERR_BADCRC;
-       else if (intmask & (SDHCI_INT_END_BIT | SDHCI_INT_INDEX))
-               host->cmd->error = MMC_ERR_FAILED;
+               host->cmd->error = -ETIMEDOUT;
+       else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
+                       SDHCI_INT_INDEX))
+               host->cmd->error = -EILSEQ;
 
-       if (host->cmd->error != MMC_ERR_NONE)
+       if (host->cmd->error)
                tasklet_schedule(&host->finish_tasklet);
        else if (intmask & SDHCI_INT_RESPONSE)
                sdhci_finish_command(host);
@@ -974,13 +999,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
        }
 
        if (intmask & SDHCI_INT_DATA_TIMEOUT)
-               host->data->error = MMC_ERR_TIMEOUT;
-       else if (intmask & SDHCI_INT_DATA_CRC)
-               host->data->error = MMC_ERR_BADCRC;
-       else if (intmask & SDHCI_INT_DATA_END_BIT)
-               host->data->error = MMC_ERR_FAILED;
+               host->data->error = -ETIMEDOUT;
+       else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT))
+               host->data->error = -EILSEQ;
 
-       if (host->data->error != MMC_ERR_NONE)
+       if (host->data->error)
                sdhci_finish_data(host);
        else {
                if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
@@ -1015,6 +1038,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
        irqreturn_t result;
        struct sdhci_host* host = dev_id;
        u32 intmask;
+       int cardint = 0;
 
        spin_lock(&host->lock);
 
@@ -1059,6 +1083,11 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
 
        intmask &= ~SDHCI_INT_BUS_POWER;
 
+       if (intmask & SDHCI_INT_CARD_INT)
+               cardint = 1;
+
+       intmask &= ~SDHCI_INT_CARD_INT;
+
        if (intmask) {
                printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
                        mmc_hostname(host->mmc), intmask);
@@ -1073,6 +1102,12 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
 out:
        spin_unlock(&host->lock);
 
+       /*
+        * We have to delay this as it calls back into the driver.
+        */
+       if (cardint)
+               mmc_signal_sdio_irq(host->mmc);
+
        return result;
 }
 
@@ -1258,20 +1293,26 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
 
        caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
 
-       if (debug_nodma)
-               DBG("DMA forced off\n");
-       else if (debug_forcedma) {
-               DBG("DMA forced on\n");
-               host->flags |= SDHCI_USE_DMA;
-       } else if (chip->quirks & SDHCI_QUIRK_FORCE_DMA)
+       if (chip->quirks & SDHCI_QUIRK_FORCE_DMA)
                host->flags |= SDHCI_USE_DMA;
-       else if ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA)
-               DBG("Controller doesn't have DMA interface\n");
        else if (!(caps & SDHCI_CAN_DO_DMA))
                DBG("Controller doesn't have DMA capability\n");
        else
                host->flags |= SDHCI_USE_DMA;
 
+       if ((chip->quirks & SDHCI_QUIRK_BROKEN_DMA) &&
+               (host->flags & SDHCI_USE_DMA)) {
+               DBG("Disabling DMA as it is marked broken");
+               host->flags &= ~SDHCI_USE_DMA;
+       }
+
+       if (((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) &&
+               (host->flags & SDHCI_USE_DMA)) {
+               printk(KERN_WARNING "%s: Will use DMA "
+                       "mode even though HW doesn't fully "
+                       "claim to support it.\n", host->slot_descr);
+       }
+
        if (host->flags & SDHCI_USE_DMA) {
                if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
                        printk(KERN_WARNING "%s: No suitable DMA available. "
@@ -1312,7 +1353,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
        mmc->ops = &sdhci_ops;
        mmc->f_min = host->max_clk / 256;
        mmc->f_max = host->max_clk;
-       mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
+       mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_SDIO_IRQ;
 
        if (caps & SDHCI_CAN_DO_HISPD)
                mmc->caps |= MMC_CAP_SD_HIGHSPEED;
@@ -1565,14 +1606,10 @@ static void __exit sdhci_drv_exit(void)
 module_init(sdhci_drv_init);
 module_exit(sdhci_drv_exit);
 
-module_param(debug_nodma, uint, 0444);
-module_param(debug_forcedma, uint, 0444);
 module_param(debug_quirks, uint, 0444);
 
 MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
 MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver");
 MODULE_LICENSE("GPL");
 
-MODULE_PARM_DESC(debug_nodma, "Forcefully disable DMA transfers. (default 0)");
-MODULE_PARM_DESC(debug_forcedma, "Forcefully enable DMA transfers. (default 0)");
 MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");
index e28987d6d2ebf4f12d9ea180916d811571fc63e2..05195ea900f490e1884a72de5a3d4081e61f38b5 100644 (file)
@@ -81,7 +81,7 @@
 
 #define SDHCI_BLOCK_GAP_CONTROL        0x2A
 
-#define SDHCI_WALK_UP_CONTROL  0x2B
+#define SDHCI_WAKE_UP_CONTROL  0x2B
 
 #define SDHCI_CLOCK_CONTROL    0x2C
 #define  SDHCI_DIVIDER_SHIFT   8
index 8b736e9684477236604e54932f1cf788a90db858..9b904795eb77acd5cb887dbbbf96dec51a742339 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/mmc/host.h>
 #include <linux/highmem.h>
 #include <linux/scatterlist.h>
+#include <linux/log2.h>
 #include <asm/io.h>
 
 #define DRIVER_NAME "tifm_sd"
@@ -404,14 +405,14 @@ static void tifm_sd_check_status(struct tifm_sd *host)
        struct tifm_dev *sock = host->dev;
        struct mmc_command *cmd = host->req->cmd;
 
-       if (cmd->error != MMC_ERR_NONE)
+       if (cmd->error)
                goto finish_request;
 
        if (!(host->cmd_flags & CMD_READY))
                return;
 
        if (cmd->data) {
-               if (cmd->data->error != MMC_ERR_NONE) {
+               if (cmd->data->error) {
                        if ((host->cmd_flags & SCMD_ACTIVE)
                            && !(host->cmd_flags & SCMD_READY))
                                return;
@@ -504,7 +505,7 @@ static void tifm_sd_card_event(struct tifm_dev *sock)
 {
        struct tifm_sd *host;
        unsigned int host_status = 0;
-       int cmd_error = MMC_ERR_NONE;
+       int cmd_error = 0;
        struct mmc_command *cmd = NULL;
        unsigned long flags;
 
@@ -521,15 +522,15 @@ static void tifm_sd_card_event(struct tifm_dev *sock)
                        writel(host_status & TIFM_MMCSD_ERRMASK,
                               sock->addr + SOCK_MMCSD_STATUS);
                        if (host_status & TIFM_MMCSD_CTO)
-                               cmd_error = MMC_ERR_TIMEOUT;
+                               cmd_error = -ETIMEDOUT;
                        else if (host_status & TIFM_MMCSD_CCRC)
-                               cmd_error = MMC_ERR_BADCRC;
+                               cmd_error = -EILSEQ;
 
                        if (cmd->data) {
                                if (host_status & TIFM_MMCSD_DTO)
-                                       cmd->data->error = MMC_ERR_TIMEOUT;
+                                       cmd->data->error = -ETIMEDOUT;
                                else if (host_status & TIFM_MMCSD_DCRC)
-                                       cmd->data->error = MMC_ERR_BADCRC;
+                                       cmd->data->error = -EILSEQ;
                        }
 
                        writel(TIFM_FIFO_INT_SETALL,
@@ -626,14 +627,21 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
        spin_lock_irqsave(&sock->lock, flags);
        if (host->eject) {
-               spin_unlock_irqrestore(&sock->lock, flags);
+               mrq->cmd->error = -ENOMEDIUM;
                goto err_out;
        }
 
        if (host->req) {
                printk(KERN_ERR "%s : unfinished request detected\n",
                       sock->dev.bus_id);
-               spin_unlock_irqrestore(&sock->lock, flags);
+               mrq->cmd->error = -ETIMEDOUT;
+               goto err_out;
+       }
+
+       if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
+               printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n",
+                       sock->dev.bus_id, mrq->data->blksz);
+               mrq->cmd->error = -EINVAL;
                goto err_out;
        }
 
@@ -722,7 +730,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
        return;
 
 err_out:
-       mrq->cmd->error = MMC_ERR_TIMEOUT;
+       spin_unlock_irqrestore(&sock->lock, flags);
        mmc_request_done(mmc, mrq);
 }
 
@@ -1012,9 +1020,9 @@ static void tifm_sd_remove(struct tifm_dev *sock)
                writel(TIFM_FIFO_INT_SETALL,
                       sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
                writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
-               host->req->cmd->error = MMC_ERR_TIMEOUT;
+               host->req->cmd->error = -ENOMEDIUM;
                if (host->req->stop)
-                       host->req->stop->error = MMC_ERR_TIMEOUT;
+                       host->req->stop->error = -ENOMEDIUM;
                tasklet_schedule(&host->finish_tasklet);
        }
        spin_unlock_irqrestore(&sock->lock, flags);
index 9bf2a877113bf0a55cf8bc21904158990c24ca39..80db11c05f2a6e91ab155d9036338448dad2f44f 100644 (file)
@@ -317,7 +317,7 @@ static inline void wbsd_get_short_reply(struct wbsd_host *host,
         * Correct response type?
         */
        if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) {
-               cmd->error = MMC_ERR_INVALID;
+               cmd->error = -EILSEQ;
                return;
        }
 
@@ -337,7 +337,7 @@ static inline void wbsd_get_long_reply(struct wbsd_host *host,
         * Correct response type?
         */
        if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) {
-               cmd->error = MMC_ERR_INVALID;
+               cmd->error = -EILSEQ;
                return;
        }
 
@@ -372,7 +372,7 @@ static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd)
        for (i = 3; i >= 0; i--)
                outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR);
 
-       cmd->error = MMC_ERR_NONE;
+       cmd->error = 0;
 
        /*
         * Wait for the request to complete.
@@ -392,13 +392,13 @@ static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd)
 
                /* Card removed? */
                if (isr & WBSD_INT_CARD)
-                       cmd->error = MMC_ERR_TIMEOUT;
+                       cmd->error = -ENOMEDIUM;
                /* Timeout? */
                else if (isr & WBSD_INT_TIMEOUT)
-                       cmd->error = MMC_ERR_TIMEOUT;
+                       cmd->error = -ETIMEDOUT;
                /* CRC? */
                else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC))
-                       cmd->error = MMC_ERR_BADCRC;
+                       cmd->error = -EILSEQ;
                /* All ok */
                else {
                        if (cmd->flags & MMC_RSP_136)
@@ -585,7 +585,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
                        ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH);
                wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
        } else {
-               data->error = MMC_ERR_INVALID;
+               data->error = -EINVAL;
                return;
        }
 
@@ -607,7 +607,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
                 */
                BUG_ON(size > 0x10000);
                if (size > 0x10000) {
-                       data->error = MMC_ERR_INVALID;
+                       data->error = -EINVAL;
                        return;
                }
 
@@ -669,7 +669,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
                }
        }
 
-       data->error = MMC_ERR_NONE;
+       data->error = 0;
 }
 
 static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
@@ -724,8 +724,8 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
                                "%d bytes left.\n",
                                mmc_hostname(host->mmc), count);
 
-                       if (data->error == MMC_ERR_NONE)
-                               data->error = MMC_ERR_FAILED;
+                       if (!data->error)
+                               data->error = -EIO;
                } else {
                        /*
                         * Transfer data from DMA buffer to
@@ -735,7 +735,7 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
                                wbsd_dma_to_sg(host, data);
                }
 
-               if (data->error != MMC_ERR_NONE) {
+               if (data->error) {
                        if (data->bytes_xfered)
                                data->bytes_xfered -= data->blksz;
                }
@@ -767,11 +767,10 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
        host->mrq = mrq;
 
        /*
-        * If there is no card in the slot then
-        * timeout immediatly.
+        * Check that there is actually a card in the slot.
         */
        if (!(host->flags & WBSD_FCARD_PRESENT)) {
-               cmd->error = MMC_ERR_TIMEOUT;
+               cmd->error = -ENOMEDIUM;
                goto done;
        }
 
@@ -807,7 +806,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
                                "supported by this controller.\n",
                                mmc_hostname(host->mmc), cmd->opcode);
 #endif
-                       cmd->error = MMC_ERR_INVALID;
+                       cmd->error = -EINVAL;
 
                        goto done;
                };
@@ -819,7 +818,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
        if (cmd->data) {
                wbsd_prepare_data(host, cmd->data);
 
-               if (cmd->data->error != MMC_ERR_NONE)
+               if (cmd->data->error)
                        goto done;
        }
 
@@ -830,7 +829,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
         * will be finished after the data has
         * transfered.
         */
-       if (cmd->data && (cmd->error == MMC_ERR_NONE)) {
+       if (cmd->data && !cmd->error) {
                /*
                 * Dirty fix for hardware bug.
                 */
@@ -1033,7 +1032,7 @@ static void wbsd_tasklet_card(unsigned long param)
                                mmc_hostname(host->mmc));
                        wbsd_reset(host);
 
-                       host->mrq->cmd->error = MMC_ERR_FAILED;
+                       host->mrq->cmd->error = -ENOMEDIUM;
                        tasklet_schedule(&host->finish_tasklet);
                }
 
@@ -1097,7 +1096,7 @@ static void wbsd_tasklet_crc(unsigned long param)
 
        DBGF("CRC error\n");
 
-       data->error = MMC_ERR_BADCRC;
+       data->error = -EILSEQ;
 
        tasklet_schedule(&host->finish_tasklet);
 
@@ -1121,7 +1120,7 @@ static void wbsd_tasklet_timeout(unsigned long param)
 
        DBGF("Timeout\n");
 
-       data->error = MMC_ERR_TIMEOUT;
+       data->error = -ETIMEDOUT;
 
        tasklet_schedule(&host->finish_tasklet);
 
@@ -1220,7 +1219,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev)
        mmc->f_min = 375000;
        mmc->f_max = 24000000;
        mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
-       mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
+       mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE;
 
        spin_lock_init(&host->lock);
 
index 84c726934ace3f029a55b1d150c0291383f3e1fd..4712f354dcca1e40dcfaa91eb5cb62a9572a70d9 100644 (file)
@@ -3,8 +3,11 @@
 
 #include <linux/mmc/host.h>
 
+struct device;
+
 struct imxmmc_platform_data {
-       int (*card_present)(void);
+       int (*card_present)(struct device *);
+       int (*get_ro)(struct device *);
 };
 
 extern void imx_set_mmc_info(struct imxmmc_platform_data *info);
index badf702fcff417c73d137ed57eba6dcd77b79be7..0d508ac17d6452b5a35429fd38704c8de2c13de4 100644 (file)
@@ -55,7 +55,28 @@ struct sd_switch_caps {
        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 @@ struct mmc_card {
        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_card {
        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)
index 63a80ea611248fe2551243548fe3ebdd883851de..d0c3abed74c2cbb5cbc71c6bd563bd04dad03fdc 100644 (file)
@@ -25,14 +25,20 @@ struct mmc_command {
 #define MMC_RSP_CRC    (1 << 2)                /* expect valid crc */
 #define MMC_RSP_BUSY   (1 << 3)                /* card may send busy */
 #define MMC_RSP_OPCODE (1 << 4)                /* response contains opcode */
-#define MMC_CMD_MASK   (3 << 5)                /* command type */
+
+#define MMC_CMD_MASK   (3 << 5)                /* non-SPI command type */
 #define MMC_CMD_AC     (0 << 5)
 #define MMC_CMD_ADTC   (1 << 5)
 #define MMC_CMD_BC     (2 << 5)
 #define MMC_CMD_BCR    (3 << 5)
 
+#define MMC_RSP_SPI_S1 (1 << 7)                /* one status byte */
+#define MMC_RSP_SPI_S2 (1 << 8)                /* second 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,11 +47,29 @@ struct mmc_command {
 #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.
  */
@@ -54,12 +78,19 @@ struct mmc_command {
        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 @@ struct mmc_data {
 #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_cmd(struct mmc_host *, struct mmc_command *, int);
 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
index b1350dfd3e9108751b9fadd15dd8f1e61b77da2d..125eee1407ff1c5ee34d2cce2984d0a6fc14c5f0 100644 (file)
@@ -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 @@ struct mmc_host_ops {
        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 @@ struct mmc_host {
 
 #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,6 +110,14 @@ struct mmc_host {
        struct mmc_ios          ios;            /* current io bus settings */
        u32                     ocr;            /* the current OCR setting */
 
+       /* group bitfields together to minimize padding */
+       unsigned int            use_spi_crc:1;
+       unsigned int            claimed:1;      /* host exclusively claimed */
+       unsigned int            bus_dead:1;     /* bus has been released */
+#ifdef CONFIG_MMC_DEBUG
+       unsigned int            removed:1;      /* host is being removed */
+#endif
+
        unsigned int            mode;           /* current card mode of host */
 #define MMC_MODE_MMC           0
 #define MMC_MODE_SD            1
@@ -113,16 +125,19 @@ struct mmc_host {
        struct mmc_card         *card;          /* device attached to this host */
 
        wait_queue_head_t       wq;
-       unsigned int            claimed:1;      /* host exclusively claimed */
 
        struct delayed_work     detect;
-#ifdef CONFIG_MMC_DEBUG
-       unsigned int            removed:1;      /* host is being removed */
-#endif
 
        const struct mmc_bus_ops *bus_ops;      /* current bus driver */
        unsigned int            bus_refs;       /* reference counter */
-       unsigned int            bus_dead:1;     /* bus has been released */
+
+       unsigned 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 +152,8 @@ static inline void *mmc_priv(struct mmc_host *host)
        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 +164,11 @@ extern int mmc_resume_host(struct mmc_host *);
 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
 
index e3ed9b95040ee51a4c5fac786b66736df2150b63..4236fbf0b6fbb831f4f617352e43ba3716d5188e 100644 (file)
@@ -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  */
 #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  */
  */
 
 /*
-  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
 #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 @@ struct _mmc_csd {
  */
 #define CCC_BASIC              (1<<0)  /* (0) Basic protocol functions */
                                        /* (CMD0,1,2,3,4,7,9,10,12,13,15) */
+                                       /* (and for SPI, CMD58,59) */
 #define CCC_STREAM_READ                (1<<1)  /* (1) Stream read commands */
                                        /* (CMD11) */
 #define CCC_BLOCK_READ         (1<<2)  /* (2) Block read commands */
@@ -227,6 +253,7 @@ struct _mmc_csd {
 #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 */
 
 /*
diff --git a/include/linux/mmc/sdio.h b/include/linux/mmc/sdio.h
new file mode 100644 (file)
index 0000000..47ba464
--- /dev/null
@@ -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
+
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
new file mode 100644 (file)
index 0000000..b050f4d
--- /dev/null
@@ -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
+
diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
new file mode 100644 (file)
index 0000000..09306d4
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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 */
+
+
+#endif
index 4dc5fa8be781760aa307cff8d01488f270c47b9a..e47e5951058be53593f68e9c8d955418a0e82ccf 100644 (file)
@@ -340,4 +340,15 @@ struct parisc_device_id {
 #define PA_HVERSION_ANY_ID     0xffff
 #define PA_SVERSION_ANY_ID     0xffffffff
 
+/* 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 */
+};
+
 #endif /* LINUX_MOD_DEVICETABLE_H */
index 55f307ffbf96ded1724ebc011fea173dc7cee1e6..c8636bb3e3a76fd9146ee5ce1c0fe17cd5a03a51 100644 (file)
 #define PCI_DEVICE_ID_RICOH_RL5C476    0x0476
 #define PCI_DEVICE_ID_RICOH_RL5C478    0x0478
 #define PCI_DEVICE_ID_RICOH_R5C822     0x0822
+#define PCI_DEVICE_ID_RICOH_R5C832     0x0832
+#define PCI_DEVICE_ID_RICOH_R5C843     0x0843
 
 #define PCI_VENDOR_ID_DLINK            0x1186
 #define PCI_DEVICE_ID_DLINK_DGE510T    0x4c00
diff --git a/include/linux/spi/mmc_spi.h b/include/linux/spi/mmc_spi.h
new file mode 100644 (file)
index 0000000..e9bbe3e
--- /dev/null
@@ -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 */
index 8a09021d8c59ab7da6474b90dfbec9f46e3582f6..1e5d4d6931955bbf9432b3309c13b0ef0b8192c5 100644 (file)
@@ -484,6 +484,22 @@ static int do_parisc_entry(const char *filename, struct parisc_device_id *id,
        return 1;
 }
 
+/* Looks like: sdio:cNvNdN. */
+static int do_sdio_entry(const char *filename,
+                       struct sdio_device_id *id, char *alias)
+{
+       id->class = TO_NATIVE(id->class);
+       id->vendor = TO_NATIVE(id->vendor);
+       id->device = TO_NATIVE(id->device);
+
+       strcpy(alias, "sdio:");
+       ADD(alias, "c", id->class != (__u8)SDIO_ANY_ID, id->class);
+       ADD(alias, "v", id->vendor != (__u16)SDIO_ANY_ID, id->vendor);
+       ADD(alias, "d", id->device != (__u16)SDIO_ANY_ID, id->device);
+
+       return 1;
+}
+
 /* Ignore any prefix, eg. v850 prepends _ */
 static inline int sym_is(const char *symbol, const char *name)
 {
@@ -599,6 +615,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
                do_table(symval, sym->st_size,
                         sizeof(struct parisc_device_id), "parisc",
                         do_parisc_entry, mod);
+       else if (sym_is(symname, "__mod_sdio_device_table"))
+               do_table(symval, sym->st_size,
+                        sizeof(struct sdio_device_id), "sdio",
+                        do_sdio_entry, mod);
 }
 
 /* Now add out buffered information to the generated C source */