From: Michael Büsch Date: Fri, 21 Mar 2008 16:52:27 +0000 (+0000) Subject: linux-2.6: Add a driver to run an MMC or SD card over a GPIO based SPI interface. X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fsvn-archive%2Farchive.git;a=commitdiff_plain;h=caca5bd9c3d45761f1ea4eef53b33f194599f215;hp=d154b55bf467a10eef5f801a76cb104ab4e214e9 linux-2.6: Add a driver to run an MMC or SD card over a GPIO based SPI interface. SVN-Revision: 10637 --- diff --git a/package/kernel/modules/other.mk b/package/kernel/modules/other.mk index f430c284ca..a3ce6e9f1c 100644 --- a/package/kernel/modules/other.mk +++ b/package/kernel/modules/other.mk @@ -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 index 0000000000..9076525b03 --- /dev/null +++ b/package/mmc_over_gpio/Makefile @@ -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 index 0000000000..d2258bb95f --- /dev/null +++ b/package/mmc_over_gpio/src/Makefile @@ -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 index 0000000000..8e7d4b189d --- /dev/null +++ b/package/mmc_over_gpio/src/linux/spi/spi_gpio.h @@ -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 +#include + + +/** 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 index 0000000000..36dbd0f6a8 --- /dev/null +++ b/package/mmc_over_gpio/src/mmc_over_spigpio.c @@ -0,0 +1,339 @@ +/* + * Driver for driving an MMC card over a bitbanging GPIO SPI bus. + * + * Copyright 2008 Michael Buesch + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include +#include +#include +#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 index 0000000000..31048acc3f --- /dev/null +++ b/package/mmc_over_gpio/src/spi_gpio.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux/spi/spi_gpio.h" //XXX +#include + + +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 + +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 "); +MODULE_AUTHOR("Michael Buesch"); +MODULE_DESCRIPTION("Platform independent GPIO bitbangling SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/brcm47xx/patches-2.6.23/700-ssb-gigabit-ethernet-driver.patch b/target/linux/brcm47xx/patches-2.6.23/700-ssb-gigabit-ethernet-driver.patch index 38df059be2..1b97d126c9 100644 --- a/target/linux/brcm47xx/patches-2.6.23/700-ssb-gigabit-ethernet-driver.patch +++ b/target/linux/brcm47xx/patches-2.6.23/700-ssb-gigabit-ethernet-driver.patch @@ -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 @@ -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 #include @@ -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 #include @@ -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 index 0000000000..f02f1002c4 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.23/300-add-mmc-spi-driver.patch @@ -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 ++ * ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include /* for R1_SPI_* bit values */ ++ ++#include ++#include ++ ++#include ++ ++ ++#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 + #include + #include +-#include ++#include + #include + + #include +@@ -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 + #include + #include ++#include + + #include + +@@ -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 +-#include + #include + + #include +@@ -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 +-#include + #include + + #include +@@ -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 ++ ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++ ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++ ++#include "sdio_ops.h" ++ ++/** ++ * sdio_claim_host - exclusively claim a bus for a certain SDIO function ++ * @func: SDIO function that will be accessed ++ * ++ * Claim a bus for a set of operations. The SDIO function given ++ * is used to figure out which bus is relevant. ++ */ ++void sdio_claim_host(struct sdio_func *func) ++{ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ mmc_claim_host(func->card->host); ++} ++EXPORT_SYMBOL_GPL(sdio_claim_host); ++ ++/** ++ * sdio_release_host - release a bus for a certain SDIO function ++ * @func: SDIO function that was accessed ++ * ++ * Release a bus, allowing others to claim the bus for their ++ * operations. ++ */ ++void sdio_release_host(struct sdio_func *func) ++{ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ mmc_release_host(func->card->host); ++} ++EXPORT_SYMBOL_GPL(sdio_release_host); ++ ++/** ++ * sdio_enable_func - enables a SDIO function for usage ++ * @func: SDIO function to enable ++ * ++ * Powers up and activates a SDIO function so that register ++ * access is possible. ++ */ ++int sdio_enable_func(struct sdio_func *func) ++{ ++ int ret; ++ unsigned char reg; ++ unsigned long timeout; ++ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func)); ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®); ++ if (ret) ++ goto err; ++ ++ reg |= 1 << func->num; ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL); ++ if (ret) ++ goto err; ++ ++ /* ++ * FIXME: This should timeout based on information in the CIS, ++ * but we don't have card to parse that yet. ++ */ ++ timeout = jiffies + HZ; ++ ++ while (1) { ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, ®); ++ if (ret) ++ goto err; ++ if (reg & (1 << func->num)) ++ break; ++ ret = -ETIME; ++ if (time_after(jiffies, timeout)) ++ goto err; ++ } ++ ++ pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func)); ++ ++ return 0; ++ ++err: ++ pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func)); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(sdio_enable_func); ++ ++/** ++ * sdio_disable_func - disable a SDIO function ++ * @func: SDIO function to disable ++ * ++ * Powers down and deactivates a SDIO function. Register access ++ * to this function will fail until the function is reenabled. ++ */ ++int sdio_disable_func(struct sdio_func *func) ++{ ++ int ret; ++ unsigned char reg; ++ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func)); ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®); ++ if (ret) ++ goto err; ++ ++ reg &= ~(1 << func->num); ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL); ++ if (ret) ++ goto err; ++ ++ pr_debug("SDIO: Disabled device %s\n", sdio_func_id(func)); ++ ++ return 0; ++ ++err: ++ pr_debug("SDIO: Failed to disable device %s\n", sdio_func_id(func)); ++ return -EIO; ++} ++EXPORT_SYMBOL_GPL(sdio_disable_func); ++ ++/** ++ * sdio_set_block_size - set the block size of an SDIO function ++ * @func: SDIO function to change ++ * @blksz: new block size or 0 to use the default. ++ * ++ * The default block size is the largest supported by both the function ++ * and the host, with a maximum of 512 to ensure that arbitrarily sized ++ * data transfer use the optimal (least) number of commands. ++ * ++ * A driver may call this to override the default block size set by the ++ * core. This can be used to set a block size greater than the maximum ++ * that reported by the card; it is the driver's responsibility to ensure ++ * it uses a value that the card supports. ++ * ++ * Returns 0 on success, -EINVAL if the host does not support the ++ * requested block size, or -EIO (etc.) if one of the resultant FBR block ++ * size register writes failed. ++ * ++ */ ++int sdio_set_block_size(struct sdio_func *func, unsigned blksz) ++{ ++ int ret; ++ ++ if (blksz > func->card->host->max_blk_size) ++ return -EINVAL; ++ ++ if (blksz == 0) { ++ blksz = min(min( ++ func->max_blksize, ++ func->card->host->max_blk_size), ++ 512u); ++ } ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, ++ SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE, ++ blksz & 0xff, NULL); ++ if (ret) ++ return ret; ++ ret = mmc_io_rw_direct(func->card, 1, 0, ++ SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1, ++ (blksz >> 8) & 0xff, NULL); ++ if (ret) ++ return ret; ++ func->cur_blksize = blksz; ++ return 0; ++} ++ ++EXPORT_SYMBOL_GPL(sdio_set_block_size); ++ ++/* Split an arbitrarily sized data transfer into several ++ * IO_RW_EXTENDED commands. */ ++static int sdio_io_rw_ext_helper(struct sdio_func *func, int write, ++ unsigned addr, int incr_addr, u8 *buf, unsigned size) ++{ ++ unsigned remainder = size; ++ unsigned max_blocks; ++ int ret; ++ ++ /* Do the bulk of the transfer using block mode (if supported). */ ++ if (func->card->cccr.multi_block) { ++ /* Blocks per command is limited by host count, host transfer ++ * size (we only use a single sg entry) and the maximum for ++ * IO_RW_EXTENDED of 511 blocks. */ ++ max_blocks = min(min( ++ func->card->host->max_blk_count, ++ func->card->host->max_seg_size / func->cur_blksize), ++ 511u); ++ ++ while (remainder > func->cur_blksize) { ++ unsigned blocks; ++ ++ blocks = remainder / func->cur_blksize; ++ if (blocks > max_blocks) ++ blocks = max_blocks; ++ size = blocks * func->cur_blksize; ++ ++ ret = mmc_io_rw_extended(func->card, write, ++ func->num, addr, incr_addr, buf, ++ blocks, func->cur_blksize); ++ if (ret) ++ return ret; ++ ++ remainder -= size; ++ buf += size; ++ if (incr_addr) ++ addr += size; ++ } ++ } ++ ++ /* Write the remainder using byte mode. */ ++ while (remainder > 0) { ++ size = remainder; ++ if (size > func->cur_blksize) ++ size = func->cur_blksize; ++ if (size > 512) ++ size = 512; /* maximum size for byte mode */ ++ ++ ret = mmc_io_rw_extended(func->card, write, func->num, addr, ++ incr_addr, buf, 1, size); ++ if (ret) ++ return ret; ++ ++ remainder -= size; ++ buf += size; ++ if (incr_addr) ++ addr += size; ++ } ++ return 0; ++} ++ ++/** ++ * sdio_readb - read a single byte from a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * ++ * Reads a single byte from the address space of a given SDIO ++ * function. If there is a problem reading the address, 0xff ++ * is returned and @err_ret will contain the error code. ++ */ ++unsigned char sdio_readb(struct sdio_func *func, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ unsigned char val; ++ ++ BUG_ON(!func); ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFF; ++ } ++ ++ return val; ++} ++EXPORT_SYMBOL_GPL(sdio_readb); ++ ++/** ++ * sdio_writeb - write a single byte to a SDIO function ++ * @func: SDIO function to access ++ * @b: byte to write ++ * @addr: address to write to ++ * @err_ret: optional status value from transfer ++ * ++ * Writes a single byte to the address space of a given SDIO ++ * function. @err_ret will contain the status of the actual ++ * transfer. ++ */ ++void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ BUG_ON(!func); ++ ++ ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL); ++ if (err_ret) ++ *err_ret = ret; ++} ++EXPORT_SYMBOL_GPL(sdio_writeb); ++ ++/** ++ * sdio_memcpy_fromio - read a chunk of memory from a SDIO function ++ * @func: SDIO function to access ++ * @dst: buffer to store the data ++ * @addr: address to begin reading from ++ * @count: number of bytes to read ++ * ++ * Reads from the address space of a given SDIO function. Return ++ * value indicates if the transfer succeeded or not. ++ */ ++int sdio_memcpy_fromio(struct sdio_func *func, void *dst, ++ unsigned int addr, int count) ++{ ++ return sdio_io_rw_ext_helper(func, 0, addr, 1, dst, count); ++} ++EXPORT_SYMBOL_GPL(sdio_memcpy_fromio); ++ ++/** ++ * sdio_memcpy_toio - write a chunk of memory to a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to start writing to ++ * @src: buffer that contains the data to write ++ * @count: number of bytes to write ++ * ++ * Writes to the address space of a given SDIO function. Return ++ * value indicates if the transfer succeeded or not. ++ */ ++int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr, ++ void *src, int count) ++{ ++ return sdio_io_rw_ext_helper(func, 1, addr, 1, src, count); ++} ++EXPORT_SYMBOL_GPL(sdio_memcpy_toio); ++ ++/** ++ * sdio_readsb - read from a FIFO on a SDIO function ++ * @func: SDIO function to access ++ * @dst: buffer to store the data ++ * @addr: address of (single byte) FIFO ++ * @count: number of bytes to read ++ * ++ * Reads from the specified FIFO of a given SDIO function. Return ++ * value indicates if the transfer succeeded or not. ++ */ ++int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr, ++ int count) ++{ ++ return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count); ++} ++ ++EXPORT_SYMBOL_GPL(sdio_readsb); ++ ++/** ++ * sdio_writesb - write to a FIFO of a SDIO function ++ * @func: SDIO function to access ++ * @addr: address of (single byte) FIFO ++ * @src: buffer that contains the data to write ++ * @count: number of bytes to write ++ * ++ * Writes to the specified FIFO of a given SDIO function. Return ++ * value indicates if the transfer succeeded or not. ++ */ ++int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src, ++ int count) ++{ ++ return sdio_io_rw_ext_helper(func, 1, addr, 0, src, count); ++} ++EXPORT_SYMBOL_GPL(sdio_writesb); ++ ++/** ++ * sdio_readw - read a 16 bit integer from a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * ++ * Reads a 16 bit integer from the address space of a given SDIO ++ * function. If there is a problem reading the address, 0xffff ++ * is returned and @err_ret will contain the error code. ++ */ ++unsigned short sdio_readw(struct sdio_func *func, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 2); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFFFF; ++ } ++ ++ return le16_to_cpu(*(u16*)func->tmpbuf); ++} ++EXPORT_SYMBOL_GPL(sdio_readw); ++ ++/** ++ * sdio_writew - write a 16 bit integer to a SDIO function ++ * @func: SDIO function to access ++ * @b: integer to write ++ * @addr: address to write to ++ * @err_ret: optional status value from transfer ++ * ++ * Writes a 16 bit integer to the address space of a given SDIO ++ * function. @err_ret will contain the status of the actual ++ * transfer. ++ */ ++void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ *(u16*)func->tmpbuf = cpu_to_le16(b); ++ ++ ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2); ++ if (err_ret) ++ *err_ret = ret; ++} ++EXPORT_SYMBOL_GPL(sdio_writew); ++ ++/** ++ * sdio_readl - read a 32 bit integer from a SDIO function ++ * @func: SDIO function to access ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * ++ * Reads a 32 bit integer from the address space of a given SDIO ++ * function. If there is a problem reading the address, ++ * 0xffffffff is returned and @err_ret will contain the error ++ * code. ++ */ ++unsigned long sdio_readl(struct sdio_func *func, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 4); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFFFFFFFF; ++ } ++ ++ return le32_to_cpu(*(u32*)func->tmpbuf); ++} ++EXPORT_SYMBOL_GPL(sdio_readl); ++ ++/** ++ * sdio_writel - write a 32 bit integer to a SDIO function ++ * @func: SDIO function to access ++ * @b: integer to write ++ * @addr: address to write to ++ * @err_ret: optional status value from transfer ++ * ++ * Writes a 32 bit integer to the address space of a given SDIO ++ * function. @err_ret will contain the status of the actual ++ * transfer. ++ */ ++void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ *(u32*)func->tmpbuf = cpu_to_le32(b); ++ ++ ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4); ++ if (err_ret) ++ *err_ret = ret; ++} ++EXPORT_SYMBOL_GPL(sdio_writel); ++ ++/** ++ * sdio_f0_readb - read a single byte from SDIO function 0 ++ * @func: an SDIO function of the card ++ * @addr: address to read ++ * @err_ret: optional status value from transfer ++ * ++ * Reads a single byte from the address space of SDIO function 0. ++ * If there is a problem reading the address, 0xff is returned ++ * and @err_ret will contain the error code. ++ */ ++unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ unsigned char val; ++ ++ BUG_ON(!func); ++ ++ if (err_ret) ++ *err_ret = 0; ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, addr, 0, &val); ++ if (ret) { ++ if (err_ret) ++ *err_ret = ret; ++ return 0xFF; ++ } ++ ++ return val; ++} ++EXPORT_SYMBOL_GPL(sdio_f0_readb); ++ ++/** ++ * sdio_f0_writeb - write a single byte to SDIO function 0 ++ * @func: an SDIO function of the card ++ * @b: byte to write ++ * @addr: address to write to ++ * @err_ret: optional status value from transfer ++ * ++ * Writes a single byte to the address space of SDIO function 0. ++ * @err_ret will contain the status of the actual transfer. ++ * ++ * Only writes to the vendor specific CCCR registers (0xF0 - ++ * 0xFF) are permiited; @err_ret will be set to -EINVAL for * ++ * writes outside this range. ++ */ ++void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, ++ int *err_ret) ++{ ++ int ret; ++ ++ BUG_ON(!func); ++ ++ if (addr < 0xF0 || addr > 0xFF) { ++ if (err_ret) ++ *err_ret = -EINVAL; ++ return; ++ } ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL); ++ if (err_ret) ++ *err_ret = ret; ++} ++EXPORT_SYMBOL_GPL(sdio_f0_writeb); +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 ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "sdio_ops.h" ++ ++static int process_sdio_pending_irqs(struct mmc_card *card) ++{ ++ int i, ret, count; ++ unsigned char pending; ++ ++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending); ++ if (ret) { ++ printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n", ++ mmc_card_id(card), ret); ++ return ret; ++ } ++ ++ count = 0; ++ for (i = 1; i <= 7; i++) { ++ if (pending & (1 << i)) { ++ struct sdio_func *func = card->sdio_func[i - 1]; ++ if (!func) { ++ printk(KERN_WARNING "%s: pending IRQ for " ++ "non-existant function\n", ++ mmc_card_id(card)); ++ ret = -EINVAL; ++ } else if (func->irq_handler) { ++ func->irq_handler(func); ++ count++; ++ } else { ++ printk(KERN_WARNING "%s: pending IRQ with no handler\n", ++ sdio_func_id(func)); ++ ret = -EINVAL; ++ } ++ } ++ } ++ ++ if (count) ++ return count; ++ ++ return ret; ++} ++ ++static int sdio_irq_thread(void *_host) ++{ ++ struct mmc_host *host = _host; ++ struct sched_param param = { .sched_priority = 1 }; ++ unsigned long period, idle_period; ++ int ret; ++ ++ sched_setscheduler(current, SCHED_FIFO, ¶m); ++ ++ /* ++ * We want to allow for SDIO cards to work even on non SDIO ++ * aware hosts. One thing that non SDIO host cannot do is ++ * asynchronous notification of pending SDIO card interrupts ++ * hence we poll for them in that case. ++ */ ++ idle_period = msecs_to_jiffies(10); ++ period = (host->caps & MMC_CAP_SDIO_IRQ) ? ++ MAX_SCHEDULE_TIMEOUT : idle_period; ++ ++ pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n", ++ mmc_hostname(host), period); ++ ++ do { ++ /* ++ * We claim the host here on drivers behalf for a couple ++ * reasons: ++ * ++ * 1) it is already needed to retrieve the CCCR_INTx; ++ * 2) we want the driver(s) to clear the IRQ condition ASAP; ++ * 3) we need to control the abort condition locally. ++ * ++ * Just like traditional hard IRQ handlers, we expect SDIO ++ * IRQ handlers to be quick and to the point, so that the ++ * holding of the host lock does not cover too much work ++ * that doesn't require that lock to be held. ++ */ ++ ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort); ++ if (ret) ++ break; ++ ret = process_sdio_pending_irqs(host->card); ++ mmc_release_host(host); ++ ++ /* ++ * Give other threads a chance to run in the presence of ++ * errors. FIXME: determine if due to card removal and ++ * possibly exit this thread if so. ++ */ ++ if (ret < 0) ++ ssleep(1); ++ ++ /* ++ * Adaptive polling frequency based on the assumption ++ * that an interrupt will be closely followed by more. ++ * This has a substantial benefit for network devices. ++ */ ++ if (!(host->caps & MMC_CAP_SDIO_IRQ)) { ++ if (ret > 0) ++ period /= 2; ++ else { ++ period++; ++ if (period > idle_period) ++ period = idle_period; ++ } ++ } ++ ++ set_task_state(current, TASK_INTERRUPTIBLE); ++ if (host->caps & MMC_CAP_SDIO_IRQ) ++ host->ops->enable_sdio_irq(host, 1); ++ if (!kthread_should_stop()) ++ schedule_timeout(period); ++ set_task_state(current, TASK_RUNNING); ++ } while (!kthread_should_stop()); ++ ++ if (host->caps & MMC_CAP_SDIO_IRQ) ++ host->ops->enable_sdio_irq(host, 0); ++ ++ pr_debug("%s: IRQ thread exiting with code %d\n", ++ mmc_hostname(host), ret); ++ ++ return ret; ++} ++ ++static int sdio_card_irq_get(struct mmc_card *card) ++{ ++ struct mmc_host *host = card->host; ++ ++ WARN_ON(!host->claimed); ++ ++ if (!host->sdio_irqs++) { ++ atomic_set(&host->sdio_irq_thread_abort, 0); ++ host->sdio_irq_thread = ++ kthread_run(sdio_irq_thread, host, "ksdiorqd"); ++ if (IS_ERR(host->sdio_irq_thread)) { ++ int err = PTR_ERR(host->sdio_irq_thread); ++ host->sdio_irqs--; ++ return err; ++ } ++ } ++ ++ return 0; ++} ++ ++static int sdio_card_irq_put(struct mmc_card *card) ++{ ++ struct mmc_host *host = card->host; ++ ++ WARN_ON(!host->claimed); ++ BUG_ON(host->sdio_irqs < 1); ++ ++ if (!--host->sdio_irqs) { ++ atomic_set(&host->sdio_irq_thread_abort, 1); ++ kthread_stop(host->sdio_irq_thread); ++ } ++ ++ return 0; ++} ++ ++/** ++ * sdio_claim_irq - claim the IRQ for a SDIO function ++ * @func: SDIO function ++ * @handler: IRQ handler callback ++ * ++ * Claim and activate the IRQ for the given SDIO function. The provided ++ * handler will be called when that IRQ is asserted. The host is always ++ * claimed already when the handler is called so the handler must not ++ * call sdio_claim_host() nor sdio_release_host(). ++ */ ++int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler) ++{ ++ int ret; ++ unsigned char reg; ++ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func)); ++ ++ if (func->irq_handler) { ++ pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func)); ++ return -EBUSY; ++ } ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®); ++ if (ret) ++ return ret; ++ ++ reg |= 1 << func->num; ++ ++ reg |= 1; /* Master interrupt enable */ ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL); ++ if (ret) ++ return ret; ++ ++ func->irq_handler = handler; ++ ret = sdio_card_irq_get(func->card); ++ if (ret) ++ func->irq_handler = NULL; ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(sdio_claim_irq); ++ ++/** ++ * sdio_release_irq - release the IRQ for a SDIO function ++ * @func: SDIO function ++ * ++ * Disable and release the IRQ for the given SDIO function. ++ */ ++int sdio_release_irq(struct sdio_func *func) ++{ ++ int ret; ++ unsigned char reg; ++ ++ BUG_ON(!func); ++ BUG_ON(!func->card); ++ ++ pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func)); ++ ++ if (func->irq_handler) { ++ func->irq_handler = NULL; ++ sdio_card_irq_put(func->card); ++ } ++ ++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®); ++ if (ret) ++ return ret; ++ ++ reg &= ~(1 << func->num); ++ ++ /* Disable master interrupt with the last function interrupt */ ++ if (!(reg & 0xFE)) ++ reg = 0; ++ ++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(sdio_release_irq); ++ +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 ++ ++#include ++#include ++#include ++#include ++ ++#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 ++ + #include + + 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 ++#include ++ ++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 + #include + #include ++#include + + #include + #include +@@ -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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++ ++#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 + diff --git a/target/linux/generic-2.6/patches-2.6.23/930-ssb_fixes.patch b/target/linux/generic-2.6/patches-2.6.23/930-ssb_fixes.patch index 975a22a68b..caec3bd56b 100644 --- a/target/linux/generic-2.6/patches-2.6.23/930-ssb_fixes.patch +++ b/target/linux/generic-2.6/patches-2.6.23/930-ssb_fixes.patch @@ -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;