Update gpiommc to use configfs

SVN-Revision: 11887
This commit is contained in:
Michael Büsch 2008-07-20 19:34:09 +00:00
parent d7bdc37560
commit 204728376c
6 changed files with 630 additions and 228 deletions

View file

@ -16,7 +16,7 @@ include $(INCLUDE_DIR)/package.mk
define KernelPackage/mmc-over-gpio define KernelPackage/mmc-over-gpio
SUBMENU:=Other modules SUBMENU:=Other modules
DEPENDS:=@GPIO_SUPPORT +kmod-mmc-spi +kmod-spi-gpio DEPENDS:=@GPIO_SUPPORT +kmod-mmc-spi +kmod-spi-gpio
KCONFIG:=CONFIG_GPIOMMC KCONFIG:=CONFIG_GPIOMMC CONFIG_CONFIGFS_FS=y
TITLE:=MMC/SD card over GPIO support TITLE:=MMC/SD card over GPIO support
FILES:=$(LINUX_DIR)/drivers/mmc/host/gpiommc.$(LINUX_KMOD_SUFFIX) FILES:=$(LINUX_DIR)/drivers/mmc/host/gpiommc.$(LINUX_KMOD_SUFFIX)
AUTOLOAD:=$(call AutoLoad,93,spi_gpio gpiommc) AUTOLOAD:=$(call AutoLoad,93,spi_gpio gpiommc)

View file

@ -2,20 +2,59 @@
# Copyright (C) 2008 OpenWrt.org # Copyright (C) 2008 OpenWrt.org
START=90 START=90
SYSFS="/sys" CONFIGFS_DIR="/config/gpiommc"
SYSFS_DRIVERDIR="$SYSFS/bus/platform/drivers/gpiommc"
# add_device(name, DI_pin, DO_pin, CLK_pin, CS_pin, mode) # add_device(name, DI_pin, DO_pin, CLK_pin, CS_pin, mode)
add_device() { add_device() {
echo -n "$1" "$2" "$3" "$4" "$5" "$6" > $SYSFS_DRIVERDIR/add local dir="$CONFIGFS_DIR/$1"
mkdir $dir
[ $? -eq 0 ] || return 1
echo $2 > $dir/gpio_data_in
[ $? -eq 0 ] || return 1
echo $3 > $dir/gpio_data_out
[ $? -eq 0 ] || return 1
echo $4 > $dir/gpio_clock
[ $? -eq 0 ] || return 1
echo $5 > $dir/gpio_chipselect
[ $? -eq 0 ] || return 1
echo $6 > $dir/spi_mode
[ $? -eq 0 ] || return 1
# XXX We have more config options available. Use defaults for now.
echo 1 > $dir/register
[ $? -eq 0 ] || return 1
return 0
} }
# remove_device(name) # remove_device(name)
remove_device() { remove_device() {
echo -n "$1" > $SYSFS_DRIVERDIR/remove local dir="$CONFIGFS_DIR/$1"
rmdir $dir
}
mount_configfs() {
# FIXME: This should probably be done somewhere else.
mount | grep configfs
if [ $? -eq 0 ]; then
# already mounted
return 0
fi
mkdir -p /config
[ $? -eq 0 ] || return 1
mount configfs -t configfs /config
[ $? -eq 0 ] || return 1
return 0
} }
start() { start() {
# Make sure configfs is mounted
mount_configfs
[ $? -eq 0 ] || return 1
#FIXME we should use a config file, but I dunno how that parser works. #FIXME we should use a config file, but I dunno how that parser works.
add_device "default" 5 4 3 7 0 add_device "default" 5 4 3 7 0
} }

View file

@ -0,0 +1,44 @@
Add gpio_is_valid() for bcm47xx
Index: linux-2.6.25.10/arch/mips/bcm47xx/gpio.c
===================================================================
--- linux-2.6.25.10.orig/arch/mips/bcm47xx/gpio.c 2008-07-20 20:48:44.000000000 +0200
+++ linux-2.6.25.10/arch/mips/bcm47xx/gpio.c 2008-07-20 20:57:55.000000000 +0200
@@ -77,3 +77,15 @@ int bcm47xx_gpio_direction_output(unsign
}
EXPORT_SYMBOL_GPL(bcm47xx_gpio_direction_output);
+int bcm47xx_gpio_is_valid(int gpio)
+{
+ if (ssb_bcm47xx.chipco.dev) {
+ if (gpio >= 0 && gpio < BCM47XX_CHIPCO_GPIO_LINES)
+ return 1;
+ } else if (ssb_bcm47xx.extif.dev) {
+ if (gpio >= 0 && gpio < BCM47XX_EXTIF_GPIO_LINES)
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(bcm47xx_gpio_is_valid);
Index: linux-2.6.25.10/include/asm-mips/mach-bcm47xx/gpio.h
===================================================================
--- linux-2.6.25.10.orig/include/asm-mips/mach-bcm47xx/gpio.h 2008-07-20 20:48:44.000000000 +0200
+++ linux-2.6.25.10/include/asm-mips/mach-bcm47xx/gpio.h 2008-07-20 20:49:20.000000000 +0200
@@ -17,6 +17,7 @@ extern int bcm47xx_gpio_get_value(unsign
extern void bcm47xx_gpio_set_value(unsigned gpio, int value);
extern int bcm47xx_gpio_direction_input(unsigned gpio);
extern int bcm47xx_gpio_direction_output(unsigned gpio, int value);
+extern int bcm47xx_gpio_is_valid(int gpio);
static inline int gpio_request(unsigned gpio, const char *label)
{
@@ -52,6 +53,10 @@ static inline int gpio_direction_output(
return bcm47xx_gpio_direction_output(gpio, value);
}
+static inline int gpio_is_valid(int gpio)
+{
+ return bcm47xx_gpio_is_valid(gpio);
+}
/* cansleep wrappers */
#include <asm-generic/gpio.h>

View file

@ -0,0 +1,18 @@
Backport gpio_is_valid() for gpiolib from linux-2.6.26
Index: linux-2.6.25.10/include/asm-generic/gpio.h
===================================================================
--- linux-2.6.25.10.orig/include/asm-generic/gpio.h 2008-07-03 05:46:47.000000000 +0200
+++ linux-2.6.25.10/include/asm-generic/gpio.h 2008-07-20 20:32:12.000000000 +0200
@@ -16,6 +16,12 @@
#define ARCH_NR_GPIOS 256
#endif
+static inline int gpio_is_valid(int number)
+{
+ /* only some non-negative numbers are valid */
+ return ((unsigned)number) < ARCH_NR_GPIOS;
+}
+
struct seq_file;
/**

View file

@ -1,8 +1,8 @@
Index: linux-2.6.25.10/include/linux/spi/spi_gpio.h Index: linux-2.6.25.10/include/linux/spi/spi_gpio.h
=================================================================== ===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000 --- /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 +++ linux-2.6.25.10/include/linux/spi/spi_gpio.h 2008-07-20 20:10:21.000000000 +0200
@@ -0,0 +1,67 @@ @@ -0,0 +1,73 @@
+/* +/*
+ * spi_gpio interface to platform code + * spi_gpio interface to platform code
+ * + *
@ -20,7 +20,9 @@ Index: linux-2.6.25.10/include/linux/spi/spi_gpio.h
+#include <linux/spi/spi.h> +#include <linux/spi/spi.h>
+ +
+ +
+/** struct spi_gpio_platform_data - Data definitions for a SPI-GPIO device. +/**
+ * struct spi_gpio_platform_data - Data definitions for a SPI-GPIO device.
+ *
+ * This structure holds information about a GPIO-based SPI device. + * This structure holds information about a GPIO-based SPI device.
+ * + *
+ * @pin_clk: The GPIO pin number of the CLOCK pin. + * @pin_clk: The GPIO pin number of the CLOCK pin.
@ -56,13 +58,17 @@ Index: linux-2.6.25.10/include/linux/spi/spi_gpio.h
+ void *boardinfo_setup_data; + void *boardinfo_setup_data;
+}; +};
+ +
+/** SPI_GPIO_PLATDEV_NAME - The platform device name string. +/**
+ * SPI_GPIO_PLATDEV_NAME - The platform device name string.
+ *
+ * The name string that has to be used for platform_device_alloc + * The name string that has to be used for platform_device_alloc
+ * when allocating a spi-gpio device. + * when allocating a spi-gpio device.
+ */ + */
+#define SPI_GPIO_PLATDEV_NAME "spi-gpio" +#define SPI_GPIO_PLATDEV_NAME "spi-gpio"
+ +
+/** spi_gpio_next_id - Get another platform device ID number. +/**
+ * spi_gpio_next_id - Get another platform device ID number.
+ *
+ * This returns the next platform device ID number that has to be used + * 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 + * for platform_device_alloc. The ID is opaque and should not be used for
+ * anything else. + * anything else.
@ -73,7 +79,7 @@ Index: linux-2.6.25.10/include/linux/spi/spi_gpio.h
Index: linux-2.6.25.10/drivers/spi/spi_gpio.c Index: linux-2.6.25.10/drivers/spi/spi_gpio.c
=================================================================== ===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000 --- /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 +++ linux-2.6.25.10/drivers/spi/spi_gpio.c 2008-07-20 20:10:21.000000000 +0200
@@ -0,0 +1,251 @@ @@ -0,0 +1,251 @@
+/* +/*
+ * Bitbanging SPI bus driver using GPIO API + * Bitbanging SPI bus driver using GPIO API
@ -328,8 +334,8 @@ Index: linux-2.6.25.10/drivers/spi/spi_gpio.c
+MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL v2");
Index: linux-2.6.25.10/drivers/spi/Kconfig 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.orig/drivers/spi/Kconfig 2008-07-20 20:09:48.000000000 +0200
+++ linux-2.6.25.10/drivers/spi/Kconfig 2008-07-18 18:19:56.000000000 +0200 +++ linux-2.6.25.10/drivers/spi/Kconfig 2008-07-20 20:11:48.000000000 +0200
@@ -100,6 +100,19 @@ config SPI_BUTTERFLY @@ -100,6 +100,19 @@ config SPI_BUTTERFLY
inexpensive battery powered microcontroller evaluation board. inexpensive battery powered microcontroller evaluation board.
This same cable can be used to flash new firmware. This same cable can be used to flash new firmware.
@ -341,7 +347,7 @@ Index: linux-2.6.25.10/drivers/spi/Kconfig
+ help + help
+ This is a platform driver that can be used for bitbanging + This is a platform driver that can be used for bitbanging
+ an SPI bus over GPIO pins. + an SPI bus over GPIO pins.
+ Select this, if you have any SPI device that is connected via + Select this if you have any SPI device that is connected via
+ GPIO pins. + GPIO pins.
+ The module will be called spi_gpio. + The module will be called spi_gpio.
+ +
@ -352,8 +358,8 @@ Index: linux-2.6.25.10/drivers/spi/Kconfig
depends on SPI_MASTER && ARCH_IMX && EXPERIMENTAL depends on SPI_MASTER && ARCH_IMX && EXPERIMENTAL
Index: linux-2.6.25.10/drivers/spi/Makefile 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.orig/drivers/spi/Makefile 2008-07-20 20:09:48.000000000 +0200
+++ linux-2.6.25.10/drivers/spi/Makefile 2008-07-18 18:19:56.000000000 +0200 +++ linux-2.6.25.10/drivers/spi/Makefile 2008-07-20 20:10:21.000000000 +0200
@@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx. @@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.
obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
obj-$(CONFIG_SPI_AU1550) += au1550_spi.o obj-$(CONFIG_SPI_AU1550) += au1550_spi.o
@ -364,8 +370,8 @@ Index: linux-2.6.25.10/drivers/spi/Makefile
obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
Index: linux-2.6.25.10/MAINTAINERS 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.orig/MAINTAINERS 2008-07-20 20:09:48.000000000 +0200
+++ linux-2.6.25.10/MAINTAINERS 2008-07-18 18:20:28.000000000 +0200 +++ linux-2.6.25.10/MAINTAINERS 2008-07-20 20:10:59.000000000 +0200
@@ -3685,6 +3685,11 @@ M: dbrownell@users.sourceforge.net @@ -3685,6 +3685,11 @@ M: dbrownell@users.sourceforge.net
L: spi-devel-general@lists.sourceforge.net L: spi-devel-general@lists.sourceforge.net
S: Maintained S: Maintained

View file

@ -1,12 +1,12 @@
Index: linux-2.6.25.10/drivers/mmc/host/gpiommc.c Index: linux-2.6.25.10/drivers/mmc/host/gpiommc.c
=================================================================== ===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000 --- /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 +++ linux-2.6.25.10/drivers/mmc/host/gpiommc.c 2008-07-20 20:49:16.000000000 +0200
@@ -0,0 +1,328 @@ @@ -0,0 +1,608 @@
+/* +/*
+ * Driver an MMC/SD card on a bitbanging GPIO SPI bus. + * Driver an MMC/SD card on a bitbanging GPIO SPI bus.
+ * This module hooks up the mmc_spi and spi_gpio modules and also + * This module hooks up the mmc_spi and spi_gpio modules and also
+ * provides a sysfs interface. + * provides a configfs interface.
+ * + *
+ * Copyright 2008 Michael Buesch <mb@bu3sch.de> + * Copyright 2008 Michael Buesch <mb@bu3sch.de>
+ * + *
@ -18,10 +18,13 @@ Index: linux-2.6.25.10/drivers/mmc/host/gpiommc.c
+#include <linux/list.h> +#include <linux/list.h>
+#include <linux/mutex.h> +#include <linux/mutex.h>
+#include <linux/spi/spi_gpio.h> +#include <linux/spi/spi_gpio.h>
+#include <linux/configfs.h>
+#include <linux/gpio.h>
+#include <asm/atomic.h>
+ +
+ +
+#define PFX "gpio-mmc: " +#define PFX "gpio-mmc: "
+#define GPIOMMC_MAX_NAMELEN_STR __stringify(GPIOMMC_MAX_NAMELEN) +
+ +
+struct gpiommc_device { +struct gpiommc_device {
+ struct platform_device *pdev; + struct platform_device *pdev;
@ -63,6 +66,14 @@ Index: linux-2.6.25.10/drivers/mmc/host/gpiommc.c
+ if (!mmc_pdata) + if (!mmc_pdata)
+ goto error; + goto error;
+ +
+#ifdef CONFIG_MMC_SPI_MODULE
+ err = request_module("mmc_spi");
+ if (err) {
+ printk(KERN_WARNING PFX
+ "Failed to request mmc_spi module.\n");
+ }
+#endif /* CONFIG_MMC_SPI_MODULE */
+
+ /* Allocate the GPIO-MMC device */ + /* Allocate the GPIO-MMC device */
+ err = -ENOMEM; + err = -ENOMEM;
+ d = kzalloc(sizeof(*d), GFP_KERNEL); + d = kzalloc(sizeof(*d), GFP_KERNEL);
@ -121,152 +132,438 @@ Index: linux-2.6.25.10/drivers/mmc/host/gpiommc.c
+ struct gpiommc_platform_data *pdata = d->pdev->dev.platform_data; + struct gpiommc_platform_data *pdata = d->pdev->dev.platform_data;
+ +
+ platform_device_unregister(d->spi_pdev); + platform_device_unregister(d->spi_pdev);
+ printk(KERN_INFO PFX "GPIO based MMC-Card \"%s\" removed\n", pdata->name); + printk(KERN_INFO PFX "GPIO based MMC-Card \"%s\" removed\n",
+ pdata->name);
+ platform_device_put(d->spi_pdev); + platform_device_put(d->spi_pdev);
+ +
+ return 0; + return 0;
+} +}
+ +
+/* Wrapper for the platform data with context data for the sysfs interface. */ +#ifdef CONFIG_GPIOMMC_CONFIGFS
+struct gpiommc_sysfs_platform_data {
+ struct gpiommc_platform_data p; /* Keep as first element */
+ +
+ /* The platform device that we allocated. */ +/* A device that was created through configfs */
+struct gpiommc_configfs_device {
+ struct config_item item;
+ /* The platform device, after registration. */
+ struct platform_device *pdev; + struct platform_device *pdev;
+ /* gpiommc_sysfs_list */ + /* The configuration */
+ struct list_head list; + struct gpiommc_platform_data pdata;
+}; +};
+ +
+static LIST_HEAD(gpiommc_sysfs_list); +#define GPIO_INVALID -1
+static DEFINE_MUTEX(gpiommc_sysfs_mutex);
+ +
+static struct gpiommc_sysfs_platform_data *gpiommc_sysfs_find_dev(const char *name) +static inline bool gpiommc_is_registered(struct gpiommc_configfs_device *dev)
+{ +{
+ struct gpiommc_sysfs_platform_data *pdata; + return (dev->pdev != NULL);
+
+ list_for_each_entry(pdata, &gpiommc_sysfs_list, list) {
+ if (strcmp(pdata->p.name, name) == 0)
+ return pdata;
+} +}
+ +
+static inline struct gpiommc_configfs_device *ci_to_gpiommc(struct config_item *item)
+{
+ return item ? container_of(item, struct gpiommc_configfs_device, item) : NULL;
+}
+
+static struct configfs_attribute gpiommc_attr_DI = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "gpio_data_in",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute gpiommc_attr_DO = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "gpio_data_out",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute gpiommc_attr_CLK = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "gpio_clock",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute gpiommc_attr_CS = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "gpio_chipselect",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute gpiommc_attr_CS_activelow = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "gpio_chipselect_activelow",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute gpiommc_attr_spimode = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "spi_mode",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute gpiommc_attr_spidelay = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "spi_delay",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute gpiommc_attr_max_bus_speed = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "max_bus_speed",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute gpiommc_attr_register = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "register",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute *gpiommc_config_attrs[] = {
+ &gpiommc_attr_DI,
+ &gpiommc_attr_DO,
+ &gpiommc_attr_CLK,
+ &gpiommc_attr_CS,
+ &gpiommc_attr_CS_activelow,
+ &gpiommc_attr_spimode,
+ &gpiommc_attr_spidelay,
+ &gpiommc_attr_max_bus_speed,
+ &gpiommc_attr_register,
+ NULL,
+};
+
+static ssize_t gpiommc_config_attr_show(struct config_item *item,
+ struct configfs_attribute *attr,
+ char *page)
+{
+ struct gpiommc_configfs_device *dev = ci_to_gpiommc(item);
+ ssize_t count = 0;
+ unsigned int gpio;
+ int err = 0;
+
+ if (attr == &gpiommc_attr_DI) {
+ gpio = dev->pdata.pins.gpio_di;
+ if (gpio == GPIO_INVALID)
+ count = snprintf(page, PAGE_SIZE, "not configured\n");
+ else
+ count = snprintf(page, PAGE_SIZE, "%u\n", gpio);
+ goto out;
+ }
+ if (attr == &gpiommc_attr_DO) {
+ gpio = dev->pdata.pins.gpio_do;
+ if (gpio == GPIO_INVALID)
+ count = snprintf(page, PAGE_SIZE, "not configured\n");
+ else
+ count = snprintf(page, PAGE_SIZE, "%u\n", gpio);
+ goto out;
+ }
+ if (attr == &gpiommc_attr_CLK) {
+ gpio = dev->pdata.pins.gpio_clk;
+ if (gpio == GPIO_INVALID)
+ count = snprintf(page, PAGE_SIZE, "not configured\n");
+ else
+ count = snprintf(page, PAGE_SIZE, "%u\n", gpio);
+ goto out;
+ }
+ if (attr == &gpiommc_attr_CS) {
+ gpio = dev->pdata.pins.gpio_cs;
+ if (gpio == GPIO_INVALID)
+ count = snprintf(page, PAGE_SIZE, "not configured\n");
+ else
+ count = snprintf(page, PAGE_SIZE, "%u\n", gpio);
+ goto out;
+ }
+ if (attr == &gpiommc_attr_CS_activelow) {
+ count = snprintf(page, PAGE_SIZE, "%u\n",
+ dev->pdata.pins.cs_activelow);
+ goto out;
+ }
+ if (attr == &gpiommc_attr_spimode) {
+ count = snprintf(page, PAGE_SIZE, "%u\n",
+ dev->pdata.mode);
+ goto out;
+ }
+ if (attr == &gpiommc_attr_spidelay) {
+ count = snprintf(page, PAGE_SIZE, "%u\n",
+ !dev->pdata.no_spi_delay);
+ goto out;
+ }
+ if (attr == &gpiommc_attr_max_bus_speed) {
+ count = snprintf(page, PAGE_SIZE, "%u\n",
+ dev->pdata.max_bus_speed);
+ goto out;
+ }
+ if (attr == &gpiommc_attr_register) {
+ count = snprintf(page, PAGE_SIZE, "%u\n",
+ gpiommc_is_registered(dev));
+ goto out;
+ }
+ WARN_ON(1);
+ err = -ENOSYS;
+out:
+ return err ? err : count;
+}
+
+static int gpiommc_do_register(struct gpiommc_configfs_device *dev,
+ const char *name)
+{
+ int err;
+
+ if (gpiommc_is_registered(dev))
+ return 0;
+
+ if (!gpio_is_valid(dev->pdata.pins.gpio_di) ||
+ !gpio_is_valid(dev->pdata.pins.gpio_do) ||
+ !gpio_is_valid(dev->pdata.pins.gpio_clk) ||
+ !gpio_is_valid(dev->pdata.pins.gpio_cs)) {
+ printk(KERN_ERR PFX
+ "configfs: Invalid GPIO pin number(s)\n");
+ return -EINVAL;
+ }
+
+ strlcpy(dev->pdata.name, name,
+ sizeof(dev->pdata.name));
+
+ dev->pdev = platform_device_alloc(GPIOMMC_PLATDEV_NAME,
+ gpiommc_next_id());
+ if (!dev->pdev)
+ return -ENOMEM;
+ err = platform_device_add_data(dev->pdev, &dev->pdata,
+ sizeof(dev->pdata));
+ if (err) {
+ platform_device_put(dev->pdev);
+ return err;
+ }
+ err = platform_device_add(dev->pdev);
+ if (err) {
+ platform_device_put(dev->pdev);
+ return err;
+ }
+
+ return 0;
+}
+
+static void gpiommc_do_unregister(struct gpiommc_configfs_device *dev)
+{
+ if (!gpiommc_is_registered(dev))
+ return;
+
+ platform_device_unregister(dev->pdev);
+ dev->pdev = NULL;
+}
+
+static ssize_t gpiommc_config_attr_store(struct config_item *item,
+ struct configfs_attribute *attr,
+ const char *page, size_t count)
+{
+ struct gpiommc_configfs_device *dev = ci_to_gpiommc(item);
+ int err = -EINVAL;
+ unsigned long data;
+
+ if (attr == &gpiommc_attr_register) {
+ err = strict_strtoul(page, 10, &data);
+ if (err)
+ goto out;
+ err = -EINVAL;
+ if (data == 1)
+ err = gpiommc_do_register(dev, item->ci_name);
+ if (data == 0) {
+ gpiommc_do_unregister(dev);
+ err = 0;
+ }
+ goto out;
+ }
+
+ if (gpiommc_is_registered(dev)) {
+ /* The rest of the config parameters can only be set
+ * as long as the device is not registered, yet. */
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (attr == &gpiommc_attr_DI) {
+ err = strict_strtoul(page, 10, &data);
+ if (err)
+ goto out;
+ err = -EINVAL;
+ if (!gpio_is_valid(data))
+ goto out;
+ dev->pdata.pins.gpio_di = data;
+ err = 0;
+ goto out;
+ }
+ if (attr == &gpiommc_attr_DO) {
+ err = strict_strtoul(page, 10, &data);
+ if (err)
+ goto out;
+ err = -EINVAL;
+ if (!gpio_is_valid(data))
+ goto out;
+ dev->pdata.pins.gpio_do = data;
+ err = 0;
+ goto out;
+ }
+ if (attr == &gpiommc_attr_CLK) {
+ err = strict_strtoul(page, 10, &data);
+ if (err)
+ goto out;
+ err = -EINVAL;
+ if (!gpio_is_valid(data))
+ goto out;
+ dev->pdata.pins.gpio_clk = data;
+ err = 0;
+ goto out;
+ }
+ if (attr == &gpiommc_attr_CS) {
+ err = strict_strtoul(page, 10, &data);
+ if (err)
+ goto out;
+ err = -EINVAL;
+ if (!gpio_is_valid(data))
+ goto out;
+ dev->pdata.pins.gpio_cs = data;
+ err = 0;
+ goto out;
+ }
+ if (attr == &gpiommc_attr_CS_activelow) {
+ err = strict_strtoul(page, 10, &data);
+ if (err)
+ goto out;
+ err = -EINVAL;
+ if (data != 0 && data != 1)
+ goto out;
+ dev->pdata.pins.cs_activelow = data;
+ err = 0;
+ goto out;
+ }
+ if (attr == &gpiommc_attr_spimode) {
+ err = strict_strtoul(page, 10, &data);
+ if (err)
+ goto out;
+ err = -EINVAL;
+ switch (data) {
+ case 0:
+ dev->pdata.mode = SPI_MODE_0;
+ break;
+ case 1:
+ dev->pdata.mode = SPI_MODE_1;
+ break;
+ case 2:
+ dev->pdata.mode = SPI_MODE_2;
+ break;
+ case 3:
+ dev->pdata.mode = SPI_MODE_3;
+ break;
+ default:
+ goto out;
+ }
+ err = 0;
+ goto out;
+ }
+ if (attr == &gpiommc_attr_spidelay) {
+ err = strict_strtoul(page, 10, &data);
+ if (err)
+ goto out;
+ err = -EINVAL;
+ if (data != 0 && data != 1)
+ goto out;
+ dev->pdata.no_spi_delay = !data;
+ err = 0;
+ goto out;
+ }
+ if (attr == &gpiommc_attr_max_bus_speed) {
+ err = strict_strtoul(page, 10, &data);
+ if (err)
+ goto out;
+ err = -EINVAL;
+ if (data > UINT_MAX)
+ goto out;
+ dev->pdata.max_bus_speed = data;
+ err = 0;
+ goto out;
+ }
+ WARN_ON(1);
+ err = -ENOSYS;
+out:
+ return err ? err : count;
+}
+
+static void gpiommc_config_item_release(struct config_item *item)
+{
+ struct gpiommc_configfs_device *dev = ci_to_gpiommc(item);
+
+ kfree(dev);
+}
+
+static struct configfs_item_operations gpiommc_config_item_ops = {
+ .release = gpiommc_config_item_release,
+ .show_attribute = gpiommc_config_attr_show,
+ .store_attribute = gpiommc_config_attr_store,
+};
+
+static struct config_item_type gpiommc_dev_ci_type = {
+ .ct_item_ops = &gpiommc_config_item_ops,
+ .ct_attrs = gpiommc_config_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *gpiommc_make_item(struct config_group *group,
+ const char *name)
+{
+ struct gpiommc_configfs_device *dev;
+
+ if (strlen(name) > GPIOMMC_MAX_NAMELEN) {
+ printk(KERN_ERR PFX "configfs: device name too long\n");
+ return NULL; + return NULL;
+ } + }
+ +
+static ssize_t gpiommc_add_store(struct device_driver *drv, + dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ const char *buf, size_t count) + if (!dev)
+ return NULL;
+
+ config_item_init_type_name(&dev->item, name,
+ &gpiommc_dev_ci_type);
+
+ /* Assign default configuration */
+ dev->pdata.pins.gpio_di = GPIO_INVALID;
+ dev->pdata.pins.gpio_do = GPIO_INVALID;
+ dev->pdata.pins.gpio_clk = GPIO_INVALID;
+ dev->pdata.pins.gpio_cs = GPIO_INVALID;
+ dev->pdata.pins.cs_activelow = 1;
+ dev->pdata.mode = SPI_MODE_0;
+ dev->pdata.no_spi_delay = 0;
+ dev->pdata.max_bus_speed = 5000000; /* 5 MHz */
+
+ return &(dev->item);
+}
+
+static void gpiommc_drop_item(struct config_group *group,
+ struct config_item *item)
+{ +{
+ int res, err; + struct gpiommc_configfs_device *dev = ci_to_gpiommc(item);
+ 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); + gpiommc_do_unregister(dev);
+ + kfree(dev);
+ 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; +static struct configfs_group_operations gpiommc_ct_group_ops = {
+ if (gpiommc_sysfs_find_dev(pdata->p.name)) + .make_item = gpiommc_make_item,
+ goto out; + .drop_item = gpiommc_drop_item,
+};
+ +
+ err = -ENOMEM; +static struct config_item_type gpiommc_ci_type = {
+ pdev = platform_device_alloc(GPIOMMC_PLATDEV_NAME, gpiommc_next_id()); + .ct_group_ops = &gpiommc_ct_group_ops,
+ if (!pdev) + .ct_owner = THIS_MODULE,
+ goto out; +};
+ +
+ err = platform_device_add_data(pdev, pdata, sizeof(*pdata)); +static struct configfs_subsystem gpiommc_subsys = {
+ if (err) + .su_group = {
+ goto err_free_pdev; + .cg_item = {
+ pdata = pdev->dev.platform_data; + .ci_namebuf = GPIOMMC_PLATDEV_NAME,
+ .ci_type = &gpiommc_ci_type,
+ },
+ },
+ .su_mutex = __MUTEX_INITIALIZER(gpiommc_subsys.su_mutex),
+};
+ +
+ err = platform_device_add(pdev); +#endif /* CONFIG_GPIOMMC_CONFIGFS */
+ 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 = { +static struct platform_driver gpiommc_plat_driver = {
+ .probe = gpiommc_probe, + .probe = gpiommc_probe,
@ -292,50 +589,33 @@ Index: linux-2.6.25.10/drivers/mmc/host/gpiommc.c
+ err = platform_driver_register(&gpiommc_plat_driver); + err = platform_driver_register(&gpiommc_plat_driver);
+ if (err) + if (err)
+ return 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; +#ifdef CONFIG_GPIOMMC_CONFIGFS
+ + config_group_init(&gpiommc_subsys.su_group);
+err_remove_add: + err = configfs_register_subsystem(&gpiommc_subsys);
+ driver_remove_file(&gpiommc_plat_driver.driver, + if (err) {
+ &driver_attr_add);
+err_drv_unreg:
+ platform_driver_unregister(&gpiommc_plat_driver); + platform_driver_unregister(&gpiommc_plat_driver);
+ return err; + return err;
+ } + }
+#endif /* CONFIG_GPIOMMC_CONFIGFS */
+
+ return 0;
+}
+module_init(gpiommc_modinit); +module_init(gpiommc_modinit);
+ +
+static void __exit gpiommc_modexit(void) +static void __exit gpiommc_modexit(void)
+{ +{
+ struct gpiommc_sysfs_platform_data *pdata, *pdata_tmp; +#ifdef CONFIG_GPIOMMC_CONFIGFS
+ + configfs_unregister_subsystem(&gpiommc_subsys);
+ driver_remove_file(&gpiommc_plat_driver.driver, +#endif
+ &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); + platform_driver_unregister(&gpiommc_plat_driver);
+} +}
+module_exit(gpiommc_modexit); +module_exit(gpiommc_modexit);
Index: linux-2.6.25.10/drivers/mmc/host/Kconfig 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.orig/drivers/mmc/host/Kconfig 2008-07-20 20:32:22.000000000 +0200
+++ linux-2.6.25.10/drivers/mmc/host/Kconfig 2008-07-18 22:31:00.000000000 +0200 +++ linux-2.6.25.10/drivers/mmc/host/Kconfig 2008-07-20 20:33:20.000000000 +0200
@@ -130,3 +130,23 @@ config MMC_SPI @@ -130,3 +130,27 @@ config MMC_SPI
If unsure, or if your system has no SPI master driver, say N. If unsure, or if your system has no SPI master driver, say N.
@ -347,8 +627,8 @@ Index: linux-2.6.25.10/drivers/mmc/host/Kconfig
+ MMC/SD cards can be used on a GPIO based bus by bitbanging + MMC/SD cards can be used on a GPIO based bus by bitbanging
+ the SPI protocol in software. + the SPI protocol in software.
+ +
+ This driver provides a sysfs interface to dynamically create + This driver provides a configfs interface to dynamically create
+ and destroy GPIO-based MMC/SD card interfaces. It also provides + and destroy GPIO-based MMC/SD card devices. It also provides
+ a platform device interface API. + a platform device interface API.
+ See Documentation/gpiommc.txt for details. + See Documentation/gpiommc.txt for details.
+ +
@ -356,23 +636,28 @@ Index: linux-2.6.25.10/drivers/mmc/host/Kconfig
+ +
+ If unsure, say N. + If unsure, say N.
+ +
+config MMC_S3C +config GPIOMMC_CONFIGFS
+ tristate "Samsung S3C SD/MMC Card Interface support" + bool
+ depends on ARCH_S3C2410 && MMC + depends on GPIOMMC && CONFIGFS_FS
+ default y
+ help
+ This option automatically enables configfs support for gpiommc
+ if configfs is available.
Index: linux-2.6.25.10/drivers/mmc/host/Makefile 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.orig/drivers/mmc/host/Makefile 2008-07-20 20:32:22.000000000 +0200
+++ linux-2.6.25.10/drivers/mmc/host/Makefile 2008-07-18 22:31:20.000000000 +0200 +++ linux-2.6.25.10/drivers/mmc/host/Makefile 2008-07-20 20:33:20.000000000 +0200
@@ -18,3 +18,4 @@ obj-$(CONFIG_MMC_AT91) += at91_mci.o @@ -17,4 +17,4 @@ obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o
-
+obj-$(CONFIG_GPIOMMC) += gpiommc.o +obj-$(CONFIG_GPIOMMC) += gpiommc.o
Index: linux-2.6.25.10/include/linux/mmc/gpiommc.h Index: linux-2.6.25.10/include/linux/mmc/gpiommc.h
=================================================================== ===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000 --- /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 +++ linux-2.6.25.10/include/linux/mmc/gpiommc.h 2008-07-20 20:33:20.000000000 +0200
@@ -0,0 +1,62 @@ @@ -0,0 +1,71 @@
+/* +/*
+ * Device driver for MMC/SD cards driven over a GPIO bus. + * Device driver for MMC/SD cards driven over a GPIO bus.
+ * + *
@ -387,8 +672,11 @@ Index: linux-2.6.25.10/include/linux/mmc/gpiommc.h
+ +
+ +
+#define GPIOMMC_MAX_NAMELEN 15 +#define GPIOMMC_MAX_NAMELEN 15
+#define GPIOMMC_MAX_NAMELEN_STR __stringify(GPIOMMC_MAX_NAMELEN)
+ +
+/** struct gpiommc_pins - Hardware pin assignments +/**
+ * struct gpiommc_pins - Hardware pin assignments
+ *
+ * @gpio_di: The GPIO number of the DATA IN pin + * @gpio_di: The GPIO number of the DATA IN pin
+ * @gpio_do: The GPIO number of the DATA OUT pin + * @gpio_do: The GPIO number of the DATA OUT pin
+ * @gpio_clk: The GPIO number of the CLOCK pin + * @gpio_clk: The GPIO number of the CLOCK pin
@ -403,7 +691,9 @@ Index: linux-2.6.25.10/include/linux/mmc/gpiommc.h
+ bool cs_activelow; + bool cs_activelow;
+}; +};
+ +
+/** struct gpiommc_platform_data - Platform data for a MMC-over-SPI-GPIO device. +/**
+ * struct gpiommc_platform_data - Platform data for a MMC-over-SPI-GPIO device.
+ *
+ * @name: The unique name string of the device. + * @name: The unique name string of the device.
+ * @pins: The hardware pin assignments. + * @pins: The hardware pin assignments.
+ * @mode: The hardware mode. This is either SPI_MODE_0, + * @mode: The hardware mode. This is either SPI_MODE_0,
@ -421,13 +711,17 @@ Index: linux-2.6.25.10/include/linux/mmc/gpiommc.h
+ unsigned int max_bus_speed; + unsigned int max_bus_speed;
+}; +};
+ +
+/** GPIOMMC_PLATDEV_NAME - The platform device name string. +/**
+ * GPIOMMC_PLATDEV_NAME - The platform device name string.
+ *
+ * The name string that has to be used for platform_device_alloc + * The name string that has to be used for platform_device_alloc
+ * when allocating a gpiommc device. + * when allocating a gpiommc device.
+ */ + */
+#define GPIOMMC_PLATDEV_NAME "gpiommc" +#define GPIOMMC_PLATDEV_NAME "gpiommc"
+ +
+/** gpiommc_next_id - Get another platform device ID number. +/**
+ * gpiommc_next_id - Get another platform device ID number.
+ *
+ * This returns the next platform device ID number that has to be used + * 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 + * for platform_device_alloc. The ID is opaque and should not be used for
+ * anything else. + * anything else.
@ -438,16 +732,16 @@ Index: linux-2.6.25.10/include/linux/mmc/gpiommc.h
Index: linux-2.6.25.10/Documentation/gpiommc.txt Index: linux-2.6.25.10/Documentation/gpiommc.txt
=================================================================== ===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000 --- /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 +++ linux-2.6.25.10/Documentation/gpiommc.txt 2008-07-20 20:33:20.000000000 +0200
@@ -0,0 +1,96 @@ @@ -0,0 +1,97 @@
+GPIOMMC - Driver for an MMC/SD card on a bitbanging GPIO SPI bus +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 +The gpiommc module hooks up the mmc_spi and spi_gpio modules for running an
+MMC or SD card on GPIO pins. +MMC or SD card on GPIO pins.
+ +
+Two interfaces for registering a new MMC/SD card device are provided. +Two interfaces for registering a new MMC/SD card device are provided:
+A static platform-device based mechanism and a dynamic sysfs based interface. +A static platform-device based mechanism and a dynamic configfs based interface.
+ +
+ +
+Registering devices via platform-device +Registering devices via platform-device
@ -457,7 +751,7 @@ Index: linux-2.6.25.10/Documentation/gpiommc.txt
+part of the hardware platform. This is most useful only for embedded machines +part of the hardware platform. This is most useful only for embedded machines
+with MMC/SD devices statically connected to the platform GPIO bus. +with MMC/SD devices statically connected to the platform GPIO bus.
+ +
+The data structures are declared in <linux/mmc/gpiommc.h> +The data structures are declared in <linux/mmc/gpiommc.h>.
+ +
+To register a new device, define an instance of struct gpiommc_platform_data. +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 +This structure holds any information about how the device is hooked up to the
@ -474,72 +768,73 @@ Index: linux-2.6.25.10/Documentation/gpiommc.txt
+ +
+ err = platform_device_add_data(pdev, pdata, sizeof(struct gpiommc_platform_data)); + err = platform_device_add_data(pdev, pdata, sizeof(struct gpiommc_platform_data));
+ +
+You may free the local instance of struct gpiommc_platform_data now. +You may free the local instance of struct gpiommc_platform_data now. (So the
+struct may be allocated on the stack, too).
+Now simply register the platform device. +Now simply register the platform device.
+ +
+ err = platform_device_add(pdev); + err = platform_device_add(pdev);
+ +
+Done. The gpiommc probe routine should be called and you should see a dmesg +Done. The gpiommc probe routine will be invoked now and you should see a kernel
+message for the added device. +log message for the added device.
+ +
+ +
+Registering devices via sysfs +Registering devices via configfs
+============================= +================================
+ +
+MMC/SD cards connected via GPIO often are a pretty dynamic thing. For example +MMC/SD cards connected via GPIO often are a pretty dynamic thing, as for example
+selfmade hacks for soldering an MMC/SD card to standard GPIO pins on embedded +selfmade hacks for soldering an MMC/SD card to standard GPIO pins on embedded
+hardware are a common situation. +hardware are a common situation.
+So we provide a dynamic interface to conveniently handle adding and removing +So we provide a dynamic interface to conveniently handle adding and removing
+devices from userspace, without the need to recompile the kernel. +devices from userspace, without the need to recompile the kernel.
+ +
+There are two sysfs files responsible for that: +The "gpiommc" subdirectory at the configfs mountpoint is used for handling
+export ADD=/sys/bus/platform/drivers/gpiommc/add +the dynamic configuration.
+export REMOVE=/sys/bus/platform/drivers/gpiommc/remove
+ +
+To add a new device, simply echo the configuration string to the "add" file. +To create a new device, it must first be allocated with mkdir.
+The config string is composed out of the following elements: +The following command will allocate a device named "my_mmc":
+ mkdir /config/gpiommc/my_mmc
+ +
+DEVNAME DIpin DOpin CLKpin CSpin SPIMODE MAXBUSSPEED NO_SPI_DELAY CSACTIVELOW +There are several configuration files available in the new
+/config/gpiommc/my_mmc/ directory:
+ +
+DEVNAME is a unique name string for the device. +gpio_data_in = The SPI data-IN GPIO pin number.
+DIpin is the SPI DI GPIO pin. +gpio_data_out = The SPI data-OUT GPIO pin number.
+DOpin is the SPI DO GPIO pin. +gpio_clock = The SPI Clock GPIO pin number.
+CLKpin is the SPI CLOCK GPIO pin. +gpio_chipselect = The SPI Chipselect GPIO pin number.
+CSpin is the SPI CHIPSELECT GPIO pin. +gpio_chipselect_activelow = Boolean. If 0, Chipselect is active-HIGH.
+SPIMODE is the hardware mode the device will run at. Can be 0-3. + If 1, Chipselect is active-LOW.
+MAXBUSSPEED is the maximum bus speed in Hertz. +spi_mode = The SPI data mode. Can be 0-3.
+NO_SPI_DELAY can be 1 or 0. If it is 1, then the lowlevel SPI delay +spi_delay = Enable all delays in the lowlevel bitbanging.
+will not be performed. This is not standards compliant, but may be required +max_bus_speed = The maximum SPI bus speed. In Hertz.
+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 +register = Not a configuration parameter.
+and can be omitted. + Used to register the configured card
+SPIMODE will default to 0. + with the kernel.
+MAXBUSSSPEED will default to 5Mhz.
+NO_SPI_DELAY will default to 0.
+CSACTIVELOW will default to 1.
+ +
+Example: +The device must first get configured and then registered by writing "1" to
+the "register" file.
+The configuration parameters "gpio_data_in", "gpio_data_out", "gpio_clock"
+and "gpio_chipselect" are essential and _must_ be configured before writing
+"1" to the "register" file. The registration will fail, otherwise.
+ +
+ echo -n "my_device 5 4 3 7 0 1000000 1" > $ADD +The default values for the other parameters are:
+gpio_chipselect_activelow = 1 (CS active-LOW)
+spi_mode = 0 (SPI_MODE_0)
+spi_delay = 1 (enabled)
+max_bus_speed = 5000000 (5 Mhz)
+ +
+This will add a new device called "my_device" with the GPIO pins assigned as +Configuration values can not be changed after registration. To unregister
+DI=5, DO=4, CLK=3, CS=7 +the device, write a "0" to the "register" file. The configuration can be
+The hardware mode will be SPI_MODE_0. +changed again after unregistering.
+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. +To completely remove the device, simply rmdir the directory
+ +(/config/gpiommc/my_mmc in this example).
+Example: +There's no need to first unregister the device before removing it. That will
+ +be done automatically.
+ echo -n "my_device" > $REMOVE
Index: linux-2.6.25.10/MAINTAINERS 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.orig/MAINTAINERS 2008-07-20 20:33:20.000000000 +0200
+++ linux-2.6.25.10/MAINTAINERS 2008-07-18 22:31:00.000000000 +0200 +++ linux-2.6.25.10/MAINTAINERS 2008-07-20 20:33:20.000000000 +0200
@@ -1736,6 +1736,11 @@ L: gigaset307x-common@lists.sourceforge. @@ -1736,6 +1736,11 @@ L: gigaset307x-common@lists.sourceforge.
W: http://gigaset307x.sourceforge.net/ W: http://gigaset307x.sourceforge.net/
S: Maintained S: Maintained