linux-2.6: Add a driver to run an MMC or SD card over a GPIO based SPI interface.
authormb <mb@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Fri, 21 Mar 2008 16:52:27 +0000 (16:52 +0000)
committermb <mb@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Fri, 21 Mar 2008 16:52:27 +0000 (16:52 +0000)
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@10637 3c298f89-4303-0410-b956-a3cf2f4a3e73

package/kernel/modules/other.mk
package/mmc_over_gpio/Makefile [new file with mode: 0644]
package/mmc_over_gpio/src/Makefile [new file with mode: 0644]
package/mmc_over_gpio/src/linux/spi/spi_gpio.h [new file with mode: 0644]
package/mmc_over_gpio/src/mmc_over_spigpio.c [new file with mode: 0644]
package/mmc_over_gpio/src/spi_gpio.c [new file with mode: 0644]
target/linux/brcm47xx/patches-2.6.23/700-ssb-gigabit-ethernet-driver.patch
target/linux/generic-2.6/patches-2.6.23/300-add-mmc-spi-driver.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.23/930-ssb_fixes.patch

index f430c28..a3ce6e9 100644 (file)
@@ -23,6 +23,21 @@ endef
 $(eval $(call KernelPackage,crc-itu-t))
 
 
+define KernelPackage/crc7
+  SUBMENU:=$(OTHER_MENU)
+  TITLE:=CRC7 support
+  KCONFIG:=CONFIG_CRC7
+  FILES:=$(LINUX_DIR)/lib/crc7.$(LINUX_KMOD_SUFFIX)
+  AUTOLOAD:=$(call AutoLoad,20,crc7)
+endef
+
+define KernelPackage/crc7/description
+ Kernel module for CRC7 support
+endef
+
+$(eval $(call KernelPackage,crc7))
+
+
 define KernelPackage/eeprom-93cx6
   SUBMENU:=$(OTHER_MENU)
   TITLE:=EEPROM 93CX6 support
@@ -211,16 +226,20 @@ $(eval $(call KernelPackage,bluetooth))
 define KernelPackage/mmc
   SUBMENU:=$(OTHER_MENU)
   TITLE:=MMC/SD Card Support
-  DEPENDS:=@TARGET_at91
+  DEPENDS:=@LINUX_2_6
   KCONFIG:= \
        CONFIG_MMC \
        CONFIG_MMC_BLOCK \
-       CONFIG_MMC_AT91
+       CONFIG_MMC_DEBUG=n \
+       CONFIG_MMC_UNSAFE_RESUME=n \
+       CONFIG_MMC_BLOCK_BOUNCE=y \
+       CONFIG_MMC_SDHCI=n \
+       CONFIG_MMC_TIFM_SD=n \
+       CONFIG_SDIO_UART=n
   FILES:= \
-       $(LINUX_DIR)/drivers/mmc/mmc_core.$(LINUX_KMOD_SUFFIX) \
-       $(LINUX_DIR)/drivers/mmc/mmc_block.$(LINUX_KMOD_SUFFIX) \
-       $(LINUX_DIR)/drivers/mmc/at91_mci.$(LINUX_KMOD_SUFFIX)
-  AUTOLOAD:=$(call AutoLoad,90,mmc_core mmc_block at91_mci)
+       $(LINUX_DIR)/drivers/mmc/core/mmc_core.$(LINUX_KMOD_SUFFIX) \
+       $(LINUX_DIR)/drivers/mmc/card/mmc_block.$(LINUX_KMOD_SUFFIX)
+  AUTOLOAD:=$(call AutoLoad,90,mmc_core mmc_block)
 endef
 
 define KernelPackage/mmc/description
@@ -230,6 +249,22 @@ endef
 $(eval $(call KernelPackage,mmc))
 
 
+define KernelPackage/mmc-at91
+  SUBMENU:=$(OTHER_MENU)
+  TITLE:=MMC/SD Card Support on AT91
+  DEPENDS:=@TARGET_at91 +kmod-mmc
+  KCONFIG:=CONFIG_MMC_AT91
+  FILES:=$(LINUX_DIR)/drivers/mmc/at91_mci.$(LINUX_KMOD_SUFFIX)
+  AUTOLOAD:=$(call AutoLoad,90,at91_mci)
+endef
+
+define KernelPackage/mmc-at91/description
+ Kernel support for MMC/SD cards on the AT91 target
+endef
+
+$(eval $(call KernelPackage,mmc-at91))
+
+
 # XXX: added a workaround for watchdog path changes
 ifeq ($(KERNEL),2.4)
   WATCHDOG_DIR=char
@@ -460,3 +495,50 @@ define KernelPackage/input-evdev/description
 endef
 
 $(eval $(call KernelPackage,input-evdev))
+
+
+define KernelPackage/mmc-spi
+  SUBMENU:=$(OTHER_MENU)
+  TITLE:=MMC/SD over SPI Support
+  DEPENDS:=@LINUX_2_6
+  KCONFIG:=CONFIG_MMC_SPI
+  FILES:=$(LINUX_DIR)/drivers/mmc/host/mmc_spi.$(LINUX_KMOD_SUFFIX)
+  AUTOLOAD:=$(call AutoLoad,90,mmc_spi)
+endef
+
+define KernelPackage/mmc-spi/description
+ Kernel support for MMC/SD over SPI
+endef
+
+$(eval $(call KernelPackage,mmc-spi))
+
+define KernelPackage/spi
+  SUBMENU:=$(OTHER_MENU)
+  DEPENDS:=@LINUX_2_6 +kmod-crc-itu-t +kmod-crc7
+  TITLE:=Serial Peripheral Interface
+  KCONFIG:=CONFIG_SPI=y \
+       CONFIG_MTD_DATAFLASH=n \
+       CONFIG_MTD_M25P80=n \
+       CONFIG_SPI_AT25=n \
+       CONFIG_SPI_SPIDEV=n \
+       CONFIG_SPI_TLE62X0=n
+endef
+
+define KernelPackage/spi/description
+ This package contains the Serial Peripheral Interface driver
+endef
+
+$(eval $(call KernelPackage,spi))
+
+define KernelPackage/spi-bitbang
+  SUBMENU:=$(OTHER_MENU)
+  DEPENDS:=@LINUX_2_6
+  TITLE:=Serial Peripheral Interface bitbanging
+  KCONFIG:=CONFIG_SPI_BITBANG=y
+endef
+
+define KernelPackage/spi-bitbang/description
+ This package contains the Serial Peripheral Interface bitbanging library
+endef
+
+$(eval $(call KernelPackage,spi-bitbang))
diff --git a/package/mmc_over_gpio/Makefile b/package/mmc_over_gpio/Makefile
new file mode 100644 (file)
index 0000000..9076525
--- /dev/null
@@ -0,0 +1,47 @@
+# 
+# Copyright (C) 2008 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+# $Id: Makefile 10138 2008-01-06 19:28:26Z nbd $
+
+#XXX This package will go away once the stuff is merged into the kernel.
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+PKG_NAME:=mmc-over-gpio
+PKG_RELEASE:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+
+define KernelPackage/mmc-over-gpio
+  SUBMENU:=Other modules
+  DEPENDS:=@LINUX_2_6 +kmod-spi +kmod-spi-bitbang +kmod-mmc +kmod-mmc-spi
+  TITLE:=MMC/SD card over GPIO support
+  FILES:=$(PKG_BUILD_DIR)/spi_gpio.$(LINUX_KMOD_SUFFIX) \
+         $(PKG_BUILD_DIR)/mmc_over_spigpio.$(LINUX_KMOD_SUFFIX)
+  AUTOLOAD:=$(call AutoLoad,90,spi_gpio mmc_over_spigpio)
+endef
+
+define KernelPackage/mmc-over-gpio/description
+ Support for driving an MMC/SD card over GPIO pins via SPI.
+endef
+
+define Build/Prepare
+       mkdir -p $(PKG_BUILD_DIR)
+       $(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+define Build/Compile
+       $(MAKE) -C "$(LINUX_DIR)" \
+               CROSS_COMPILE="$(TARGET_CROSS)" \
+               ARCH="$(LINUX_KARCH)" \
+               SUBDIRS="$(PKG_BUILD_DIR)" \
+               EXTRA_CFLAGS="$(BUILDFLAGS)" \
+               modules
+endef
+
+$(eval $(call KernelPackage,mmc-over-gpio))
diff --git a/package/mmc_over_gpio/src/Makefile b/package/mmc_over_gpio/src/Makefile
new file mode 100644 (file)
index 0000000..d2258bb
--- /dev/null
@@ -0,0 +1,2 @@
+obj-m += spi_gpio.o
+obj-m += mmc_over_spigpio.o
diff --git a/package/mmc_over_gpio/src/linux/spi/spi_gpio.h b/package/mmc_over_gpio/src/linux/spi/spi_gpio.h
new file mode 100644 (file)
index 0000000..8e7d4b1
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * spi_gpio interface to platform code
+ *
+ * Copyright (c) 2008 Piotr Skamruk
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _LINUX_SPI_SPI_GPIO
+#define _LINUX_SPI_SPI_GPIO
+
+#include <linux/types.h>
+#include <linux/spi/spi.h>
+
+
+/** struct spi_gpio_platform_data - Data definitions for a SPI-GPIO device.
+ * This structure holds information about a GPIO-based SPI device.
+ *
+ * @pin_clk: The GPIO pin number of the CLOCK pin.
+ *
+ * @pin_miso: The GPIO pin number of the MISO pin.
+ *
+ * @pin_mosi: The GPIO pin number of the MOSI pin.
+ *
+ * @pin_cs: The GPIO pin number of the CHIPSELECT pin.
+ *
+ * @cs_activelow: If true, the chip is selected when the CS line is low.
+ *
+ * @no_spi_delay: If true, no delay is done in the lowlevel bitbanging.
+ *                Note that doing no delay is not standards compliant,
+ *                but it might be needed to speed up transfers on some
+ *                slow embedded machines.
+ *
+ * @boardinfo_setup: This callback is called after the
+ *                   SPI master device was registered, but before the
+ *                   device is registered.
+ * @boardinfo_setup_data: Data argument passed to boardinfo_setup().
+ */
+struct spi_gpio_platform_data {
+       unsigned int pin_clk;
+       unsigned int pin_miso;
+       unsigned int pin_mosi;
+       unsigned int pin_cs;
+       bool cs_activelow;
+       bool no_spi_delay;
+       int (*boardinfo_setup)(struct spi_board_info *bi,
+                              struct spi_master *master,
+                              void *data);
+       void *boardinfo_setup_data;
+};
+
+#endif /* _LINUX_SPI_SPI_GPIO */
diff --git a/package/mmc_over_gpio/src/mmc_over_spigpio.c b/package/mmc_over_gpio/src/mmc_over_spigpio.c
new file mode 100644 (file)
index 0000000..36dbd0f
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Driver for driving an MMC card over a bitbanging GPIO SPI bus.
+ *
+ * Copyright 2008 Michael Buesch <mb@bu3sch.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include "linux/spi/spi_gpio.h" //XXX
+
+
+/* This is the maximum speed in Hz */
+#define GPIOMMC_MAXSPEED       5000000 /* Hz */
+
+
+#define DRIVER_NAME            "spi-gpio-mmc"
+#define PFX                    DRIVER_NAME ": "
+
+
+#define GPIOMMC_MAX_NAMELEN            15
+#define GPIOMMC_MAX_NAMELEN_STR                __stringify(GPIOMMC_MAX_NAMELEN)
+
+struct gpiommc_pins {
+       unsigned int gpio_di;   /* Card DI pin */
+       unsigned int gpio_do;   /* Card DO pin */
+       unsigned int gpio_clk;  /* Card CLK pin */
+       unsigned int gpio_cs;   /* Card CS pin */
+};
+
+struct gpiommc_device {
+       char name[GPIOMMC_MAX_NAMELEN + 1];
+       struct platform_device *pdev;
+       struct platform_device *spi_pdev;
+       struct gpiommc_pins pins;
+       u8 mode; /* SPI_MODE_X */
+       struct spi_board_info boardinfo;
+
+       struct list_head list;
+};
+
+
+static LIST_HEAD(gpiommc_devices_list);
+static DEFINE_MUTEX(gpiommc_mutex);
+
+
+MODULE_DESCRIPTION("SPI-GPIO based MMC driver");
+MODULE_AUTHOR("Michael Buesch");
+MODULE_LICENSE("GPL");
+
+
+static int gpiommc_boardinfo_setup(struct spi_board_info *bi,
+                                  struct spi_master *master,
+                                  void *data)
+{
+       struct gpiommc_device *d = data;
+
+       /* Bind the SPI master to the MMC-SPI host driver. */
+       strlcpy(bi->modalias, "mmc_spi", sizeof(bi->modalias));
+
+       bi->max_speed_hz = GPIOMMC_MAXSPEED;
+       bi->bus_num = master->bus_num;
+       bi->mode = d->mode;
+
+       return 0;
+}
+
+static int gpiommc_probe(struct platform_device *pdev)
+{
+       static int instance;
+       struct gpiommc_device *d = platform_get_drvdata(pdev);
+       struct spi_gpio_platform_data pdata;
+       int err = -ENOMEM;
+
+       d->spi_pdev = platform_device_alloc("spi-gpio", instance++);
+       if (!d->spi_pdev)
+               goto out;
+
+       memset(&pdata, 0, sizeof(pdata));
+       pdata.pin_clk = d->pins.gpio_clk;
+       pdata.pin_miso = d->pins.gpio_do;
+       pdata.pin_mosi = d->pins.gpio_di;
+       pdata.pin_cs = d->pins.gpio_cs;
+       pdata.cs_activelow = 1;
+       pdata.no_spi_delay = 1;
+       pdata.boardinfo_setup = gpiommc_boardinfo_setup;
+       pdata.boardinfo_setup_data = d;
+
+       err = platform_device_add_data(d->spi_pdev, &pdata, sizeof(pdata));
+       if (err)
+               goto err_free_pdev;
+       err = platform_device_register(d->spi_pdev);
+       if (err)
+               goto err_free_pdata;
+
+       printk(KERN_INFO PFX "MMC-Card \"%s\" "
+              "attached to GPIO pins %u,%u,%u,%u\n",
+              d->name, d->pins.gpio_di, d->pins.gpio_do,
+              d->pins.gpio_clk, d->pins.gpio_cs);
+out:
+       return err;
+
+err_free_pdata:
+       kfree(d->spi_pdev->dev.platform_data);
+       d->spi_pdev->dev.platform_data = NULL;
+err_free_pdev:
+       platform_device_put(d->spi_pdev);
+       return err;
+}
+
+static int gpiommc_remove(struct platform_device *pdev)
+{
+       struct gpiommc_device *d = platform_get_drvdata(pdev);
+
+       platform_device_unregister(d->spi_pdev);
+       printk(KERN_INFO PFX "MMC-Card \"%s\" removed\n", d->name);
+
+       return 0;
+}
+
+static void gpiommc_free(struct gpiommc_device *d)
+{
+       kfree(d);
+}
+
+static struct gpiommc_device * gpiommc_alloc(struct platform_device *pdev,
+                                            const char *name,
+                                            const struct gpiommc_pins *pins,
+                                            u8 mode)
+{
+       struct gpiommc_device *d;
+
+       d = kmalloc(sizeof(*d), GFP_KERNEL);
+       if (!d)
+               return NULL;
+
+       strcpy(d->name, name);
+       memcpy(&d->pins, pins, sizeof(d->pins));
+       d->mode = mode;
+       INIT_LIST_HEAD(&d->list);
+
+       return d;
+}
+
+/* List must be locked. */
+static struct gpiommc_device * gpiommc_find_device(const char *name)
+{
+       struct gpiommc_device *d;
+
+       list_for_each_entry(d, &gpiommc_devices_list, list) {
+               if (strcmp(d->name, name) == 0)
+                       return d;
+       }
+
+       return NULL;
+}
+
+static void gpiommc_do_destroy_device(struct gpiommc_device *d)
+{
+       list_del(&d->list);
+       platform_device_unregister(d->pdev);
+       gpiommc_free(d);
+}
+
+static int gpiommc_destroy_device(const char *name)
+{
+       struct gpiommc_device *d;
+       int err = -ENODEV;
+
+       mutex_lock(&gpiommc_mutex);
+       d = gpiommc_find_device(name);
+       if (!d)
+               goto out_unlock;
+       gpiommc_do_destroy_device(d);
+       err = 0;
+out_unlock:
+       mutex_unlock(&gpiommc_mutex);
+
+       return err;
+}
+
+static int gpiommc_create_device(const char *name,
+                                const struct gpiommc_pins *pins,
+                                u8 mode)
+{
+       static int instance;
+       struct platform_device *pdev;
+       struct gpiommc_device *d;
+       int err;
+
+       mutex_lock(&gpiommc_mutex);
+       err = -EEXIST;
+       if (gpiommc_find_device(name))
+               goto out_unlock;
+       err = -ENOMEM;
+       pdev = platform_device_alloc(DRIVER_NAME, instance++);
+       if (!pdev)
+               goto out_unlock;
+       d = gpiommc_alloc(pdev, name, pins, mode);
+       if (!d)
+               goto err_free_pdev;
+       platform_set_drvdata(pdev, d);
+       d->pdev = pdev;
+       err = platform_device_register(pdev);
+       if (err)
+               goto err_free_mdev;
+       list_add(&d->list, &gpiommc_devices_list);
+
+       err = 0;
+out_unlock:
+       mutex_unlock(&gpiommc_mutex);
+
+       return err;
+
+err_free_mdev:
+       gpiommc_free(d);
+err_free_pdev:
+       platform_device_put(pdev);
+       goto out_unlock;
+}
+
+static ssize_t gpiommc_add_show(struct device_driver *drv,
+                               char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "NAME  DI_pin,DO_pin,CLK_pin,CS_pin  [MODE]\n");
+}
+
+static ssize_t gpiommc_add_store(struct device_driver *drv,
+                                const char *buf, size_t count)
+{
+       int res, err;
+       char name[GPIOMMC_MAX_NAMELEN + 1];
+       struct gpiommc_pins pins;
+       unsigned int mode;
+
+       res = sscanf(buf, "%" GPIOMMC_MAX_NAMELEN_STR "s %u,%u,%u,%u %u",
+                    name, &pins.gpio_di, &pins.gpio_do,
+                    &pins.gpio_clk, &pins.gpio_cs, &mode);
+       if (res == 5)
+               mode = 0;
+       else if (res != 6)
+               return -EINVAL;
+       switch (mode) {
+       case 0:
+               mode = SPI_MODE_0;
+               break;
+       case 1:
+               mode = SPI_MODE_1;
+               break;
+       case 2:
+               mode = SPI_MODE_2;
+               break;
+       case 3:
+               mode = SPI_MODE_3;
+               break;
+       default:
+               return -EINVAL;
+       }
+       err = gpiommc_create_device(name, &pins, mode);
+
+       return err ? err : count;
+}
+
+static ssize_t gpiommc_remove_show(struct device_driver *drv,
+                                  char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "write device-name to remove the device\n");
+}
+
+static ssize_t gpiommc_remove_store(struct device_driver *drv,
+                                   const char *buf, size_t count)
+{
+       int err;
+
+       err = gpiommc_destroy_device(buf);
+
+       return err ? err : count;
+}
+
+static DRIVER_ATTR(add, 0600,
+                  gpiommc_add_show, gpiommc_add_store);
+static DRIVER_ATTR(remove, 0600,
+                  gpiommc_remove_show, gpiommc_remove_store);
+
+static struct platform_driver gpiommc_plat_driver = {
+       .probe  = gpiommc_probe,
+       .remove = gpiommc_remove,
+       .driver = {
+               .name   = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init gpiommc_modinit(void)
+{
+       int err;
+
+       err = platform_driver_register(&gpiommc_plat_driver);
+       if (err)
+               return err;
+       err = driver_create_file(&gpiommc_plat_driver.driver,
+                                &driver_attr_add);
+       if (err)
+               goto err_drv_unreg;
+       err = driver_create_file(&gpiommc_plat_driver.driver,
+                                &driver_attr_remove);
+       if (err)
+               goto err_remove_add;
+
+       return 0;
+
+err_remove_add:
+       driver_remove_file(&gpiommc_plat_driver.driver,
+                          &driver_attr_add);
+err_drv_unreg:
+       platform_driver_unregister(&gpiommc_plat_driver);
+       return err;
+}
+module_init(gpiommc_modinit);
+
+static void __exit gpiommc_modexit(void)
+{
+       struct gpiommc_device *d, *tmp;
+
+       driver_remove_file(&gpiommc_plat_driver.driver,
+                          &driver_attr_remove);
+       driver_remove_file(&gpiommc_plat_driver.driver,
+                          &driver_attr_add);
+
+       mutex_lock(&gpiommc_mutex);
+       list_for_each_entry_safe(d, tmp, &gpiommc_devices_list, list)
+               gpiommc_do_destroy_device(d);
+       mutex_unlock(&gpiommc_mutex);
+
+       platform_driver_unregister(&gpiommc_plat_driver);
+}
+module_exit(gpiommc_modexit);
diff --git a/package/mmc_over_gpio/src/spi_gpio.c b/package/mmc_over_gpio/src/spi_gpio.c
new file mode 100644 (file)
index 0000000..31048ac
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Bitbanging SPI bus driver using GPIO API
+ *
+ * Copyright (c) 2008 Piotr Skamruk
+ * Copyright (c) 2008 Michael Buesch
+ *
+ * based on spi_s3c2410_gpio.c
+ *   Copyright (c) 2006 Ben Dooks
+ *   Copyright (c) 2006 Simtec Electronics
+ * and on i2c-gpio.c
+ *   Copyright (C) 2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include "linux/spi/spi_gpio.h" //XXX
+#include <asm/gpio.h>
+
+
+struct spi_gpio {
+       struct spi_bitbang bitbang;
+       struct spi_gpio_platform_data *info;
+       struct platform_device *pdev;
+       struct spi_board_info bi;
+};
+
+
+static inline struct spi_gpio *spidev_to_sg(struct spi_device *dev)
+{
+       return dev->controller_data;
+}
+
+static inline void setsck(struct spi_device *dev, int val)
+{
+       struct spi_gpio *sp = spidev_to_sg(dev);
+       gpio_set_value(sp->info->pin_clk, val ? 1 : 0);
+}
+
+static inline void setmosi(struct spi_device *dev, int val )
+{
+       struct spi_gpio *sp = spidev_to_sg(dev);
+       gpio_set_value(sp->info->pin_mosi, val ? 1 : 0);
+}
+
+static inline u32 getmiso(struct spi_device *dev)
+{
+       struct spi_gpio *sp = spidev_to_sg(dev);
+       return gpio_get_value(sp->info->pin_miso) ? 1 : 0;
+}
+
+static inline void do_spidelay(struct spi_device *dev, unsigned nsecs)
+{
+       struct spi_gpio *sp = spidev_to_sg(dev);
+
+       if (!sp->info->no_spi_delay)
+               ndelay(nsecs);
+}
+
+#define spidelay(nsecs) do {                                   \
+       /* Steal the spi_device pointer from our caller.        \
+        * The bitbang-API should probably get fixed here... */ \
+       do_spidelay(spi, nsecs);                                \
+  } while (0)
+
+#define EXPAND_BITBANG_TXRX
+#include <linux/spi/spi_bitbang.h>
+
+static u32 spi_gpio_txrx_mode0(struct spi_device *spi,
+                              unsigned nsecs, u32 word, u8 bits)
+{
+        return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
+}
+
+static u32 spi_gpio_txrx_mode1(struct spi_device *spi,
+                              unsigned nsecs, u32 word, u8 bits)
+{
+        return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits);
+}
+
+static u32 spi_gpio_txrx_mode2(struct spi_device *spi,
+                              unsigned nsecs, u32 word, u8 bits)
+{
+        return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits);
+}
+
+static u32 spi_gpio_txrx_mode3(struct spi_device *spi,
+                              unsigned nsecs, u32 word, u8 bits)
+{
+        return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits);
+}
+
+static void spi_gpio_chipselect(struct spi_device *dev, int on)
+{
+       struct spi_gpio *sp = spidev_to_sg(dev);
+
+       if (sp->info->cs_activelow)
+               on = !on;
+       gpio_set_value(sp->info->pin_cs, on ? 1 : 0);
+}
+
+static int spi_gpio_probe(struct platform_device *pdev)
+{
+       struct spi_master *master;
+       struct spi_gpio_platform_data *pdata;
+       struct spi_gpio *sp;
+       struct spi_device *spidev;
+       int err;
+
+       pdata = pdev->dev.platform_data;
+       if (!pdata)
+               return -ENXIO;
+
+       err = -ENOMEM;
+       master = spi_alloc_master(&pdev->dev, sizeof(struct spi_gpio));
+       if (!master)
+               goto err_alloc_master;
+
+       sp = spi_master_get_devdata(master);
+       platform_set_drvdata(pdev, sp);
+       sp->info = pdata;
+
+       err = gpio_request(pdata->pin_clk, "spi_clock");
+       if (err)
+               goto err_request_clk;
+       err = gpio_request(pdata->pin_mosi, "spi_mosi");
+       if (err)
+               goto err_request_mosi;
+       err = gpio_request(pdata->pin_miso, "spi_miso");
+       if (err)
+               goto err_request_miso;
+       err = gpio_request(pdata->pin_cs, "spi_cs");
+       if (err)
+               goto err_request_cs;
+
+       sp->bitbang.master = spi_master_get(master);
+       sp->bitbang.master->bus_num = -1;
+       sp->bitbang.master->num_chipselect = 1;
+       sp->bitbang.chipselect = spi_gpio_chipselect;
+       sp->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_mode0;
+       sp->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_mode1;
+       sp->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_mode2;
+       sp->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_mode3;
+
+       gpio_direction_output(pdata->pin_clk, 0);
+       gpio_direction_output(pdata->pin_mosi, 0);
+       gpio_direction_output(pdata->pin_cs,
+                             pdata->cs_activelow ? 1 : 0);
+       gpio_direction_input(pdata->pin_miso);
+
+       err = spi_bitbang_start(&sp->bitbang);
+       if (err)
+               goto err_no_bitbang;
+       err = pdata->boardinfo_setup(&sp->bi, master,
+                                    pdata->boardinfo_setup_data);
+       if (err)
+               goto err_bi_setup;
+       sp->bi.controller_data = sp;
+       spidev = spi_new_device(master, &sp->bi);
+       if (!spidev)
+               goto err_new_dev;
+
+       return 0;
+
+err_new_dev:
+err_bi_setup:
+       spi_bitbang_stop(&sp->bitbang);
+err_no_bitbang:
+       spi_master_put(sp->bitbang.master);
+       gpio_free(pdata->pin_cs);
+err_request_cs:
+       gpio_free(pdata->pin_miso);
+err_request_miso:
+       gpio_free(pdata->pin_mosi);
+err_request_mosi:
+       gpio_free(pdata->pin_clk);
+err_request_clk:
+       kfree(master);
+
+err_alloc_master:
+       return err;
+}
+
+static int __devexit spi_gpio_remove(struct platform_device *pdev)
+{
+       struct spi_gpio *sp;
+       struct spi_gpio_platform_data *pdata;
+
+       pdata = pdev->dev.platform_data;
+       sp = platform_get_drvdata(pdev);
+
+       gpio_free(pdata->pin_clk);
+       gpio_free(pdata->pin_mosi);
+       gpio_free(pdata->pin_miso);
+       gpio_free(pdata->pin_cs);
+       spi_bitbang_stop(&sp->bitbang);
+       spi_master_put(sp->bitbang.master);
+
+       return 0;
+}
+
+static struct platform_driver spi_gpio_driver = {
+       .driver         = {
+               .name   = "spi-gpio",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = spi_gpio_probe,
+       .remove         = __devexit_p(spi_gpio_remove),
+};
+
+static int __init spi_gpio_init(void)
+{
+       int err;
+
+       err = platform_driver_register(&spi_gpio_driver);
+       if (err)
+               printk(KERN_ERR "spi-gpio: register failed: %d\n", err);
+
+       return err;
+}
+module_init(spi_gpio_init);
+
+static void __exit spi_gpio_exit(void)
+{
+       platform_driver_unregister(&spi_gpio_driver);
+}
+module_exit(spi_gpio_exit);
+
+MODULE_AUTHOR("Piot Skamruk <piotr.skamruk at gmail.com>");
+MODULE_AUTHOR("Michael Buesch");
+MODULE_DESCRIPTION("Platform independent GPIO bitbangling SPI driver");
+MODULE_LICENSE("GPL v2");
index 38df059..1b97d12 100644 (file)
@@ -1,7 +1,7 @@
 Index: linux-2.6.23.16/drivers/ssb/Kconfig
 ===================================================================
---- linux-2.6.23.16.orig/drivers/ssb/Kconfig   2008-02-28 12:52:40.000000000 +0100
-+++ linux-2.6.23.16/drivers/ssb/Kconfig        2008-02-29 01:01:00.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/ssb/Kconfig   2008-03-19 11:16:18.000000000 +0100
++++ linux-2.6.23.16/drivers/ssb/Kconfig        2008-03-19 11:16:18.000000000 +0100
 @@ -120,4 +120,13 @@ config SSB_DRIVER_EXTIF
  
          If unsure, say N
@@ -18,8 +18,8 @@ Index: linux-2.6.23.16/drivers/ssb/Kconfig
  endmenu
 Index: linux-2.6.23.16/drivers/ssb/Makefile
 ===================================================================
---- linux-2.6.23.16.orig/drivers/ssb/Makefile  2008-02-28 12:52:40.000000000 +0100
-+++ linux-2.6.23.16/drivers/ssb/Makefile       2008-02-28 12:52:40.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/ssb/Makefile  2008-03-19 11:16:18.000000000 +0100
++++ linux-2.6.23.16/drivers/ssb/Makefile       2008-03-19 11:16:18.000000000 +0100
 @@ -11,6 +11,7 @@ ssb-y                                        += driver_chipcommon.o
  ssb-$(CONFIG_SSB_DRIVER_MIPS)         += driver_mipscore.o
  ssb-$(CONFIG_SSB_DRIVER_EXTIF)                += driver_extif.o
@@ -31,7 +31,7 @@ Index: linux-2.6.23.16/drivers/ssb/Makefile
 Index: linux-2.6.23.16/drivers/ssb/driver_gige.c
 ===================================================================
 --- /dev/null  1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.23.16/drivers/ssb/driver_gige.c  2008-02-29 01:05:04.000000000 +0100
++++ linux-2.6.23.16/drivers/ssb/driver_gige.c  2008-03-19 11:16:18.000000000 +0100
 @@ -0,0 +1,294 @@
 +/*
 + * Sonics Silicon Backplane
@@ -330,7 +330,7 @@ Index: linux-2.6.23.16/drivers/ssb/driver_gige.c
 Index: linux-2.6.23.16/include/linux/ssb/ssb_driver_gige.h
 ===================================================================
 --- /dev/null  1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.23.16/include/linux/ssb/ssb_driver_gige.h        2008-02-29 01:10:50.000000000 +0100
++++ linux-2.6.23.16/include/linux/ssb/ssb_driver_gige.h        2008-03-19 11:16:18.000000000 +0100
 @@ -0,0 +1,174 @@
 +#ifndef LINUX_SSB_DRIVER_GIGE_H_
 +#define LINUX_SSB_DRIVER_GIGE_H_
@@ -508,8 +508,8 @@ Index: linux-2.6.23.16/include/linux/ssb/ssb_driver_gige.h
 +#endif /* LINUX_SSB_DRIVER_GIGE_H_ */
 Index: linux-2.6.23.16/drivers/ssb/driver_pcicore.c
 ===================================================================
---- linux-2.6.23.16.orig/drivers/ssb/driver_pcicore.c  2008-02-28 12:52:40.000000000 +0100
-+++ linux-2.6.23.16/drivers/ssb/driver_pcicore.c       2008-02-28 12:52:40.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/ssb/driver_pcicore.c  2008-03-19 11:16:18.000000000 +0100
++++ linux-2.6.23.16/drivers/ssb/driver_pcicore.c       2008-03-19 11:16:18.000000000 +0100
 @@ -60,74 +60,6 @@ static DEFINE_SPINLOCK(cfgspace_lock);
  /* Core to access the external PCI config space. Can only have one. */
  static struct ssb_pcicore *extpci_core;
@@ -680,8 +680,8 @@ Index: linux-2.6.23.16/drivers/ssb/driver_pcicore.c
        u32 val;
 Index: linux-2.6.23.16/drivers/ssb/embedded.c
 ===================================================================
---- linux-2.6.23.16.orig/drivers/ssb/embedded.c        2008-02-28 12:52:40.000000000 +0100
-+++ linux-2.6.23.16/drivers/ssb/embedded.c     2008-02-28 12:52:40.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/ssb/embedded.c        2008-03-19 11:16:18.000000000 +0100
++++ linux-2.6.23.16/drivers/ssb/embedded.c     2008-03-19 11:16:18.000000000 +0100
 @@ -10,6 +10,9 @@
  
  #include <linux/ssb/ssb.h>
@@ -785,8 +785,8 @@ Index: linux-2.6.23.16/drivers/ssb/embedded.c
 +}
 Index: linux-2.6.23.16/include/linux/ssb/ssb.h
 ===================================================================
---- linux-2.6.23.16.orig/include/linux/ssb/ssb.h       2008-02-28 12:52:40.000000000 +0100
-+++ linux-2.6.23.16/include/linux/ssb/ssb.h    2008-02-28 12:52:40.000000000 +0100
+--- linux-2.6.23.16.orig/include/linux/ssb/ssb.h       2008-03-19 11:16:18.000000000 +0100
++++ linux-2.6.23.16/include/linux/ssb/ssb.h    2008-03-19 11:16:18.000000000 +0100
 @@ -422,5 +422,12 @@ extern int ssb_bus_powerup(struct ssb_bu
  extern u32 ssb_admatch_base(u32 adm);
  extern u32 ssb_admatch_size(u32 adm);
@@ -802,8 +802,8 @@ Index: linux-2.6.23.16/include/linux/ssb/ssb.h
  #endif /* LINUX_SSB_H_ */
 Index: linux-2.6.23.16/include/linux/ssb/ssb_driver_pci.h
 ===================================================================
---- linux-2.6.23.16.orig/include/linux/ssb/ssb_driver_pci.h    2008-02-28 12:52:40.000000000 +0100
-+++ linux-2.6.23.16/include/linux/ssb/ssb_driver_pci.h 2008-02-28 12:52:40.000000000 +0100
+--- linux-2.6.23.16.orig/include/linux/ssb/ssb_driver_pci.h    2008-03-19 11:16:18.000000000 +0100
++++ linux-2.6.23.16/include/linux/ssb/ssb_driver_pci.h 2008-03-19 11:16:18.000000000 +0100
 @@ -1,6 +1,11 @@
  #ifndef LINUX_SSB_PCICORE_H_
  #define LINUX_SSB_PCICORE_H_
@@ -845,8 +845,8 @@ Index: linux-2.6.23.16/include/linux/ssb/ssb_driver_pci.h
  #endif /* LINUX_SSB_PCICORE_H_ */
 Index: linux-2.6.23.16/drivers/ssb/main.c
 ===================================================================
---- linux-2.6.23.16.orig/drivers/ssb/main.c    2008-02-28 12:52:40.000000000 +0100
-+++ linux-2.6.23.16/drivers/ssb/main.c 2008-02-28 12:52:40.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/ssb/main.c    2008-03-19 11:16:18.000000000 +0100
++++ linux-2.6.23.16/drivers/ssb/main.c 2008-03-19 11:16:18.000000000 +0100
 @@ -14,6 +14,7 @@
  #include <linux/io.h>
  #include <linux/ssb/ssb.h>
@@ -907,8 +907,8 @@ Index: linux-2.6.23.16/drivers/ssb/main.c
  }
 Index: linux-2.6.23.16/drivers/ssb/ssb_private.h
 ===================================================================
---- linux-2.6.23.16.orig/drivers/ssb/ssb_private.h     2008-02-28 12:52:37.000000000 +0100
-+++ linux-2.6.23.16/drivers/ssb/ssb_private.h  2008-02-28 12:52:40.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/ssb/ssb_private.h     2008-03-19 11:16:15.000000000 +0100
++++ linux-2.6.23.16/drivers/ssb/ssb_private.h  2008-03-19 11:16:18.000000000 +0100
 @@ -118,6 +118,8 @@ extern u32 ssb_calc_clock_rate(u32 pllty
  extern int ssb_devices_freeze(struct ssb_bus *bus);
  extern int ssb_devices_thaw(struct ssb_bus *bus);
@@ -920,8 +920,8 @@ Index: linux-2.6.23.16/drivers/ssb/ssb_private.h
  #ifdef CONFIG_SSB_PCIHOST
 Index: linux-2.6.23.16/drivers/net/tg3.c
 ===================================================================
---- linux-2.6.23.16.orig/drivers/net/tg3.c     2008-02-28 12:52:37.000000000 +0100
-+++ linux-2.6.23.16/drivers/net/tg3.c  2008-02-29 00:59:00.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/net/tg3.c     2008-03-19 11:16:15.000000000 +0100
++++ linux-2.6.23.16/drivers/net/tg3.c  2008-03-19 11:16:18.000000000 +0100
 @@ -38,6 +38,7 @@
  #include <linux/workqueue.h>
  #include <linux/prefetch.h>
@@ -1207,8 +1207,8 @@ Index: linux-2.6.23.16/drivers/net/tg3.c
         * swapping.  DMA data byte swapping is controlled in the GRC_MODE
 Index: linux-2.6.23.16/drivers/net/tg3.h
 ===================================================================
---- linux-2.6.23.16.orig/drivers/net/tg3.h     2008-02-28 12:52:37.000000000 +0100
-+++ linux-2.6.23.16/drivers/net/tg3.h  2008-02-28 12:52:40.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/net/tg3.h     2008-03-19 11:16:15.000000000 +0100
++++ linux-2.6.23.16/drivers/net/tg3.h  2008-03-19 11:16:18.000000000 +0100
 @@ -2279,6 +2279,10 @@ struct tg3 {
  #define TG3_FLG2_PHY_JITTER_BUG               0x20000000
  #define TG3_FLG2_NO_FWARE_REPORTED    0x40000000
@@ -1240,8 +1240,8 @@ Index: linux-2.6.23.16/drivers/net/tg3.h
        dma_addr_t                      stats_mapping;
 Index: linux-2.6.23.16/drivers/ssb/driver_mipscore.c
 ===================================================================
---- linux-2.6.23.16.orig/drivers/ssb/driver_mipscore.c 2008-02-28 12:52:40.000000000 +0100
-+++ linux-2.6.23.16/drivers/ssb/driver_mipscore.c      2008-02-28 12:52:40.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/ssb/driver_mipscore.c 2008-03-19 11:16:18.000000000 +0100
++++ linux-2.6.23.16/drivers/ssb/driver_mipscore.c      2008-03-19 11:16:18.000000000 +0100
 @@ -211,6 +211,7 @@ void ssb_mipscore_init(struct ssb_mipsco
                        /* fallthrough */
                case SSB_DEV_PCI:
diff --git a/target/linux/generic-2.6/patches-2.6.23/300-add-mmc-spi-driver.patch b/target/linux/generic-2.6/patches-2.6.23/300-add-mmc-spi-driver.patch
new file mode 100644 (file)
index 0000000..f02f100
--- /dev/null
@@ -0,0 +1,7635 @@
+This is a port of the MMC-SPI driver from 2.6.24.3
+--mb
+
+
+Index: linux-2.6.23.16/drivers/mmc/host/Kconfig
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/host/Kconfig      2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/host/Kconfig   2008-03-21 17:30:25.000000000 +0100
+@@ -100,3 +100,16 @@ config MMC_TIFM_SD
+           To compile this driver as a module, choose M here: the
+         module will be called tifm_sd.
++config MMC_SPI
++      tristate "MMC/SD over SPI (EXPERIMENTAL)"
++      depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL
++      select CRC7
++      select CRC_ITU_T
++      help
++        Some systems accss MMC/SD cards using a SPI controller instead of
++        using a "native" MMC/SD controller.  This has a disadvantage of
++        being relatively high overhead, but a compensating advantage of
++        working on many systems without dedicated MMC/SD controllers.
++
++        If unsure, or if your system has no SPI master driver, say N.
++
+Index: linux-2.6.23.16/drivers/mmc/host/Makefile
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/host/Makefile     2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/host/Makefile  2008-03-21 17:30:25.000000000 +0100
+@@ -15,4 +15,5 @@ obj-$(CONFIG_MMC_AU1X)               += au1xmmc.o
+ obj-$(CONFIG_MMC_OMAP)                += omap.o
+ obj-$(CONFIG_MMC_AT91)                += at91_mci.o
+ obj-$(CONFIG_MMC_TIFM_SD)     += tifm_sd.o
++obj-$(CONFIG_MMC_SPI)         += mmc_spi.o
+Index: linux-2.6.23.16/drivers/mmc/host/mmc_spi.c
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/drivers/mmc/host/mmc_spi.c 2008-03-21 17:30:25.000000000 +0100
+@@ -0,0 +1,1419 @@
++/*
++ * mmc_spi.c - Access SD/MMC cards through SPI master controllers
++ *
++ * (C) Copyright 2005, Intec Automation,
++ *            Mike Lavender (mike@steroidmicros)
++ * (C) Copyright 2006-2007, David Brownell
++ * (C) Copyright 2007, Axis Communications,
++ *            Hans-Peter Nilsson (hp@axis.com)
++ * (C) Copyright 2007, ATRON electronic GmbH,
++ *            Jan Nikitenko <jan.nikitenko@gmail.com>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++#include <linux/hrtimer.h>
++#include <linux/delay.h>
++#include <linux/bio.h>
++#include <linux/dma-mapping.h>
++#include <linux/crc7.h>
++#include <linux/crc-itu-t.h>
++#include <linux/scatterlist.h>
++
++#include <linux/mmc/host.h>
++#include <linux/mmc/mmc.h>            /* for R1_SPI_* bit values */
++
++#include <linux/spi/spi.h>
++#include <linux/spi/mmc_spi.h>
++
++#include <asm/unaligned.h>
++
++
++#define sg_page(sg)   (sg)->page
++
++
++/* NOTES:
++ *
++ * - For now, we won't try to interoperate with a real mmc/sd/sdio
++ *   controller, although some of them do have hardware support for
++ *   SPI protocol.  The main reason for such configs would be mmc-ish
++ *   cards like DataFlash, which don't support that "native" protocol.
++ *
++ *   We don't have a "DataFlash/MMC/SD/SDIO card slot" abstraction to
++ *   switch between driver stacks, and in any case if "native" mode
++ *   is available, it will be faster and hence preferable.
++ *
++ * - MMC depends on a different chipselect management policy than the
++ *   SPI interface currently supports for shared bus segments:  it needs
++ *   to issue multiple spi_message requests with the chipselect active,
++ *   using the results of one message to decide the next one to issue.
++ *
++ *   Pending updates to the programming interface, this driver expects
++ *   that it not share the bus with other drivers (precluding conflicts).
++ *
++ * - We tell the controller to keep the chipselect active from the
++ *   beginning of an mmc_host_ops.request until the end.  So beware
++ *   of SPI controller drivers that mis-handle the cs_change flag!
++ *
++ *   However, many cards seem OK with chipselect flapping up/down
++ *   during that time ... at least on unshared bus segments.
++ */
++
++
++/*
++ * Local protocol constants, internal to data block protocols.
++ */
++
++/* Response tokens used to ack each block written: */
++#define SPI_MMC_RESPONSE_CODE(x)      ((x) & 0x1f)
++#define SPI_RESPONSE_ACCEPTED         ((2 << 1)|1)
++#define SPI_RESPONSE_CRC_ERR          ((5 << 1)|1)
++#define SPI_RESPONSE_WRITE_ERR                ((6 << 1)|1)
++
++/* Read and write blocks start with these tokens and end with crc;
++ * on error, read tokens act like a subset of R2_SPI_* values.
++ */
++#define SPI_TOKEN_SINGLE      0xfe    /* single block r/w, multiblock read */
++#define SPI_TOKEN_MULTI_WRITE 0xfc    /* multiblock write */
++#define SPI_TOKEN_STOP_TRAN   0xfd    /* terminate multiblock write */
++
++#define MMC_SPI_BLOCKSIZE     512
++
++
++/* These fixed timeouts come from the latest SD specs, which say to ignore
++ * the CSD values.  The R1B value is for card erase (e.g. the "I forgot the
++ * card's password" scenario); it's mostly applied to STOP_TRANSMISSION after
++ * reads which takes nowhere near that long.  Older cards may be able to use
++ * shorter timeouts ... but why bother?
++ */
++#define readblock_timeout     ktime_set(0, 100 * 1000 * 1000)
++#define writeblock_timeout    ktime_set(0, 250 * 1000 * 1000)
++#define r1b_timeout           ktime_set(3, 0)
++
++
++/****************************************************************************/
++
++/*
++ * Local Data Structures
++ */
++
++/* "scratch" is per-{command,block} data exchanged with the card */
++struct scratch {
++      u8                      status[29];
++      u8                      data_token;
++      __be16                  crc_val;
++};
++
++struct mmc_spi_host {
++      struct mmc_host         *mmc;
++      struct spi_device       *spi;
++
++      unsigned char           power_mode;
++      u16                     powerup_msecs;
++
++      struct mmc_spi_platform_data    *pdata;
++
++      /* for bulk data transfers */
++      struct spi_transfer     token, t, crc, early_status;
++      struct spi_message      m;
++
++      /* for status readback */
++      struct spi_transfer     status;
++      struct spi_message      readback;
++
++      /* underlying DMA-aware controller, or null */
++      struct device           *dma_dev;
++
++      /* buffer used for commands and for message "overhead" */
++      struct scratch          *data;
++      dma_addr_t              data_dma;
++
++      /* Specs say to write ones most of the time, even when the card
++       * has no need to read its input data; and many cards won't care.
++       * This is our source of those ones.
++       */
++      void                    *ones;
++      dma_addr_t              ones_dma;
++};
++
++
++/****************************************************************************/
++
++/*
++ * MMC-over-SPI protocol glue, used by the MMC stack interface
++ */
++
++static inline int mmc_cs_off(struct mmc_spi_host *host)
++{
++      /* chipselect will always be inactive after setup() */
++      return spi_setup(host->spi);
++}
++
++static int
++mmc_spi_readbytes(struct mmc_spi_host *host, unsigned len)
++{
++      int status;
++
++      if (len > sizeof(*host->data)) {
++              WARN_ON(1);
++              return -EIO;
++      }
++
++      host->status.len = len;
++
++      if (host->dma_dev)
++              dma_sync_single_for_device(host->dma_dev,
++                              host->data_dma, sizeof(*host->data),
++                              DMA_FROM_DEVICE);
++
++      status = spi_sync(host->spi, &host->readback);
++
++      if (host->dma_dev)
++              dma_sync_single_for_cpu(host->dma_dev,
++                              host->data_dma, sizeof(*host->data),
++                              DMA_FROM_DEVICE);
++
++      return status;
++}
++
++static int
++mmc_spi_skip(struct mmc_spi_host *host, ktime_t timeout, unsigned n, u8 byte)
++{
++      u8              *cp = host->data->status;
++
++      timeout = ktime_add(timeout, ktime_get());
++
++      while (1) {
++              int             status;
++              unsigned        i;
++
++              status = mmc_spi_readbytes(host, n);
++              if (status < 0)
++                      return status;
++
++              for (i = 0; i < n; i++) {
++                      if (cp[i] != byte)
++                              return cp[i];
++              }
++
++              /* REVISIT investigate msleep() to avoid busy-wait I/O
++               * in at least some cases.
++               */
++              if (ktime_to_ns(ktime_sub(ktime_get(), timeout)) > 0)
++                      break;
++      }
++      return -ETIMEDOUT;
++}
++
++static inline int
++mmc_spi_wait_unbusy(struct mmc_spi_host *host, ktime_t timeout)
++{
++      return mmc_spi_skip(host, timeout, sizeof(host->data->status), 0);
++}
++
++static int mmc_spi_readtoken(struct mmc_spi_host *host)
++{
++      return mmc_spi_skip(host, readblock_timeout, 1, 0xff);
++}
++
++
++/*
++ * Note that for SPI, cmd->resp[0] is not the same data as "native" protocol
++ * hosts return!  The low byte holds R1_SPI bits.  The next byte may hold
++ * R2_SPI bits ... for SEND_STATUS, or after data read errors.
++ *
++ * cmd->resp[1] holds any four-byte response, for R3 (READ_OCR) and on
++ * newer cards R7 (IF_COND).
++ */
++
++static char *maptype(struct mmc_command *cmd)
++{
++      switch (mmc_spi_resp_type(cmd)) {
++      case MMC_RSP_SPI_R1:    return "R1";
++      case MMC_RSP_SPI_R1B:   return "R1B";
++      case MMC_RSP_SPI_R2:    return "R2/R5";
++      case MMC_RSP_SPI_R3:    return "R3/R4/R7";
++      default:                return "?";
++      }
++}
++
++/* return zero, else negative errno after setting cmd->error */
++static int mmc_spi_response_get(struct mmc_spi_host *host,
++              struct mmc_command *cmd, int cs_on)
++{
++      u8      *cp = host->data->status;
++      u8      *end = cp + host->t.len;
++      int     value = 0;
++      char    tag[32];
++
++      snprintf(tag, sizeof(tag), "  ... CMD%d response SPI_%s",
++              cmd->opcode, maptype(cmd));
++
++      /* Except for data block reads, the whole response will already
++       * be stored in the scratch buffer.  It's somewhere after the
++       * command and the first byte we read after it.  We ignore that
++       * first byte.  After STOP_TRANSMISSION command it may include
++       * two data bits, but otherwise it's all ones.
++       */
++      cp += 8;
++      while (cp < end && *cp == 0xff)
++              cp++;
++
++      /* Data block reads (R1 response types) may need more data... */
++      if (cp == end) {
++              unsigned        i;
++
++              cp = host->data->status;
++
++              /* Card sends N(CR) (== 1..8) bytes of all-ones then one
++               * status byte ... and we already scanned 2 bytes.
++               *
++               * REVISIT block read paths use nasty byte-at-a-time I/O
++               * so it can always DMA directly into the target buffer.
++               * It'd probably be better to memcpy() the first chunk and
++               * avoid extra i/o calls...
++               */
++              for (i = 2; i < 9; i++) {
++                      value = mmc_spi_readbytes(host, 1);
++                      if (value < 0)
++                              goto done;
++                      if (*cp != 0xff)
++                              goto checkstatus;
++              }
++              value = -ETIMEDOUT;
++              goto done;
++      }
++
++checkstatus:
++      if (*cp & 0x80) {
++              dev_dbg(&host->spi->dev, "%s: INVALID RESPONSE, %02x\n",
++                                      tag, *cp);
++              value = -EBADR;
++              goto done;
++      }
++
++      cmd->resp[0] = *cp++;
++      cmd->error = 0;
++
++      /* Status byte: the entire seven-bit R1 response.  */
++      if (cmd->resp[0] != 0) {
++              if ((R1_SPI_PARAMETER | R1_SPI_ADDRESS
++                                      | R1_SPI_ILLEGAL_COMMAND)
++                              & cmd->resp[0])
++                      value = -EINVAL;
++              else if (R1_SPI_COM_CRC & cmd->resp[0])
++                      value = -EILSEQ;
++              else if ((R1_SPI_ERASE_SEQ | R1_SPI_ERASE_RESET)
++                              & cmd->resp[0])
++                      value = -EIO;
++              /* else R1_SPI_IDLE, "it's resetting" */
++      }
++
++      switch (mmc_spi_resp_type(cmd)) {
++
++      /* SPI R1B == R1 + busy; STOP_TRANSMISSION (for multiblock reads)
++       * and less-common stuff like various erase operations.
++       */
++      case MMC_RSP_SPI_R1B:
++              /* maybe we read all the busy tokens already */
++              while (cp < end && *cp == 0)
++                      cp++;
++              if (cp == end)
++                      mmc_spi_wait_unbusy(host, r1b_timeout);
++              break;
++
++      /* SPI R2 == R1 + second status byte; SEND_STATUS
++       * SPI R5 == R1 + data byte; IO_RW_DIRECT
++       */
++      case MMC_RSP_SPI_R2:
++              cmd->resp[0] |= *cp << 8;
++              break;
++
++      /* SPI R3, R4, or R7 == R1 + 4 bytes */
++      case MMC_RSP_SPI_R3:
++              cmd->resp[1] = be32_to_cpu(get_unaligned((u32 *)cp));
++              break;
++
++      /* SPI R1 == just one status byte */
++      case MMC_RSP_SPI_R1:
++              break;
++
++      default:
++              dev_dbg(&host->spi->dev, "bad response type %04x\n",
++                              mmc_spi_resp_type(cmd));
++              if (value >= 0)
++                      value = -EINVAL;
++              goto done;
++      }
++
++      if (value < 0)
++              dev_dbg(&host->spi->dev, "%s: resp %04x %08x\n",
++                      tag, cmd->resp[0], cmd->resp[1]);
++
++      /* disable chipselect on errors and some success cases */
++      if (value >= 0 && cs_on)
++              return value;
++done:
++      if (value < 0)
++              cmd->error = value;
++      mmc_cs_off(host);
++      return value;
++}
++
++/* Issue command and read its response.
++ * Returns zero on success, negative for error.
++ *
++ * On error, caller must cope with mmc core retry mechanism.  That
++ * means immediate low-level resubmit, which affects the bus lock...
++ */
++static int
++mmc_spi_command_send(struct mmc_spi_host *host,
++              struct mmc_request *mrq,
++              struct mmc_command *cmd, int cs_on)
++{
++      struct scratch          *data = host->data;
++      u8                      *cp = data->status;
++      u32                     arg = cmd->arg;
++      int                     status;
++      struct spi_transfer     *t;
++
++      /* We can handle most commands (except block reads) in one full
++       * duplex I/O operation before either starting the next transfer
++       * (data block or command) or else deselecting the card.
++       *
++       * First, write 7 bytes:
++       *  - an all-ones byte to ensure the card is ready
++       *  - opcode byte (plus start and transmission bits)
++       *  - four bytes of big-endian argument
++       *  - crc7 (plus end bit) ... always computed, it's cheap
++       *
++       * We init the whole buffer to all-ones, which is what we need
++       * to write while we're reading (later) response data.
++       */
++      memset(cp++, 0xff, sizeof(data->status));
++
++      *cp++ = 0x40 | cmd->opcode;
++      *cp++ = (u8)(arg >> 24);
++      *cp++ = (u8)(arg >> 16);
++      *cp++ = (u8)(arg >> 8);
++      *cp++ = (u8)arg;
++      *cp++ = (crc7(0, &data->status[1], 5) << 1) | 0x01;
++
++      /* Then, read up to 13 bytes (while writing all-ones):
++       *  - N(CR) (== 1..8) bytes of all-ones
++       *  - status byte (for all response types)
++       *  - the rest of the response, either:
++       *      + nothing, for R1 or R1B responses
++       *      + second status byte, for R2 responses
++       *      + four data bytes, for R3 and R7 responses
++       *
++       * Finally, read some more bytes ... in the nice cases we know in
++       * advance how many, and reading 1 more is always OK:
++       *  - N(EC) (== 0..N) bytes of all-ones, before deselect/finish
++       *  - N(RC) (== 1..N) bytes of all-ones, before next command
++       *  - N(WR) (== 1..N) bytes of all-ones, before data write
++       *
++       * So in those cases one full duplex I/O of at most 21 bytes will
++       * handle the whole command, leaving the card ready to receive a
++       * data block or new command.  We do that whenever we can, shaving
++       * CPU and IRQ costs (especially when using DMA or FIFOs).
++       *
++       * There are two other cases, where it's not generally practical
++       * to rely on a single I/O:
++       *
++       *  - R1B responses need at least N(EC) bytes of all-zeroes.
++       *
++       *    In this case we can *try* to fit it into one I/O, then
++       *    maybe read more data later.
++       *
++       *  - Data block reads are more troublesome, since a variable
++       *    number of padding bytes precede the token and data.
++       *      + N(CX) (== 0..8) bytes of all-ones, before CSD or CID
++       *      + N(AC) (== 1..many) bytes of all-ones
++       *
++       *    In this case we currently only have minimal speedups here:
++       *    when N(CR) == 1 we can avoid I/O in response_get().
++       */
++      if (cs_on && (mrq->data->flags & MMC_DATA_READ)) {
++              cp += 2;        /* min(N(CR)) + status */
++              /* R1 */
++      } else {
++              cp += 10;       /* max(N(CR)) + status + min(N(RC),N(WR)) */
++              if (cmd->flags & MMC_RSP_SPI_S2)        /* R2/R5 */
++                      cp++;
++              else if (cmd->flags & MMC_RSP_SPI_B4)   /* R3/R4/R7 */
++                      cp += 4;
++              else if (cmd->flags & MMC_RSP_BUSY)     /* R1B */
++                      cp = data->status + sizeof(data->status);
++              /* else:  R1 (most commands) */
++      }
++
++      dev_dbg(&host->spi->dev, "  mmc_spi: CMD%d, resp %s\n",
++              cmd->opcode, maptype(cmd));
++
++      /* send command, leaving chipselect active */
++      spi_message_init(&host->m);
++
++      t = &host->t;
++      memset(t, 0, sizeof(*t));
++      t->tx_buf = t->rx_buf = data->status;
++      t->tx_dma = t->rx_dma = host->data_dma;
++      t->len = cp - data->status;
++      t->cs_change = 1;
++      spi_message_add_tail(t, &host->m);
++
++      if (host->dma_dev) {
++              host->m.is_dma_mapped = 1;
++              dma_sync_single_for_device(host->dma_dev,
++                              host->data_dma, sizeof(*host->data),
++                              DMA_BIDIRECTIONAL);
++      }
++      status = spi_sync(host->spi, &host->m);
++
++      if (host->dma_dev)
++              dma_sync_single_for_cpu(host->dma_dev,
++                              host->data_dma, sizeof(*host->data),
++                              DMA_BIDIRECTIONAL);
++      if (status < 0) {
++              dev_dbg(&host->spi->dev, "  ... write returned %d\n", status);
++              cmd->error = status;
++              return status;
++      }
++
++      /* after no-data commands and STOP_TRANSMISSION, chipselect off */
++      return mmc_spi_response_get(host, cmd, cs_on);
++}
++
++/* Build data message with up to four separate transfers.  For TX, we
++ * start by writing the data token.  And in most cases, we finish with
++ * a status transfer.
++ *
++ * We always provide TX data for data and CRC.  The MMC/SD protocol
++ * requires us to write ones; but Linux defaults to writing zeroes;
++ * so we explicitly initialize it to all ones on RX paths.
++ *
++ * We also handle DMA mapping, so the underlying SPI controller does
++ * not need to (re)do it for each message.
++ */
++static void
++mmc_spi_setup_data_message(
++      struct mmc_spi_host     *host,
++      int                     multiple,
++      enum dma_data_direction direction)
++{
++      struct spi_transfer     *t;
++      struct scratch          *scratch = host->data;
++      dma_addr_t              dma = host->data_dma;
++
++      spi_message_init(&host->m);
++      if (dma)
++              host->m.is_dma_mapped = 1;
++
++      /* for reads, readblock() skips 0xff bytes before finding
++       * the token; for writes, this transfer issues that token.
++       */
++      if (direction == DMA_TO_DEVICE) {
++              t = &host->token;
++              memset(t, 0, sizeof(*t));
++              t->len = 1;
++              if (multiple)
++                      scratch->data_token = SPI_TOKEN_MULTI_WRITE;
++              else
++                      scratch->data_token = SPI_TOKEN_SINGLE;
++              t->tx_buf = &scratch->data_token;
++              if (dma)
++                      t->tx_dma = dma + offsetof(struct scratch, data_token);
++              spi_message_add_tail(t, &host->m);
++      }
++
++      /* Body of transfer is buffer, then CRC ...
++       * either TX-only, or RX with TX-ones.
++       */
++      t = &host->t;
++      memset(t, 0, sizeof(*t));
++      t->tx_buf = host->ones;
++      t->tx_dma = host->ones_dma;
++      /* length and actual buffer info are written later */
++      spi_message_add_tail(t, &host->m);
++
++      t = &host->crc;
++      memset(t, 0, sizeof(*t));
++      t->len = 2;
++      if (direction == DMA_TO_DEVICE) {
++              /* the actual CRC may get written later */
++              t->tx_buf = &scratch->crc_val;
++              if (dma)
++                      t->tx_dma = dma + offsetof(struct scratch, crc_val);
++      } else {
++              t->tx_buf = host->ones;
++              t->tx_dma = host->ones_dma;
++              t->rx_buf = &scratch->crc_val;
++              if (dma)
++                      t->rx_dma = dma + offsetof(struct scratch, crc_val);
++      }
++      spi_message_add_tail(t, &host->m);
++
++      /*
++       * A single block read is followed by N(EC) [0+] all-ones bytes
++       * before deselect ... don't bother.
++       *
++       * Multiblock reads are followed by N(AC) [1+] all-ones bytes before
++       * the next block is read, or a STOP_TRANSMISSION is issued.  We'll
++       * collect that single byte, so readblock() doesn't need to.
++       *
++       * For a write, the one-byte data response follows immediately, then
++       * come zero or more busy bytes, then N(WR) [1+] all-ones bytes.
++       * Then single block reads may deselect, and multiblock ones issue
++       * the next token (next data block, or STOP_TRAN).  We can try to
++       * minimize I/O ops by using a single read to collect end-of-busy.
++       */
++      if (multiple || direction == DMA_TO_DEVICE) {
++              t = &host->early_status;
++              memset(t, 0, sizeof(*t));
++              t->len = (direction == DMA_TO_DEVICE)
++                              ? sizeof(scratch->status)
++                              : 1;
++              t->tx_buf = host->ones;
++              t->tx_dma = host->ones_dma;
++              t->rx_buf = scratch->status;
++              if (dma)
++                      t->rx_dma = dma + offsetof(struct scratch, status);
++              t->cs_change = 1;
++              spi_message_add_tail(t, &host->m);
++      }
++}
++
++/*
++ * Write one block:
++ *  - caller handled preceding N(WR) [1+] all-ones bytes
++ *  - data block
++ *    + token
++ *    + data bytes
++ *    + crc16
++ *  - an all-ones byte ... card writes a data-response byte
++ *  - followed by N(EC) [0+] all-ones bytes, card writes zero/'busy'
++ *
++ * Return negative errno, else success.
++ */
++static int
++mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t)
++{
++      struct spi_device       *spi = host->spi;
++      int                     status, i;
++      struct scratch          *scratch = host->data;
++
++      if (host->mmc->use_spi_crc)
++              scratch->crc_val = cpu_to_be16(
++                              crc_itu_t(0, t->tx_buf, t->len));
++      if (host->dma_dev)
++              dma_sync_single_for_device(host->dma_dev,
++                              host->data_dma, sizeof(*scratch),
++                              DMA_BIDIRECTIONAL);
++
++      status = spi_sync(spi, &host->m);
++
++      if (status != 0) {
++              dev_dbg(&spi->dev, "write error (%d)\n", status);
++              return status;
++      }
++
++      if (host->dma_dev)
++              dma_sync_single_for_cpu(host->dma_dev,
++                              host->data_dma, sizeof(*scratch),
++                              DMA_BIDIRECTIONAL);
++
++      /*
++       * Get the transmission data-response reply.  It must follow
++       * immediately after the data block we transferred.  This reply
++       * doesn't necessarily tell whether the write operation succeeded;
++       * it just says if the transmission was ok and whether *earlier*
++       * writes succeeded; see the standard.
++       */
++      switch (SPI_MMC_RESPONSE_CODE(scratch->status[0])) {
++      case SPI_RESPONSE_ACCEPTED:
++              status = 0;
++              break;
++      case SPI_RESPONSE_CRC_ERR:
++              /* host shall then issue MMC_STOP_TRANSMISSION */
++              status = -EILSEQ;
++              break;
++      case SPI_RESPONSE_WRITE_ERR:
++              /* host shall then issue MMC_STOP_TRANSMISSION,
++               * and should MMC_SEND_STATUS to sort it out
++               */
++              status = -EIO;
++              break;
++      default:
++              status = -EPROTO;
++              break;
++      }
++      if (status != 0) {
++              dev_dbg(&spi->dev, "write error %02x (%d)\n",
++                      scratch->status[0], status);
++              return status;
++      }
++
++      t->tx_buf += t->len;
++      if (host->dma_dev)
++              t->tx_dma += t->len;
++
++      /* Return when not busy.  If we didn't collect that status yet,
++       * we'll need some more I/O.
++       */
++      for (i = 1; i < sizeof(scratch->status); i++) {
++              if (scratch->status[i] != 0)
++                      return 0;
++      }
++      return mmc_spi_wait_unbusy(host, writeblock_timeout);
++}
++
++/*
++ * Read one block:
++ *  - skip leading all-ones bytes ... either
++ *      + N(AC) [1..f(clock,CSD)] usually, else
++ *      + N(CX) [0..8] when reading CSD or CID
++ *  - data block
++ *    + token ... if error token, no data or crc
++ *    + data bytes
++ *    + crc16
++ *
++ * After single block reads, we're done; N(EC) [0+] all-ones bytes follow
++ * before dropping chipselect.
++ *
++ * For multiblock reads, caller either reads the next block or issues a
++ * STOP_TRANSMISSION command.
++ */
++static int
++mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t)
++{
++      struct spi_device       *spi = host->spi;
++      int                     status;
++      struct scratch          *scratch = host->data;
++
++      /* At least one SD card sends an all-zeroes byte when N(CX)
++       * applies, before the all-ones bytes ... just cope with that.
++       */
++      status = mmc_spi_readbytes(host, 1);
++      if (status < 0)
++              return status;
++      status = scratch->status[0];
++      if (status == 0xff || status == 0)
++              status = mmc_spi_readtoken(host);
++
++      if (status == SPI_TOKEN_SINGLE) {
++              if (host->dma_dev) {
++                      dma_sync_single_for_device(host->dma_dev,
++                                      host->data_dma, sizeof(*scratch),
++                                      DMA_BIDIRECTIONAL);
++                      dma_sync_single_for_device(host->dma_dev,
++                                      t->rx_dma, t->len,
++                                      DMA_FROM_DEVICE);
++              }
++
++              status = spi_sync(spi, &host->m);
++
++              if (host->dma_dev) {
++                      dma_sync_single_for_cpu(host->dma_dev,
++                                      host->data_dma, sizeof(*scratch),
++                                      DMA_BIDIRECTIONAL);
++                      dma_sync_single_for_cpu(host->dma_dev,
++                                      t->rx_dma, t->len,
++                                      DMA_FROM_DEVICE);
++              }
++
++      } else {
++              dev_dbg(&spi->dev, "read error %02x (%d)\n", status, status);
++
++              /* we've read extra garbage, timed out, etc */
++              if (status < 0)
++                      return status;
++
++              /* low four bits are an R2 subset, fifth seems to be
++               * vendor specific ... map them all to generic error..
++               */
++              return -EIO;
++      }
++
++      if (host->mmc->use_spi_crc) {
++              u16 crc = crc_itu_t(0, t->rx_buf, t->len);
++
++              be16_to_cpus(&scratch->crc_val);
++              if (scratch->crc_val != crc) {
++                      dev_dbg(&spi->dev, "read - crc error: crc_val=0x%04x, "
++                                      "computed=0x%04x len=%d\n",
++                                      scratch->crc_val, crc, t->len);
++                      return -EILSEQ;
++              }
++      }
++
++      t->rx_buf += t->len;
++      if (host->dma_dev)
++              t->rx_dma += t->len;
++
++      return 0;
++}
++
++/*
++ * An MMC/SD data stage includes one or more blocks, optional CRCs,
++ * and inline handshaking.  That handhaking makes it unlike most
++ * other SPI protocol stacks.
++ */
++static void
++mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
++              struct mmc_data *data, u32 blk_size)
++{
++      struct spi_device       *spi = host->spi;
++      struct device           *dma_dev = host->dma_dev;
++      struct spi_transfer     *t;
++      enum dma_data_direction direction;
++      struct scatterlist      *sg;
++      unsigned                n_sg;
++      int                     multiple = (data->blocks > 1);
++
++      if (data->flags & MMC_DATA_READ)
++              direction = DMA_FROM_DEVICE;
++      else
++              direction = DMA_TO_DEVICE;
++      mmc_spi_setup_data_message(host, multiple, direction);
++      t = &host->t;
++
++      /* Handle scatterlist segments one at a time, with synch for
++       * each 512-byte block
++       */
++      for (sg = data->sg, n_sg = data->sg_len; n_sg; n_sg--, sg++) {
++              int                     status = 0;
++              dma_addr_t              dma_addr = 0;
++              void                    *kmap_addr;
++              unsigned                length = sg->length;
++              enum dma_data_direction dir = direction;
++
++              /* set up dma mapping for controller drivers that might
++               * use DMA ... though they may fall back to PIO
++               */
++              if (dma_dev) {
++                      /* never invalidate whole *shared* pages ... */
++                      if ((sg->offset != 0 || length != PAGE_SIZE)
++                                      && dir == DMA_FROM_DEVICE)
++                              dir = DMA_BIDIRECTIONAL;
++
++                      dma_addr = dma_map_page(dma_dev, sg_page(sg), 0,
++                                              PAGE_SIZE, dir);
++                      if (direction == DMA_TO_DEVICE)
++                              t->tx_dma = dma_addr + sg->offset;
++                      else
++                              t->rx_dma = dma_addr + sg->offset;
++              }
++
++              /* allow pio too; we don't allow highmem */
++              kmap_addr = kmap(sg_page(sg));
++              if (direction == DMA_TO_DEVICE)
++                      t->tx_buf = kmap_addr + sg->offset;
++              else
++                      t->rx_buf = kmap_addr + sg->offset;
++
++              /* transfer each block, and update request status */
++              while (length) {
++                      t->len = min(length, blk_size);
++
++                      dev_dbg(&host->spi->dev,
++                              "    mmc_spi: %s block, %d bytes\n",
++                              (direction == DMA_TO_DEVICE)
++                              ? "write"
++                              : "read",
++                              t->len);
++
++                      if (direction == DMA_TO_DEVICE)
++                              status = mmc_spi_writeblock(host, t);
++                      else
++                              status = mmc_spi_readblock(host, t);
++                      if (status < 0)
++                              break;
++
++                      data->bytes_xfered += t->len;
++                      length -= t->len;
++
++                      if (!multiple)
++                              break;
++              }
++
++              /* discard mappings */
++              if (direction == DMA_FROM_DEVICE)
++                      flush_kernel_dcache_page(sg_page(sg));
++              kunmap(sg_page(sg));
++              if (dma_dev)
++                      dma_unmap_page(dma_dev, dma_addr, PAGE_SIZE, dir);
++
++              if (status < 0) {
++                      data->error = status;
++                      dev_dbg(&spi->dev, "%s status %d\n",
++                              (direction == DMA_TO_DEVICE)
++                                      ? "write" : "read",
++                              status);
++                      break;
++              }
++      }
++
++      /* NOTE some docs describe an MMC-only SET_BLOCK_COUNT (CMD23) that
++       * can be issued before multiblock writes.  Unlike its more widely
++       * documented analogue for SD cards (SET_WR_BLK_ERASE_COUNT, ACMD23),
++       * that can affect the STOP_TRAN logic.   Complete (and current)
++       * MMC specs should sort that out before Linux starts using CMD23.
++       */
++      if (direction == DMA_TO_DEVICE && multiple) {
++              struct scratch  *scratch = host->data;
++              int             tmp;
++              const unsigned  statlen = sizeof(scratch->status);
++
++              dev_dbg(&spi->dev, "    mmc_spi: STOP_TRAN\n");
++
++              /* Tweak the per-block message we set up earlier by morphing
++               * it to hold single buffer with the token followed by some
++               * all-ones bytes ... skip N(BR) (0..1), scan the rest for
++               * "not busy any longer" status, and leave chip selected.
++               */
++              INIT_LIST_HEAD(&host->m.transfers);
++              list_add(&host->early_status.transfer_list,
++                              &host->m.transfers);
++
++              memset(scratch->status, 0xff, statlen);
++              scratch->status[0] = SPI_TOKEN_STOP_TRAN;
++
++              host->early_status.tx_buf = host->early_status.rx_buf;
++              host->early_status.tx_dma = host->early_status.rx_dma;
++              host->early_status.len = statlen;
++
++              if (host->dma_dev)
++                      dma_sync_single_for_device(host->dma_dev,
++                                      host->data_dma, sizeof(*scratch),
++                                      DMA_BIDIRECTIONAL);
++
++              tmp = spi_sync(spi, &host->m);
++
++              if (host->dma_dev)
++                      dma_sync_single_for_cpu(host->dma_dev,
++                                      host->data_dma, sizeof(*scratch),
++                                      DMA_BIDIRECTIONAL);
++
++              if (tmp < 0) {
++                      if (!data->error)
++                              data->error = tmp;
++                      return;
++              }
++
++              /* Ideally we collected "not busy" status with one I/O,
++               * avoiding wasteful byte-at-a-time scanning... but more
++               * I/O is often needed.
++               */
++              for (tmp = 2; tmp < statlen; tmp++) {
++                      if (scratch->status[tmp] != 0)
++                              return;
++              }
++              tmp = mmc_spi_wait_unbusy(host, writeblock_timeout);
++              if (tmp < 0 && !data->error)
++                      data->error = tmp;
++      }
++}
++
++/****************************************************************************/
++
++/*
++ * MMC driver implementation -- the interface to the MMC stack
++ */
++
++static void mmc_spi_request(struct mmc_host *mmc, struct mmc_request *mrq)
++{
++      struct mmc_spi_host     *host = mmc_priv(mmc);
++      int                     status = -EINVAL;
++
++#ifdef DEBUG
++      /* MMC core and layered drivers *MUST* issue SPI-aware commands */
++      {
++              struct mmc_command      *cmd;
++              int                     invalid = 0;
++
++              cmd = mrq->cmd;
++              if (!mmc_spi_resp_type(cmd)) {
++                      dev_dbg(&host->spi->dev, "bogus command\n");
++                      cmd->error = -EINVAL;
++                      invalid = 1;
++              }
++
++              cmd = mrq->stop;
++              if (cmd && !mmc_spi_resp_type(cmd)) {
++                      dev_dbg(&host->spi->dev, "bogus STOP command\n");
++                      cmd->error = -EINVAL;
++                      invalid = 1;
++              }
++
++              if (invalid) {
++                      dump_stack();
++                      mmc_request_done(host->mmc, mrq);
++                      return;
++              }
++      }
++#endif
++
++      /* issue command; then optionally data and stop */
++      status = mmc_spi_command_send(host, mrq, mrq->cmd, mrq->data != NULL);
++      if (status == 0 && mrq->data) {
++              mmc_spi_data_do(host, mrq->cmd, mrq->data, mrq->data->blksz);
++              if (mrq->stop)
++                      status = mmc_spi_command_send(host, mrq, mrq->stop, 0);
++              else
++                      mmc_cs_off(host);
++      }
++
++      mmc_request_done(host->mmc, mrq);
++}
++
++/* See Section 6.4.1, in SD "Simplified Physical Layer Specification 2.0"
++ *
++ * NOTE that here we can't know that the card has just been powered up;
++ * not all MMC/SD sockets support power switching.
++ *
++ * FIXME when the card is still in SPI mode, e.g. from a previous kernel,
++ * this doesn't seem to do the right thing at all...
++ */
++static void mmc_spi_initsequence(struct mmc_spi_host *host)
++{
++      /* Try to be very sure any previous command has completed;
++       * wait till not-busy, skip debris from any old commands.
++       */
++      mmc_spi_wait_unbusy(host, r1b_timeout);
++      mmc_spi_readbytes(host, 10);
++
++      /*
++       * Do a burst with chipselect active-high.  We need to do this to
++       * meet the requirement of 74 clock cycles with both chipselect
++       * and CMD (MOSI) high before CMD0 ... after the card has been
++       * powered up to Vdd(min), and so is ready to take commands.
++       *
++       * Some cards are particularly needy of this (e.g. Viking "SD256")
++       * while most others don't seem to care.
++       *
++       * Note that this is one of the places MMC/SD plays games with the
++       * SPI protocol.  Another is that when chipselect is released while
++       * the card returns BUSY status, the clock must issue several cycles
++       * with chipselect high before the card will stop driving its output.
++       */
++      host->spi->mode |= SPI_CS_HIGH;
++      if (spi_setup(host->spi) != 0) {
++              /* Just warn; most cards work without it. */
++              dev_warn(&host->spi->dev,
++                              "can't change chip-select polarity\n");
++              host->spi->mode &= ~SPI_CS_HIGH;
++      } else {
++              mmc_spi_readbytes(host, 18);
++
++              host->spi->mode &= ~SPI_CS_HIGH;
++              if (spi_setup(host->spi) != 0) {
++                      /* Wot, we can't get the same setup we had before? */
++                      dev_err(&host->spi->dev,
++                                      "can't restore chip-select polarity\n");
++              }
++      }
++}
++
++static char *mmc_powerstring(u8 power_mode)
++{
++      switch (power_mode) {
++      case MMC_POWER_OFF: return "off";
++      case MMC_POWER_UP:  return "up";
++      case MMC_POWER_ON:  return "on";
++      }
++      return "?";
++}
++
++static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
++{
++      struct mmc_spi_host *host = mmc_priv(mmc);
++
++      if (host->power_mode != ios->power_mode) {
++              int             canpower;
++
++              canpower = host->pdata && host->pdata->setpower;
++
++              dev_dbg(&host->spi->dev, "mmc_spi: power %s (%d)%s\n",
++                              mmc_powerstring(ios->power_mode),
++                              ios->vdd,
++                              canpower ? ", can switch" : "");
++
++              /* switch power on/off if possible, accounting for
++               * max 250msec powerup time if needed.
++               */
++              if (canpower) {
++                      switch (ios->power_mode) {
++                      case MMC_POWER_OFF:
++                      case MMC_POWER_UP:
++                              host->pdata->setpower(&host->spi->dev,
++                                              ios->vdd);
++                              if (ios->power_mode == MMC_POWER_UP)
++                                      msleep(host->powerup_msecs);
++                      }
++              }
++
++              /* See 6.4.1 in the simplified SD card physical spec 2.0 */
++              if (ios->power_mode == MMC_POWER_ON)
++                      mmc_spi_initsequence(host);
++
++              /* If powering down, ground all card inputs to avoid power
++               * delivery from data lines!  On a shared SPI bus, this
++               * will probably be temporary; 6.4.2 of the simplified SD
++               * spec says this must last at least 1msec.
++               *
++               *   - Clock low means CPOL 0, e.g. mode 0
++               *   - MOSI low comes from writing zero
++               *   - Chipselect is usually active low...
++               */
++              if (canpower && ios->power_mode == MMC_POWER_OFF) {
++                      int mres;
++
++                      host->spi->mode &= ~(SPI_CPOL|SPI_CPHA);
++                      mres = spi_setup(host->spi);
++                      if (mres < 0)
++                              dev_dbg(&host->spi->dev,
++                                      "switch to SPI mode 0 failed\n");
++
++                      if (spi_w8r8(host->spi, 0x00) < 0)
++                              dev_dbg(&host->spi->dev,
++                                      "put spi signals to low failed\n");
++
++                      /*
++                       * Now clock should be low due to spi mode 0;
++                       * MOSI should be low because of written 0x00;
++                       * chipselect should be low (it is active low)
++                       * power supply is off, so now MMC is off too!
++                       *
++                       * FIXME no, chipselect can be high since the
++                       * device is inactive and SPI_CS_HIGH is clear...
++                       */
++                      msleep(10);
++                      if (mres == 0) {
++                              host->spi->mode |= (SPI_CPOL|SPI_CPHA);
++                              mres = spi_setup(host->spi);
++                              if (mres < 0)
++                                      dev_dbg(&host->spi->dev,
++                                              "switch back to SPI mode 3"
++                                              " failed\n");
++                      }
++              }
++
++              host->power_mode = ios->power_mode;
++      }
++
++      if (host->spi->max_speed_hz != ios->clock && ios->clock != 0) {
++              int             status;
++
++              host->spi->max_speed_hz = ios->clock;
++              status = spi_setup(host->spi);
++              dev_dbg(&host->spi->dev,
++                      "mmc_spi:  clock to %d Hz, %d\n",
++                      host->spi->max_speed_hz, status);
++      }
++}
++
++static int mmc_spi_get_ro(struct mmc_host *mmc)
++{
++      struct mmc_spi_host *host = mmc_priv(mmc);
++
++      if (host->pdata && host->pdata->get_ro)
++              return host->pdata->get_ro(mmc->parent);
++      /* board doesn't support read only detection; assume writeable */
++      return 0;
++}
++
++
++static const struct mmc_host_ops mmc_spi_ops = {
++      .request        = mmc_spi_request,
++      .set_ios        = mmc_spi_set_ios,
++      .get_ro         = mmc_spi_get_ro,
++};
++
++
++/****************************************************************************/
++
++/*
++ * SPI driver implementation
++ */
++
++static irqreturn_t
++mmc_spi_detect_irq(int irq, void *mmc)
++{
++      struct mmc_spi_host *host = mmc_priv(mmc);
++      u16 delay_msec = max(host->pdata->detect_delay, (u16)100);
++
++      mmc_detect_change(mmc, msecs_to_jiffies(delay_msec));
++      return IRQ_HANDLED;
++}
++
++struct count_children {
++      unsigned        n;
++      struct bus_type *bus;
++};
++
++static int maybe_count_child(struct device *dev, void *c)
++{
++      struct count_children *ccp = c;
++
++      if (dev->bus == ccp->bus) {
++              if (ccp->n)
++                      return -EBUSY;
++              ccp->n++;
++      }
++      return 0;
++}
++
++static int mmc_spi_probe(struct spi_device *spi)
++{
++      void                    *ones;
++      struct mmc_host         *mmc;
++      struct mmc_spi_host     *host;
++      int                     status;
++
++      /* MMC and SD specs only seem to care that sampling is on the
++       * rising edge ... meaning SPI modes 0 or 3.  So either SPI mode
++       * should be legit.  We'll use mode 0 since it seems to be a
++       * bit less troublesome on some hardware ... unclear why.
++       */
++      spi->mode = SPI_MODE_0;
++      spi->bits_per_word = 8;
++
++      status = spi_setup(spi);
++      if (status < 0) {
++              dev_dbg(&spi->dev, "needs SPI mode %02x, %d KHz; %d\n",
++                              spi->mode, spi->max_speed_hz / 1000,
++                              status);
++              return status;
++      }
++
++      /* We can use the bus safely iff nobody else will interfere with us.
++       * Most commands consist of one SPI message to issue a command, then
++       * several more to collect its response, then possibly more for data
++       * transfer.  Clocking access to other devices during that period will
++       * corrupt the command execution.
++       *
++       * Until we have software primitives which guarantee non-interference,
++       * we'll aim for a hardware-level guarantee.
++       *
++       * REVISIT we can't guarantee another device won't be added later...
++       */
++      if (spi->master->num_chipselect > 1) {
++              struct count_children cc;
++
++              cc.n = 0;
++              cc.bus = spi->dev.bus;
++              status = device_for_each_child(spi->dev.parent, &cc,
++                              maybe_count_child);
++              if (status < 0) {
++                      dev_err(&spi->dev, "can't share SPI bus\n");
++                      return status;
++              }
++
++              dev_warn(&spi->dev, "ASSUMING SPI bus stays unshared!\n");
++      }
++
++      /* We need a supply of ones to transmit.  This is the only time
++       * the CPU touches these, so cache coherency isn't a concern.
++       *
++       * NOTE if many systems use more than one MMC-over-SPI connector
++       * it'd save some memory to share this.  That's evidently rare.
++       */
++      status = -ENOMEM;
++      ones = kmalloc(MMC_SPI_BLOCKSIZE, GFP_KERNEL);
++      if (!ones)
++              goto nomem;
++      memset(ones, 0xff, MMC_SPI_BLOCKSIZE);
++
++      mmc = mmc_alloc_host(sizeof(*host), &spi->dev);
++      if (!mmc)
++              goto nomem;
++
++      mmc->ops = &mmc_spi_ops;
++      mmc->max_blk_size = MMC_SPI_BLOCKSIZE;
++
++      /* As long as we keep track of the number of successfully
++       * transmitted blocks, we're good for multiwrite.
++       */
++      mmc->caps = MMC_CAP_SPI | MMC_CAP_MULTIWRITE;
++
++      /* SPI doesn't need the lowspeed device identification thing for
++       * MMC or SD cards, since it never comes up in open drain mode.
++       * That's good; some SPI masters can't handle very low speeds!
++       *
++       * However, low speed SDIO cards need not handle over 400 KHz;
++       * that's the only reason not to use a few MHz for f_min (until
++       * the upper layer reads the target frequency from the CSD).
++       */
++      mmc->f_min = 400000;
++      mmc->f_max = spi->max_speed_hz;
++
++      host = mmc_priv(mmc);
++      host->mmc = mmc;
++      host->spi = spi;
++
++      host->ones = ones;
++
++      /* Platform data is used to hook up things like card sensing
++       * and power switching gpios.
++       */
++      host->pdata = spi->dev.platform_data;
++      if (host->pdata)
++              mmc->ocr_avail = host->pdata->ocr_mask;
++      if (!mmc->ocr_avail) {
++              dev_warn(&spi->dev, "ASSUMING 3.2-3.4 V slot power\n");
++              mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
++      }
++      if (host->pdata && host->pdata->setpower) {
++              host->powerup_msecs = host->pdata->powerup_msecs;
++              if (!host->powerup_msecs || host->powerup_msecs > 250)
++                      host->powerup_msecs = 250;
++      }
++
++      dev_set_drvdata(&spi->dev, mmc);
++
++      /* preallocate dma buffers */
++      host->data = kmalloc(sizeof(*host->data), GFP_KERNEL);
++      if (!host->data)
++              goto fail_nobuf1;
++
++//FIXME
++#if 0
++      if (spi->master->dev.parent->dma_mask) {
++              struct device   *dev = spi->master->dev.parent;
++
++              host->dma_dev = dev;
++              host->ones_dma = dma_map_single(dev, ones,
++                              MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
++              host->data_dma = dma_map_single(dev, host->data,
++                              sizeof(*host->data), DMA_BIDIRECTIONAL);
++
++              /* REVISIT in theory those map operations can fail... */
++
++              dma_sync_single_for_cpu(host->dma_dev,
++                              host->data_dma, sizeof(*host->data),
++                              DMA_BIDIRECTIONAL);
++      }
++#endif
++
++      /* setup message for status/busy readback */
++      spi_message_init(&host->readback);
++      host->readback.is_dma_mapped = (host->dma_dev != NULL);
++
++      spi_message_add_tail(&host->status, &host->readback);
++      host->status.tx_buf = host->ones;
++      host->status.tx_dma = host->ones_dma;
++      host->status.rx_buf = &host->data->status;
++      host->status.rx_dma = host->data_dma + offsetof(struct scratch, status);
++      host->status.cs_change = 1;
++
++      /* register card detect irq */
++      if (host->pdata && host->pdata->init) {
++              status = host->pdata->init(&spi->dev, mmc_spi_detect_irq, mmc);
++              if (status != 0)
++                      goto fail_glue_init;
++      }
++
++      status = mmc_add_host(mmc);
++      if (status != 0)
++              goto fail_add_host;
++
++      dev_info(&spi->dev, "SD/MMC host %s%s%s%s\n",
++                      mmc->class_dev.bus_id,
++                      host->dma_dev ? "" : ", no DMA",
++                      (host->pdata && host->pdata->get_ro)
++                              ? "" : ", no WP",
++                      (host->pdata && host->pdata->setpower)
++                              ? "" : ", no poweroff");
++      return 0;
++
++fail_add_host:
++      mmc_remove_host (mmc);
++fail_glue_init:
++      if (host->dma_dev)
++              dma_unmap_single(host->dma_dev, host->data_dma,
++                              sizeof(*host->data), DMA_BIDIRECTIONAL);
++      kfree(host->data);
++
++fail_nobuf1:
++      mmc_free_host(mmc);
++      dev_set_drvdata(&spi->dev, NULL);
++
++nomem:
++      kfree(ones);
++      return status;
++}
++
++
++static int __devexit mmc_spi_remove(struct spi_device *spi)
++{
++      struct mmc_host         *mmc = dev_get_drvdata(&spi->dev);
++      struct mmc_spi_host     *host;
++
++      if (mmc) {
++              host = mmc_priv(mmc);
++
++              /* prevent new mmc_detect_change() calls */
++              if (host->pdata && host->pdata->exit)
++                      host->pdata->exit(&spi->dev, mmc);
++
++              mmc_remove_host(mmc);
++
++              if (host->dma_dev) {
++                      dma_unmap_single(host->dma_dev, host->ones_dma,
++                              MMC_SPI_BLOCKSIZE, DMA_TO_DEVICE);
++                      dma_unmap_single(host->dma_dev, host->data_dma,
++                              sizeof(*host->data), DMA_BIDIRECTIONAL);
++              }
++
++              kfree(host->data);
++              kfree(host->ones);
++
++              spi->max_speed_hz = mmc->f_max;
++              mmc_free_host(mmc);
++              dev_set_drvdata(&spi->dev, NULL);
++      }
++      return 0;
++}
++
++
++static struct spi_driver mmc_spi_driver = {
++      .driver = {
++              .name =         "mmc_spi",
++              .bus =          &spi_bus_type,
++              .owner =        THIS_MODULE,
++      },
++      .probe =        mmc_spi_probe,
++      .remove =       __devexit_p(mmc_spi_remove),
++};
++
++
++static int __init mmc_spi_init(void)
++{
++      return spi_register_driver(&mmc_spi_driver);
++}
++module_init(mmc_spi_init);
++
++
++static void __exit mmc_spi_exit(void)
++{
++      spi_unregister_driver(&mmc_spi_driver);
++}
++module_exit(mmc_spi_exit);
++
++
++MODULE_AUTHOR("Mike Lavender, David Brownell, "
++              "Hans-Peter Nilsson, Jan Nikitenko");
++MODULE_DESCRIPTION("SPI SD/MMC host driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.23.16/include/linux/spi/mmc_spi.h
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/include/linux/spi/mmc_spi.h        2008-03-21 17:30:25.000000000 +0100
+@@ -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: linux-2.6.23.16/drivers/mmc/core/bus.c
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/core/bus.c        2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/core/bus.c     2008-03-21 17:30:25.000000000 +0100
+@@ -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 devi
+               return sprintf(buf, "MMC\n");
+       case MMC_TYPE_SD:
+               return sprintf(buf, "SD\n");
++      case MMC_TYPE_SDIO:
++              return sprintf(buf, "SDIO\n");
+       default:
+               return -EFAULT;
+       }
+@@ -55,36 +58,37 @@ static int mmc_bus_match(struct device *
+ }
+ static int
+-mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
+-              int buf_size)
++mmc_bus_uevent(struct device *dev, char **envp,
++              int num_envp, char *buffer, int buffer_size)
+ {
+       struct mmc_card *card = dev_to_mmc_card(dev);
+-      int retval = 0, i = 0, length = 0;
+-
+-#define add_env(fmt,val) do {                                 \
+-      retval = add_uevent_var(envp, num_envp, &i,             \
+-                              buf, buf_size, &length,         \
+-                              fmt, val);                      \
+-      if (retval)                                             \
+-              return retval;                                  \
+-} while (0);
++      const char *type;
++      int retval = 0;
++      int i = 0, len = 0;
+       switch (card->type) {
+       case MMC_TYPE_MMC:
+-              add_env("MMC_TYPE=%s", "MMC");
++              type = "MMC";
+               break;
+       case MMC_TYPE_SD:
+-              add_env("MMC_TYPE=%s", "SD");
++              type = "SD";
+               break;
++      case MMC_TYPE_SDIO:
++              type = "SDIO";
++              break;
++      default:
++              type = NULL;
+       }
+-      add_env("MMC_NAME=%s", mmc_card_name(card));
+-
+-#undef add_env
++      if (type) {
++              retval = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, "MMC_TYPE=%s", type);
++              if (retval)
++                      return retval;
++      }
+-      envp[i] = NULL;
++      retval = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, "MMC_NAME=%s", mmc_card_name(card));
+-      return 0;
++      return retval;
+ }
+ static int mmc_bus_probe(struct device *dev)
+@@ -176,6 +180,11 @@ static void mmc_release_card(struct devi
+ {
+       struct mmc_card *card = dev_to_mmc_card(dev);
++      sdio_free_common_cis(card);
++
++      if (card->info)
++              kfree(card->info);
++
+       kfree(card);
+ }
+@@ -221,15 +230,25 @@ 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 +280,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: linux-2.6.23.16/drivers/mmc/core/core.c
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/core/core.c       2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/core/core.c    2008-03-21 17:30:25.000000000 +0100
+@@ -18,7 +18,7 @@
+ #include <linux/delay.h>
+ #include <linux/pagemap.h>
+ #include <linux/err.h>
+-#include <asm/scatterlist.h>
++#include <linux/leds.h>
+ #include <linux/scatterlist.h>
+ #include <linux/mmc/card.h>
+@@ -29,16 +29,27 @@
+ #include "core.h"
+ #include "bus.h"
+ #include "host.h"
++#include "sdio_bus.h"
+ #include "mmc_ops.h"
+ #include "sd_ops.h"
++#include "sdio_ops.h"
+ extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
+ extern int mmc_attach_sd(struct mmc_host *host, u32 ocr);
++extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
+ static struct workqueue_struct *workqueue;
+ /*
++ * Enabling software CRCs on the data blocks can be a significant (30%)
++ * performance cost, and for other reasons may not always be desired.
++ * So we allow it it to be disabled.
++ */
++int use_spi_crc = 1;
++module_param(use_spi_crc, bool, 0);
++
++/*
+  * Internal function. Schedule delayed work in the MMC work queue.
+  */
+ static int mmc_schedule_delayed_work(struct delayed_work *work,
+@@ -68,6 +79,11 @@ void mmc_request_done(struct mmc_host *h
+       struct mmc_command *cmd = mrq->cmd;
+       int err = cmd->error;
++      if (err && cmd->retries && mmc_host_is_spi(host)) {
++              if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
++                      cmd->retries = 0;
++      }
++
+       if (err && cmd->retries) {
+               pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
+                       mmc_hostname(host), cmd->opcode, err);
+@@ -76,6 +92,8 @@ void mmc_request_done(struct mmc_host *h
+               cmd->error = 0;
+               host->ops->request(host, mrq);
+       } else {
++              led_trigger_event(host->led, LED_OFF);
++
+               pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
+                       mmc_hostname(host), cmd->opcode, err,
+                       cmd->resp[0], cmd->resp[1],
+@@ -118,7 +136,7 @@ mmc_start_request(struct mmc_host *host,
+                       "tsac %d ms nsac %d\n",
+                       mmc_hostname(host), mrq->data->blksz,
+                       mrq->data->blocks, mrq->data->flags,
+-                      mrq->data->timeout_ns / 10000000,
++                      mrq->data->timeout_ns / 1000000,
+                       mrq->data->timeout_clks);
+       }
+@@ -130,6 +148,8 @@ mmc_start_request(struct mmc_host *host,
+       WARN_ON(!host->claimed);
++      led_trigger_event(host->led, LED_FULL);
++
+       mrq->cmd->error = 0;
+       mrq->cmd->mrq = mrq;
+       if (mrq->data) {
+@@ -199,7 +219,7 @@ int mmc_wait_for_cmd(struct mmc_host *ho
+ {
+       struct mmc_request mrq;
+-      BUG_ON(!host->claimed);
++      WARN_ON(!host->claimed);
+       memset(&mrq, 0, sizeof(struct mmc_request));
+@@ -220,17 +240,24 @@ 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
+        */
+       mult = mmc_card_sd(card) ? 100 : 10;
+@@ -239,7 +266,7 @@ void mmc_set_data_timeout(struct mmc_dat
+        * Scale up the multiplier (and therefore the timeout) by
+        * the r2w factor for writes.
+        */
+-      if (write)
++      if (data->flags & MMC_DATA_WRITE)
+               mult <<= card->csd.r2w_factor;
+       data->timeout_ns = card->csd.tacc_ns * mult;
+@@ -255,7 +282,7 @@ void mmc_set_data_timeout(struct mmc_dat
+               timeout_us += data->timeout_clks * 1000 /
+                       (card->host->ios.clock / 1000);
+-              if (write)
++              if (data->flags & MMC_DATA_WRITE)
+                       limit_us = 250000;
+               else
+                       limit_us = 100000;
+@@ -272,15 +299,20 @@ void mmc_set_data_timeout(struct mmc_dat
+ EXPORT_SYMBOL(mmc_set_data_timeout);
+ /**
+- *    mmc_claim_host - exclusively claim a host
++ *    __mmc_claim_host - exclusively claim a host
+  *    @host: mmc host to claim
++ *    @abort: whether or not the operation should be aborted
+  *
+- *    Claim a host for a set of operations.
++ *    Claim a host for a set of operations.  If @abort is non null and
++ *    dereference a non-zero value then this will return prematurely with
++ *    that non-zero value without acquiring the lock.  Returns zero
++ *    with the lock held otherwise.
+  */
+-void mmc_claim_host(struct mmc_host *host)
++int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
+ {
+       DECLARE_WAITQUEUE(wait, current);
+       unsigned long flags;
++      int stop;
+       might_sleep();
+@@ -288,19 +320,24 @@ void mmc_claim_host(struct mmc_host *hos
+       spin_lock_irqsave(&host->lock, flags);
+       while (1) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+-              if (!host->claimed)
++              stop = abort ? atomic_read(abort) : 0;
++              if (stop || !host->claimed)
+                       break;
+               spin_unlock_irqrestore(&host->lock, flags);
+               schedule();
+               spin_lock_irqsave(&host->lock, flags);
+       }
+       set_current_state(TASK_RUNNING);
+-      host->claimed = 1;
++      if (!stop)
++              host->claimed = 1;
++      else
++              wake_up(&host->wq);
+       spin_unlock_irqrestore(&host->lock, flags);
+       remove_wait_queue(&host->wq, &wait);
++      return stop;
+ }
+-EXPORT_SYMBOL(mmc_claim_host);
++EXPORT_SYMBOL(__mmc_claim_host);
+ /**
+  *    mmc_release_host - release a host
+@@ -313,7 +350,7 @@ void mmc_release_host(struct mmc_host *h
+ {
+       unsigned long flags;
+-      BUG_ON(!host->claimed);
++      WARN_ON(!host->claimed);
+       spin_lock_irqsave(&host->lock, flags);
+       host->claimed = 0;
+@@ -433,19 +470,32 @@ static void mmc_power_up(struct mmc_host
+       int bit = fls(host->ocr_avail) - 1;
+       host->ios.vdd = bit;
+-      host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+-      host->ios.chip_select = MMC_CS_DONTCARE;
++      if (mmc_host_is_spi(host)) {
++              host->ios.chip_select = MMC_CS_HIGH;
++              host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
++      } else {
++              host->ios.chip_select = MMC_CS_DONTCARE;
++              host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
++      }
+       host->ios.power_mode = MMC_POWER_UP;
+       host->ios.bus_width = MMC_BUS_WIDTH_1;
+       host->ios.timing = MMC_TIMING_LEGACY;
+       mmc_set_ios(host);
+-      mmc_delay(1);
++      /*
++       * This delay should be sufficient to allow the power supply
++       * to reach the minimum voltage.
++       */
++      mmc_delay(2);
+       host->ios.clock = host->f_min;
+       host->ios.power_mode = MMC_POWER_ON;
+       mmc_set_ios(host);
++      /*
++       * This delay must be at least 74 clock sizes, or 1 ms, or the
++       * time required to reach a stable voltage.
++       */
+       mmc_delay(2);
+ }
+@@ -453,8 +503,10 @@ static void mmc_power_off(struct mmc_hos
+ {
+       host->ios.clock = 0;
+       host->ios.vdd = 0;
+-      host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
+-      host->ios.chip_select = MMC_CS_DONTCARE;
++      if (!mmc_host_is_spi(host)) {
++              host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
++              host->ios.chip_select = MMC_CS_DONTCARE;
++      }
+       host->ios.power_mode = MMC_POWER_OFF;
+       host->ios.bus_width = MMC_BUS_WIDTH_1;
+       host->ios.timing = MMC_TIMING_LEGACY;
+@@ -511,7 +563,7 @@ void mmc_attach_bus(struct mmc_host *hos
+       BUG_ON(!host);
+       BUG_ON(!ops);
+-      BUG_ON(!host->claimed);
++      WARN_ON(!host->claimed);
+       spin_lock_irqsave(&host->lock, flags);
+@@ -535,8 +587,8 @@ void mmc_detach_bus(struct mmc_host *hos
+       BUG_ON(!host);
+-      BUG_ON(!host->claimed);
+-      BUG_ON(!host->bus_ops);
++      WARN_ON(!host->claimed);
++      WARN_ON(!host->bus_ops);
+       spin_lock_irqsave(&host->lock, flags);
+@@ -564,7 +616,7 @@ void mmc_detect_change(struct mmc_host *
+ #ifdef CONFIG_MMC_DEBUG
+       unsigned long flags;
+       spin_lock_irqsave(&host->lock, flags);
+-      BUG_ON(host->removed);
++      WARN_ON(host->removed);
+       spin_unlock_irqrestore(&host->lock, flags);
+ #endif
+@@ -597,24 +649,38 @@ 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 +791,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: linux-2.6.23.16/drivers/mmc/core/core.h
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/core/core.h       2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/core/core.h    2008-03-21 17:30:25.000000000 +0100
+@@ -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: linux-2.6.23.16/drivers/mmc/core/host.c
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/core/host.c       2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/core/host.c    2008-03-21 17:30:25.000000000 +0100
+@@ -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 *ho
+       device_del(&host->class_dev);
++      led_trigger_unregister_simple(host->led);
++
+       spin_lock(&mmc_host_lock);
+       idr_remove(&mmc_host_idr, host->index);
+       spin_unlock(&mmc_host_lock);
+Index: linux-2.6.23.16/drivers/mmc/core/mmc.c
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/core/mmc.c        2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/core/mmc.c     2008-03-21 17:30:25.000000000 +0100
+@@ -161,13 +161,12 @@ static int mmc_read_ext_csd(struct mmc_c
+ {
+       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_c
+       ext_csd = kmalloc(512, GFP_KERNEL);
+       if (!ext_csd) {
+               printk(KERN_ERR "%s: could not allocate a buffer to "
+-                      "receive the ext_csd. mmc v4 cards will be "
+-                      "treated as v3.\n", mmc_hostname(card->host));
+-              return MMC_ERR_FAILED;
++                      "receive the ext_csd.\n", mmc_hostname(card->host));
++              return -ENOMEM;
+       }
+       err = mmc_send_ext_csd(card, ext_csd);
+-      if (err != MMC_ERR_NONE) {
++      if (err) {
++              /*
++               * We all hosts that cannot perform the command
++               * to fail more gracefully
++               */
++              if (err != -EINVAL)
++                      goto out;
++
+               /*
+                * High capacity cards should have this "magic" size
+                * stored in their CSD.
+@@ -197,18 +202,30 @@ static int mmc_read_ext_csd(struct mmc_c
+                               "EXT_CSD, performance might "
+                               "suffer.\n",
+                               mmc_hostname(card->host));
+-                      err = MMC_ERR_NONE;
++                      err = 0;
+               }
++
+               goto out;
+       }
+-      card->ext_csd.sectors =
+-              ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
+-              ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
+-              ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
+-              ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
+-      if (card->ext_csd.sectors)
+-              mmc_card_set_blockaddr(card);
++      ext_csd_struct = ext_csd[EXT_CSD_REV];
++      if (ext_csd_struct > 2) {
++              printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
++                      "version %d\n", mmc_hostname(card->host),
++                      ext_csd_struct);
++              err = -EINVAL;
++              goto out;
++      }
++
++      if (ext_csd_struct >= 2) {
++              card->ext_csd.sectors =
++                      ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
++                      ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
++                      ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
++                      ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
++              if (card->ext_csd.sectors)
++                      mmc_card_set_blockaddr(card);
++      }
+       switch (ext_csd[EXT_CSD_CARD_TYPE]) {
+       case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
+@@ -246,7 +263,7 @@ static int mmc_init_card(struct mmc_host
+       unsigned int max_dtr;
+       BUG_ON(!host);
+-      BUG_ON(!host->claimed);
++      WARN_ON(!host->claimed);
+       /*
+        * Since we're changing the OCR value, we seem to
+@@ -258,19 +275,33 @@ static int mmc_init_card(struct mmc_host
+       /* The extra bit indicates that we support high capacity */
+       err = mmc_send_op_cond(host, ocr | (1 << 30), NULL);
+-      if (err != MMC_ERR_NONE)
++      if (err)
+               goto err;
+       /*
++       * For SPI, enable CRC as appropriate.
++       */
++      if (mmc_host_is_spi(host)) {
++              err = mmc_spi_set_crc(host, use_spi_crc);
++              if (err)
++                      goto err;
++      }
++
++      /*
+        * Fetch CID from card.
+        */
+-      err = mmc_all_send_cid(host, cid);
+-      if (err != MMC_ERR_NONE)
++      if (mmc_host_is_spi(host))
++              err = mmc_send_cid(host, cid);
++      else
++              err = mmc_all_send_cid(host, cid);
++      if (err)
+               goto err;
+       if (oldcard) {
+-              if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
++              if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
++                      err = -ENOENT;
+                       goto err;
++              }
+               card = oldcard;
+       } else {
+@@ -278,8 +309,10 @@ static int mmc_init_card(struct mmc_host
+                * Allocate card structure.
+                */
+               card = mmc_alloc_card(host);
+-              if (IS_ERR(card))
++              if (IS_ERR(card)) {
++                      err = PTR_ERR(card);
+                       goto err;
++              }
+               card->type = MMC_TYPE_MMC;
+               card->rca = 1;
+@@ -287,43 +320,47 @@ static int mmc_init_card(struct mmc_host
+       }
+       /*
+-       * Set card RCA.
++       * For native busses:  set card RCA and quit open drain mode.
+        */
+-      err = mmc_set_relative_addr(card);
+-      if (err != MMC_ERR_NONE)
+-              goto free_card;
++      if (!mmc_host_is_spi(host)) {
++              err = mmc_set_relative_addr(card);
++              if (err)
++                      goto free_card;
+-      mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
++              mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
++      }
+       if (!oldcard) {
+               /*
+                * Fetch CSD from card.
+                */
+               err = mmc_send_csd(card, card->raw_csd);
+-              if (err != MMC_ERR_NONE)
++              if (err)
+                       goto free_card;
+               err = mmc_decode_csd(card);
+-              if (err < 0)
++              if (err)
+                       goto free_card;
+               err = mmc_decode_cid(card);
+-              if (err < 0)
++              if (err)
+                       goto free_card;
+       }
+       /*
+        * Select card, as all following commands rely on that.
+        */
+-      err = mmc_select_card(card);
+-      if (err != MMC_ERR_NONE)
+-              goto free_card;
++      if (!mmc_host_is_spi(host)) {
++              err = mmc_select_card(card);
++              if (err)
++                      goto free_card;
++      }
+       if (!oldcard) {
+               /*
+-               * Fetch and process extened CSD.
++               * Fetch and process extended CSD.
+                */
+               err = mmc_read_ext_csd(card);
+-              if (err != MMC_ERR_NONE)
++              if (err)
+                       goto free_card;
+       }
+@@ -334,7 +371,7 @@ static int mmc_init_card(struct mmc_host
+               (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
+               err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                       EXT_CSD_HS_TIMING, 1);
+-              if (err != MMC_ERR_NONE)
++              if (err)
+                       goto free_card;
+               mmc_card_set_highspeed(card);
+@@ -363,7 +400,7 @@ static int mmc_init_card(struct mmc_host
+               (host->caps & MMC_CAP_4_BIT_DATA)) {
+               err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+                       EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
+-              if (err != MMC_ERR_NONE)
++              if (err)
+                       goto free_card;
+               mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+@@ -372,14 +409,14 @@ static int mmc_init_card(struct mmc_host
+       if (!oldcard)
+               host->card = card;
+-      return MMC_ERR_NONE;
++      return 0;
+ free_card:
+       if (!oldcard)
+               mmc_remove_card(card);
+ err:
+-      return MMC_ERR_FAILED;
++      return err;
+ }
+ /*
+@@ -413,7 +450,7 @@ static void mmc_detect(struct mmc_host *
+       mmc_release_host(host);
+-      if (err != MMC_ERR_NONE) {
++      if (err) {
+               mmc_remove(host);
+               mmc_claim_host(host);
+@@ -480,7 +517,8 @@ static void mmc_suspend(struct mmc_host 
+       BUG_ON(!host->card);
+       mmc_claim_host(host);
+-      mmc_deselect_cards(host);
++      if (!mmc_host_is_spi(host))
++              mmc_deselect_cards(host);
+       host->card->state &= ~MMC_STATE_HIGHSPEED;
+       mmc_release_host(host);
+ }
+@@ -502,7 +540,7 @@ static void mmc_resume(struct mmc_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,11 +574,20 @@ int mmc_attach_mmc(struct mmc_host *host
+       int err;
+       BUG_ON(!host);
+-      BUG_ON(!host->claimed);
++      WARN_ON(!host->claimed);
+       mmc_attach_bus(host, &mmc_ops);
+       /*
++       * We need to get OCR a different way for SPI.
++       */
++      if (mmc_host_is_spi(host)) {
++              err = mmc_spi_read_ocr(host, 1, &ocr);
++              if (err)
++                      goto err;
++      }
++
++      /*
+        * Sanity check the voltages that the card claims to
+        * support.
+        */
+@@ -565,7 +612,7 @@ int mmc_attach_mmc(struct mmc_host *host
+        * Detect and init the card.
+        */
+       err = mmc_init_card(host, host->ocr, NULL);
+-      if (err != MMC_ERR_NONE)
++      if (err)
+               goto err;
+       mmc_release_host(host);
+@@ -587,6 +634,6 @@ err:
+       printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
+               mmc_hostname(host), err);
+-      return 0;
++      return err;
+ }
+Index: linux-2.6.23.16/drivers/mmc/core/mmc_ops.c
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/core/mmc_ops.c    2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/core/mmc_ops.c 2008-03-21 17:30:25.000000000 +0100
+@@ -10,7 +10,6 @@
+  */
+ #include <linux/types.h>
+-#include <asm/scatterlist.h>
+ #include <linux/scatterlist.h>
+ #include <linux/mmc/host.h>
+@@ -40,10 +39,10 @@ static int _mmc_select_card(struct mmc_h
+       }
+       err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+-      if (err != MMC_ERR_NONE)
++      if (err)
+               return err;
+-      return MMC_ERR_NONE;
++      return 0;
+ }
+ int mmc_select_card(struct mmc_card *card)
+@@ -63,23 +62,36 @@ int 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 +106,33 @@ int mmc_send_op_cond(struct mmc_host *ho
+       memset(&cmd, 0, sizeof(struct mmc_command));
+       cmd.opcode = MMC_SEND_OP_COND;
+-      cmd.arg = ocr;
+-      cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
++      cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
++      cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
+       for (i = 100; i; i--) {
+               err = mmc_wait_for_cmd(host, &cmd, 0);
+-              if (err != MMC_ERR_NONE)
++              if (err)
+                       break;
+-              if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
++              /* if we're just probing, do a single pass */
++              if (ocr == 0)
+                       break;
+-              err = MMC_ERR_TIMEOUT;
++              /* otherwise wait until reset completes */
++              if (mmc_host_is_spi(host)) {
++                      if (!(cmd.resp[0] & R1_SPI_IDLE))
++                              break;
++              } else {
++                      if (cmd.resp[0] & MMC_CARD_BUSY)
++                              break;
++              }
++
++              err = -ETIMEDOUT;
+               mmc_delay(10);
+       }
+-      if (rocr)
++      if (rocr && !mmc_host_is_spi(host))
+               *rocr = cmd.resp[0];
+       return err;
+@@ -131,12 +153,12 @@ int mmc_all_send_cid(struct mmc_host *ho
+       cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
+       err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+-      if (err != MMC_ERR_NONE)
++      if (err)
+               return err;
+       memcpy(cid, cmd.resp, sizeof(u32) * 4);
+-      return MMC_ERR_NONE;
++      return 0;
+ }
+ int mmc_set_relative_addr(struct mmc_card *card)
+@@ -154,46 +176,52 @@ int mmc_set_relative_addr(struct mmc_car
+       cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+       err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+-      if (err != MMC_ERR_NONE)
++      if (err)
+               return err;
+-      return MMC_ERR_NONE;
++      return 0;
+ }
+-int mmc_send_csd(struct mmc_card *card, u32 *csd)
++static int
++mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
+ {
+       int err;
+       struct mmc_command cmd;
+-      BUG_ON(!card);
+-      BUG_ON(!card->host);
+-      BUG_ON(!csd);
++      BUG_ON(!host);
++      BUG_ON(!cxd);
+       memset(&cmd, 0, sizeof(struct mmc_command));
+-      cmd.opcode = MMC_SEND_CSD;
+-      cmd.arg = card->rca << 16;
++      cmd.opcode = opcode;
++      cmd.arg = arg;
+       cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
+-      err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+-      if (err != MMC_ERR_NONE)
++      err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
++      if (err)
+               return err;
+-      memcpy(csd, cmd.resp, sizeof(u32) * 4);
++      memcpy(cxd, cmd.resp, sizeof(u32) * 4);
+-      return MMC_ERR_NONE;
++      return 0;
+ }
+-int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
++static int
++mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
++              u32 opcode, void *buf, unsigned len)
+ {
+       struct mmc_request mrq;
+       struct mmc_command cmd;
+       struct mmc_data data;
+       struct scatterlist sg;
++      void *data_buf;
+-      BUG_ON(!card);
+-      BUG_ON(!card->host);
+-      BUG_ON(!ext_csd);
++      /* dma onto stack is unsafe/nonportable, but callers to this
++       * routine normally provide temporary on-stack buffers ...
++       */
++      data_buf = kmalloc(len, GFP_KERNEL);
++      if (data_buf == NULL)
++              return -ENOMEM;
+       memset(&mrq, 0, sizeof(struct mmc_request));
+       memset(&cmd, 0, sizeof(struct mmc_command));
+@@ -202,28 +230,117 @@ int mmc_send_ext_csd(struct mmc_card *ca
+       mrq.cmd = &cmd;
+       mrq.data = &data;
+-      cmd.opcode = MMC_SEND_EXT_CSD;
++      cmd.opcode = opcode;
+       cmd.arg = 0;
+-      cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+-      data.blksz = 512;
++      /* NOTE HACK:  the MMC_RSP_SPI_R1 is always correct here, but we
++       * rely on callers to never use this with "native" calls for reading
++       * CSD or CID.  Native versions of those commands use the R2 type,
++       * not R1 plus a data block.
++       */
++      cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
++
++      data.blksz = len;
+       data.blocks = 1;
+       data.flags = MMC_DATA_READ;
+       data.sg = &sg;
+       data.sg_len = 1;
+-      sg_init_one(&sg, ext_csd, 512);
++      sg_init_one(&sg, data_buf, len);
++
++      if (card)
++              mmc_set_data_timeout(&data, card);
+-      mmc_set_data_timeout(&data, card, 0);
++      mmc_wait_for_req(host, &mrq);
+-      mmc_wait_for_req(card->host, &mrq);
++      memcpy(buf, data_buf, len);
++      kfree(data_buf);
+-      if (cmd.error != MMC_ERR_NONE)
++      if (cmd.error)
+               return cmd.error;
+-      if (data.error != MMC_ERR_NONE)
++      if (data.error)
+               return data.error;
+-      return MMC_ERR_NONE;
++      return 0;
++}
++
++int mmc_send_csd(struct mmc_card *card, u32 *csd)
++{
++      int ret, i;
++
++      if (!mmc_host_is_spi(card->host))
++              return mmc_send_cxd_native(card->host, card->rca << 16,
++                              csd, MMC_SEND_CSD);
++
++      ret = mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd, 16);
++      if (ret)
++              return ret;
++
++      for (i = 0;i < 4;i++)
++              csd[i] = be32_to_cpu(csd[i]);
++
++      return 0;
++}
++
++int mmc_send_cid(struct mmc_host *host, u32 *cid)
++{
++      int ret, i;
++
++      if (!mmc_host_is_spi(host)) {
++              if (!host->card)
++                      return -EINVAL;
++              return mmc_send_cxd_native(host, host->card->rca << 16,
++                              cid, MMC_SEND_CID);
++      }
++
++      ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid, 16);
++      if (ret)
++              return ret;
++
++      for (i = 0;i < 4;i++)
++              cid[i] = be32_to_cpu(cid[i]);
++
++      return 0;
++}
++
++int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
++{
++      return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
++                      ext_csd, 512);
++}
++
++int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
++{
++      struct mmc_command cmd;
++      int err;
++
++      memset(&cmd, 0, sizeof(struct mmc_command));
++
++      cmd.opcode = MMC_SPI_READ_OCR;
++      cmd.arg = highcap ? (1 << 30) : 0;
++      cmd.flags = MMC_RSP_SPI_R3;
++
++      err = mmc_wait_for_cmd(host, &cmd, 0);
++
++      *ocrp = cmd.resp[1];
++      return err;
++}
++
++int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
++{
++      struct mmc_command cmd;
++      int err;
++
++      memset(&cmd, 0, sizeof(struct mmc_command));
++
++      cmd.opcode = MMC_SPI_CRC_ON_OFF;
++      cmd.flags = MMC_RSP_SPI_R1;
++      cmd.arg = use_crc;
++
++      err = mmc_wait_for_cmd(host, &cmd, 0);
++      if (!err)
++              host->use_spi_crc = use_crc;
++      return err;
+ }
+ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
+@@ -241,13 +358,13 @@ int mmc_switch(struct mmc_card *card, u8
+                 (index << 16) |
+                 (value << 8) |
+                 set;
+-      cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
++      cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+       err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+-      if (err != MMC_ERR_NONE)
++      if (err)
+               return err;
+-      return MMC_ERR_NONE;
++      return 0;
+ }
+ int mmc_send_status(struct mmc_card *card, u32 *status)
+@@ -261,16 +378,20 @@ int mmc_send_status(struct mmc_card *car
+       memset(&cmd, 0, sizeof(struct mmc_command));
+       cmd.opcode = MMC_SEND_STATUS;
+-      cmd.arg = card->rca << 16;
+-      cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
++      if (!mmc_host_is_spi(card->host))
++              cmd.arg = card->rca << 16;
++      cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+       err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+-      if (err != MMC_ERR_NONE)
++      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: linux-2.6.23.16/drivers/mmc/core/mmc_ops.h
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/core/mmc_ops.h    2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/core/mmc_ops.h 2008-03-21 17:30:25.000000000 +0100
+@@ -22,6 +22,9 @@ int mmc_send_csd(struct mmc_card *card, 
+ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
+ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
+ int mmc_send_status(struct mmc_card *card, u32 *status);
++int mmc_send_cid(struct mmc_host *host, u32 *cid);
++int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
++int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
+ #endif
+Index: linux-2.6.23.16/drivers/mmc/core/sd.c
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/core/sd.c 2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/core/sd.c      2008-03-21 17:30:25.000000000 +0100
+@@ -166,8 +166,6 @@ static int mmc_decode_scr(struct mmc_car
+       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_ca
+       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
+       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_h
+       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_h
+        * 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_h
+                * 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_h
+       /*
+        * Select card, as all following commands rely on that.
+        */
+-      err = mmc_select_card(card);
+-      if (err != MMC_ERR_NONE)
+-              goto free_card;
++      if (!mmc_host_is_spi(host)) {
++              err = mmc_select_card(card);
++              if (err)
++                      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_h
+                * 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_h
+        * 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_h
+       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_h
+       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_hos
+       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_ho
+       BUG_ON(!host->card);
+       mmc_claim_host(host);
+-      mmc_deselect_cards(host);
++      if (!mmc_host_is_spi(host))
++              mmc_deselect_cards(host);
+       host->card->state &= ~MMC_STATE_HIGHSPEED;
+       mmc_release_host(host);
+ }
+@@ -574,7 +601,7 @@ static void mmc_sd_resume(struct mmc_hos
+       err = mmc_sd_init_card(host, host->ocr, host->card);
+       mmc_release_host(host);
+-      if (err != MMC_ERR_NONE) {
++      if (err) {
+               mmc_sd_remove(host);
+               mmc_claim_host(host);
+@@ -608,11 +635,22 @@ int mmc_attach_sd(struct mmc_host *host,
+       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,
+        * 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: linux-2.6.23.16/drivers/mmc/core/sd_ops.c
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/core/sd_ops.c     2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/core/sd_ops.c  2008-03-21 17:30:25.000000000 +0100
+@@ -10,7 +10,6 @@
+  */
+ #include <linux/types.h>
+-#include <asm/scatterlist.h>
+ #include <linux/scatterlist.h>
+ #include <linux/mmc/host.h>
+@@ -33,21 +32,21 @@ static int mmc_app_cmd(struct mmc_host *
+       if (card) {
+               cmd.arg = card->rca << 16;
+-              cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
++              cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+       } else {
+               cmd.arg = 0;
+-              cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR;
++              cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR;
+       }
+       err = mmc_wait_for_cmd(host, &cmd, 0);
+-      if (err != MMC_ERR_NONE)
++      if (err)
+               return err;
+       /* Check that card supported application commands */
+-      if (!(cmd.resp[0] & R1_APP_CMD))
+-              return MMC_ERR_FAILED;
++      if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
++              return -EOPNOTSUPP;
+-      return MMC_ERR_NONE;
++      return 0;
+ }
+ /**
+@@ -73,7 +72,7 @@ int mmc_wait_for_app_cmd(struct mmc_host
+       BUG_ON(!cmd);
+       BUG_ON(retries < 0);
+-      err = MMC_ERR_INVALID;
++      err = -EIO;
+       /*
+        * We have to resend MMC_APP_CMD for each attempt so
+@@ -83,8 +82,14 @@ int mmc_wait_for_app_cmd(struct mmc_host
+               memset(&mrq, 0, sizeof(struct mmc_request));
+               err = mmc_app_cmd(host, card);
+-              if (err != MMC_ERR_NONE)
++              if (err) {
++                      /* no point in retrying; no APP commands allowed */
++                      if (mmc_host_is_spi(host)) {
++                              if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
++                                      break;
++                      }
+                       continue;
++              }
+               memset(&mrq, 0, sizeof(struct mmc_request));
+@@ -97,8 +102,14 @@ int mmc_wait_for_app_cmd(struct mmc_host
+               mmc_wait_for_req(host, &mrq);
+               err = cmd->error;
+-              if (cmd->error == MMC_ERR_NONE)
++              if (!cmd->error)
+                       break;
++
++              /* no point in retrying illegal APP commands */
++              if (mmc_host_is_spi(host)) {
++                      if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
++                              break;
++              }
+       }
+       return err;
+@@ -127,14 +138,14 @@ int mmc_app_set_bus_width(struct mmc_car
+               cmd.arg = SD_BUS_WIDTH_4;
+               break;
+       default:
+-              return MMC_ERR_INVALID;
++              return -EINVAL;
+       }
+       err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
+-      if (err != MMC_ERR_NONE)
++      if (err)
+               return err;
+-      return MMC_ERR_NONE;
++      return 0;
+ }
+ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
+@@ -147,23 +158,36 @@ int mmc_send_app_op_cond(struct mmc_host
+       memset(&cmd, 0, sizeof(struct mmc_command));
+       cmd.opcode = SD_APP_OP_COND;
+-      cmd.arg = ocr;
+-      cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
++      if (mmc_host_is_spi(host))
++              cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
++      else
++              cmd.arg = ocr;
++      cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
+       for (i = 100; i; i--) {
+               err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
+-              if (err != MMC_ERR_NONE)
++              if (err)
+                       break;
+-              if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
++              /* if we're just probing, do a single pass */
++              if (ocr == 0)
+                       break;
+-              err = MMC_ERR_TIMEOUT;
++              /* otherwise wait until reset completes */
++              if (mmc_host_is_spi(host)) {
++                      if (!(cmd.resp[0] & R1_SPI_IDLE))
++                              break;
++              } else {
++                      if (cmd.resp[0] & MMC_CARD_BUSY)
++                              break;
++              }
++
++              err = -ETIMEDOUT;
+               mmc_delay(10);
+       }
+-      if (rocr)
++      if (rocr && !mmc_host_is_spi(host))
+               *rocr = cmd.resp[0];
+       return err;
+@@ -174,6 +198,7 @@ int mmc_send_if_cond(struct mmc_host *ho
+       struct mmc_command cmd;
+       int err;
+       static const u8 test_pattern = 0xAA;
++      u8 result_pattern;
+       /*
+        * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
+@@ -182,16 +207,21 @@ int mmc_send_if_cond(struct mmc_host *ho
+        */
+       cmd.opcode = SD_SEND_IF_COND;
+       cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
+-      cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR;
++      cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;
+       err = mmc_wait_for_cmd(host, &cmd, 0);
+-      if (err != MMC_ERR_NONE)
++      if (err)
+               return err;
+-      if ((cmd.resp[0] & 0xFF) != test_pattern)
+-              return MMC_ERR_FAILED;
++      if (mmc_host_is_spi(host))
++              result_pattern = cmd.resp[1] & 0xFF;
++      else
++              result_pattern = cmd.resp[0] & 0xFF;
++
++      if (result_pattern != test_pattern)
++              return -EIO;
+-      return MMC_ERR_NONE;
++      return 0;
+ }
+ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
+@@ -209,12 +239,12 @@ int mmc_send_relative_addr(struct mmc_ho
+       cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
+       err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
+-      if (err != MMC_ERR_NONE)
++      if (err)
+               return err;
+       *rca = cmd.resp[0] >> 16;
+-      return MMC_ERR_NONE;
++      return 0;
+ }
+ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
+@@ -229,8 +259,10 @@ int mmc_app_send_scr(struct mmc_card *ca
+       BUG_ON(!card->host);
+       BUG_ON(!scr);
++      /* NOTE: caller guarantees scr is heap-allocated */
++
+       err = mmc_app_cmd(card->host, card);
+-      if (err != MMC_ERR_NONE)
++      if (err)
+               return err;
+       memset(&mrq, 0, sizeof(struct mmc_request));
+@@ -242,7 +274,7 @@ int mmc_app_send_scr(struct mmc_card *ca
+       cmd.opcode = SD_APP_SEND_SCR;
+       cmd.arg = 0;
+-      cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
++      cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+       data.blksz = 8;
+       data.blocks = 1;
+@@ -252,19 +284,19 @@ int mmc_app_send_scr(struct mmc_card *ca
+       sg_init_one(&sg, scr, 8);
+-      mmc_set_data_timeout(&data, card, 0);
++      mmc_set_data_timeout(&data, card);
+       mmc_wait_for_req(card->host, &mrq);
+-      if (cmd.error != MMC_ERR_NONE)
++      if (cmd.error)
+               return cmd.error;
+-      if (data.error != MMC_ERR_NONE)
++      if (data.error)
+               return data.error;
+-      scr[0] = ntohl(scr[0]);
+-      scr[1] = ntohl(scr[1]);
++      scr[0] = be32_to_cpu(scr[0]);
++      scr[1] = be32_to_cpu(scr[1]);
+-      return MMC_ERR_NONE;
++      return 0;
+ }
+ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
+@@ -278,6 +310,8 @@ int mmc_sd_switch(struct mmc_card *card,
+       BUG_ON(!card);
+       BUG_ON(!card->host);
++      /* NOTE: caller guarantees resp is heap-allocated */
++
+       mode = !!mode;
+       value &= 0xF;
+@@ -292,7 +326,7 @@ int mmc_sd_switch(struct mmc_card *card,
+       cmd.arg = mode << 31 | 0x00FFFFFF;
+       cmd.arg &= ~(0xF << (group * 4));
+       cmd.arg |= value << (group * 4);
+-      cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
++      cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+       data.blksz = 64;
+       data.blocks = 1;
+@@ -302,15 +336,15 @@ int mmc_sd_switch(struct mmc_card *card,
+       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;
+ }
+Index: linux-2.6.23.16/drivers/mmc/core/sdio.c
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/drivers/mmc/core/sdio.c    2008-03-21 17:30:25.000000000 +0100
+@@ -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;
++}
++
+Index: linux-2.6.23.16/drivers/mmc/core/sdio_bus.c
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/drivers/mmc/core/sdio_bus.c        2008-03-21 17:30:25.000000000 +0100
+@@ -0,0 +1,265 @@
++/*
++ *  linux/drivers/mmc/core/sdio_bus.c
++ *
++ *  Copyright 2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ *
++ * SDIO function driver model
++ */
++
++#include <linux/device.h>
++#include <linux/err.h>
++
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio_func.h>
++
++#include "sdio_cis.h"
++#include "sdio_bus.h"
++
++#define dev_to_sdio_func(d)   container_of(d, struct sdio_func, dev)
++#define to_sdio_driver(d)      container_of(d, struct sdio_driver, drv)
++
++/* show configuration fields */
++#define sdio_config_attr(field, format_string)                                \
++static ssize_t                                                                \
++field##_show(struct device *dev, struct device_attribute *attr, char *buf)                            \
++{                                                                     \
++      struct sdio_func *func;                                         \
++                                                                      \
++      func = dev_to_sdio_func (dev);                                  \
++      return sprintf (buf, format_string, func->field);               \
++}
++
++sdio_config_attr(class, "0x%02x\n");
++sdio_config_attr(vendor, "0x%04x\n");
++sdio_config_attr(device, "0x%04x\n");
++
++static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
++      struct sdio_func *func = dev_to_sdio_func (dev);
++
++      return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n",
++                      func->class, func->vendor, func->device);
++}
++
++static struct device_attribute sdio_dev_attrs[] = {
++      __ATTR_RO(class),
++      __ATTR_RO(vendor),
++      __ATTR_RO(device),
++      __ATTR_RO(modalias),
++      __ATTR_NULL,
++};
++
++static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
++      const struct sdio_device_id *id)
++{
++      if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)
++              return NULL;
++      if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)
++              return NULL;
++      if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)
++              return NULL;
++      return id;
++}
++
++static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
++      struct sdio_driver *sdrv)
++{
++      const struct sdio_device_id *ids;
++
++      ids = sdrv->id_table;
++
++      if (ids) {
++              while (ids->class || ids->vendor || ids->device) {
++                      if (sdio_match_one(func, ids))
++                              return ids;
++                      ids++;
++              }
++      }
++
++      return NULL;
++}
++
++static int sdio_bus_match(struct device *dev, struct device_driver *drv)
++{
++      struct sdio_func *func = dev_to_sdio_func(dev);
++      struct sdio_driver *sdrv = to_sdio_driver(drv);
++
++      if (sdio_match_device(func, sdrv))
++              return 1;
++
++      return 0;
++}
++
++static int
++sdio_bus_uevent(struct device *dev, char **envp,
++              int num_envp, char *buffer, int buffer_size)
++{
++      struct sdio_func *func = dev_to_sdio_func(dev);
++      int i = 0, len = 0;
++
++      if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
++                      "SDIO_CLASS=%02X", func->class))
++              return -ENOMEM;
++
++      if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
++                      "SDIO_ID=%04X:%04X", func->vendor, func->device))
++              return -ENOMEM;
++
++      if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
++                      "MODALIAS=sdio:c%02Xv%04Xd%04X",
++                      func->class, func->vendor, func->device))
++              return -ENOMEM;
++
++      return 0;
++}
++
++static int sdio_bus_probe(struct device *dev)
++{
++      struct sdio_driver *drv = to_sdio_driver(dev->driver);
++      struct sdio_func *func = dev_to_sdio_func(dev);
++      const struct sdio_device_id *id;
++      int ret;
++
++      id = sdio_match_device(func, drv);
++      if (!id)
++              return -ENODEV;
++
++      /* Set the default block size so the driver is sure it's something
++       * sensible. */
++      sdio_claim_host(func);
++      ret = sdio_set_block_size(func, 0);
++      sdio_release_host(func);
++      if (ret)
++              return ret;
++
++      return drv->probe(func, id);
++}
++
++static int sdio_bus_remove(struct device *dev)
++{
++      struct sdio_driver *drv = to_sdio_driver(dev->driver);
++      struct sdio_func *func = dev_to_sdio_func(dev);
++
++      drv->remove(func);
++
++      if (func->irq_handler) {
++              printk(KERN_WARNING "WARNING: driver %s did not remove "
++                      "its interrupt handler!\n", drv->name);
++              sdio_claim_host(func);
++              sdio_release_irq(func);
++              sdio_release_host(func);
++      }
++
++      return 0;
++}
++
++static struct bus_type sdio_bus_type = {
++      .name           = "sdio",
++      .dev_attrs      = sdio_dev_attrs,
++      .match          = sdio_bus_match,
++      .uevent         = sdio_bus_uevent,
++      .probe          = sdio_bus_probe,
++      .remove         = sdio_bus_remove,
++};
++
++int sdio_register_bus(void)
++{
++      return bus_register(&sdio_bus_type);
++}
++
++void sdio_unregister_bus(void)
++{
++      bus_unregister(&sdio_bus_type);
++}
++
++/**
++ *    sdio_register_driver - register a function driver
++ *    @drv: SDIO function driver
++ */
++int sdio_register_driver(struct sdio_driver *drv)
++{
++      drv->drv.name = drv->name;
++      drv->drv.bus = &sdio_bus_type;
++      return driver_register(&drv->drv);
++}
++EXPORT_SYMBOL_GPL(sdio_register_driver);
++
++/**
++ *    sdio_unregister_driver - unregister a function driver
++ *    @drv: SDIO function driver
++ */
++void sdio_unregister_driver(struct sdio_driver *drv)
++{
++      drv->drv.bus = &sdio_bus_type;
++      driver_unregister(&drv->drv);
++}
++EXPORT_SYMBOL_GPL(sdio_unregister_driver);
++
++static void sdio_release_func(struct device *dev)
++{
++      struct sdio_func *func = dev_to_sdio_func(dev);
++
++      sdio_free_func_cis(func);
++
++      if (func->info)
++              kfree(func->info);
++
++      kfree(func);
++}
++
++/*
++ * Allocate and initialise a new SDIO function structure.
++ */
++struct sdio_func *sdio_alloc_func(struct mmc_card *card)
++{
++      struct sdio_func *func;
++
++      func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL);
++      if (!func)
++              return ERR_PTR(-ENOMEM);
++
++      func->card = card;
++
++      device_initialize(&func->dev);
++
++      func->dev.parent = &card->dev;
++      func->dev.bus = &sdio_bus_type;
++      func->dev.release = sdio_release_func;
++
++      return func;
++}
++
++/*
++ * Register a new SDIO function with the driver model.
++ */
++int sdio_add_func(struct sdio_func *func)
++{
++      int ret;
++
++      snprintf(func->dev.bus_id, sizeof(func->dev.bus_id),
++               "%s:%d", mmc_card_id(func->card), func->num);
++
++      ret = device_add(&func->dev);
++      if (ret == 0)
++              sdio_func_set_present(func);
++
++      return ret;
++}
++
++/*
++ * Unregister a SDIO function with the driver model, and
++ * (eventually) free it.
++ */
++void sdio_remove_func(struct sdio_func *func)
++{
++      if (sdio_func_present(func))
++              device_del(&func->dev);
++
++      put_device(&func->dev);
++}
++
+Index: linux-2.6.23.16/drivers/mmc/core/sdio_bus.h
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/drivers/mmc/core/sdio_bus.h        2008-03-21 17:30:25.000000000 +0100
+@@ -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
++
+Index: linux-2.6.23.16/drivers/mmc/core/sdio_cis.c
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/drivers/mmc/core/sdio_cis.c        2008-03-21 17:30:25.000000000 +0100
+@@ -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);
++}
++
+Index: linux-2.6.23.16/drivers/mmc/core/sdio_cis.h
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/drivers/mmc/core/sdio_cis.h        2008-03-21 17:30:25.000000000 +0100
+@@ -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
+Index: linux-2.6.23.16/drivers/mmc/core/sdio_io.c
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/drivers/mmc/core/sdio_io.c 2008-03-21 17:30:25.000000000 +0100
+@@ -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);
+Index: linux-2.6.23.16/drivers/mmc/core/sdio_irq.c
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/drivers/mmc/core/sdio_irq.c        2008-03-21 17:30:25.000000000 +0100
+@@ -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);
++
+Index: linux-2.6.23.16/drivers/mmc/core/sdio_ops.c
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/drivers/mmc/core/sdio_ops.c        2008-03-21 17:30:25.000000000 +0100
+@@ -0,0 +1,175 @@
++/*
++ *  linux/drivers/mmc/sdio_ops.c
++ *
++ *  Copyright 2006-2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#include <linux/scatterlist.h>
++
++#include <linux/mmc/host.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/mmc.h>
++#include <linux/mmc/sdio.h>
++
++#include "core.h"
++
++int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
++{
++      struct mmc_command cmd;
++      int i, err = 0;
++
++      BUG_ON(!host);
++
++      memset(&cmd, 0, sizeof(struct mmc_command));
++
++      cmd.opcode = SD_IO_SEND_OP_COND;
++      cmd.arg = ocr;
++      cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR;
++
++      for (i = 100; i; i--) {
++              err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
++              if (err)
++                      break;
++
++              /* if we're just probing, do a single pass */
++              if (ocr == 0)
++                      break;
++
++              /* otherwise wait until reset completes */
++              if (mmc_host_is_spi(host)) {
++                      /*
++                       * Both R1_SPI_IDLE and MMC_CARD_BUSY indicate
++                       * an initialized card under SPI, but some cards
++                       * (Marvell's) only behave when looking at this
++                       * one.
++                       */
++                      if (cmd.resp[1] & MMC_CARD_BUSY)
++                              break;
++              } else {
++                      if (cmd.resp[0] & MMC_CARD_BUSY)
++                              break;
++              }
++
++              err = -ETIMEDOUT;
++
++              mmc_delay(10);
++      }
++
++      if (rocr)
++              *rocr = cmd.resp[mmc_host_is_spi(host) ? 1 : 0];
++
++      return err;
++}
++
++int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
++      unsigned addr, u8 in, u8* out)
++{
++      struct mmc_command cmd;
++      int err;
++
++      BUG_ON(!card);
++      BUG_ON(fn > 7);
++
++      memset(&cmd, 0, sizeof(struct mmc_command));
++
++      cmd.opcode = SD_IO_RW_DIRECT;
++      cmd.arg = write ? 0x80000000 : 0x00000000;
++      cmd.arg |= fn << 28;
++      cmd.arg |= (write && out) ? 0x08000000 : 0x00000000;
++      cmd.arg |= addr << 9;
++      cmd.arg |= in;
++      cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
++
++      err = mmc_wait_for_cmd(card->host, &cmd, 0);
++      if (err)
++              return err;
++
++      if (mmc_host_is_spi(card->host)) {
++              /* host driver already reported errors */
++      } else {
++              if (cmd.resp[0] & R5_ERROR)
++                      return -EIO;
++              if (cmd.resp[0] & R5_FUNCTION_NUMBER)
++                      return -EINVAL;
++              if (cmd.resp[0] & R5_OUT_OF_RANGE)
++                      return -ERANGE;
++      }
++
++      if (out) {
++              if (mmc_host_is_spi(card->host))
++                      *out = (cmd.resp[0] >> 8) & 0xFF;
++              else
++                      *out = cmd.resp[0] & 0xFF;
++      }
++
++      return 0;
++}
++
++int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
++      unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
++{
++      struct mmc_request mrq;
++      struct mmc_command cmd;
++      struct mmc_data data;
++      struct scatterlist sg;
++
++      BUG_ON(!card);
++      BUG_ON(fn > 7);
++      BUG_ON(blocks == 1 && blksz > 512);
++      WARN_ON(blocks == 0);
++      WARN_ON(blksz == 0);
++
++      memset(&mrq, 0, sizeof(struct mmc_request));
++      memset(&cmd, 0, sizeof(struct mmc_command));
++      memset(&data, 0, sizeof(struct mmc_data));
++
++      mrq.cmd = &cmd;
++      mrq.data = &data;
++
++      cmd.opcode = SD_IO_RW_EXTENDED;
++      cmd.arg = write ? 0x80000000 : 0x00000000;
++      cmd.arg |= fn << 28;
++      cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
++      cmd.arg |= addr << 9;
++      if (blocks == 1 && blksz <= 512)
++              cmd.arg |= (blksz == 512) ? 0 : blksz;  /* byte mode */
++      else
++              cmd.arg |= 0x08000000 | blocks;         /* block mode */
++      cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
++
++      data.blksz = blksz;
++      data.blocks = blocks;
++      data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
++      data.sg = &sg;
++      data.sg_len = 1;
++
++      sg_init_one(&sg, buf, blksz * blocks);
++
++      mmc_set_data_timeout(&data, card);
++
++      mmc_wait_for_req(card->host, &mrq);
++
++      if (cmd.error)
++              return cmd.error;
++      if (data.error)
++              return data.error;
++
++      if (mmc_host_is_spi(card->host)) {
++              /* host driver already reported errors */
++      } else {
++              if (cmd.resp[0] & R5_ERROR)
++                      return -EIO;
++              if (cmd.resp[0] & R5_FUNCTION_NUMBER)
++                      return -EINVAL;
++              if (cmd.resp[0] & R5_OUT_OF_RANGE)
++                      return -ERANGE;
++      }
++
++      return 0;
++}
++
+Index: linux-2.6.23.16/drivers/mmc/core/sdio_ops.h
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/drivers/mmc/core/sdio_ops.h        2008-03-21 17:30:25.000000000 +0100
+@@ -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: linux-2.6.23.16/include/linux/mmc/card.h
+===================================================================
+--- linux-2.6.23.16.orig/include/linux/mmc/card.h      2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/include/linux/mmc/card.h   2008-03-21 17:30:25.000000000 +0100
+@@ -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: linux-2.6.23.16/include/linux/mmc/core.h
+===================================================================
+--- linux-2.6.23.16.orig/include/linux/mmc/core.h      2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/include/linux/mmc/core.h   2008-03-21 17:30:25.000000000 +0100
+@@ -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,12 +47,30 @@ 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.
+  */
+ #define mmc_cmd_type(cmd)     ((cmd)->flags & MMC_CMD_MASK)
+@@ -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_h
+ 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: linux-2.6.23.16/include/linux/mmc/host.h
+===================================================================
+--- linux-2.6.23.16.orig/include/linux/mmc/host.h      2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/include/linux/mmc/host.h   2008-03-21 17:30:25.000000000 +0100
+@@ -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,23 +110,30 @@ struct mmc_host {
+       struct mmc_ios          ios;            /* current io bus settings */
+       u32                     ocr;            /* the current OCR setting */
+-      unsigned int            mode;           /* current card mode of host */
+-#define MMC_MODE_MMC          0
+-#define MMC_MODE_SD           1
++      /* group bitfields together to minimize padding */
++      unsigned int            use_spi_crc:1;
++      unsigned int            claimed:1;      /* host exclusively claimed */
++      unsigned int            bus_dead:1;     /* bus has been released */
++#ifdef CONFIG_MMC_DEBUG
++      unsigned int            removed:1;      /* host is being removed */
++#endif
+       struct mmc_card         *card;          /* device attached to this host */
+       wait_queue_head_t       wq;
+-      unsigned int            claimed:1;      /* host exclusively claimed */
+       struct delayed_work     detect;
+-#ifdef CONFIG_MMC_DEBUG
+-      unsigned int            removed:1;      /* host is being removed */
+-#endif
+       const struct mmc_bus_ops *bus_ops;      /* current bus driver */
+       unsigned int            bus_refs;       /* reference counter */
+-      unsigned int            bus_dead:1;     /* bus has been released */
++
++      unsigned int            sdio_irqs;
++      struct task_struct      *sdio_irq_thread;
++      atomic_t                sdio_irq_thread_abort;
++
++#ifdef CONFIG_LEDS_TRIGGERS
++      struct led_trigger      *led;           /* activity led */
++#endif
+       unsigned long           private[0] ____cacheline_aligned;
+ };
+@@ -137,6 +148,8 @@ static inline void *mmc_priv(struct mmc_
+       return (void *)host->private;
+ }
++#define mmc_host_is_spi(host) ((host)->caps & MMC_CAP_SPI)
++
+ #define mmc_dev(x)    ((x)->parent)
+ #define mmc_classdev(x)       (&(x)->class_dev)
+ #define mmc_hostname(x)       ((x)->class_dev.bus_id)
+@@ -147,5 +160,11 @@ extern int mmc_resume_host(struct mmc_ho
+ 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: linux-2.6.23.16/include/linux/mmc/mmc.h
+===================================================================
+--- linux-2.6.23.16.orig/include/linux/mmc/mmc.h       2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/include/linux/mmc/mmc.h    2008-03-21 17:30:25.000000000 +0100
+@@ -27,7 +27,7 @@
+ /* Standard MMC commands (4.1)           type  argument     response */
+    /* class 1 */
+-#define       MMC_GO_IDLE_STATE         0   /* bc                          */
++#define MMC_GO_IDLE_STATE         0   /* bc                          */
+ #define MMC_SEND_OP_COND          1   /* bcr  [31:0] OCR         R3  */
+ #define MMC_ALL_SEND_CID          2   /* bcr                     R2  */
+ #define MMC_SET_RELATIVE_ADDR     3   /* ac   [31:16] RCA        R1  */
+@@ -39,8 +39,10 @@
+ #define MMC_SEND_CID             10   /* ac   [31:16] RCA        R2  */
+ #define MMC_READ_DAT_UNTIL_STOP  11   /* adtc [31:0] dadr        R1  */
+ #define MMC_STOP_TRANSMISSION    12   /* ac                      R1b */
+-#define MMC_SEND_STATUS                13   /* ac   [31:16] RCA        R1  */
++#define MMC_SEND_STATUS          13   /* ac   [31:16] RCA        R1  */
+ #define MMC_GO_INACTIVE_STATE    15   /* ac   [31:16] RCA            */
++#define MMC_SPI_READ_OCR         58   /* spi                  spi_R3 */
++#define MMC_SPI_CRC_ON_OFF       59   /* spi  [0:0] flag      spi_R1 */
+   /* class 2 */
+ #define MMC_SET_BLOCKLEN         16   /* ac   [31:0] block len   R1  */
+@@ -90,15 +92,15 @@
+  */
+ /*
+-  MMC status in R1
++  MMC status in R1, for native mode (SPI bits are different)
+   Type
+-      e : error bit
++      e : error bit
+       s : status bit
+       r : detected and set for the actual command response
+       x : detected and set during command execution. the host must poll
+             the card by sending status command in order to read these bits.
+   Clear condition
+-      a : according to the card state
++      a : according to the card state
+       b : always related to the previous command. Reception of
+             a valid command will clear it (with a delay of one command)
+       c : clear by read
+@@ -124,10 +126,33 @@
+ #define R1_CARD_ECC_DISABLED  (1 << 14)       /* sx, a */
+ #define R1_ERASE_RESET                (1 << 13)       /* sr, c */
+ #define R1_STATUS(x)            (x & 0xFFFFE000)
+-#define R1_CURRENT_STATE(x)           ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
++#define R1_CURRENT_STATE(x)   ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
+ #define R1_READY_FOR_DATA     (1 << 8)        /* sx, a */
+ #define R1_APP_CMD            (1 << 5)        /* sr, c */
++/*
++ * MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS
++ * R1 is the low order byte; R2 is the next highest byte, when present.
++ */
++#define R1_SPI_IDLE           (1 << 0)
++#define R1_SPI_ERASE_RESET    (1 << 1)
++#define R1_SPI_ILLEGAL_COMMAND        (1 << 2)
++#define R1_SPI_COM_CRC                (1 << 3)
++#define R1_SPI_ERASE_SEQ      (1 << 4)
++#define R1_SPI_ADDRESS                (1 << 5)
++#define R1_SPI_PARAMETER      (1 << 6)
++/* R1 bit 7 is always zero */
++#define R2_SPI_CARD_LOCKED    (1 << 8)
++#define R2_SPI_WP_ERASE_SKIP  (1 << 9)        /* or lock/unlock fail */
++#define R2_SPI_LOCK_UNLOCK_FAIL       R2_SPI_WP_ERASE_SKIP
++#define R2_SPI_ERROR          (1 << 10)
++#define R2_SPI_CC_ERROR               (1 << 11)
++#define R2_SPI_CARD_ECC_ERROR (1 << 12)
++#define R2_SPI_WP_VIOLATION   (1 << 13)
++#define R2_SPI_ERASE_PARAM    (1 << 14)
++#define R2_SPI_OUT_OF_RANGE   (1 << 15)       /* or CSD overwrite */
++#define R2_SPI_CSD_OVERWRITE  R2_SPI_OUT_OF_RANGE
++
+ /* These are unpacked versions of the actual responses */
+ struct _mmc_csd {
+@@ -182,6 +207,7 @@ 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 */
+ /*
+Index: linux-2.6.23.16/include/linux/mmc/sdio.h
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/include/linux/mmc/sdio.h   2008-03-21 17:30:25.000000000 +0100
+@@ -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
++
+Index: linux-2.6.23.16/include/linux/mmc/sdio_func.h
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/include/linux/mmc/sdio_func.h      2008-03-21 17:30:25.000000000 +0100
+@@ -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
++
+Index: linux-2.6.23.16/include/linux/mmc/sdio_ids.h
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/include/linux/mmc/sdio_ids.h       2008-03-21 17:30:25.000000000 +0100
+@@ -0,0 +1,29 @@
++/*
++ * SDIO Classes, Interface Types, Manufacturer IDs, etc.
++ */
++
++#ifndef MMC_SDIO_IDS_H
++#define MMC_SDIO_IDS_H
++
++/*
++ * Standard SDIO Function Interfaces
++ */
++
++#define SDIO_CLASS_NONE               0x00    /* Not a SDIO standard interface */
++#define SDIO_CLASS_UART               0x01    /* standard UART interface */
++#define SDIO_CLASS_BT_A               0x02    /* Type-A BlueTooth std interface */
++#define SDIO_CLASS_BT_B               0x03    /* Type-B BlueTooth std interface */
++#define SDIO_CLASS_GPS                0x04    /* GPS standard interface */
++#define SDIO_CLASS_CAMERA     0x05    /* Camera standard interface */
++#define SDIO_CLASS_PHS                0x06    /* PHS standard interface */
++#define SDIO_CLASS_WLAN               0x07    /* WLAN interface */
++#define SDIO_CLASS_ATA                0x08    /* Embedded SDIO-ATA std interface */
++
++/*
++ * Vendors and devices.  Sort key: vendor first, device next.
++ */
++
++#define SDIO_VENDOR_ID_MARVELL                        0x02df
++#define SDIO_DEVICE_ID_MARVELL_LIBERTAS               0x9103
++
++#endif
+Index: linux-2.6.23.16/include/linux/mod_devicetable.h
+===================================================================
+--- linux-2.6.23.16.orig/include/linux/mod_devicetable.h       2008-03-21 17:28:37.000000000 +0100
++++ linux-2.6.23.16/include/linux/mod_devicetable.h    2008-03-21 17:32:50.000000000 +0100
+@@ -22,6 +22,18 @@ struct pci_device_id {
+ };
++/* SDIO */
++
++#define SDIO_ANY_ID (~0)
++
++struct sdio_device_id {
++      __u8    class;                  /* Standard interface or SDIO_ANY_ID */
++      __u16   vendor;                 /* Vendor or SDIO_ANY_ID */
++      __u16   device;                 /* Device ID or SDIO_ANY_ID */
++      kernel_ulong_t driver_data;     /* Data private to the driver */
++};
++
++
+ #define IEEE1394_MATCH_VENDOR_ID      0x0001
+ #define IEEE1394_MATCH_MODEL_ID               0x0002
+ #define IEEE1394_MATCH_SPECIFIER_ID   0x0004
+Index: linux-2.6.23.16/drivers/mmc/card/Kconfig
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/card/Kconfig      2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/card/Kconfig   2008-03-21 17:30:25.000000000 +0100
+@@ -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: linux-2.6.23.16/drivers/mmc/card/Makefile
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/card/Makefile     2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/card/Makefile  2008-03-21 17:30:25.000000000 +0100
+@@ -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: linux-2.6.23.16/drivers/mmc/card/block.c
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/card/block.c      2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/card/block.c   2008-03-21 17:30:25.000000000 +0100
+@@ -44,6 +44,9 @@
+  * max 8 partitions per card
+  */
+ #define MMC_SHIFT     3
++#define MMC_NUM_MINORS        (256 >> MMC_SHIFT)
++
++static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))];
+ /*
+  * There is one mmc_blk_data per slot.
+@@ -80,6 +83,9 @@ static void mmc_blk_put(struct mmc_blk_d
+       mutex_lock(&open_lock);
+       md->usage--;
+       if (md->usage == 0) {
++              int devidx = md->disk->first_minor >> MMC_SHIFT;
++              __clear_bit(devidx, dev_use);
++
+               put_disk(md->disk);
+               kfree(md);
+       }
+@@ -151,17 +157,19 @@ static u32 mmc_sd_num_wr_blocks(struct m
+       cmd.opcode = MMC_APP_CMD;
+       cmd.arg = card->rca << 16;
+-      cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
++      cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+       err = mmc_wait_for_cmd(card->host, &cmd, 0);
+-      if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD))
++      if (err)
++              return (u32)-1;
++      if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD))
+               return (u32)-1;
+       memset(&cmd, 0, sizeof(struct mmc_command));
+       cmd.opcode = SD_APP_SEND_NUM_WR_BLKS;
+       cmd.arg = 0;
+-      cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
++      cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+       memset(&data, 0, sizeof(struct mmc_data));
+@@ -192,7 +200,7 @@ static u32 mmc_sd_num_wr_blocks(struct m
+       mmc_wait_for_req(card->host, &mrq);
+-      if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE)
++      if (cmd.error || data.error)
+               return (u32)-1;
+       blocks = ntohl(blocks);
+@@ -220,17 +228,15 @@ static int mmc_blk_issue_rq(struct mmc_q
+               brq.cmd.arg = req->sector;
+               if (!mmc_card_blockaddr(card))
+                       brq.cmd.arg <<= 9;
+-              brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
++              brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
+               brq.data.blksz = 1 << md->block_bits;
+               brq.stop.opcode = MMC_STOP_TRANSMISSION;
+               brq.stop.arg = 0;
+-              brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
++              brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+               brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);
+               if (brq.data.blocks > card->host->max_blk_count)
+                       brq.data.blocks = card->host->max_blk_count;
+-              mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ);
+-
+               /*
+                * If the host doesn't support multiple block writes, force
+                * block writes to single block. SD cards are excepted from
+@@ -243,8 +249,12 @@ static int mmc_blk_issue_rq(struct mmc_q
+                       brq.data.blocks = 1;
+               if (brq.data.blocks > 1) {
+-                      brq.data.flags |= MMC_DATA_MULTI;
+-                      brq.mrq.stop = &brq.stop;
++                      /* SPI multiblock writes terminate using a special
++                       * token, not a STOP_TRANSMISSION request.
++                       */
++                      if (!mmc_host_is_spi(card->host)
++                                      || rq_data_dir(req) == READ)
++                              brq.mrq.stop = &brq.stop;
+                       readcmd = MMC_READ_MULTIPLE_BLOCK;
+                       writecmd = MMC_WRITE_MULTIPLE_BLOCK;
+               } else {
+@@ -261,6 +271,8 @@ static int mmc_blk_issue_rq(struct mmc_q
+                       brq.data.flags |= MMC_DATA_WRITE;
+               }
++              mmc_set_data_timeout(&brq.data, card);
++
+               brq.data.sg = mq->sg;
+               brq.data.sg_len = mmc_queue_map_sg(mq);
+@@ -302,7 +314,7 @@ static int mmc_blk_issue_rq(struct mmc_q
+                       goto cmd_err;
+               }
+-              if (rq_data_dir(req) != READ) {
++              if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
+                       do {
+                               int err;
+@@ -315,7 +327,13 @@ static int mmc_blk_issue_rq(struct mmc_q
+                                              req->rq_disk->disk_name, err);
+                                       goto cmd_err;
+                               }
+-                      } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
++                              /*
++                               * Some cards mishandle the status bits,
++                               * so make sure to check both the busy
++                               * indication and the card state.
++                               */
++                      } while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
++                              (R1_CURRENT_STATE(cmd.resp[0]) == 7));
+ #if 0
+                       if (cmd.resp[0] & ~0x00000900)
+@@ -394,9 +412,6 @@ static int mmc_blk_issue_rq(struct mmc_q
+       return 0;
+ }
+-#define MMC_NUM_MINORS        (256 >> MMC_SHIFT)
+-
+-static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))];
+ static inline int mmc_blk_readonly(struct mmc_card *card)
+ {
+@@ -510,7 +525,7 @@ mmc_blk_set_blksize(struct mmc_blk_data 
+       mmc_claim_host(card->host);
+       cmd.opcode = MMC_SET_BLOCKLEN;
+       cmd.arg = 1 << md->block_bits;
+-      cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
++      cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+       err = mmc_wait_for_cmd(card->host, &cmd, 5);
+       mmc_release_host(card->host);
+@@ -562,17 +577,12 @@ static void mmc_blk_remove(struct mmc_ca
+       struct mmc_blk_data *md = mmc_get_drvdata(card);
+       if (md) {
+-              int devidx;
+-
+               /* Stop new requests from getting into the queue */
+               del_gendisk(md->disk);
+               /* Then flush out any already in there */
+               mmc_cleanup_queue(&md->queue);
+-              devidx = md->disk->first_minor >> MMC_SHIFT;
+-              __clear_bit(devidx, dev_use);
+-
+               mmc_blk_put(md);
+       }
+       mmc_set_drvdata(card, NULL);
+Index: linux-2.6.23.16/drivers/mmc/card/queue.c
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/card/queue.c      2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/card/queue.c   2008-03-21 17:30:25.000000000 +0100
+@@ -13,6 +13,7 @@
+ #include <linux/blkdev.h>
+ #include <linux/freezer.h>
+ #include <linux/kthread.h>
++#include <linux/scatterlist.h>
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/host.h>
+@@ -22,6 +23,12 @@
+ #define MMC_QUEUE_SUSPENDED   (1 << 0)
++#define sg_init_table(sg, n) do { \
++      memset(sg, 0, sizeof(*(sg)) * (n)); \
++} while (0)
++
++#define sg_virt(sg)   (page_address((sg)->page) + (sg)->offset)
++
+ /*
+  * Prepare a MMC request. This just filters out odd stuff.
+  */
+@@ -159,6 +166,7 @@ int mmc_init_queue(struct mmc_queue *mq,
+                               ret = -ENOMEM;
+                               goto cleanup_queue;
+                       }
++                      sg_init_table(mq->sg, 1);
+                       mq->bounce_sg = kmalloc(sizeof(struct scatterlist) *
+                               bouncesz / 512, GFP_KERNEL);
+@@ -166,6 +174,7 @@ int mmc_init_queue(struct mmc_queue *mq,
+                               ret = -ENOMEM;
+                               goto cleanup_queue;
+                       }
++                      sg_init_table(mq->bounce_sg, bouncesz / 512);
+               }
+       }
+ #endif
+@@ -183,6 +192,7 @@ int mmc_init_queue(struct mmc_queue *mq,
+                       ret = -ENOMEM;
+                       goto cleanup_queue;
+               }
++              sg_init_table(mq->sg, host->max_phys_segs);
+       }
+       init_MUTEX(&mq->thread_sem);
+@@ -302,12 +312,12 @@ static void copy_sg(struct scatterlist *
+               BUG_ON(dst_len == 0);
+               if (dst_size == 0) {
+-                      dst_buf = page_address(dst->page) + dst->offset;
++                      dst_buf = sg_virt(dst);
+                       dst_size = dst->length;
+               }
+               if (src_size == 0) {
+-                      src_buf = page_address(src->page) + src->offset;
++                      src_buf = sg_virt(src);
+                       src_size = src->length;
+               }
+@@ -353,9 +363,7 @@ unsigned int mmc_queue_map_sg(struct mmc
+               return 1;
+       }
+-      mq->sg[0].page = virt_to_page(mq->bounce_buf);
+-      mq->sg[0].offset = offset_in_page(mq->bounce_buf);
+-      mq->sg[0].length = 0;
++      sg_init_one(mq->sg, mq->bounce_buf, 0);
+       while (sg_len) {
+               mq->sg[0].length += mq->bounce_sg[sg_len - 1].length;
+Index: linux-2.6.23.16/drivers/mmc/card/sdio_uart.c
+===================================================================
+--- /dev/null  1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23.16/drivers/mmc/card/sdio_uart.c       2008-03-21 17:30:25.000000000 +0100
+@@ -0,0 +1,1158 @@
++/*
++ * linux/drivers/mmc/card/sdio_uart.c - SDIO UART/GPS driver
++ *
++ * Based on drivers/serial/8250.c and drivers/serial/serial_core.c
++ * by Russell King.
++ *
++ * Author:    Nicolas Pitre
++ * Created:   June 15, 2007
++ * Copyright: MontaVista Software, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++/*
++ * Note: Although this driver assumes a 16550A-like UART implementation,
++ * it is not possible to leverage the common 8250/16550 driver, nor the
++ * core UART infrastructure, as they assumes direct access to the hardware
++ * registers, often under a spinlock.  This is not possible in the SDIO
++ * context as SDIO access functions must be able to sleep.
++ *
++ * Because we need to lock the SDIO host to ensure an exclusive access to
++ * the card, we simply rely on that lock to also prevent and serialize
++ * concurrent access to the same port.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/mutex.h>
++#include <linux/serial_reg.h>
++#include <linux/circ_buf.h>
++#include <linux/gfp.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++
++#include <linux/mmc/core.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio_func.h>
++#include <linux/mmc/sdio_ids.h>
++
++
++#define UART_NR               8       /* Number of UARTs this driver can handle */
++
++
++#define UART_XMIT_SIZE        PAGE_SIZE
++#define WAKEUP_CHARS  256
++
++#define circ_empty(circ)      ((circ)->head == (circ)->tail)
++#define circ_clear(circ)      ((circ)->head = (circ)->tail = 0)
++
++#define circ_chars_pending(circ) \
++              (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
++
++#define circ_chars_free(circ) \
++              (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
++
++
++struct uart_icount {
++      __u32   cts;
++      __u32   dsr;
++      __u32   rng;
++      __u32   dcd;
++      __u32   rx;
++      __u32   tx;
++      __u32   frame;
++      __u32   overrun;
++      __u32   parity;
++      __u32   brk;
++};
++
++struct sdio_uart_port {
++      struct kref             kref;
++      struct tty_struct       *tty;
++      unsigned int            index;
++      unsigned int            opened;
++      struct mutex            open_lock;
++      struct sdio_func        *func;
++      struct mutex            func_lock;
++      struct task_struct      *in_sdio_uart_irq;
++      unsigned int            regs_offset;
++      struct circ_buf         xmit;
++      spinlock_t              write_lock;
++      struct uart_icount      icount;
++      unsigned int            uartclk;
++      unsigned int            mctrl;
++      unsigned int            read_status_mask;
++      unsigned int            ignore_status_mask;
++      unsigned char           x_char;
++      unsigned char           ier;
++      unsigned char           lcr;
++};
++
++static struct sdio_uart_port *sdio_uart_table[UART_NR];
++static DEFINE_SPINLOCK(sdio_uart_table_lock);
++
++static int sdio_uart_add_port(struct sdio_uart_port *port)
++{
++      int index, ret = -EBUSY;
++
++      kref_init(&port->kref);
++      mutex_init(&port->open_lock);
++      mutex_init(&port->func_lock);
++      spin_lock_init(&port->write_lock);
++
++      spin_lock(&sdio_uart_table_lock);
++      for (index = 0; index < UART_NR; index++) {
++              if (!sdio_uart_table[index]) {
++                      port->index = index;
++                      sdio_uart_table[index] = port;
++                      ret = 0;
++                      break;
++              }
++      }
++      spin_unlock(&sdio_uart_table_lock);
++
++      return ret;
++}
++
++static struct sdio_uart_port *sdio_uart_port_get(unsigned index)
++{
++      struct sdio_uart_port *port;
++
++      if (index >= UART_NR)
++              return NULL;
++
++      spin_lock(&sdio_uart_table_lock);
++      port = sdio_uart_table[index];
++      if (port)
++              kref_get(&port->kref);
++      spin_unlock(&sdio_uart_table_lock);
++
++      return port;
++}
++
++static void sdio_uart_port_destroy(struct kref *kref)
++{
++      struct sdio_uart_port *port =
++              container_of(kref, struct sdio_uart_port, kref);
++      kfree(port);
++}
++
++static void sdio_uart_port_put(struct sdio_uart_port *port)
++{
++      kref_put(&port->kref, sdio_uart_port_destroy);
++}
++
++static void sdio_uart_port_remove(struct sdio_uart_port *port)
++{
++      struct sdio_func *func;
++
++      BUG_ON(sdio_uart_table[port->index] != port);
++
++      spin_lock(&sdio_uart_table_lock);
++      sdio_uart_table[port->index] = NULL;
++      spin_unlock(&sdio_uart_table_lock);
++
++      /*
++       * We're killing a port that potentially still is in use by
++       * the tty layer. Be careful to prevent any further access
++       * to the SDIO function and arrange for the tty layer to
++       * give up on that port ASAP.
++       * Beware: the lock ordering is critical.
++       */
++      mutex_lock(&port->open_lock);
++      mutex_lock(&port->func_lock);
++      func = port->func;
++      sdio_claim_host(func);
++      port->func = NULL;
++      mutex_unlock(&port->func_lock);
++      if (port->opened)
++              tty_hangup(port->tty);
++      mutex_unlock(&port->open_lock);
++      sdio_release_irq(func);
++      sdio_disable_func(func);
++      sdio_release_host(func);
++
++      sdio_uart_port_put(port);
++}
++
++static int sdio_uart_claim_func(struct sdio_uart_port *port)
++{
++      mutex_lock(&port->func_lock);
++      if (unlikely(!port->func)) {
++              mutex_unlock(&port->func_lock);
++              return -ENODEV;
++      }
++      if (likely(port->in_sdio_uart_irq != current))
++              sdio_claim_host(port->func);
++      mutex_unlock(&port->func_lock);
++      return 0;
++}
++
++static inline void sdio_uart_release_func(struct sdio_uart_port *port)
++{
++      if (likely(port->in_sdio_uart_irq != current))
++              sdio_release_host(port->func);
++}
++
++static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset)
++{
++      unsigned char c;
++      c = sdio_readb(port->func, port->regs_offset + offset, NULL);
++      return c;
++}
++
++static inline void sdio_out(struct sdio_uart_port *port, int offset, int value)
++{
++      sdio_writeb(port->func, value, port->regs_offset + offset, NULL);
++}
++
++static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port)
++{
++      unsigned char status;
++      unsigned int ret;
++
++      status = sdio_in(port, UART_MSR);
++
++      ret = 0;
++      if (status & UART_MSR_DCD)
++              ret |= TIOCM_CAR;
++      if (status & UART_MSR_RI)
++              ret |= TIOCM_RNG;
++      if (status & UART_MSR_DSR)
++              ret |= TIOCM_DSR;
++      if (status & UART_MSR_CTS)
++              ret |= TIOCM_CTS;
++      return ret;
++}
++
++static void sdio_uart_write_mctrl(struct sdio_uart_port *port, unsigned int mctrl)
++{
++      unsigned char mcr = 0;
++
++      if (mctrl & TIOCM_RTS)
++              mcr |= UART_MCR_RTS;
++      if (mctrl & TIOCM_DTR)
++              mcr |= UART_MCR_DTR;
++      if (mctrl & TIOCM_OUT1)
++              mcr |= UART_MCR_OUT1;
++      if (mctrl & TIOCM_OUT2)
++              mcr |= UART_MCR_OUT2;
++      if (mctrl & TIOCM_LOOP)
++              mcr |= UART_MCR_LOOP;
++
++      sdio_out(port, UART_MCR, mcr);
++}
++
++static inline void sdio_uart_update_mctrl(struct sdio_uart_port *port,
++                                        unsigned int set, unsigned int clear)
++{
++      unsigned int old;
++
++      old = port->mctrl;
++      port->mctrl = (old & ~clear) | set;
++      if (old != port->mctrl)
++              sdio_uart_write_mctrl(port, port->mctrl);
++}
++
++#define sdio_uart_set_mctrl(port, x)  sdio_uart_update_mctrl(port, x, 0)
++#define sdio_uart_clear_mctrl(port, x)        sdio_uart_update_mctrl(port, 0, x)
++
++static void sdio_uart_change_speed(struct sdio_uart_port *port,
++                                 struct ktermios *termios,
++                                 struct ktermios *old)
++{
++      unsigned char cval, fcr = 0;
++      unsigned int baud, quot;
++
++      switch (termios->c_cflag & CSIZE) {
++      case CS5:
++              cval = UART_LCR_WLEN5;
++              break;
++      case CS6:
++              cval = UART_LCR_WLEN6;
++              break;
++      case CS7:
++              cval = UART_LCR_WLEN7;
++              break;
++      default:
++      case CS8:
++              cval = UART_LCR_WLEN8;
++              break;
++      }
++
++      if (termios->c_cflag & CSTOPB)
++              cval |= UART_LCR_STOP;
++      if (termios->c_cflag & PARENB)
++              cval |= UART_LCR_PARITY;
++      if (!(termios->c_cflag & PARODD))
++              cval |= UART_LCR_EPAR;
++
++      for (;;) {
++              baud = tty_termios_baud_rate(termios);
++              if (baud == 0)
++                      baud = 9600;  /* Special case: B0 rate. */
++              if (baud <= port->uartclk)
++                      break;
++              /*
++               * Oops, the quotient was zero.  Try again with the old
++               * baud rate if possible, otherwise default to 9600.
++               */
++              termios->c_cflag &= ~CBAUD;
++              if (old) {
++                      termios->c_cflag |= old->c_cflag & CBAUD;
++                      old = NULL;
++              } else
++                      termios->c_cflag |= B9600;
++      }
++      quot = (2 * port->uartclk + baud) / (2 * baud);
++
++      if (baud < 2400)
++              fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
++      else
++              fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10;
++
++      port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
++      if (termios->c_iflag & INPCK)
++              port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
++      if (termios->c_iflag & (BRKINT | PARMRK))
++              port->read_status_mask |= UART_LSR_BI;
++
++      /*
++       * Characters to ignore
++       */
++      port->ignore_status_mask = 0;
++      if (termios->c_iflag & IGNPAR)
++              port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
++      if (termios->c_iflag & IGNBRK) {
++              port->ignore_status_mask |= UART_LSR_BI;
++              /*
++               * If we're ignoring parity and break indicators,
++               * ignore overruns too (for real raw support).
++               */
++              if (termios->c_iflag & IGNPAR)
++                      port->ignore_status_mask |= UART_LSR_OE;
++      }
++
++      /*
++       * ignore all characters if CREAD is not set
++       */
++      if ((termios->c_cflag & CREAD) == 0)
++              port->ignore_status_mask |= UART_LSR_DR;
++
++      /*
++       * CTS flow control flag and modem status interrupts
++       */
++      port->ier &= ~UART_IER_MSI;
++      if ((termios->c_cflag & CRTSCTS) || !(termios->c_cflag & CLOCAL))
++              port->ier |= UART_IER_MSI;
++
++      port->lcr = cval;
++
++      sdio_out(port, UART_IER, port->ier);
++      sdio_out(port, UART_LCR, cval | UART_LCR_DLAB);
++      sdio_out(port, UART_DLL, quot & 0xff);
++      sdio_out(port, UART_DLM, quot >> 8);
++      sdio_out(port, UART_LCR, cval);
++      sdio_out(port, UART_FCR, fcr);
++
++      sdio_uart_write_mctrl(port, port->mctrl);
++}
++
++static void sdio_uart_start_tx(struct sdio_uart_port *port)
++{
++      if (!(port->ier & UART_IER_THRI)) {
++              port->ier |= UART_IER_THRI;
++              sdio_out(port, UART_IER, port->ier);
++      }
++}
++
++static void sdio_uart_stop_tx(struct sdio_uart_port *port)
++{
++      if (port->ier & UART_IER_THRI) {
++              port->ier &= ~UART_IER_THRI;
++              sdio_out(port, UART_IER, port->ier);
++      }
++}
++
++static void sdio_uart_stop_rx(struct sdio_uart_port *port)
++{
++      port->ier &= ~UART_IER_RLSI;
++      port->read_status_mask &= ~UART_LSR_DR;
++      sdio_out(port, UART_IER, port->ier);
++}
++
++static void sdio_uart_receive_chars(struct sdio_uart_port *port, unsigned int *status)
++{
++      struct tty_struct *tty = port->tty;
++      unsigned int ch, flag;
++      int max_count = 256;
++
++      do {
++              ch = sdio_in(port, UART_RX);
++              flag = TTY_NORMAL;
++              port->icount.rx++;
++
++              if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
++                                      UART_LSR_FE | UART_LSR_OE))) {
++                      /*
++                       * For statistics only
++                       */
++                      if (*status & UART_LSR_BI) {
++                              *status &= ~(UART_LSR_FE | UART_LSR_PE);
++                              port->icount.brk++;
++                      } else if (*status & UART_LSR_PE)
++                              port->icount.parity++;
++                      else if (*status & UART_LSR_FE)
++                              port->icount.frame++;
++                      if (*status & UART_LSR_OE)
++                              port->icount.overrun++;
++
++                      /*
++                       * Mask off conditions which should be ignored.
++                       */
++                      *status &= port->read_status_mask;
++                      if (*status & UART_LSR_BI) {
++                              flag = TTY_BREAK;
++                      } else if (*status & UART_LSR_PE)
++                              flag = TTY_PARITY;
++                      else if (*status & UART_LSR_FE)
++                              flag = TTY_FRAME;
++              }
++
++              if ((*status & port->ignore_status_mask & ~UART_LSR_OE) == 0)
++                      tty_insert_flip_char(tty, ch, flag);
++
++              /*
++               * Overrun is special.  Since it's reported immediately,
++               * it doesn't affect the current character.
++               */
++              if (*status & ~port->ignore_status_mask & UART_LSR_OE)
++                      tty_insert_flip_char(tty, 0, TTY_OVERRUN);
++
++              *status = sdio_in(port, UART_LSR);
++      } while ((*status & UART_LSR_DR) && (max_count-- > 0));
++      tty_flip_buffer_push(tty);
++}
++
++static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
++{
++      struct circ_buf *xmit = &port->xmit;
++      int count;
++
++      if (port->x_char) {
++              sdio_out(port, UART_TX, port->x_char);
++              port->icount.tx++;
++              port->x_char = 0;
++              return;
++      }
++      if (circ_empty(xmit) || port->tty->stopped || port->tty->hw_stopped) {
++              sdio_uart_stop_tx(port);
++              return;
++      }
++
++      count = 16;
++      do {
++              sdio_out(port, UART_TX, xmit->buf[xmit->tail]);
++              xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
++              port->icount.tx++;
++              if (circ_empty(xmit))
++                      break;
++      } while (--count > 0);
++
++      if (circ_chars_pending(xmit) < WAKEUP_CHARS)
++              tty_wakeup(port->tty);
++
++      if (circ_empty(xmit))
++              sdio_uart_stop_tx(port);
++}
++
++static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
++{
++      int status;
++
++      status = sdio_in(port, UART_MSR);
++
++      if ((status & UART_MSR_ANY_DELTA) == 0)
++              return;
++
++      if (status & UART_MSR_TERI)
++              port->icount.rng++;
++      if (status & UART_MSR_DDSR)
++              port->icount.dsr++;
++      if (status & UART_MSR_DDCD)
++              port->icount.dcd++;
++      if (status & UART_MSR_DCTS) {
++              port->icount.cts++;
++              if (port->tty->termios->c_cflag & CRTSCTS) {
++                      int cts = (status & UART_MSR_CTS);
++                      if (port->tty->hw_stopped) {
++                              if (cts) {
++                                      port->tty->hw_stopped = 0;
++                                      sdio_uart_start_tx(port);
++                                      tty_wakeup(port->tty);
++                              }
++                      } else {
++                              if (!cts) {
++                                      port->tty->hw_stopped = 1;
++                                      sdio_uart_stop_tx(port);
++                              }
++                      }
++              }
++      }
++}
++
++/*
++ * This handles the interrupt from one port.
++ */
++static void sdio_uart_irq(struct sdio_func *func)
++{
++      struct sdio_uart_port *port = sdio_get_drvdata(func);
++      unsigned int iir, lsr;
++
++      /*
++       * In a few places sdio_uart_irq() is called directly instead of
++       * waiting for the actual interrupt to be raised and the SDIO IRQ
++       * thread scheduled in order to reduce latency.  However, some
++       * interaction with the tty core may end up calling us back
++       * (serial echo, flow control, etc.) through those same places
++       * causing undesirable effects.  Let's stop the recursion here.
++       */
++      if (unlikely(port->in_sdio_uart_irq == current))
++              return;
++
++      iir = sdio_in(port, UART_IIR);
++      if (iir & UART_IIR_NO_INT)
++              return;
++
++      port->in_sdio_uart_irq = current;
++      lsr = sdio_in(port, UART_LSR);
++      if (lsr & UART_LSR_DR)
++              sdio_uart_receive_chars(port, &lsr);
++      sdio_uart_check_modem_status(port);
++      if (lsr & UART_LSR_THRE)
++              sdio_uart_transmit_chars(port);
++      port->in_sdio_uart_irq = NULL;
++}
++
++static int sdio_uart_startup(struct sdio_uart_port *port)
++{
++      unsigned long page;
++      int ret;
++
++      /*
++       * Set the TTY IO error marker - we will only clear this
++       * once we have successfully opened the port.
++       */
++      set_bit(TTY_IO_ERROR, &port->tty->flags);
++
++      /* Initialise and allocate the transmit buffer. */
++      page = __get_free_page(GFP_KERNEL);
++      if (!page)
++              return -ENOMEM;
++      port->xmit.buf = (unsigned char *)page;
++      circ_clear(&port->xmit);
++
++      ret = sdio_uart_claim_func(port);
++      if (ret)
++              goto err1;
++      ret = sdio_enable_func(port->func);
++      if (ret)
++              goto err2;
++      ret = sdio_claim_irq(port->func, sdio_uart_irq);
++      if (ret)
++              goto err3;
++
++      /*
++       * Clear the FIFO buffers and disable them.
++       * (they will be reenabled in sdio_change_speed())
++       */
++      sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO);
++      sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
++                      UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
++      sdio_out(port, UART_FCR, 0);
++
++      /*
++       * Clear the interrupt registers.
++       */
++      (void) sdio_in(port, UART_LSR);
++      (void) sdio_in(port, UART_RX);
++      (void) sdio_in(port, UART_IIR);
++      (void) sdio_in(port, UART_MSR);
++
++      /*
++       * Now, initialize the UART
++       */
++      sdio_out(port, UART_LCR, UART_LCR_WLEN8);
++
++      port->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
++      port->mctrl = TIOCM_OUT2;
++
++      sdio_uart_change_speed(port, port->tty->termios, NULL);
++
++      if (port->tty->termios->c_cflag & CBAUD)
++              sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
++
++      if (port->tty->termios->c_cflag & CRTSCTS)
++              if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS))
++                      port->tty->hw_stopped = 1;
++
++      clear_bit(TTY_IO_ERROR, &port->tty->flags);
++
++      /* Kick the IRQ handler once while we're still holding the host lock */
++      sdio_uart_irq(port->func);
++
++      sdio_uart_release_func(port);
++      return 0;
++
++err3:
++      sdio_disable_func(port->func);
++err2:
++      sdio_uart_release_func(port);
++err1:
++      free_page((unsigned long)port->xmit.buf);
++      return ret;
++}
++
++static void sdio_uart_shutdown(struct sdio_uart_port *port)
++{
++      int ret;
++
++      ret = sdio_uart_claim_func(port);
++      if (ret)
++              goto skip;
++
++      sdio_uart_stop_rx(port);
++
++      /* TODO: wait here for TX FIFO to drain */
++
++      /* Turn off DTR and RTS early. */
++      if (port->tty->termios->c_cflag & HUPCL)
++              sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
++
++       /* Disable interrupts from this port */
++      sdio_release_irq(port->func);
++      port->ier = 0;
++      sdio_out(port, UART_IER, 0);
++
++      sdio_uart_clear_mctrl(port, TIOCM_OUT2);
++
++      /* Disable break condition and FIFOs. */
++      port->lcr &= ~UART_LCR_SBC;
++      sdio_out(port, UART_LCR, port->lcr);
++      sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
++                               UART_FCR_CLEAR_RCVR |
++                               UART_FCR_CLEAR_XMIT);
++      sdio_out(port, UART_FCR, 0);
++
++      sdio_disable_func(port->func);
++
++      sdio_uart_release_func(port);
++
++skip:
++      /* Free the transmit buffer page. */
++      free_page((unsigned long)port->xmit.buf);
++}
++
++static int sdio_uart_open (struct tty_struct *tty, struct file * filp)
++{
++      struct sdio_uart_port *port;
++      int ret;
++
++      port = sdio_uart_port_get(tty->index);
++      if (!port)
++              return -ENODEV;
++
++      mutex_lock(&port->open_lock);
++
++      /*
++       * Make sure not to mess up with a dead port
++       * which has not been closed yet.
++       */
++      if (tty->driver_data && tty->driver_data != port) {
++              mutex_unlock(&port->open_lock);
++              sdio_uart_port_put(port);
++              return -EBUSY;
++      }
++
++      if (!port->opened) {
++              tty->driver_data = port;
++              port->tty = tty;
++              ret = sdio_uart_startup(port);
++              if (ret) {
++                      tty->driver_data = NULL;
++                      port->tty = NULL;
++                      mutex_unlock(&port->open_lock);
++                      sdio_uart_port_put(port);
++                      return ret;
++              }
++      }
++      port->opened++;
++      mutex_unlock(&port->open_lock);
++      return 0;
++}
++
++static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
++{
++      struct sdio_uart_port *port = tty->driver_data;
++
++      if (!port)
++              return;
++
++      mutex_lock(&port->open_lock);
++      BUG_ON(!port->opened);
++
++      /*
++       * This is messy.  The tty layer calls us even when open()
++       * returned an error.  Ignore this close request if tty->count
++       * is larger than port->count.
++       */
++      if (tty->count > port->opened) {
++              mutex_unlock(&port->open_lock);
++              return;
++      }
++
++      if (--port->opened == 0) {
++              tty->closing = 1;
++              sdio_uart_shutdown(port);
++              tty_ldisc_flush(tty);
++              port->tty = NULL;
++              tty->driver_data = NULL;
++              tty->closing = 0;
++      }
++      mutex_unlock(&port->open_lock);
++      sdio_uart_port_put(port);
++}
++
++static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,
++                         int count)
++{
++      struct sdio_uart_port *port = tty->driver_data;
++      struct circ_buf *circ = &port->xmit;
++      int c, ret = 0;
++
++      if (!port->func)
++              return -ENODEV;
++
++      spin_lock(&port->write_lock);
++      while (1) {
++              c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
++              if (count < c)
++                      c = count;
++              if (c <= 0)
++                      break;
++              memcpy(circ->buf + circ->head, buf, c);
++              circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
++              buf += c;
++              count -= c;
++              ret += c;
++      }
++      spin_unlock(&port->write_lock);
++
++      if ( !(port->ier & UART_IER_THRI)) {
++              int err = sdio_uart_claim_func(port);
++              if (!err) {
++                      sdio_uart_start_tx(port);
++                      sdio_uart_irq(port->func);
++                      sdio_uart_release_func(port);
++              } else
++                      ret = err;
++      }
++
++      return ret;
++}
++
++static int sdio_uart_write_room(struct tty_struct *tty)
++{
++      struct sdio_uart_port *port = tty->driver_data;
++      return port ? circ_chars_free(&port->xmit) : 0;
++}
++
++static int sdio_uart_chars_in_buffer(struct tty_struct *tty)
++{
++      struct sdio_uart_port *port = tty->driver_data;
++      return port ? circ_chars_pending(&port->xmit) : 0;
++}
++
++static void sdio_uart_send_xchar(struct tty_struct *tty, char ch)
++{
++      struct sdio_uart_port *port = tty->driver_data;
++
++      port->x_char = ch;
++      if (ch && !(port->ier & UART_IER_THRI)) {
++              if (sdio_uart_claim_func(port) != 0)
++                      return;
++              sdio_uart_start_tx(port);
++              sdio_uart_irq(port->func);
++              sdio_uart_release_func(port);
++      }
++}
++
++static void sdio_uart_throttle(struct tty_struct *tty)
++{
++      struct sdio_uart_port *port = tty->driver_data;
++
++      if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))
++              return;
++
++      if (sdio_uart_claim_func(port) != 0)
++              return;
++
++      if (I_IXOFF(tty)) {
++              port->x_char = STOP_CHAR(tty);
++              sdio_uart_start_tx(port);
++      }
++
++      if (tty->termios->c_cflag & CRTSCTS)
++              sdio_uart_clear_mctrl(port, TIOCM_RTS);
++
++      sdio_uart_irq(port->func);
++      sdio_uart_release_func(port);
++}
++
++static void sdio_uart_unthrottle(struct tty_struct *tty)
++{
++      struct sdio_uart_port *port = tty->driver_data;
++
++      if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))
++              return;
++
++      if (sdio_uart_claim_func(port) != 0)
++              return;
++
++      if (I_IXOFF(tty)) {
++              if (port->x_char) {
++                      port->x_char = 0;
++              } else {
++                      port->x_char = START_CHAR(tty);
++                      sdio_uart_start_tx(port);
++              }
++      }
++
++      if (tty->termios->c_cflag & CRTSCTS)
++              sdio_uart_set_mctrl(port, TIOCM_RTS);
++
++      sdio_uart_irq(port->func);
++      sdio_uart_release_func(port);
++}
++
++static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
++{
++      struct sdio_uart_port *port = tty->driver_data;
++      unsigned int cflag = tty->termios->c_cflag;
++
++#define RELEVANT_IFLAG(iflag)   ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
++
++      if ((cflag ^ old_termios->c_cflag) == 0 &&
++          RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
++              return;
++
++      if (sdio_uart_claim_func(port) != 0)
++              return;
++
++      sdio_uart_change_speed(port, tty->termios, old_termios);
++
++      /* Handle transition to B0 status */
++      if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
++              sdio_uart_clear_mctrl(port, TIOCM_RTS | TIOCM_DTR);
++
++      /* Handle transition away from B0 status */
++      if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
++              unsigned int mask = TIOCM_DTR;
++              if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags))
++                      mask |= TIOCM_RTS;
++              sdio_uart_set_mctrl(port, mask);
++      }
++
++      /* Handle turning off CRTSCTS */
++      if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
++              tty->hw_stopped = 0;
++              sdio_uart_start_tx(port);
++      }
++
++      /* Handle turning on CRTSCTS */
++      if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
++              if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) {
++                      tty->hw_stopped = 1;
++                      sdio_uart_stop_tx(port);
++              }
++      }
++
++      sdio_uart_release_func(port);
++}
++
++static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state)
++{
++      struct sdio_uart_port *port = tty->driver_data;
++
++      if (sdio_uart_claim_func(port) != 0)
++              return;
++
++      if (break_state == -1)
++              port->lcr |= UART_LCR_SBC;
++      else
++              port->lcr &= ~UART_LCR_SBC;
++      sdio_out(port, UART_LCR, port->lcr);
++
++      sdio_uart_release_func(port);
++}
++
++static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file)
++{
++      struct sdio_uart_port *port = tty->driver_data;
++      int result;
++
++      result = sdio_uart_claim_func(port);
++      if (!result) {
++              result = port->mctrl | sdio_uart_get_mctrl(port);
++              sdio_uart_release_func(port);
++      }
++
++      return result;
++}
++
++static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file,
++                            unsigned int set, unsigned int clear)
++{
++      struct sdio_uart_port *port = tty->driver_data;
++      int result;
++
++      result =sdio_uart_claim_func(port);
++      if(!result) {
++              sdio_uart_update_mctrl(port, set, clear);
++              sdio_uart_release_func(port);
++      }
++
++      return result;
++}
++
++static int sdio_uart_read_proc(char *page, char **start, off_t off,
++                             int count, int *eof, void *data)
++{
++      int i, len = 0;
++      off_t begin = 0;
++
++      len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n",
++                     "", "", "");
++      for (i = 0; i < UART_NR && len < PAGE_SIZE - 96; i++) {
++              struct sdio_uart_port *port = sdio_uart_port_get(i);
++              if (port) {
++                      len += sprintf(page+len, "%d: uart:SDIO", i);
++                      if(capable(CAP_SYS_ADMIN)) {
++                              len += sprintf(page + len, " tx:%d rx:%d",
++                                             port->icount.tx, port->icount.rx);
++                              if (port->icount.frame)
++                                      len += sprintf(page + len, " fe:%d",
++                                                     port->icount.frame);
++                              if (port->icount.parity)
++                                      len += sprintf(page + len, " pe:%d",
++                                                     port->icount.parity);
++                              if (port->icount.brk)
++                                      len += sprintf(page + len, " brk:%d",
++                                                     port->icount.brk);
++                              if (port->icount.overrun)
++                                      len += sprintf(page + len, " oe:%d",
++                                                     port->icount.overrun);
++                              if (port->icount.cts)
++                                      len += sprintf(page + len, " cts:%d",
++                                                     port->icount.cts);
++                              if (port->icount.dsr)
++                                      len += sprintf(page + len, " dsr:%d",
++                                                     port->icount.dsr);
++                              if (port->icount.rng)
++                                      len += sprintf(page + len, " rng:%d",
++                                                     port->icount.rng);
++                              if (port->icount.dcd)
++                                      len += sprintf(page + len, " dcd:%d",
++                                                     port->icount.dcd);
++                      }
++                      strcat(page, "\n");
++                      len++;
++                      sdio_uart_port_put(port);
++              }
++
++              if (len + begin > off + count)
++                      goto done;
++              if (len + begin < off) {
++                      begin += len;
++                      len = 0;
++              }
++      }
++      *eof = 1;
++
++done:
++      if (off >= len + begin)
++              return 0;
++      *start = page + (off - begin);
++      return (count < begin + len - off) ? count : (begin + len - off);
++}
++
++static const struct tty_operations sdio_uart_ops = {
++      .open                   = sdio_uart_open,
++      .close                  = sdio_uart_close,
++      .write                  = sdio_uart_write,
++      .write_room             = sdio_uart_write_room,
++      .chars_in_buffer        = sdio_uart_chars_in_buffer,
++      .send_xchar             = sdio_uart_send_xchar,
++      .throttle               = sdio_uart_throttle,
++      .unthrottle             = sdio_uart_unthrottle,
++      .set_termios            = sdio_uart_set_termios,
++      .break_ctl              = sdio_uart_break_ctl,
++      .tiocmget               = sdio_uart_tiocmget,
++      .tiocmset               = sdio_uart_tiocmset,
++      .read_proc              = sdio_uart_read_proc,
++};
++
++static struct tty_driver *sdio_uart_tty_driver;
++
++static int sdio_uart_probe(struct sdio_func *func,
++                         const struct sdio_device_id *id)
++{
++      struct sdio_uart_port *port;
++      int ret;
++
++      port = kzalloc(sizeof(struct sdio_uart_port), GFP_KERNEL);
++      if (!port)
++              return -ENOMEM;
++
++      if (func->class == SDIO_CLASS_UART) {
++              printk(KERN_WARNING "%s: need info on UART class basic setup\n",
++                     sdio_func_id(func));
++              kfree(port);
++              return -ENOSYS;
++      } else if (func->class == SDIO_CLASS_GPS) {
++              /*
++               * We need tuple 0x91.  It contains SUBTPL_SIOREG
++               * and SUBTPL_RCVCAPS.
++               */
++              struct sdio_func_tuple *tpl;
++              for (tpl = func->tuples; tpl; tpl = tpl->next) {
++                      if (tpl->code != 0x91)
++                              continue;
++                      if (tpl->size < 10)
++                              continue;
++                      if (tpl->data[1] == 0)  /* SUBTPL_SIOREG */
++                              break;
++              }
++              if (!tpl) {
++                      printk(KERN_WARNING
++                             "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
++                             sdio_func_id(func));
++                      kfree(port);
++                      return -EINVAL;
++              }
++              printk(KERN_DEBUG "%s: Register ID = 0x%02x, Exp ID = 0x%02x\n",
++                     sdio_func_id(func), tpl->data[2], tpl->data[3]);
++              port->regs_offset = (tpl->data[4] << 0) |
++                                  (tpl->data[5] << 8) |
++                                  (tpl->data[6] << 16);
++              printk(KERN_DEBUG "%s: regs offset = 0x%x\n",
++                     sdio_func_id(func), port->regs_offset);
++              port->uartclk = tpl->data[7] * 115200;
++              if (port->uartclk == 0)
++                      port->uartclk = 115200;
++              printk(KERN_DEBUG "%s: clk %d baudcode %u 4800-div %u\n",
++                     sdio_func_id(func), port->uartclk,
++                     tpl->data[7], tpl->data[8] | (tpl->data[9] << 8));
++      } else {
++              kfree(port);
++              return -EINVAL;
++      }
++
++      port->func = func;
++      sdio_set_drvdata(func, port);
++
++      ret = sdio_uart_add_port(port);
++      if (ret) {
++              kfree(port);
++      } else {
++              struct device *dev;
++              dev = tty_register_device(sdio_uart_tty_driver, port->index, &func->dev);
++              if (IS_ERR(dev)) {
++                      sdio_uart_port_remove(port);
++                      ret = PTR_ERR(dev);
++              }
++      }
++
++      return ret;
++}
++
++static void sdio_uart_remove(struct sdio_func *func)
++{
++      struct sdio_uart_port *port = sdio_get_drvdata(func);
++
++      tty_unregister_device(sdio_uart_tty_driver, port->index);
++      sdio_uart_port_remove(port);
++}
++
++static const struct sdio_device_id sdio_uart_ids[] = {
++      { SDIO_DEVICE_CLASS(SDIO_CLASS_UART)            },
++      { SDIO_DEVICE_CLASS(SDIO_CLASS_GPS)             },
++      { /* end: all zeroes */                         },
++};
++
++MODULE_DEVICE_TABLE(sdio, sdio_uart_ids);
++
++static struct sdio_driver sdio_uart_driver = {
++      .probe          = sdio_uart_probe,
++      .remove         = sdio_uart_remove,
++      .name           = "sdio_uart",
++      .id_table       = sdio_uart_ids,
++};
++
++static int __init sdio_uart_init(void)
++{
++      int ret;
++      struct tty_driver *tty_drv;
++
++      sdio_uart_tty_driver = tty_drv = alloc_tty_driver(UART_NR);
++      if (!tty_drv)
++              return -ENOMEM;
++
++      tty_drv->owner = THIS_MODULE;
++      tty_drv->driver_name = "sdio_uart";
++      tty_drv->name =   "ttySDIO";
++      tty_drv->major = 0;  /* dynamically allocated */
++      tty_drv->minor_start = 0;
++      tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
++      tty_drv->subtype = SERIAL_TYPE_NORMAL;
++      tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
++      tty_drv->init_termios = tty_std_termios;
++      tty_drv->init_termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL;
++      tty_drv->init_termios.c_ispeed = 4800;
++      tty_drv->init_termios.c_ospeed = 4800;
++      tty_set_operations(tty_drv, &sdio_uart_ops);
++
++      ret = tty_register_driver(tty_drv);
++      if (ret)
++              goto err1;
++
++      ret = sdio_register_driver(&sdio_uart_driver);
++      if (ret)
++              goto err2;
++
++      return 0;
++
++err2:
++      tty_unregister_driver(tty_drv);
++err1:
++      put_tty_driver(tty_drv);
++      return ret;
++}
++
++static void __exit sdio_uart_exit(void)
++{
++      sdio_unregister_driver(&sdio_uart_driver);
++      tty_unregister_driver(sdio_uart_tty_driver);
++      put_tty_driver(sdio_uart_tty_driver);
++}
++
++module_init(sdio_uart_init);
++module_exit(sdio_uart_exit);
++
++MODULE_AUTHOR("Nicolas Pitre");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.23.16/drivers/mmc/core/Makefile
+===================================================================
+--- linux-2.6.23.16.orig/drivers/mmc/core/Makefile     2008-03-21 17:28:26.000000000 +0100
++++ linux-2.6.23.16/drivers/mmc/core/Makefile  2008-03-21 17:30:25.000000000 +0100
+@@ -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 975a22a..caec3bd 100644 (file)
@@ -2,8 +2,8 @@ This patch ports ssb to the 2.6.23 kernel.
 
 Index: linux-2.6.23.16/drivers/ssb/driver_mipscore.c
 ===================================================================
---- linux-2.6.23.16.orig/drivers/ssb/driver_mipscore.c 2008-02-19 13:45:04.000000000 +0100
-+++ linux-2.6.23.16/drivers/ssb/driver_mipscore.c      2008-02-19 13:45:07.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/ssb/driver_mipscore.c 2008-03-21 17:33:28.000000000 +0100
++++ linux-2.6.23.16/drivers/ssb/driver_mipscore.c      2008-03-21 17:33:34.000000000 +0100
 @@ -222,3 +222,5 @@ void ssb_mipscore_init(struct ssb_mipsco
        ssb_mips_serial_init(mcore);
        ssb_mips_flash_detect(mcore);
@@ -12,9 +12,9 @@ Index: linux-2.6.23.16/drivers/ssb/driver_mipscore.c
 +EXPORT_SYMBOL(ssb_mips_irq);
 Index: linux-2.6.23.16/include/linux/mod_devicetable.h
 ===================================================================
---- linux-2.6.23.16.orig/include/linux/mod_devicetable.h       2008-02-19 13:45:04.000000000 +0100
-+++ linux-2.6.23.16/include/linux/mod_devicetable.h    2008-02-19 13:45:07.000000000 +0100
-@@ -340,4 +340,19 @@ struct parisc_device_id {
+--- linux-2.6.23.16.orig/include/linux/mod_devicetable.h       2008-03-21 17:33:28.000000000 +0100
++++ linux-2.6.23.16/include/linux/mod_devicetable.h    2008-03-21 17:33:34.000000000 +0100
+@@ -352,4 +352,19 @@ struct parisc_device_id {
  #define PA_HVERSION_ANY_ID    0xffff
  #define PA_SVERSION_ANY_ID    0xffffffff
  
@@ -36,8 +36,8 @@ Index: linux-2.6.23.16/include/linux/mod_devicetable.h
  #endif /* LINUX_MOD_DEVICETABLE_H */
 Index: linux-2.6.23.16/drivers/ssb/main.c
 ===================================================================
---- linux-2.6.23.16.orig/drivers/ssb/main.c    2008-02-19 13:45:04.000000000 +0100
-+++ linux-2.6.23.16/drivers/ssb/main.c 2008-02-19 13:45:07.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/ssb/main.c    2008-03-21 17:33:28.000000000 +0100
++++ linux-2.6.23.16/drivers/ssb/main.c 2008-03-21 17:33:34.000000000 +0100
 @@ -321,14 +321,16 @@ static int ssb_bus_match(struct device *
        return 0;
  }
@@ -59,8 +59,8 @@ Index: linux-2.6.23.16/drivers/ssb/main.c
                             ssb_dev->id.revision);
 Index: linux-2.6.23.16/drivers/ssb/scan.c
 ===================================================================
---- linux-2.6.23.16.orig/drivers/ssb/scan.c    2008-02-19 13:45:04.000000000 +0100
-+++ linux-2.6.23.16/drivers/ssb/scan.c 2008-02-19 13:45:07.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/ssb/scan.c    2008-03-21 17:33:28.000000000 +0100
++++ linux-2.6.23.16/drivers/ssb/scan.c 2008-03-21 17:33:34.000000000 +0100
 @@ -391,12 +391,14 @@ int ssb_bus_scan(struct ssb_bus *bus,
                        if (bus->bustype == SSB_BUSTYPE_PCI) {
                                /* Ignore PCI cores on PCI-E cards.
@@ -82,8 +82,8 @@ Index: linux-2.6.23.16/drivers/ssb/scan.c
                        if (bus->pcicore.dev) {
 Index: linux-2.6.23.16/drivers/ssb/driver_pcicore.c
 ===================================================================
---- linux-2.6.23.16.orig/drivers/ssb/driver_pcicore.c  2008-02-19 13:45:04.000000000 +0100
-+++ linux-2.6.23.16/drivers/ssb/driver_pcicore.c       2008-02-19 13:45:07.000000000 +0100
+--- linux-2.6.23.16.orig/drivers/ssb/driver_pcicore.c  2008-03-21 17:33:28.000000000 +0100
++++ linux-2.6.23.16/drivers/ssb/driver_pcicore.c       2008-03-21 17:33:34.000000000 +0100
 @@ -362,7 +362,7 @@ static int pcicore_is_in_hostmode(struct
            chipid_top != 0x5300)
                return 0;
This page took 0.194294 seconds and 4 git commands to generate.