Add gpio_parport and gpio_test
authorPeter Henn <Peter.Henn@web.de>
Thu, 1 Jan 2009 10:59:10 +0000 (11:59 +0100)
committerPeter Henn <Peter.Henn@web.de>
Thu, 1 Jan 2009 10:59:10 +0000 (11:59 +0100)
Correct some compile errors in spi_parport.
Note: spi_parport just compiles and is not yet tested functional.

Getting more familiar with GPIO handling using the GPIO lib
of kernel with the GPIO controller driver and gpio_test driver.
Both drivers gpio_parport and spi_parport should work later
close together or should share the same parport device.

Makefile
gpio_parport.c [new file with mode: 0644]
gpio_test.c [new file with mode: 0644]
spi_parport.c

index f490819..42afee9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -8,17 +8,22 @@
 # If CONFIG_SPI_PARPORT isn't set, we'll assume the user has never configured
 # their kernel to include this module and set up some defaults.
 #
 # If CONFIG_SPI_PARPORT isn't set, we'll assume the user has never configured
 # their kernel to include this module and set up some defaults.
 #
-# NOTE: If you have previously added the SPI_TTY project to your kernel 
-#      and configured it for inclusion, these settings will be 
-#      overridden by your kernel configuration.
+ifndef CONFIG_GPIO_PARPORT
+EXTERNAL_BUILD=y
+CONFIG_GPIO_PARPORT=m
+# CONFIG_GPIO_PARPORT_MUTEX is not set
+# CONFIG_GPIO_PARPORT_EXCLUSIVE is not set
+CONFIG_GPIO_PARPORT_NOCLAIME=y
+endif
+
 ifndef CONFIG_SPI_PARPORT
 EXTERNAL_BUILD=y
 CONFIG_SPI_PARPORT=m
 ifndef CONFIG_SPI_PARPORT
 EXTERNAL_BUILD=y
 CONFIG_SPI_PARPORT=m
-CONFIG_SPI_PARPORT_DEBUG=n
+# CONFIG_SPI_PARPORT_EXCLUSIVE is not set
 endif
 
 
 endif
 
 
-ifeq ($(CONFIG_SPI_PARPORT_DEBUG),y)
+ifeq ($(CONFIG_SPI_HSO_DEBUG),y)
        EXTRA_CFLAGS += -g -Wa,-adhlms=$@.lst
 endif
 
        EXTRA_CFLAGS += -g -Wa,-adhlms=$@.lst
 endif
 
@@ -28,7 +33,9 @@ endif
 
 
 list-m :=
 
 
 list-m :=
+list-$(CONFIG_GPIO_PARPORT) += gpio_parport gpio_test
 list-$(CONFIG_SPI_PARPORT) += spi_parport
 list-$(CONFIG_SPI_PARPORT) += spi_parport
+obj-$(CONFIG_GPIO_PARPORT) += gpio_parport.o gpio_test.o
 obj-$(CONFIG_SPI_PARPORT) += spi_parport.o
 
 #
 obj-$(CONFIG_SPI_PARPORT) += spi_parport.o
 
 #
@@ -40,8 +47,22 @@ ifneq ($(KERNELRELEASE),)
 EXTRA_CFLAGS += -DCONFIG_PM
 
 ifeq ($(EXTERNAL_BUILD),y)
 EXTRA_CFLAGS += -DCONFIG_PM
 
 ifeq ($(EXTERNAL_BUILD),y)
-ifeq ($(CONFIG_SPI_PARPORT_DEBUG),y)
-EXTRA_CFLAGS += -DCONFIG_SPI_PARPORT_DEBUG
+ifeq ($(CONFIG_GPIO_PARPORT_MUTEX),y)
+       EXTRA_CFLAGS += -DCONFIG_GPIO_PARPORT_MUTEX
+endif
+ifeq ($(CONFIG_GPIO_PARPORT_EXCLUSIVE),y)
+       EXTRA_CFLAGS += -DCONFIG_GPIO_PARPORT_EXCLUSIVE
+endif
+ifeq ($(CONFIG_GPIO_PARPORT_NOCLAIME),y)
+       EXTRA_CFLAGS += -DCONFIG_GPIO_PARPORT_NOCLAIME
+endif
+
+ifeq ($(CONFIG_SPI_PARPORT_EXCLUSIVE),y)
+       EXTRA_CFLAGS += -DCONFIG_SPI_PARPORT_EXCLUSIVE
+endif
+
+ifeq ($(CONFIG_SPI_HSO_DEBUG),y)
+       EXTRA_CFLAGS += -DCONFIG_SPI_HSO_DEBUG
 endif
 endif
 
 endif
 endif
 
@@ -105,7 +126,7 @@ else # We've got a kernel with seperate output, copy the config, and use O=
 endif
 
 install: modules
 endif
 
 install: modules
-       modprobe -r spi_parport || true
+       for mod; $(list-m); do modprobe -r $mod || true; done
        install -d $(INSTALLDIR)
        install -m 644 -c $(addsuffix .ko,$(list-m)) $(INSTALLDIR)
        /sbin/depmod -a ${KVER}
        install -d $(INSTALLDIR)
        install -m 644 -c $(addsuffix .ko,$(list-m)) $(INSTALLDIR)
        /sbin/depmod -a ${KVER}
diff --git a/gpio_parport.c b/gpio_parport.c
new file mode 100644 (file)
index 0000000..73a16b1
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * gpio_parport.c - GPIO master controller driver based on the parallel port adapter
+ *
+ * Copyright (C) 2008 Peter Henn
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * The gpio_parport driver uses the gpio framwork started with kernel 2.6.25.
+ * It is designed as a PC reference GPIO driver just for testing on Intel based
+ * PC architectures. Therefore the whole functionality of a parport used as 
+ * GPIO was never in focus.  We initial need only one dedicated output and one 
+ * dedicated input, but with interrupt support.
+ *
+ * The driver uses caches, which saves the output status, because the IO output
+ * need long time on x86 architecture. The caches will be setup with the initial
+ * values read from those ports during driver startup. If you are using the parport
+ * in shared operation mode, the caches has to be updated too.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/parport.h>
+#include <asm/gpio.h>
+
+#ifdef CONFIG_GPIO_PARPORT_MUTEX
+#include <linux/mutex.h>
+#define sCONFIG_GPIO_PARPORT_MUTEX "CONFIG_GPIO_PARPORT_MUTEX"
+#else
+#include <linux/spinlock.h>
+#define sCONFIG_GPIO_PARPORT_MUTEX ""
+#endif
+
+
+/* ToDo:
+ * - proper handling error flags for gpio_to_irq ...
+ * - using pp->dev-port should be equal to pp->port ...
+ * - Sharing parport with other devices controled by module load
+ *   paramter with using mutex mode ... but check with releasing parport IRQ handler  
+ * - Verifing Parport modes
+ * - Add sysfs Debug functionality
+ * - Support PM functions like resume, suspend
+ *
+ *   Parallel                
+ *     Port      Direction  Register Name                   Signal
+ *  -----------  ---------  -------- ---------------------- ------
+ *   nStrobe  1     -->     nC0      PARPORT_CONTROL_STROBE nGPO08
+ *      D0    2     -->      D0      (1 << 0)                GPO00
+ *      D1    3     -->      D1      (1 << 1)                GPO01
+ *      D2    4     -->      D2      (1 << 2)                GPO02
+ *      D3    5     -->      D3      (1 << 3)                GPO03
+ *      D4    6     -->      D4      (1 << 4)                GPO04
+ *      D5    7     -->      D5      (1 << 5)                GPO05
+ *      D6    8     -->      D6      (1 << 6)                GPO06
+ *      D7    9     -->      D7      (1 << 7)                GPO07
+ *   nAckn   10     <--      S6      PARPORT_STATUS_ACK      GPI15*
+ *    Busy   11     <--     nS7      PARPORT_STATUS_BUSY    nGPI16
+ *    Paper  12     <--      S5      PARPORT_STATUS_PAPEROUT GPI14
+ *    Sel    13     <--      S4      PARPORT_STATUS_SELECT   GPI13
+ *   nFeed   14     -->     nC1      PARPORT_CONTROL_AUTOFD nGPO09
+ *   nError  15     <--      S3      PARPORT_STATUS_ERROR    GPI12
+ *   nInit   16     -->      C2      PARPORT_CONTROL_INIT    GPO10
+ *   nSelIn  17     -->     nC3      PARPORT_CONTROL_SELECT nGPO11 
+ *     GND   25     --       --                              GND
+ *
+ *  Signal:
+ *  - GPOx  : general purpose output only
+ *  - GPIx  : general purpose input only
+ *  - GPIx* : interrupt in
+ *  - nGPXx : signal is inverted
+ */
+
+//*****************************************************************************
+// MODULE PARAMETERS 
+//*****************************************************************************
+#define DRVNAME                 "gpio_parport"   /* name of the driver */
+#define DRIVER_VERSION            "0.2"   /* helps identifing different versions of that driver */
+
+#define GPIO_PARPORT_CTRL_PORT_BITS  8    /* starting with bit 8 use the parport control port */
+#define GPIO_PARPORT_STAT_PORT_BITS 12    /* starting with bit 12 use the parport status port */
+#define GPIO_PARPORT_STAT_PORT_OFF   3    /* parport status port needs internally 3 bit offset */
+#define GPIO_PARPORT_INTERRUPT      15    /* gpio offset for parport pin Acknolge, which support interrupt */
+#define GPIO_PARPORT_NR_GPIOS       17    /* parport support up to 17 general purpose (dedicated) Inputs and Outputs */
+
+
+static int modparam_gpiobase = -1         /* dynamic assingment */;
+
+typedef struct {
+       struct pardevice        *dev;     /* parport device entry */
+        struct parport         *port;    /* parport port entry */
+        u8                     dcache;   /* parport data port mirror bits */
+        u8                     ccache;   /* parport control port mirror bits */
+        struct gpio_chip        gpio;     /* parport GPIO seen as a chip */
+        int                     irq;      /* parport interrupt */
+        #ifdef CONFIG_GPIO_PARPORT_MUTEX
+            struct mutex        lock;     /* prevent double accessing bits */
+        #else
+            spinlock_t          lock;     /* prevent double accessing bits */
+        #endif
+        struct list_head        list;     /* gpio_parport host controller list */
+} gpio_pp_struct;
+
+
+/*----------------------------------------------------------------------*/
+/*
+** use the lock and unlock macros to get rid of the bad #ifdef
+** stuff in the rest of the code.
+*/
+#ifdef CONFIG_GPIO_PARPORT_MUTEX
+#define gpio_parport_lock(lock, flags)           \
+        do {                                     \
+           mutex_lock(lock);                    \
+        } while (0)
+#else
+#define gpio_parport_lock(lock, flags)           \
+        do {                                     \
+           spin_lock_irqsave(lock, flags);      \
+        } while (0)
+#endif
+
+#ifdef CONFIG_GPIO_PARPORT_MUTEX
+#define gpio_parport_unlock(lock, flags)         \
+        do {                                     \
+           mutex_unlock(lock);                  \
+        } while (0)
+#else
+#define gpio_parport_unlock(lock, flags)         \
+        do {                                     \
+           spin_unlock_irqrestore(lock, flags); \
+        } while (0)
+#endif
+
+
+/*----------------------------------------------------------------------*/
+/*
+** parport data bit input function
+*/
+static inline int gpio_parport_data_get(gpio_pp_struct *pp, unsigned mask)
+{
+        // good idea to update also the cache
+       pp->dcache = parport_read_data(pp->port);
+       return ((pp->dcache & mask) != 0);
+}
+
+/*
+** parport control bit input function
+*/
+static inline int gpio_parport_ctrl_get(gpio_pp_struct *pp, unsigned mask)
+{
+        // good idea to update also the cache
+       pp->ccache = parport_read_control(pp->port);
+       return ((pp->ccache & mask) != 0);
+}
+
+/*
+** parport status bit input function
+*/
+static inline int gpio_parport_status_get(gpio_pp_struct *pp, unsigned mask)
+{
+        unsigned int cache = parport_read_status(pp->port);
+       return ((cache & mask) != 0);
+}
+
+
+/*
+** general parport read bit function
+*/ 
+static int gpio_parport_get(struct gpio_chip *gpio, unsigned offset)
+{
+        gpio_pp_struct *pp = container_of(gpio, gpio_pp_struct, gpio);
+       int            status = 0; /* inititalization ensures returm zero for unhandled bits */
+       unsigned       long flags;
+
+       if (offset < GPIO_PARPORT_CTRL_PORT_BITS) {
+               u8 mask = 1 << offset;
+               gpio_parport_lock(&pp->lock, flags);
+               status = gpio_parport_data_get(pp, mask);
+                gpio_parport_unlock(&pp->lock, flags);
+       } else if (offset < GPIO_PARPORT_STAT_PORT_BITS) {
+               u8 mask = 1 << (offset - GPIO_PARPORT_CTRL_PORT_BITS);
+               gpio_parport_lock(&pp->lock, flags);
+               status = gpio_parport_ctrl_get(pp, mask);
+                gpio_parport_unlock(&pp->lock, flags);
+       } else if (offset < GPIO_PARPORT_NR_GPIOS) {
+               u8 mask = 1 << (offset - GPIO_PARPORT_STAT_PORT_BITS + GPIO_PARPORT_STAT_PORT_OFF);
+               gpio_parport_lock(&pp->lock, flags);
+               status = gpio_parport_status_get(pp, mask);
+                gpio_parport_unlock(&pp->lock, flags);
+       }
+       return status;
+}
+
+
+/*----------------------------------------------------------------------*/
+/*
+** parport data bit output function
+*/
+static inline void gpio_parport_data_set(gpio_pp_struct *pp, unsigned mask, int value)
+{
+        u8 byte = pp->dcache; // use old value from cache
+       if (value)
+               byte |= mask;
+       else
+               byte &= ~mask;
+       pp->dcache = byte;   // restore cache
+       return parport_write_data(pp->port, byte);
+}
+
+/*
+** parport control bit output function
+** most PARPORT_CONTROL_* bits are negated, but they are currently _not_ corrected 
+** to keep it as simple as possible
+*/
+static inline void gpio_parport_ctrl_set(gpio_pp_struct *pp, unsigned mask, int value)
+{
+        u8 byte = pp->ccache; // use old value from cache
+       if (value)
+               byte |= mask;
+       else
+               byte &= ~mask;
+       pp->ccache = byte;   // restore cache
+       return parport_write_control(pp->port, byte);
+}
+
+/*
+** general parport set bit output function
+*/
+static void gpio_parport_set(struct gpio_chip *gpio, unsigned offset, int value)
+{
+        gpio_pp_struct *pp = container_of(gpio, gpio_pp_struct, gpio);
+       unsigned        long flags;
+
+       if (offset < GPIO_PARPORT_CTRL_PORT_BITS) {
+               u8 mask = 1 << offset;
+               gpio_parport_lock(&pp->lock, flags);
+               gpio_parport_data_set(pp, mask, value);
+                gpio_parport_unlock(&pp->lock, flags);
+       } else if (offset < GPIO_PARPORT_STAT_PORT_BITS) {
+               u8 mask = 1 << (offset - GPIO_PARPORT_CTRL_PORT_BITS);
+               gpio_parport_lock(&pp->lock, flags);
+               gpio_parport_ctrl_set(pp, mask, value);
+                gpio_parport_unlock(&pp->lock, flags);
+       }
+}
+
+/*----------------------------------------------------------------------*/
+/*
+** general parport direction input funtion
+** we support only the parport control bits as dedicated input bits
+*/
+static int gpio_parport_direction_input(struct gpio_chip * gpio, unsigned offset)
+{
+       if (offset < GPIO_PARPORT_STAT_PORT_BITS) {
+                return -ENOSYS;
+       } else if (offset < GPIO_PARPORT_NR_GPIOS) {
+                gpio_pp_struct *pp = container_of(gpio, gpio_pp_struct, gpio);
+               unsigned long flags;
+               gpio_parport_lock(&pp->lock, flags);
+               if ((GPIO_PARPORT_INTERRUPT == offset) && (pp->irq >= 0)) {
+                       parport_enable_irq(pp->port);
+               }
+                gpio_parport_unlock(&pp->lock, flags);
+               return 0;
+       }
+       return -ENODEV;
+}
+
+/*
+** general parport direction output function
+** we support only data bits and control bits as dedicated output bits
+*/
+static int gpio_parport_direction_output(struct gpio_chip * gpio, unsigned offset, int value) 
+{
+        gpio_pp_struct *pp = container_of(gpio, gpio_pp_struct, gpio);
+       unsigned        long flags;
+
+       if (offset < GPIO_PARPORT_CTRL_PORT_BITS) {
+               u8 mask = 1 << offset;
+               gpio_parport_lock(&pp->lock, flags);
+               gpio_parport_data_set(pp, mask, value);
+                gpio_parport_unlock(&pp->lock, flags);
+               return 0;
+       } else if (offset < GPIO_PARPORT_STAT_PORT_BITS) {
+               u8 mask = 1 << (offset - GPIO_PARPORT_CTRL_PORT_BITS);
+               gpio_parport_lock(&pp->lock, flags);
+               gpio_parport_ctrl_set(pp, mask, value);
+                gpio_parport_unlock(&pp->lock, flags);
+               return 0;
+       } else if (offset < GPIO_PARPORT_NR_GPIOS) {
+               return -ENOSYS;
+       }
+        return -ENODEV;
+}
+
+/*----------------------------------------------------------------------*/
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27))
+/*
+** parport interrupt support only for acknoledge on GPI15 interrupt
+*/
+static int gpio_parport_gpio_to_irq(struct gpio_chip * gpio, unsigned offset)
+{
+        gpio_pp_struct *pp = container_of(gpio, gpio_pp_struct, gpio);
+
+        if (GPIO_PARPORT_INTERRUPT == offset) {
+               return pp->irq;
+       } else {
+               return -ENOSYS;
+       }
+} 
+#endif
+
+/*----------------------------------------------------------------------*/
+/*
+** gpio_parport setup function
+** add all entries into the GPIO framework
+*/
+static void gpio_parport_gpio_setup(gpio_pp_struct *pp)
+{
+       struct gpio_chip *c = &pp->gpio;
+       c->label = (char *)pp->port->name;
+       c->owner = THIS_MODULE;
+       c->direction_input = gpio_parport_direction_input;
+       c->get = gpio_parport_get;
+       c->direction_output = gpio_parport_direction_output;
+       c->set = gpio_parport_set;
+       c->dbg_show = NULL;
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27))
+       c->request = NULL;
+       c->free = NULL;
+       c->to_irq = gpio_parport_gpio_to_irq;
+#endif
+       c->base = modparam_gpiobase;
+       c->ngpio = GPIO_PARPORT_NR_GPIOS;
+        #ifdef CONFIG_GPIO_PARPORT_MUTEX
+           c->can_sleep = 1;
+           c->dev = pp->dev;
+        #else
+           c->can_sleep = 0;
+        #endif
+}
+
+/***************************************************************************
+ *         Parallel port attaching and detaching  routines                 *
+ ***************************************************************************/
+
+static LIST_HEAD(gpio_parport_hosts);
+
+/*
+** attach driver to parport is currently done with exclusive access. Unfortunately 
+** the driver is still loaded, although the attach might not be done successful.
+** If you have  already registered e.g. the lp driver, please unload this drivers
+** first as long as no shared supported is here implemented.
+*/
+static int __gpio_parport_attach(struct parport *pb)
+{
+        gpio_pp_struct *pp;
+       int err;
+
+       /* we need a zero initialized structure here */
+       pp = kzalloc(sizeof(*pp), GFP_KERNEL);
+       if (!pp)
+               return -ENOMEM;
+
+        #ifdef CONFIG_GPIO_PARPORT_MUTEX
+           mutex_init(&pp->lock);
+        #else
+           spin_lock_init(&pp->lock);
+        #endif
+
+        /* ----------------- ParPort Hooks ------------------- */
+       /* REVISIT:  no probe, just think it is there */
+       pp->port = pb;
+
+       //ToDo: share parport with other device, but may need a mutex lock!
+       //ToDo: check supported modes are ok
+       //ToDo: PM support, e.g. Suspend and Resume
+       /* interrupt handler will not be here registered. 
+           But an interrupt handler can be later registered to IRQ pin */
+       pp->dev = parport_register_device(pb, /* port */
+                                         DRVNAME, /* driver name */
+                                         NULL, /* preemption fkt */ 
+                                         NULL, /* wake-up fkt */
+                                         NULL, /* interrupt handler */
+                                         PARPORT_FLAG_EXCL, /* flags */
+                                         pp /* handle */ );
+       if (pp->dev == NULL) {
+               pr_err("%s: unable to register with parport\n", DRVNAME);
+               err = -ENOMEM;
+               goto err_release_mem;
+       }
+
+       err = parport_claim(pp->dev);
+       if (err < 0) {
+               pr_err("%s: unable to claim parport\n", DRVNAME);
+               err = -ENODEV;
+               goto err_unregister_parport;
+       }
+
+        /* ------------- GPIO PARPORT init --------------- */  
+       /* cache initialisation just preventing changing unused bits */
+       pp->dcache = parport_read_data(pp->port);
+       pp->ccache = parport_read_control(pp->port);
+
+       /* interrrut initialidation */
+        /* works for x86 arch, because we have nothing special to handle here */
+       if(pp->dev->port->irq != PARPORT_IRQ_NONE) {
+               pp->irq = pp->dev->port->irq;
+               /* we free the standard parport handler, because we want to use our own later */
+               free_irq(pp->irq, pp->dev->port);
+               pr_info("%s: IRQ %d is available\n", DRVNAME, pp->irq);
+       } else {
+               pp->irq = -ENXIO;
+       }
+
+        /* ----------------- GPIO init ------------------- */
+       gpio_parport_gpio_setup(pp);
+       err = gpiochip_add(&pp->gpio);
+       if (err != 0) {
+               pr_err("%s: Failed to register GPIOs\n", DRVNAME);
+               goto err_release_parport;
+       }
+
+       pr_info("%s: Abusing Parport for GPIO  %d to %d, labled as %s\n",
+               DRVNAME, pp->gpio.base, pp->gpio.base + GPIO_PARPORT_NR_GPIOS - 1, pp->gpio.label);
+
+       list_add_tail(&pp->list, &gpio_parport_hosts);
+       return 0;
+
+ err_release_parport:
+       parport_release(pp->dev);
+ err_unregister_parport:
+       parport_unregister_device(pp->dev);
+ err_release_mem:
+       kfree(pp);
+       return err;
+}
+
+
+
+static void gpio_parport_attach(struct parport *pb)
+{
+        (void)__gpio_parport_attach(pb);
+}
+
+
+static int gpio_parport_remove(gpio_pp_struct *pp)
+{
+        int err;
+        if (pp->irq >= 0) {
+               parport_disable_irq(pp->port);
+        }
+       err = gpiochip_remove(&pp->gpio);
+       if (err < 0)
+               pr_err("%s: gpio_remove error: %d\n", DRVNAME, err);
+       parport_release(pp->dev);
+        parport_unregister_device(pp->dev);
+       //ToDo: Check the unregister return value
+       return err;
+}
+
+static void gpio_parport_detach(struct parport *pb)
+{
+       gpio_pp_struct *pp;
+
+       list_for_each_entry(pp, &gpio_parport_hosts, list) {
+               if (pp->dev->port == pb) {
+                       (void)gpio_parport_remove(pp);   
+                       list_del_init(&pp->list);
+                       kfree(pp);
+                       break;
+               }
+       }
+}
+
+static struct parport_driver gpio_parport_driver = {
+       .name =         DRVNAME,
+       .attach =       gpio_parport_attach,
+       .detach =       gpio_parport_detach,
+};
+
+
+static int __init gpio_parport_init(void)
+{
+       return parport_register_driver(&gpio_parport_driver);
+}
+device_initcall(gpio_parport_init);
+
+static void __exit gpio_parport_exit(void)
+{
+       parport_unregister_driver(&gpio_parport_driver);
+}
+module_exit(gpio_parport_exit);
+
+MODULE_AUTHOR("Option Wireless");
+MODULE_DESCRIPTION("GPIO master controller driver for Parport Adapter");
+MODULE_LICENSE("GPL");
+
+MODULE_INFO(Flags, sCONFIG_GPIO_PARPORT_MUTEX);
+MODULE_INFO(Version, DRIVER_VERSION);
diff --git a/gpio_test.c b/gpio_test.c
new file mode 100644 (file)
index 0000000..90d0892
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * gpio_test.c - GPIO test driver
+ *
+ * Copyright (C) 2008 Peter Henn
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * It just triggers a specified GPIO line setup the output
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+
+
+//*****************************************************************************
+// MODULE PARAMETERS 
+//*****************************************************************************
+#define DRVNAME                    "gpio_test"   /* name of the driver */
+#define DRIVER_VERSION            "0.1"   /* helps identifing different versions of that driver */
+#define LABEL               NULL
+
+static unsigned int gpi;
+static unsigned int gpo;
+static unsigned int val;
+static unsigned int irq = -1;
+
+typedef struct {
+        int out_cansleep;
+        int in_cansleep;
+        int irq;
+        int cnt;
+} gpio_t_struct;
+
+static gpio_t_struct gt;
+
+static irqreturn_t gpio_test_irq_handler(int irq, void *dev_id)
+{
+        
+  // todo use dev_id to get pointer to own driver structure data;
+  // container can be memory init with GPK_KERNEL 
+        if (irq == gt.irq) {
+               gt.cnt++;
+               val = !val;
+               if (!gt.in_cansleep) {
+                       pr_info("%s: GPIO #%d in value %d with counter %d\n", DRVNAME, gpi, gpio_get_value(gpi), gt.cnt);
+               }
+               if (!gt.out_cansleep) {
+                       gpio_set_value(gpo, !val);
+               }
+       }
+        return IRQ_HANDLED;
+}
+
+/*
+** just set here the value
+*/
+static int __init gpio_test_init(void)
+{
+        int err;
+        err = gpio_is_valid(gpo);
+       if (err<0) {
+               pr_err("%s: GPIO #%d for output is not usable\n", DRVNAME, gpo);
+               return err;
+       }
+       //ToDo: Test, how a text might be used as LABEL
+       err = gpio_request(gpo, LABEL);
+       if (err<0) {
+               pr_err("%s: GPIO #%d for output is can not be requested\n", DRVNAME, gpo);
+               return err;
+       }
+       gt.out_cansleep = gpio_cansleep(gpo);
+       pr_info("%s: GPIO #%d for output %s sleep\n", DRVNAME, gpo, ((gt.out_cansleep>0) ? "can" : "will not"));
+       err = gpio_direction_output(gpo, val);
+       if (err<0) {
+               pr_err("%s: GPIO #%d output direction is not possible\n", DRVNAME, gpo);
+                gpio_free(gpo);
+               return err;
+       }
+
+        err = gpio_is_valid(gpi);
+       if (err<0) {
+               pr_err("%s: GPIO #%d for inout is not usable\n", DRVNAME, gpi);
+                gpio_free(gpo);
+               return err;
+       }
+       err = gpio_request(gpi, LABEL);
+       if (err<0) {
+               pr_err("%s: GPIO #%d for input is can not be requested\n", DRVNAME, gpi);
+                gpio_free(gpo);
+               return err;
+       }
+       gt.in_cansleep = gpio_cansleep(gpi);
+       pr_info("%s: GPIO #%d for input %s sleep\n", DRVNAME, gpi, ((gt.in_cansleep>0) ? "can" : "will not"));
+       gt.irq = gpio_to_irq(gpi);
+       if (gt.irq<0) {
+               pr_info("%s: GPIO #%d IRQ is not supported\n", DRVNAME, gpi);
+               gt.irq = irq;
+               if (gt.irq >= 0) {
+                       pr_info("%s: we force interrupt to IRQ %d\n", DRVNAME, gt.irq);
+               }
+       } else {
+               pr_info("%s: GPIO #%d IRQ %d is supported\n", DRVNAME, gpi, gt.irq);
+       }
+       /* gpio_direction_output must be call from task context, we use insmod as task context here */
+       err = gpio_direction_input(gpi);
+       if (err<0) {
+               pr_err("%s: GPIO #%d input direction is not possible\n", DRVNAME, gpi);
+                gpio_free(gpo);
+                gpio_free(gpi);
+               return err;
+       }
+        gt.cnt=0;
+       /* register irq handler */
+       // ToDo: Test with other flag types: IRQF_SHARED, IRQF_TRIGGER_*
+       if (gt.irq >= 0) {
+               err = request_irq(gt.irq, gpio_test_irq_handler, IRQF_TRIGGER_NONE, "gpio_test_handler", NULL);
+               if (err<0) {
+                       pr_err("%s: GPIO #%d IRQ %d can not be requested, got %d\n", DRVNAME, gpi, gt.irq, err);
+                       gt.irq = -1;
+               }
+       }
+        return 0;
+}
+
+device_initcall(gpio_test_init);
+
+/*
+** just restore here the old value
+*/
+static void __exit gpio_test_exit(void)
+{
+        if (gt.out_cansleep) {
+               gpio_set_value_cansleep(gpo, !val);
+       } else {
+               gpio_set_value(gpo, !val);
+       }
+       if (gt.irq >= 0) free_irq(gt.irq, NULL);
+        gpio_free(gpo);
+        gpio_free(gpi);
+}
+module_exit(gpio_test_exit);
+
+module_param(gpo, uint, S_IRUGO);
+MODULE_PARM_DESC(gpo, "GPIO output address or number");
+
+module_param(gpi, uint, S_IRUGO);
+MODULE_PARM_DESC(gpi, "GPIO input address or number");
+
+module_param(val, bool, S_IRUGO);
+MODULE_PARM_DESC(val, "GPIO output value 0 or 1");
+
+module_param(irq, uint, S_IRUGO);
+MODULE_PARM_DESC(irq, "GPIO interrupt number");
+
+
+MODULE_AUTHOR("Option Wireless");
+MODULE_DESCRIPTION("GPIO output test");
+MODULE_LICENSE("GPL");
+
+MODULE_INFO(Version, DRIVER_VERSION);
index 00c6b51..0c2b67e 100644 (file)
 /*
  * spi_device
  * todo:
 /*
  * spi_device
  * todo:
- * - implement SPI mode 1,2,3 is yet missing
- * - prevent ugly handling of global spi_parport structure
-//drivers/i2c/busses/i2c-parport.c
-// uses one static pointer of a list:
-//static struct i2c_par *adapter_list;
-//drivers/scsi/ppa.c
-// uses one static pointer of a list:
-// static LIST_HEAD(ppa_hosts);
-//the spi_parport structure has to be pushed into the heap and the pointer has to be saved into the SPI core structre ...
+ * - clean up
+ * - test spi modes
  * - make driver possible to share the parport with a gpio_parport.c
  */
 
  * - make driver possible to share the parport with a gpio_parport.c
  */
 
@@ -92,7 +85,7 @@
  */
 
 //*****************************************************************************
  */
 
 //*****************************************************************************
-// Globals
+// MODULE PARAMETERS 
 //*****************************************************************************
 /* module parameters */
 static  char                   modalias[MODALIASLEN] = "gtm501l_spi";
 //*****************************************************************************
 /* module parameters */
 static  char                   modalias[MODALIASLEN] = "gtm501l_spi";
@@ -113,7 +106,7 @@ typedef struct {
         struct spi_message      *msg;
        struct spi_device       *spi_parport;
        struct spi_board_info   info;
         struct spi_message      *msg;
        struct spi_device       *spi_parport;
        struct spi_board_info   info;
-       struct list_head list;
+        struct list_head        list;
 } spi_parport_struct;
 
 
 } spi_parport_struct;
 
 
@@ -129,14 +122,9 @@ struct spi_parport {
         struct spi_message      *msg;
        struct spi_device       *spi_parport;
        struct spi_board_info   info;
         struct spi_message      *msg;
        struct spi_device       *spi_parport;
        struct spi_board_info   info;
-
+        struct list_head        list;
 };
 
 };
 
-static LIST_HEAD(spi_parport);
-
-/* REVISIT remove this ugly global and its "only one" limitation */
-static struct spi_parport *spi_parport;
-
 /*----------------------------------------------------------------------*/
 
 
 /*----------------------------------------------------------------------*/
 
 
@@ -257,18 +245,28 @@ spi_parport_txrx_word_mode3(struct spi_device *spi,
 
 
 
 
 
 
-/*----------------------------------------------------------------------*/
+/***************************************************************************
+ *                   Parallel port probing routines                        *
+ ***************************************************************************/
+
+static LIST_HEAD(spi_parport_hosts);
+
 
 static void spi_parport_attach(struct parport *p)
 {
        int                     status;
        struct spi_parport      *pp;
        struct spi_master       *master;
 
 static void spi_parport_attach(struct parport *p)
 {
        int                     status;
        struct spi_parport      *pp;
        struct spi_master       *master;
-       struct device           *dev = p->physport->dev;
+       struct device           *dev;
 
 
+#if 0
+       *dev = p->physport->dev;
+        # spi_parport no longer existing, we use now a standard list structure
+        # therefore getting from p->physport->dev makes no sense any longer
+        # end testing as well
        if (spi_parport || !dev)
                return;
        if (spi_parport || !dev)
                return;
-
+#endif
        /* REVISIT:  this just _assumes_ a spi_parport is there ... no probe,
         * and no way to be selective about what it binds to.
         */
        /* REVISIT:  this just _assumes_ a spi_parport is there ... no probe,
         * and no way to be selective about what it binds to.
         */
@@ -311,6 +309,7 @@ static void spi_parport_attach(struct parport *p)
                goto clean0;
        }
 
                goto clean0;
        }
 
+       //ToDo: Check and share really parport device as it is done e.g. in ppa.c
        status = parport_claim(pp->pdev);
        if (status < 0)
                goto clean1;
        status = parport_claim(pp->pdev);
        if (status < 0)
                goto clean1;
@@ -401,7 +400,7 @@ static void spi_parport_attach(struct parport *p)
 #endif
              }
        }
 #endif
              }
        }
-       spi_parport = pp;
+       list_add_tail(&pp->list, &spi_parport_hosts);
        return;
 
 out_bitbang_stop:
        return;
 
 out_bitbang_stop:
@@ -424,48 +423,47 @@ static void spi_parport_detach(struct parport *p)
        struct spi_parport      *pp;
        int                     status;
 
        struct spi_parport      *pp;
        int                     status;
 
-       /* FIXME this global is ugly ... but, how to quickly get from
-        * the parport to the "struct spi_parport" associated with it?
-        * "old school" driver-internal device lists?
-        */
-       if (!spi_parport || spi_parport->port != p)
-               return;
-       pp = spi_parport;
-       if (pp->msg) {
-               spi_message_free(pp->msg);
-               pr_info("Dealloc SPI message buffer\n");
+       list_for_each_entry(pp, &spi_parport_hosts, list) {
+               if (pp->pdev->port == p) {
+                       list_del_init(&pp->list);
+                       if (pp->msg) {
+                               spi_message_free(pp->msg);
+                               pr_info("Dealloc SPI message buffer\n");
+                       }
+                       /* stop() unregisters child devices too */
+                       status = spi_bitbang_stop(&pp->bitbang);
+
+                       /* turn off VCC */
+                       parport_write_data(pp->port, 0);
+                       msleep(10);
+                       parport_release(pp->pdev);
+                       parport_unregister_device(pp->pdev);
+                       (void) spi_master_put(pp->bitbang.master);
+                       kfree(pp);
+                       break;
+               }
        }
        }
-
-       /* stop() unregisters child devices too */
-       status = spi_bitbang_stop(&pp->bitbang);
-
-       /* turn off VCC */
-       parport_write_data(pp->port, 0);
-       msleep(10);
-
-       parport_release(pp->pdev);
-       parport_unregister_device(pp->pdev);
-
-       (void) spi_master_put(pp->bitbang.master);
-
-       spi_parport = NULL;
 }
 
 }
 
+#if 0
 static void spi_parport_detach(struct parport *pb)
 {
 static void spi_parport_detach(struct parport *pb)
 {
-       ppa_struct *dev;
-       list_for_each_entry(dev, &ppa_hosts, list) {
+       spi_parport_struct *dev;
+       list_for_each_entry(dev, &spi_parport_hosts, list) {
                if (dev->dev->port == pb) {
                        list_del_init(&dev->list);
                if (dev->dev->port == pb) {
                        list_del_init(&dev->list);
-                       scsi_remove_host(dev->host);
-                       scsi_host_put(dev->host);
-                       parport_unregister_device(dev->dev);
+
+                       spi_remove_host(dev->host);
+                       spi_host_put(dev->host);
+
+                       parport_release(dev->pdev);
+                       parport_unregister_device(dev->pdev);
                        kfree(dev);
                        break;
                }
        }
 }
                        kfree(dev);
                        break;
                }
        }
 }
-
+#endif
 
 static struct parport_driver spi_parport_driver = {
        .name =         DRVNAME,
 
 static struct parport_driver spi_parport_driver = {
        .name =         DRVNAME,
@@ -496,6 +494,6 @@ module_param(spi_mode, uint, S_IRUGO);
 MODULE_PARM_DESC(spi_mode, "spi mode");
 
 module_param(spi_bits, uint, S_IRUGO);
 MODULE_PARM_DESC(spi_mode, "spi mode");
 
 module_param(spi_bits, uint, S_IRUGO);
-MODULE_PARM_DESC(spi_mode, "spi bits per word");
+MODULE_PARM_DESC(spi_bits, "spi bits per word");
 
 MODULE_INFO(Version, DRIVER_VERSION);
 
 MODULE_INFO(Version, DRIVER_VERSION);