From: Michael Büsch Date: Fri, 18 Jul 2008 20:53:08 +0000 (+0000) Subject: Update GPIO-based MMC driver X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=f4b516ef34211bfff04b45570bbe0b3f62d43f7f;p=openwrt%2Fstaging%2Fnbd.git Update GPIO-based MMC driver SVN-Revision: 11864 --- diff --git a/package/mmc_over_gpio/Makefile b/package/mmc_over_gpio/Makefile index abf89379f3..20b7b6ac42 100644 --- a/package/mmc_over_gpio/Makefile +++ b/package/mmc_over_gpio/Makefile @@ -4,8 +4,6 @@ # This is free software, licensed under the GNU General Public License v2. # See /LICENSE for more information. -#XXX This package will go away once the stuff is merged into the kernel. - include $(TOPDIR)/rules.mk include $(INCLUDE_DIR)/kernel.mk @@ -18,9 +16,10 @@ include $(INCLUDE_DIR)/package.mk define KernelPackage/mmc-over-gpio SUBMENU:=Other modules DEPENDS:=@GPIO_SUPPORT +kmod-mmc-spi +kmod-spi-gpio + KCONFIG:=CONFIG_GPIOMMC TITLE:=MMC/SD card over GPIO support - FILES:=$(PKG_BUILD_DIR)/mmc_over_spigpio.$(LINUX_KMOD_SUFFIX) - AUTOLOAD:=$(call AutoLoad,93,spi_gpio mmc_over_spigpio) + FILES:=$(LINUX_DIR)/drivers/mmc/host/gpiommc.$(LINUX_KMOD_SUFFIX) + AUTOLOAD:=$(call AutoLoad,93,spi_gpio gpiommc) endef define KernelPackage/mmc-over-gpio/description @@ -29,16 +28,9 @@ 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 define KernelPackage/mmc-over-gpio/install diff --git a/package/mmc_over_gpio/files/mmc_over_gpio.init b/package/mmc_over_gpio/files/mmc_over_gpio.init index 88d9e41027..57c5695d19 100644 --- a/package/mmc_over_gpio/files/mmc_over_gpio.init +++ b/package/mmc_over_gpio/files/mmc_over_gpio.init @@ -3,11 +3,11 @@ START=90 SYSFS="/sys" -SYSFS_DRIVERDIR="$SYSFS/bus/platform/drivers/spi-gpio-mmc" +SYSFS_DRIVERDIR="$SYSFS/bus/platform/drivers/gpiommc" # add_device(name, DI_pin, DO_pin, CLK_pin, CS_pin, mode) add_device() { - echo -n "$1" "$2","$3","$4","$5" "$6" > $SYSFS_DRIVERDIR/add + echo -n "$1" "$2" "$3" "$4" "$5" "$6" > $SYSFS_DRIVERDIR/add } # remove_device(name) diff --git a/package/mmc_over_gpio/src/Makefile b/package/mmc_over_gpio/src/Makefile deleted file mode 100644 index b052c405ad..0000000000 --- a/package/mmc_over_gpio/src/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-m += mmc_over_spigpio.o diff --git a/package/mmc_over_gpio/src/mmc_over_spigpio.c b/package/mmc_over_gpio/src/mmc_over_spigpio.c deleted file mode 100644 index 113b518a56..0000000000 --- a/package/mmc_over_gpio/src/mmc_over_spigpio.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * 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 - - -/* 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/target/linux/generic-2.6/files-2.6.25/drivers/spi/spi_gpio.c b/target/linux/generic-2.6/files-2.6.25/drivers/spi/spi_gpio.c deleted file mode 100644 index 31048acc3f..0000000000 --- a/target/linux/generic-2.6/files-2.6.25/drivers/spi/spi_gpio.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * 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/generic-2.6/files-2.6.25/include/linux/spi/spi_gpio.h b/target/linux/generic-2.6/files-2.6.25/include/linux/spi/spi_gpio.h deleted file mode 100644 index 8e7d4b189d..0000000000 --- a/target/linux/generic-2.6/files-2.6.25/include/linux/spi/spi_gpio.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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/target/linux/generic-2.6/patches-2.6.25/921-gpio_spi_driver.patch b/target/linux/generic-2.6/patches-2.6.25/921-gpio_spi_driver.patch index eeb7285a04..264386f330 100644 --- a/target/linux/generic-2.6/patches-2.6.25/921-gpio_spi_driver.patch +++ b/target/linux/generic-2.6/patches-2.6.25/921-gpio_spi_driver.patch @@ -1,20 +1,360 @@ ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -100,6 +100,11 @@ +Index: linux-2.6.25.10/include/linux/spi/spi_gpio.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.25.10/include/linux/spi/spi_gpio.h 2008-07-18 18:19:56.000000000 +0200 +@@ -0,0 +1,67 @@ ++/* ++ * spi_gpio interface to platform code ++ * ++ * Copyright (c) 2008 Piotr Skamruk ++ * Copyright (c) 2008 Michael Buesch ++ * ++ * 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; ++}; ++ ++/** SPI_GPIO_PLATDEV_NAME - The platform device name string. ++ * The name string that has to be used for platform_device_alloc ++ * when allocating a spi-gpio device. ++ */ ++#define SPI_GPIO_PLATDEV_NAME "spi-gpio" ++ ++/** spi_gpio_next_id - Get another platform device ID number. ++ * This returns the next platform device ID number that has to be used ++ * for platform_device_alloc. The ID is opaque and should not be used for ++ * anything else. ++ */ ++int spi_gpio_next_id(void); ++ ++#endif /* _LINUX_SPI_SPI_GPIO */ +Index: linux-2.6.25.10/drivers/spi/spi_gpio.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.25.10/drivers/spi/spi_gpio.c 2008-07-18 18:19:56.000000000 +0200 +@@ -0,0 +1,251 @@ ++/* ++ * 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 ++#include ++#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_PLATDEV_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = spi_gpio_probe, ++ .remove = __devexit_p(spi_gpio_remove), ++}; ++ ++int spi_gpio_next_id(void) ++{ ++ static atomic_t counter = ATOMIC_INIT(-1); ++ ++ return atomic_inc_return(&counter); ++} ++EXPORT_SYMBOL(spi_gpio_next_id); ++ ++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 bitbanging SPI driver"); ++MODULE_LICENSE("GPL v2"); +Index: linux-2.6.25.10/drivers/spi/Kconfig +=================================================================== +--- linux-2.6.25.10.orig/drivers/spi/Kconfig 2008-07-18 18:19:43.000000000 +0200 ++++ linux-2.6.25.10/drivers/spi/Kconfig 2008-07-18 18:19:56.000000000 +0200 +@@ -100,6 +100,19 @@ config SPI_BUTTERFLY inexpensive battery powered microcontroller evaluation board. This same cable can be used to flash new firmware. +config SPI_GPIO + tristate "GPIO API based bitbanging SPI controller" -+ depends on SPI_MASTER && GENERIC_GPIO && EXPERIMENTAL ++ depends on SPI_MASTER && GENERIC_GPIO + select SPI_BITBANG ++ help ++ This is a platform driver that can be used for bitbanging ++ an SPI bus over GPIO pins. ++ Select this, if you have any SPI device that is connected via ++ GPIO pins. ++ The module will be called spi_gpio. ++ ++ If unsure, say N. + config SPI_IMX tristate "Freescale iMX SPI controller" depends on SPI_MASTER && ARCH_IMX && EXPERIMENTAL ---- a/drivers/spi/Makefile -+++ b/drivers/spi/Makefile -@@ -16,6 +16,7 @@ +Index: linux-2.6.25.10/drivers/spi/Makefile +=================================================================== +--- linux-2.6.25.10.orig/drivers/spi/Makefile 2008-07-18 18:19:43.000000000 +0200 ++++ linux-2.6.25.10/drivers/spi/Makefile 2008-07-18 18:19:56.000000000 +0200 +@@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx. obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_AU1550) += au1550_spi.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o @@ -22,3 +362,19 @@ obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o +Index: linux-2.6.25.10/MAINTAINERS +=================================================================== +--- linux-2.6.25.10.orig/MAINTAINERS 2008-07-03 05:46:47.000000000 +0200 ++++ linux-2.6.25.10/MAINTAINERS 2008-07-18 18:20:28.000000000 +0200 +@@ -3685,6 +3685,11 @@ M: dbrownell@users.sourceforge.net + L: spi-devel-general@lists.sourceforge.net + S: Maintained + ++SPI GPIO MASTER DRIVER ++P: Michael Buesch ++M: mb@bu3sch.de ++S: Maintained ++ + STABLE BRANCH: + P: Greg Kroah-Hartman + M: greg@kroah.com diff --git a/target/linux/generic-2.6/patches-2.6.25/922-gpiommc.patch b/target/linux/generic-2.6/patches-2.6.25/922-gpiommc.patch new file mode 100644 index 0000000000..8253202c09 --- /dev/null +++ b/target/linux/generic-2.6/patches-2.6.25/922-gpiommc.patch @@ -0,0 +1,554 @@ +Index: linux-2.6.25.10/drivers/mmc/host/gpiommc.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.25.10/drivers/mmc/host/gpiommc.c 2008-07-18 22:31:00.000000000 +0200 +@@ -0,0 +1,328 @@ ++/* ++ * Driver an MMC/SD card on a bitbanging GPIO SPI bus. ++ * This module hooks up the mmc_spi and spi_gpio modules and also ++ * provides a sysfs interface. ++ * ++ * Copyright 2008 Michael Buesch ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define PFX "gpio-mmc: " ++#define GPIOMMC_MAX_NAMELEN_STR __stringify(GPIOMMC_MAX_NAMELEN) ++ ++struct gpiommc_device { ++ struct platform_device *pdev; ++ struct platform_device *spi_pdev; ++ struct spi_board_info boardinfo; ++}; ++ ++ ++MODULE_DESCRIPTION("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; ++ struct gpiommc_platform_data *pdata = d->pdev->dev.platform_data; ++ ++ /* Bind the SPI master to the MMC-SPI host driver. */ ++ strlcpy(bi->modalias, "mmc_spi", sizeof(bi->modalias)); ++ ++ bi->max_speed_hz = pdata->max_bus_speed; ++ bi->bus_num = master->bus_num; ++ bi->mode = pdata->mode; ++ ++ return 0; ++} ++ ++static int gpiommc_probe(struct platform_device *pdev) ++{ ++ struct gpiommc_platform_data *mmc_pdata = pdev->dev.platform_data; ++ struct spi_gpio_platform_data spi_pdata; ++ struct gpiommc_device *d; ++ int err; ++ ++ err = -ENXIO; ++ if (!mmc_pdata) ++ goto error; ++ ++ /* Allocate the GPIO-MMC device */ ++ err = -ENOMEM; ++ d = kzalloc(sizeof(*d), GFP_KERNEL); ++ if (!d) ++ goto error; ++ d->pdev = pdev; ++ ++ /* Create the SPI-GPIO device */ ++ d->spi_pdev = platform_device_alloc(SPI_GPIO_PLATDEV_NAME, ++ spi_gpio_next_id()); ++ if (!d->spi_pdev) ++ goto err_free_d; ++ ++ memset(&spi_pdata, 0, sizeof(spi_pdata)); ++ spi_pdata.pin_clk = mmc_pdata->pins.gpio_clk; ++ spi_pdata.pin_miso = mmc_pdata->pins.gpio_do; ++ spi_pdata.pin_mosi = mmc_pdata->pins.gpio_di; ++ spi_pdata.pin_cs = mmc_pdata->pins.gpio_cs; ++ spi_pdata.cs_activelow = mmc_pdata->pins.cs_activelow; ++ spi_pdata.no_spi_delay = mmc_pdata->no_spi_delay; ++ spi_pdata.boardinfo_setup = gpiommc_boardinfo_setup; ++ spi_pdata.boardinfo_setup_data = d; ++ ++ err = platform_device_add_data(d->spi_pdev, &spi_pdata, ++ sizeof(spi_pdata)); ++ if (err) ++ goto err_free_pdev; ++ err = platform_device_add(d->spi_pdev); ++ if (err) ++ goto err_free_pdata; ++ platform_set_drvdata(pdev, d); ++ ++ printk(KERN_INFO PFX "MMC-Card \"%s\" " ++ "attached to GPIO pins di=%u, do=%u, clk=%u, cs=%u\n", ++ mmc_pdata->name, mmc_pdata->pins.gpio_di, ++ mmc_pdata->pins.gpio_do, ++ mmc_pdata->pins.gpio_clk, ++ mmc_pdata->pins.gpio_cs); ++ ++ return 0; ++ ++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); ++err_free_d: ++ kfree(d); ++error: ++ return err; ++} ++ ++static int gpiommc_remove(struct platform_device *pdev) ++{ ++ struct gpiommc_device *d = platform_get_drvdata(pdev); ++ struct gpiommc_platform_data *pdata = d->pdev->dev.platform_data; ++ ++ platform_device_unregister(d->spi_pdev); ++ printk(KERN_INFO PFX "GPIO based MMC-Card \"%s\" removed\n", pdata->name); ++ platform_device_put(d->spi_pdev); ++ ++ return 0; ++} ++ ++/* Wrapper for the platform data with context data for the sysfs interface. */ ++struct gpiommc_sysfs_platform_data { ++ struct gpiommc_platform_data p; /* Keep as first element */ ++ ++ /* The platform device that we allocated. */ ++ struct platform_device *pdev; ++ /* gpiommc_sysfs_list */ ++ struct list_head list; ++}; ++ ++static LIST_HEAD(gpiommc_sysfs_list); ++static DEFINE_MUTEX(gpiommc_sysfs_mutex); ++ ++static struct gpiommc_sysfs_platform_data *gpiommc_sysfs_find_dev(const char *name) ++{ ++ struct gpiommc_sysfs_platform_data *pdata; ++ ++ list_for_each_entry(pdata, &gpiommc_sysfs_list, list) { ++ if (strcmp(pdata->p.name, name) == 0) ++ return pdata; ++ } ++ ++ return NULL; ++} ++ ++static ssize_t gpiommc_add_store(struct device_driver *drv, ++ const char *buf, size_t count) ++{ ++ int res, err; ++ struct gpiommc_sysfs_platform_data pdata_local, *pdata; ++ struct platform_device *pdev; ++ unsigned int no_spi_delay = 0, mode = 0, csactivelow = 0; ++ ++ mutex_lock(&gpiommc_sysfs_mutex); ++ ++ pdata = &pdata_local; ++ memset(pdata, 0, sizeof(*pdata)); ++ ++ err = -EINVAL; ++ res = sscanf(buf, "%" GPIOMMC_MAX_NAMELEN_STR "s %u %u %u %u %u %u %u %u", ++ pdata->p.name, ++ &pdata->p.pins.gpio_di, ++ &pdata->p.pins.gpio_do, ++ &pdata->p.pins.gpio_clk, ++ &pdata->p.pins.gpio_cs, ++ &mode, ++ &pdata->p.max_bus_speed, ++ &no_spi_delay, ++ &csactivelow); ++ pdata->p.mode = mode; ++ pdata->p.no_spi_delay = !!no_spi_delay; ++ pdata->p.pins.cs_activelow = !!csactivelow; ++ if (res < 9) ++ pdata->p.pins.cs_activelow = 1; /* Default: CS = activelow */ ++ if (res < 8) ++ pdata->p.no_spi_delay = 0; /* Default: Delay turned on */ ++ if (res < 7) ++ pdata->p.max_bus_speed = 5000000; /* Default: 5Mhz */ ++ if (res < 6) ++ pdata->p.mode = 0; /* Default: SPI mode 0 */ ++ if (res < 5 || res > 9) ++ goto out; /* First 5 args are mandatory. */ ++ ++ /* Convert mode so that the SPI subsystem does understand it. */ ++ switch (pdata->p.mode) { ++ case 0: ++ pdata->p.mode = SPI_MODE_0; ++ break; ++ case 1: ++ pdata->p.mode = SPI_MODE_1; ++ break; ++ case 2: ++ pdata->p.mode = SPI_MODE_2; ++ break; ++ case 3: ++ pdata->p.mode = SPI_MODE_3; ++ break; ++ default: ++ goto out; /* Invalid mode */ ++ } ++ ++ err = -EEXIST; ++ if (gpiommc_sysfs_find_dev(pdata->p.name)) ++ goto out; ++ ++ err = -ENOMEM; ++ pdev = platform_device_alloc(GPIOMMC_PLATDEV_NAME, gpiommc_next_id()); ++ if (!pdev) ++ goto out; ++ ++ err = platform_device_add_data(pdev, pdata, sizeof(*pdata)); ++ if (err) ++ goto err_free_pdev; ++ pdata = pdev->dev.platform_data; ++ ++ err = platform_device_add(pdev); ++ if (err) ++ goto err_free_pdev; ++ ++ pdata->pdev = pdev; ++ INIT_LIST_HEAD(&pdata->list); ++ list_add(&pdata->list, &gpiommc_sysfs_list); ++ ++ err = 0; ++out: ++ mutex_unlock(&gpiommc_sysfs_mutex); ++ ++ return err ? err : count; ++ ++err_free_pdev: ++ platform_device_put(pdev); ++ goto out; ++} ++ ++static ssize_t gpiommc_remove_store(struct device_driver *drv, ++ const char *buf, size_t count) ++{ ++ struct gpiommc_sysfs_platform_data *pdata; ++ int err; ++ ++ mutex_lock(&gpiommc_sysfs_mutex); ++ ++ err = -ENODEV; ++ pdata = gpiommc_sysfs_find_dev(buf); ++ if (!pdata) ++ goto out; ++ ++ list_del(&pdata->list); ++ platform_device_unregister(pdata->pdev); ++ ++out: ++ mutex_unlock(&gpiommc_sysfs_mutex); ++ ++ return err ? err : count; ++} ++ ++static DRIVER_ATTR(add, 0200, ++ NULL, gpiommc_add_store); ++static DRIVER_ATTR(remove, 0200, ++ NULL, gpiommc_remove_store); ++ ++static struct platform_driver gpiommc_plat_driver = { ++ .probe = gpiommc_probe, ++ .remove = gpiommc_remove, ++ .driver = { ++ .name = GPIOMMC_PLATDEV_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++int gpiommc_next_id(void) ++{ ++ static atomic_t counter = ATOMIC_INIT(-1); ++ ++ return atomic_inc_return(&counter); ++} ++EXPORT_SYMBOL(gpiommc_next_id); ++ ++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_sysfs_platform_data *pdata, *pdata_tmp; ++ ++ driver_remove_file(&gpiommc_plat_driver.driver, ++ &driver_attr_remove); ++ driver_remove_file(&gpiommc_plat_driver.driver, ++ &driver_attr_add); ++ ++ mutex_lock(&gpiommc_sysfs_mutex); ++ list_for_each_entry_safe(pdata, pdata_tmp, &gpiommc_sysfs_list, list) { ++ list_del(&pdata->list); ++ platform_device_unregister(pdata->pdev); ++ } ++ mutex_unlock(&gpiommc_sysfs_mutex); ++ ++ platform_driver_unregister(&gpiommc_plat_driver); ++} ++module_exit(gpiommc_modexit); +Index: linux-2.6.25.10/drivers/mmc/host/Kconfig +=================================================================== +--- linux-2.6.25.10.orig/drivers/mmc/host/Kconfig 2008-07-18 22:30:36.000000000 +0200 ++++ linux-2.6.25.10/drivers/mmc/host/Kconfig 2008-07-18 22:31:00.000000000 +0200 +@@ -130,3 +130,23 @@ config MMC_SPI + + If unsure, or if your system has no SPI master driver, say N. + ++config GPIOMMC ++ tristate "MMC/SD over GPIO-based SPI" ++ depends on MMC && MMC_SPI && SPI_GPIO ++ help ++ This driver hooks up the mmc_spi and spi_gpio modules so that ++ MMC/SD cards can be used on a GPIO based bus by bitbanging ++ the SPI protocol in software. ++ ++ This driver provides a sysfs interface to dynamically create ++ and destroy GPIO-based MMC/SD card interfaces. It also provides ++ a platform device interface API. ++ See Documentation/gpiommc.txt for details. ++ ++ The module will be called gpiommc. ++ ++ If unsure, say N. ++ ++config MMC_S3C ++ tristate "Samsung S3C SD/MMC Card Interface support" ++ depends on ARCH_S3C2410 && MMC +Index: linux-2.6.25.10/drivers/mmc/host/Makefile +=================================================================== +--- linux-2.6.25.10.orig/drivers/mmc/host/Makefile 2008-07-18 22:30:36.000000000 +0200 ++++ linux-2.6.25.10/drivers/mmc/host/Makefile 2008-07-18 22:31:20.000000000 +0200 +@@ -18,3 +18,4 @@ obj-$(CONFIG_MMC_AT91) += at91_mci.o + obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o + obj-$(CONFIG_MMC_SPI) += mmc_spi.o + ++obj-$(CONFIG_GPIOMMC) += gpiommc.o +Index: linux-2.6.25.10/include/linux/mmc/gpiommc.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.25.10/include/linux/mmc/gpiommc.h 2008-07-18 22:31:00.000000000 +0200 +@@ -0,0 +1,62 @@ ++/* ++ * Device driver for MMC/SD cards driven over a GPIO bus. ++ * ++ * Copyright (c) 2008 Michael Buesch ++ * ++ * Licensed under the GNU/GPL version 2. ++ */ ++#ifndef LINUX_GPIOMMC_H_ ++#define LINUX_GPIOMMC_H_ ++ ++#include ++ ++ ++#define GPIOMMC_MAX_NAMELEN 15 ++ ++/** struct gpiommc_pins - Hardware pin assignments ++ * @gpio_di: The GPIO number of the DATA IN pin ++ * @gpio_do: The GPIO number of the DATA OUT pin ++ * @gpio_clk: The GPIO number of the CLOCK pin ++ * @gpio_cs: The GPIO number of the CHIPSELECT pin ++ * @cs_activelow: If true, the chip is considered selected if @gpio_cs is low. ++ */ ++struct gpiommc_pins { ++ unsigned int gpio_di; ++ unsigned int gpio_do; ++ unsigned int gpio_clk; ++ unsigned int gpio_cs; ++ bool cs_activelow; ++}; ++ ++/** struct gpiommc_platform_data - Platform data for a MMC-over-SPI-GPIO device. ++ * @name: The unique name string of the device. ++ * @pins: The hardware pin assignments. ++ * @mode: The hardware mode. This is either SPI_MODE_0, ++ * SPI_MODE_1, SPI_MODE_2 or SPI_MODE_3. See the SPI documentation. ++ * @no_spi_delay: Do not use delays in the lowlevel SPI bitbanging code. ++ * This is not standards compliant, but may be required for some ++ * embedded machines to gain reasonable speed. ++ * @max_bus_speed: The maximum speed of the SPI bus, in Hertz. ++ */ ++struct gpiommc_platform_data { ++ char name[GPIOMMC_MAX_NAMELEN + 1]; ++ struct gpiommc_pins pins; ++ u8 mode; ++ bool no_spi_delay; ++ unsigned int max_bus_speed; ++}; ++ ++/** GPIOMMC_PLATDEV_NAME - The platform device name string. ++ * The name string that has to be used for platform_device_alloc ++ * when allocating a gpiommc device. ++ */ ++#define GPIOMMC_PLATDEV_NAME "gpiommc" ++ ++/** gpiommc_next_id - Get another platform device ID number. ++ * This returns the next platform device ID number that has to be used ++ * for platform_device_alloc. The ID is opaque and should not be used for ++ * anything else. ++ */ ++int gpiommc_next_id(void); ++ ++#endif /* LINUX_GPIOMMC_H_ */ +Index: linux-2.6.25.10/Documentation/gpiommc.txt +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.25.10/Documentation/gpiommc.txt 2008-07-18 22:31:00.000000000 +0200 +@@ -0,0 +1,96 @@ ++GPIOMMC - Driver for an MMC/SD card on a bitbanging GPIO SPI bus ++================================================================ ++ ++The gpiommc module hooks up the mmc_spi and spi_gpio modules for running an ++MMC or SD card on GPIO pins. ++ ++Two interfaces for registering a new MMC/SD card device are provided. ++A static platform-device based mechanism and a dynamic sysfs based interface. ++ ++ ++Registering devices via platform-device ++======================================= ++ ++The platform-device interface is used for registering MMC/SD devices that are ++part of the hardware platform. This is most useful only for embedded machines ++with MMC/SD devices statically connected to the platform GPIO bus. ++ ++The data structures are declared in ++ ++To register a new device, define an instance of struct gpiommc_platform_data. ++This structure holds any information about how the device is hooked up to the ++GPIO pins and what hardware modes the device supports. See the docbook-style ++documentation in the header file for more information on the struct fields. ++ ++Then allocate a new instance of a platform device by doing: ++ ++ pdev = platform_device_alloc(GPIOMMC_PLATDEV_NAME, gpiommc_next_id()); ++ ++This will allocate the platform device data structures and hook it up to the ++gpiommc driver. ++Then add the gpiommc_platform_data to the platform device. ++ ++ err = platform_device_add_data(pdev, pdata, sizeof(struct gpiommc_platform_data)); ++ ++You may free the local instance of struct gpiommc_platform_data now. ++Now simply register the platform device. ++ ++ err = platform_device_add(pdev); ++ ++Done. The gpiommc probe routine should be called and you should see a dmesg ++message for the added device. ++ ++ ++Registering devices via sysfs ++============================= ++ ++MMC/SD cards connected via GPIO often are a pretty dynamic thing. For example ++selfmade hacks for soldering an MMC/SD card to standard GPIO pins on embedded ++hardware are a common situation. ++So we provide a dynamic interface to conveniently handle adding and removing ++devices from userspace, without the need to recompile the kernel. ++ ++There are two sysfs files responsible for that: ++export ADD=/sys/bus/platform/drivers/gpiommc/add ++export REMOVE=/sys/bus/platform/drivers/gpiommc/remove ++ ++To add a new device, simply echo the configuration string to the "add" file. ++The config string is composed out of the following elements: ++ ++DEVNAME DIpin DOpin CLKpin CSpin SPIMODE MAXBUSSPEED NO_SPI_DELAY CSACTIVELOW ++ ++DEVNAME is a unique name string for the device. ++DIpin is the SPI DI GPIO pin. ++DOpin is the SPI DO GPIO pin. ++CLKpin is the SPI CLOCK GPIO pin. ++CSpin is the SPI CHIPSELECT GPIO pin. ++SPIMODE is the hardware mode the device will run at. Can be 0-3. ++MAXBUSSPEED is the maximum bus speed in Hertz. ++NO_SPI_DELAY can be 1 or 0. If it is 1, then the lowlevel SPI delay ++will not be performed. This is not standards compliant, but may be required ++to gain reasonable speeds on embedded hardware. ++CSACTIVELOW can be 1 or 0. If it is 1, the chip is considered to be selected, if CS ++is at a logical 0. ++ ++Note that the elements SPIMODE, MAXBUSSPEED and NO_SPI_DELAY are optional ++and can be omitted. ++SPIMODE will default to 0. ++MAXBUSSSPEED will default to 5Mhz. ++NO_SPI_DELAY will default to 0. ++CSACTIVELOW will default to 1. ++ ++Example: ++ ++ echo -n "my_device 5 4 3 7 0 1000000 1" > $ADD ++ ++This will add a new device called "my_device" with the GPIO pins assigned as ++DI=5, DO=4, CLK=3, CS=7 ++The hardware mode will be SPI_MODE_0. ++The maximum bus speed will be 1000000 Hz (1Mhz) ++And the explicit SPI delay at the lowlevel bitbang loop will be switched off. ++ ++To remove a device, simply echo the device name string to the "remove" file. ++ ++Example: ++ ++ echo -n "my_device" > $REMOVE +Index: linux-2.6.25.10/MAINTAINERS +=================================================================== +--- linux-2.6.25.10.orig/MAINTAINERS 2008-07-18 22:30:41.000000000 +0200 ++++ linux-2.6.25.10/MAINTAINERS 2008-07-18 22:31:00.000000000 +0200 +@@ -1736,6 +1736,11 @@ L: gigaset307x-common@lists.sourceforge. + W: http://gigaset307x.sourceforge.net/ + S: Maintained + ++GPIOMMC DRIVER ++P: Michael Buesch ++M: mb@bu3sch.de ++S: Maintained ++ + HARDWARE MONITORING + P: Mark M. Hoffman + M: mhoffman@lightlink.com