diff options
| -rw-r--r-- | package/mmc_over_gpio/Makefile | 14 | ||||
| -rw-r--r-- | package/mmc_over_gpio/files/mmc_over_gpio.init | 4 | ||||
| -rw-r--r-- | package/mmc_over_gpio/src/Makefile | 1 | ||||
| -rw-r--r-- | package/mmc_over_gpio/src/mmc_over_spigpio.c | 339 | ||||
| -rw-r--r-- | target/linux/generic-2.6/files-2.6.25/drivers/spi/spi_gpio.c | 242 | ||||
| -rw-r--r-- | target/linux/generic-2.6/files-2.6.25/include/linux/spi/spi_gpio.h | 53 | ||||
| -rw-r--r-- | target/linux/generic-2.6/patches-2.6.25/921-gpio_spi_driver.patch | 370 | ||||
| -rw-r--r-- | target/linux/generic-2.6/patches-2.6.25/922-gpiommc.patch | 554 | 
8 files changed, 922 insertions, 655 deletions
| diff --git a/package/mmc_over_gpio/Makefile b/package/mmc_over_gpio/Makefile index abf89379f..20b7b6ac4 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 88d9e4102..57c5695d1 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 b052c405a..000000000 --- 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 113b518a5..000000000 --- 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 <mb@bu3sch.de> - * - * Licensed under the GNU/GPL. See COPYING for details. - */ - -#include <linux/platform_device.h> -#include <linux/list.h> -#include <linux/mutex.h> -#include <linux/spi/spi_gpio.h> - - -/* 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 31048acc3..000000000 --- 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 <linux/kernel.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/spinlock.h> -#include <linux/workqueue.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/spi/spi.h> -#include <linux/spi/spi_bitbang.h> -#include "linux/spi/spi_gpio.h" //XXX -#include <asm/gpio.h> - - -struct spi_gpio { -	struct spi_bitbang bitbang; -	struct spi_gpio_platform_data *info; -	struct platform_device *pdev; -	struct spi_board_info bi; -}; - - -static inline struct spi_gpio *spidev_to_sg(struct spi_device *dev) -{ -	return dev->controller_data; -} - -static inline void setsck(struct spi_device *dev, int val) -{ -	struct spi_gpio *sp = spidev_to_sg(dev); -	gpio_set_value(sp->info->pin_clk, val ? 1 : 0); -} - -static inline void setmosi(struct spi_device *dev, int val ) -{ -	struct spi_gpio *sp = spidev_to_sg(dev); -	gpio_set_value(sp->info->pin_mosi, val ? 1 : 0); -} - -static inline u32 getmiso(struct spi_device *dev) -{ -	struct spi_gpio *sp = spidev_to_sg(dev); -	return gpio_get_value(sp->info->pin_miso) ? 1 : 0; -} - -static inline void do_spidelay(struct spi_device *dev, unsigned nsecs) -{ -	struct spi_gpio *sp = spidev_to_sg(dev); - -	if (!sp->info->no_spi_delay) -		ndelay(nsecs); -} - -#define spidelay(nsecs) do {					\ -	/* Steal the spi_device pointer from our caller.	\ -	 * The bitbang-API should probably get fixed here... */	\ -	do_spidelay(spi, nsecs);				\ -  } while (0) - -#define EXPAND_BITBANG_TXRX -#include <linux/spi/spi_bitbang.h> - -static u32 spi_gpio_txrx_mode0(struct spi_device *spi, -			       unsigned nsecs, u32 word, u8 bits) -{ -        return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits); -} - -static u32 spi_gpio_txrx_mode1(struct spi_device *spi, -			       unsigned nsecs, u32 word, u8 bits) -{ -        return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits); -} - -static u32 spi_gpio_txrx_mode2(struct spi_device *spi, -			       unsigned nsecs, u32 word, u8 bits) -{ -        return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits); -} - -static u32 spi_gpio_txrx_mode3(struct spi_device *spi, -			       unsigned nsecs, u32 word, u8 bits) -{ -        return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits); -} - -static void spi_gpio_chipselect(struct spi_device *dev, int on) -{ -	struct spi_gpio *sp = spidev_to_sg(dev); - -	if (sp->info->cs_activelow) -		on = !on; -	gpio_set_value(sp->info->pin_cs, on ? 1 : 0); -} - -static int spi_gpio_probe(struct platform_device *pdev) -{ -	struct spi_master *master; -	struct spi_gpio_platform_data *pdata; -	struct spi_gpio *sp; -	struct spi_device *spidev; -	int err; - -	pdata = pdev->dev.platform_data; -	if (!pdata) -		return -ENXIO; - -	err = -ENOMEM; -	master = spi_alloc_master(&pdev->dev, sizeof(struct spi_gpio)); -	if (!master) -		goto err_alloc_master; - -	sp = spi_master_get_devdata(master); -	platform_set_drvdata(pdev, sp); -	sp->info = pdata; - -	err = gpio_request(pdata->pin_clk, "spi_clock"); -	if (err) -		goto err_request_clk; -	err = gpio_request(pdata->pin_mosi, "spi_mosi"); -	if (err) -		goto err_request_mosi; -	err = gpio_request(pdata->pin_miso, "spi_miso"); -	if (err) -		goto err_request_miso; -	err = gpio_request(pdata->pin_cs, "spi_cs"); -	if (err) -		goto err_request_cs; - -	sp->bitbang.master = spi_master_get(master); -	sp->bitbang.master->bus_num = -1; -	sp->bitbang.master->num_chipselect = 1; -	sp->bitbang.chipselect = spi_gpio_chipselect; -	sp->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_mode0; -	sp->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_mode1; -	sp->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_mode2; -	sp->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_mode3; - -	gpio_direction_output(pdata->pin_clk, 0); -	gpio_direction_output(pdata->pin_mosi, 0); -	gpio_direction_output(pdata->pin_cs, -			      pdata->cs_activelow ? 1 : 0); -	gpio_direction_input(pdata->pin_miso); - -	err = spi_bitbang_start(&sp->bitbang); -	if (err) -		goto err_no_bitbang; -	err = pdata->boardinfo_setup(&sp->bi, master, -				     pdata->boardinfo_setup_data); -	if (err) -		goto err_bi_setup; -	sp->bi.controller_data = sp; -	spidev = spi_new_device(master, &sp->bi); -	if (!spidev) -		goto err_new_dev; - -	return 0; - -err_new_dev: -err_bi_setup: -	spi_bitbang_stop(&sp->bitbang); -err_no_bitbang: -	spi_master_put(sp->bitbang.master); -	gpio_free(pdata->pin_cs); -err_request_cs: -	gpio_free(pdata->pin_miso); -err_request_miso: -	gpio_free(pdata->pin_mosi); -err_request_mosi: -	gpio_free(pdata->pin_clk); -err_request_clk: -	kfree(master); - -err_alloc_master: -	return err; -} - -static int __devexit spi_gpio_remove(struct platform_device *pdev) -{ -	struct spi_gpio *sp; -	struct spi_gpio_platform_data *pdata; - -	pdata = pdev->dev.platform_data; -	sp = platform_get_drvdata(pdev); - -	gpio_free(pdata->pin_clk); -	gpio_free(pdata->pin_mosi); -	gpio_free(pdata->pin_miso); -	gpio_free(pdata->pin_cs); -	spi_bitbang_stop(&sp->bitbang); -	spi_master_put(sp->bitbang.master); - -	return 0; -} - -static struct platform_driver spi_gpio_driver = { -	.driver		= { -		.name	= "spi-gpio", -		.owner	= THIS_MODULE, -	}, -	.probe		= spi_gpio_probe, -	.remove		= __devexit_p(spi_gpio_remove), -}; - -static int __init spi_gpio_init(void) -{ -	int err; - -	err = platform_driver_register(&spi_gpio_driver); -	if (err) -		printk(KERN_ERR "spi-gpio: register failed: %d\n", err); - -	return err; -} -module_init(spi_gpio_init); - -static void __exit spi_gpio_exit(void) -{ -	platform_driver_unregister(&spi_gpio_driver); -} -module_exit(spi_gpio_exit); - -MODULE_AUTHOR("Piot Skamruk <piotr.skamruk at gmail.com>"); -MODULE_AUTHOR("Michael Buesch"); -MODULE_DESCRIPTION("Platform independent GPIO bitbangling SPI driver"); -MODULE_LICENSE("GPL v2"); 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 8e7d4b189..000000000 --- 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 <linux/types.h> -#include <linux/spi/spi.h> - - -/** struct spi_gpio_platform_data - Data definitions for a SPI-GPIO device. - * This structure holds information about a GPIO-based SPI device. - * - * @pin_clk: The GPIO pin number of the CLOCK pin. - * - * @pin_miso: The GPIO pin number of the MISO pin. - * - * @pin_mosi: The GPIO pin number of the MOSI pin. - * - * @pin_cs: The GPIO pin number of the CHIPSELECT pin. - * - * @cs_activelow: If true, the chip is selected when the CS line is low. - * - * @no_spi_delay: If true, no delay is done in the lowlevel bitbanging. - *                Note that doing no delay is not standards compliant, - *                but it might be needed to speed up transfers on some - *                slow embedded machines. - * - * @boardinfo_setup: This callback is called after the - *                   SPI master device was registered, but before the - *                   device is registered. - * @boardinfo_setup_data: Data argument passed to boardinfo_setup(). - */ -struct spi_gpio_platform_data { -	unsigned int pin_clk; -	unsigned int pin_miso; -	unsigned int pin_mosi; -	unsigned int pin_cs; -	bool cs_activelow; -	bool no_spi_delay; -	int (*boardinfo_setup)(struct spi_board_info *bi, -			       struct spi_master *master, -			       void *data); -	void *boardinfo_setup_data; -}; - -#endif /* _LINUX_SPI_SPI_GPIO */ diff --git a/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 eeb7285a0..264386f33 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 <linux/types.h> ++#include <linux/spi/spi.h> ++ ++ ++/** struct spi_gpio_platform_data - Data definitions for a SPI-GPIO device. ++ * This structure holds information about a GPIO-based SPI device. ++ * ++ * @pin_clk: The GPIO pin number of the CLOCK pin. ++ * ++ * @pin_miso: The GPIO pin number of the MISO pin. ++ * ++ * @pin_mosi: The GPIO pin number of the MOSI pin. ++ * ++ * @pin_cs: The GPIO pin number of the CHIPSELECT pin. ++ * ++ * @cs_activelow: If true, the chip is selected when the CS line is low. ++ * ++ * @no_spi_delay: If true, no delay is done in the lowlevel bitbanging. ++ *                Note that doing no delay is not standards compliant, ++ *                but it might be needed to speed up transfers on some ++ *                slow embedded machines. ++ * ++ * @boardinfo_setup: This callback is called after the ++ *                   SPI master device was registered, but before the ++ *                   device is registered. ++ * @boardinfo_setup_data: Data argument passed to boardinfo_setup(). ++ */ ++struct spi_gpio_platform_data { ++	unsigned int pin_clk; ++	unsigned int pin_miso; ++	unsigned int pin_mosi; ++	unsigned int pin_cs; ++	bool cs_activelow; ++	bool no_spi_delay; ++	int (*boardinfo_setup)(struct spi_board_info *bi, ++			       struct spi_master *master, ++			       void *data); ++	void *boardinfo_setup_data; ++}; ++ ++/** 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 <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/spinlock.h> ++#include <linux/workqueue.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/spi_bitbang.h> ++#include <linux/spi/spi_gpio.h> ++#include <linux/gpio.h> ++#include <asm/atomic.h> ++ ++ ++struct spi_gpio { ++	struct spi_bitbang bitbang; ++	struct spi_gpio_platform_data *info; ++	struct platform_device *pdev; ++	struct spi_board_info bi; ++}; ++ ++ ++static inline struct spi_gpio *spidev_to_sg(struct spi_device *dev) ++{ ++	return dev->controller_data; ++} ++ ++static inline void setsck(struct spi_device *dev, int val) ++{ ++	struct spi_gpio *sp = spidev_to_sg(dev); ++	gpio_set_value(sp->info->pin_clk, val ? 1 : 0); ++} ++ ++static inline void setmosi(struct spi_device *dev, int val) ++{ ++	struct spi_gpio *sp = spidev_to_sg(dev); ++	gpio_set_value(sp->info->pin_mosi, val ? 1 : 0); ++} ++ ++static inline u32 getmiso(struct spi_device *dev) ++{ ++	struct spi_gpio *sp = spidev_to_sg(dev); ++	return gpio_get_value(sp->info->pin_miso) ? 1 : 0; ++} ++ ++static inline void do_spidelay(struct spi_device *dev, unsigned nsecs) ++{ ++	struct spi_gpio *sp = spidev_to_sg(dev); ++ ++	if (!sp->info->no_spi_delay) ++		ndelay(nsecs); ++} ++ ++#define spidelay(nsecs) do {					\ ++	/* Steal the spi_device pointer from our caller.	\ ++	 * The bitbang-API should probably get fixed here... */	\ ++	do_spidelay(spi, nsecs);				\ ++  } while (0) ++ ++#define EXPAND_BITBANG_TXRX ++#include <linux/spi/spi_bitbang.h> ++ ++static u32 spi_gpio_txrx_mode0(struct spi_device *spi, ++			       unsigned nsecs, u32 word, u8 bits) ++{ ++	return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits); ++} ++ ++static u32 spi_gpio_txrx_mode1(struct spi_device *spi, ++			       unsigned nsecs, u32 word, u8 bits) ++{ ++	return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits); ++} ++ ++static u32 spi_gpio_txrx_mode2(struct spi_device *spi, ++			       unsigned nsecs, u32 word, u8 bits) ++{ ++	return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits); ++} ++ ++static u32 spi_gpio_txrx_mode3(struct spi_device *spi, ++			       unsigned nsecs, u32 word, u8 bits) ++{ ++	return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits); ++} ++ ++static void spi_gpio_chipselect(struct spi_device *dev, int on) ++{ ++	struct spi_gpio *sp = spidev_to_sg(dev); ++ ++	if (sp->info->cs_activelow) ++		on = !on; ++	gpio_set_value(sp->info->pin_cs, on ? 1 : 0); ++} ++ ++static int spi_gpio_probe(struct platform_device *pdev) ++{ ++	struct spi_master *master; ++	struct spi_gpio_platform_data *pdata; ++	struct spi_gpio *sp; ++	struct spi_device *spidev; ++	int err; ++ ++	pdata = pdev->dev.platform_data; ++	if (!pdata) ++		return -ENXIO; ++ ++	err = -ENOMEM; ++	master = spi_alloc_master(&pdev->dev, sizeof(struct spi_gpio)); ++	if (!master) ++		goto err_alloc_master; ++ ++	sp = spi_master_get_devdata(master); ++	platform_set_drvdata(pdev, sp); ++	sp->info = pdata; ++ ++	err = gpio_request(pdata->pin_clk, "spi_clock"); ++	if (err) ++		goto err_request_clk; ++	err = gpio_request(pdata->pin_mosi, "spi_mosi"); ++	if (err) ++		goto err_request_mosi; ++	err = gpio_request(pdata->pin_miso, "spi_miso"); ++	if (err) ++		goto err_request_miso; ++	err = gpio_request(pdata->pin_cs, "spi_cs"); ++	if (err) ++		goto err_request_cs; ++ ++	sp->bitbang.master = spi_master_get(master); ++	sp->bitbang.master->bus_num = -1; ++	sp->bitbang.master->num_chipselect = 1; ++	sp->bitbang.chipselect = spi_gpio_chipselect; ++	sp->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_mode0; ++	sp->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_mode1; ++	sp->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_mode2; ++	sp->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_mode3; ++ ++	gpio_direction_output(pdata->pin_clk, 0); ++	gpio_direction_output(pdata->pin_mosi, 0); ++	gpio_direction_output(pdata->pin_cs, ++			      pdata->cs_activelow ? 1 : 0); ++	gpio_direction_input(pdata->pin_miso); ++ ++	err = spi_bitbang_start(&sp->bitbang); ++	if (err) ++		goto err_no_bitbang; ++	err = pdata->boardinfo_setup(&sp->bi, master, ++				     pdata->boardinfo_setup_data); ++	if (err) ++		goto err_bi_setup; ++	sp->bi.controller_data = sp; ++	spidev = spi_new_device(master, &sp->bi); ++	if (!spidev) ++		goto err_new_dev; ++ ++	return 0; ++ ++err_new_dev: ++err_bi_setup: ++	spi_bitbang_stop(&sp->bitbang); ++err_no_bitbang: ++	spi_master_put(sp->bitbang.master); ++	gpio_free(pdata->pin_cs); ++err_request_cs: ++	gpio_free(pdata->pin_miso); ++err_request_miso: ++	gpio_free(pdata->pin_mosi); ++err_request_mosi: ++	gpio_free(pdata->pin_clk); ++err_request_clk: ++	kfree(master); ++ ++err_alloc_master: ++	return err; ++} ++ ++static int __devexit spi_gpio_remove(struct platform_device *pdev) ++{ ++	struct spi_gpio *sp; ++	struct spi_gpio_platform_data *pdata; ++ ++	pdata = pdev->dev.platform_data; ++	sp = platform_get_drvdata(pdev); ++ ++	gpio_free(pdata->pin_clk); ++	gpio_free(pdata->pin_mosi); ++	gpio_free(pdata->pin_miso); ++	gpio_free(pdata->pin_cs); ++	spi_bitbang_stop(&sp->bitbang); ++	spi_master_put(sp->bitbang.master); ++ ++	return 0; ++} ++ ++static struct platform_driver spi_gpio_driver = { ++	.driver		= { ++		.name	= SPI_GPIO_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 <piotr.skamruk at gmail.com>"); ++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 000000000..8253202c0 --- /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 <mb@bu3sch.de> ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#include <linux/mmc/gpiommc.h> ++#include <linux/platform_device.h> ++#include <linux/list.h> ++#include <linux/mutex.h> ++#include <linux/spi/spi_gpio.h> ++ ++ ++#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 <linux/types.h> ++ ++ ++#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 <linux/mmc/gpiommc.h> ++ ++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 | 
