From f4b516ef34211bfff04b45570bbe0b3f62d43f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20B=C3=BCsch?= Date: Fri, 18 Jul 2008 20:53:08 +0000 Subject: Update GPIO-based MMC driver SVN-Revision: 11864 --- .../generic-2.6/patches-2.6.25/922-gpiommc.patch | 554 +++++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100644 target/linux/generic-2.6/patches-2.6.25/922-gpiommc.patch (limited to 'target/linux/generic-2.6/patches-2.6.25/922-gpiommc.patch') 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 -- cgit v1.2.3