spi_parport and gpio_parport first release
authorPeter Henn <Peter.Henn@web.de>
Thu, 1 Jan 2009 11:02:53 +0000 (12:02 +0100)
committerPeter Henn <Peter.Henn@web.de>
Thu, 1 Jan 2009 11:02:53 +0000 (12:02 +0100)
spi_parport:
- fully rework of the spi_parport
- first real test done

gpio_parport:
- add compile feature 'use parport exclusive'
- add compile feature 'claim parport' as a hack to share one parport
  by both drivers gpio_parport and spi_parport.
- correct GPIO signals, which are inverted used
- add module parameter for force setting interrupt line
- add module parameter for force setting GPIO base address

gpio_parport.c
spi_parport.c

index 73a16b1..0bddbcd 100644 (file)
 #include <linux/spinlock.h>
 #define sCONFIG_GPIO_PARPORT_MUTEX ""
 #endif
 #include <linux/spinlock.h>
 #define sCONFIG_GPIO_PARPORT_MUTEX ""
 #endif
-
+#ifdef CONFIG_GPIO_PARPORT_EXCLUSIVE
+#define sCONFIG_GPIO_PARPORT_EXCLUSIVE " CONFIG_GPIO_PARPORT_EXCLUSIVE"
+#undef CONFIG_GPIO_PARPORT_NOCLAIME
+#else
+#define sCONFIG_GPIO_PARPORT_EXCLUSIVE ""
+#endif
+#ifdef CONFIG_GPIO_PARPORT_NOCLAIME
+#define sCONFIG_GPIO_PARPORT_NOCLAIME " CONFIG_GPIO_PARPORT_NOCLAIME"
+#else
+#define sCONFIG_GPIO_PARPORT_NOCLAIME ""
+#endif
 
 /* ToDo:
  * - proper handling error flags for gpio_to_irq ...
 
 /* 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
  * - 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
  *  -----------  ---------  -------- ---------------------- ------
  * - Support PM functions like resume, suspend
  *
  *   Parallel                
  *     Port      Direction  Register Name                   Signal
  *  -----------  ---------  -------- ---------------------- ------
- *   nStrobe  1     -->     nC0      PARPORT_CONTROL_STROBE nGPO08
+ *   nStrobe  1     -->     nC0      PARPORT_CONTROL_STROBE  GPO08
  *      D0    2     -->      D0      (1 << 0)                GPO00
  *      D1    3     -->      D1      (1 << 1)                GPO01
  *      D2    4     -->      D2      (1 << 2)                GPO02
  *      D0    2     -->      D0      (1 << 0)                GPO00
  *      D1    3     -->      D1      (1 << 1)                GPO01
  *      D2    4     -->      D2      (1 << 2)                GPO02
  *      D6    8     -->      D6      (1 << 6)                GPO06
  *      D7    9     -->      D7      (1 << 7)                GPO07
  *   nAckn   10     <--      S6      PARPORT_STATUS_ACK      GPI15*
  *      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
+ *    Busy   11     <--     nS7      PARPORT_STATUS_BUSY     GPI16
  *    Paper  12     <--      S5      PARPORT_STATUS_PAPEROUT GPI14
  *    Sel    13     <--      S4      PARPORT_STATUS_SELECT   GPI13
  *    Paper  12     <--      S5      PARPORT_STATUS_PAPEROUT GPI14
  *    Sel    13     <--      S4      PARPORT_STATUS_SELECT   GPI13
- *   nFeed   14     -->     nC1      PARPORT_CONTROL_AUTOFD nGPO09
+ *   nFeed   14     -->     nC1      PARPORT_CONTROL_AUTOFD  GPO09
  *   nError  15     <--      S3      PARPORT_STATUS_ERROR    GPI12
  *   nInit   16     -->      C2      PARPORT_CONTROL_INIT    GPO10
  *   nError  15     <--      S3      PARPORT_STATUS_ERROR    GPI12
  *   nInit   16     -->      C2      PARPORT_CONTROL_INIT    GPO10
- *   nSelIn  17     -->     nC3      PARPORT_CONTROL_SELECT nGPO11 
+ *   nSelIn  17     -->     nC3      PARPORT_CONTROL_SELECT  GPO11 
  *     GND   25     --       --                              GND
  *
  *  Signal:
  *  - GPOx  : general purpose output only
  *  - GPIx  : general purpose input only
  *  - GPIx* : interrupt in
  *     GND   25     --       --                              GND
  *
  *  Signal:
  *  - GPOx  : general purpose output only
  *  - GPIx  : general purpose input only
  *  - GPIx* : interrupt in
- *  - nGPXx : signal is inverted
  */
 
 //*****************************************************************************
  */
 
 //*****************************************************************************
 #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 */
 #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 */
+#define GPIO_PARPORT_STATUS_INV   0x80    /* invert internal bit 7 of status parport bit (Busy) */
+#define GPIO_PARPORT_CONTROL_INV  0x0b    /* invert internal bit 0, 1 and 3 of control parport bits (strobe, autofd, select) */
 
 
-
-static int modparam_gpiobase = -1         /* dynamic assingment */;
+/* Module load parameter */
+static int irq=-1;                        /* force irq for parport pin 10 */ 
+static int gpiobase = -1                  /* default dynamic assignment */;
 
 typedef struct {
        struct pardevice        *dev;     /* parport device entry */
 
 typedef struct {
        struct pardevice        *dev;     /* parport device entry */
@@ -161,7 +171,7 @@ static inline int gpio_parport_data_get(gpio_pp_struct *pp, unsigned mask)
 static inline int gpio_parport_ctrl_get(gpio_pp_struct *pp, unsigned mask)
 {
         // good idea to update also the cache
 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);
+       pp->ccache = GPIO_PARPORT_CONTROL_INV ^ parport_read_control(pp->port);
        return ((pp->ccache & mask) != 0);
 }
 
        return ((pp->ccache & mask) != 0);
 }
 
@@ -170,7 +180,7 @@ static inline int gpio_parport_ctrl_get(gpio_pp_struct *pp, unsigned mask)
 */
 static inline int gpio_parport_status_get(gpio_pp_struct *pp, unsigned mask)
 {
 */
 static inline int gpio_parport_status_get(gpio_pp_struct *pp, unsigned mask)
 {
-        unsigned int cache = parport_read_status(pp->port);
+        unsigned int cache = GPIO_PARPORT_STATUS_INV ^ parport_read_status(pp->port);
        return ((cache & mask) != 0);
 }
 
        return ((cache & mask) != 0);
 }
 
@@ -181,7 +191,7 @@ static inline int gpio_parport_status_get(gpio_pp_struct *pp, unsigned mask)
 static int gpio_parport_get(struct gpio_chip *gpio, unsigned offset)
 {
         gpio_pp_struct *pp = container_of(gpio, gpio_pp_struct, gpio);
 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 */
+       int            status = 0; /* inititalization ensures return zero for unhandled bits */
        unsigned       long flags;
 
        if (offset < GPIO_PARPORT_CTRL_PORT_BITS) {
        unsigned       long flags;
 
        if (offset < GPIO_PARPORT_CTRL_PORT_BITS) {
@@ -232,7 +242,7 @@ static inline void gpio_parport_ctrl_set(gpio_pp_struct *pp, unsigned mask, int
        else
                byte &= ~mask;
        pp->ccache = byte;   // restore cache
        else
                byte &= ~mask;
        pp->ccache = byte;   // restore cache
-       return parport_write_control(pp->port, byte);
+       return parport_write_control(pp->port, (GPIO_PARPORT_CONTROL_INV ^ byte));
 }
 
 /*
 }
 
 /*
@@ -342,7 +352,7 @@ static void gpio_parport_gpio_setup(gpio_pp_struct *pp)
        c->free = NULL;
        c->to_irq = gpio_parport_gpio_to_irq;
 #endif
        c->free = NULL;
        c->to_irq = gpio_parport_gpio_to_irq;
 #endif
-       c->base = modparam_gpiobase;
+       c->base = gpiobase;
        c->ngpio = GPIO_PARPORT_NR_GPIOS;
         #ifdef CONFIG_GPIO_PARPORT_MUTEX
            c->can_sleep = 1;
        c->ngpio = GPIO_PARPORT_NR_GPIOS;
         #ifdef CONFIG_GPIO_PARPORT_MUTEX
            c->can_sleep = 1;
@@ -359,10 +369,10 @@ static void gpio_parport_gpio_setup(gpio_pp_struct *pp)
 static LIST_HEAD(gpio_parport_hosts);
 
 /*
 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.
+** If attaching the driver to parport is done with exclusive access, 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 any drivers
+** first as long as you use exclusive access.
 */
 static int __gpio_parport_attach(struct parport *pb)
 {
 */
 static int __gpio_parport_attach(struct parport *pb)
 {
@@ -394,7 +404,11 @@ static int __gpio_parport_attach(struct parport *pb)
                                          NULL, /* preemption fkt */ 
                                          NULL, /* wake-up fkt */
                                          NULL, /* interrupt handler */
                                          NULL, /* preemption fkt */ 
                                          NULL, /* wake-up fkt */
                                          NULL, /* interrupt handler */
-                                         PARPORT_FLAG_EXCL, /* flags */
+                                          #ifdef CONFIG_GPIO_PARPORT_EXCLUSIVE
+                                             PARPORT_FLAG_EXCL, /* flags */
+                                         #else
+                                             0, /* flags, 0 means used port shared */
+                                         #endif
                                          pp /* handle */ );
        if (pp->dev == NULL) {
                pr_err("%s: unable to register with parport\n", DRVNAME);
                                          pp /* handle */ );
        if (pp->dev == NULL) {
                pr_err("%s: unable to register with parport\n", DRVNAME);
@@ -402,25 +416,37 @@ static int __gpio_parport_attach(struct parport *pb)
                goto err_release_mem;
        }
 
                goto err_release_mem;
        }
 
+       #ifndef CONFIG_GPIO_PARPORT_NOCLAIME
        err = parport_claim(pp->dev);
        if (err < 0) {
                pr_err("%s: unable to claim parport\n", DRVNAME);
                err = -ENODEV;
                goto err_unregister_parport;
        }
        err = parport_claim(pp->dev);
        if (err < 0) {
                pr_err("%s: unable to claim parport\n", DRVNAME);
                err = -ENODEV;
                goto err_unregister_parport;
        }
+       #endif
 
         /* ------------- GPIO PARPORT init --------------- */  
        /* cache initialisation just preventing changing unused bits */
        pp->dcache = parport_read_data(pp->port);
 
         /* ------------- 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;
+       pp->ccache = GPIO_PARPORT_CONTROL_INV ^ parport_read_control(pp->port);
+
+       /* interrrut initialization */
+        /* tested on x86 arch, because irq has nothing special to handle here */
+       if(irq >= 0) {
+                pp->irq = irq;
+               pr_info("%s: force usage IRQ %d\n", DRVNAME, pp->irq);
+               if(pp->port->irq == pp->irq) {
+                       /* we free the standard parport handler, because we want to use our own later */
+                       free_irq(pp->irq, pp->port);
+                       pr_info("%s: Note parport driver will claim later irq as already freed during its module unload\n", 
+                               DRVNAME);
+               }
+       } else if(pp->port->irq != PARPORT_IRQ_NONE) {
+                pp->irq = pp->port->irq;
                /* we free the standard parport handler, because we want to use our own later */
                /* 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);
+               free_irq(pp->irq, pp->port);
+               pr_info("%s: will use IRQ %d exclusive\n", DRVNAME, pp->irq);
+               pr_info("%s: Note parport driver will claim later irq as already freed during its module unload\n", DRVNAME);
        } else {
                pp->irq = -ENXIO;
        }
        } else {
                pp->irq = -ENXIO;
        }
@@ -440,8 +466,10 @@ static int __gpio_parport_attach(struct parport *pb)
        return 0;
 
  err_release_parport:
        return 0;
 
  err_release_parport:
+       #ifndef CONFIG_GPIO_PARPORT_NOCLAIME
        parport_release(pp->dev);
  err_unregister_parport:
        parport_release(pp->dev);
  err_unregister_parport:
+       #endif
        parport_unregister_device(pp->dev);
  err_release_mem:
        kfree(pp);
        parport_unregister_device(pp->dev);
  err_release_mem:
        kfree(pp);
@@ -465,9 +493,10 @@ static int gpio_parport_remove(gpio_pp_struct *pp)
        err = gpiochip_remove(&pp->gpio);
        if (err < 0)
                pr_err("%s: gpio_remove error: %d\n", DRVNAME, err);
        err = gpiochip_remove(&pp->gpio);
        if (err < 0)
                pr_err("%s: gpio_remove error: %d\n", DRVNAME, err);
+       #ifndef CONFIG_GPIO_PARPORT_NOCLAIME
        parport_release(pp->dev);
        parport_release(pp->dev);
+       #endif
         parport_unregister_device(pp->dev);
         parport_unregister_device(pp->dev);
-       //ToDo: Check the unregister return value
        return err;
 }
 
        return err;
 }
 
@@ -476,7 +505,7 @@ static void gpio_parport_detach(struct parport *pb)
        gpio_pp_struct *pp;
 
        list_for_each_entry(pp, &gpio_parport_hosts, list) {
        gpio_pp_struct *pp;
 
        list_for_each_entry(pp, &gpio_parport_hosts, list) {
-               if (pp->dev->port == pb) {
+               if (pp->port == pb) {
                        (void)gpio_parport_remove(pp);   
                        list_del_init(&pp->list);
                        kfree(pp);
                        (void)gpio_parport_remove(pp);   
                        list_del_init(&pp->list);
                        kfree(pp);
@@ -508,5 +537,11 @@ MODULE_AUTHOR("Option Wireless");
 MODULE_DESCRIPTION("GPIO master controller driver for Parport Adapter");
 MODULE_LICENSE("GPL");
 
 MODULE_DESCRIPTION("GPIO master controller driver for Parport Adapter");
 MODULE_LICENSE("GPL");
 
-MODULE_INFO(Flags, sCONFIG_GPIO_PARPORT_MUTEX);
+module_param(irq, uint, S_IRUGO);
+MODULE_PARM_DESC(irq, "force set IRQ for parport pin 10");
+
+module_param_named(base, gpiobase, uint, S_IRUGO);
+MODULE_PARM_DESC(base, "force set GPIO base address");
+
+MODULE_INFO(Flags, sCONFIG_GPIO_PARPORT_MUTEX sCONFIG_GPIO_PARPORT_EXCLUSIVE sCONFIG_GPIO_PARPORT_NOCLAIME);
 MODULE_INFO(Version, DRIVER_VERSION);
 MODULE_INFO(Version, DRIVER_VERSION);
index 0c2b67e..82c71b2 100644 (file)
@@ -1,8 +1,7 @@
 /*
  * spi_parport.c - SPI master controller driver based on the parallel port adapter
  *
 /*
  * spi_parport.c - SPI master controller driver based on the parallel port adapter
  *
- * Copyright (C) 2005 David Brownell
- *               2008 Peter Henn
+ * Copyright (C) 2008 Peter Henn <Peter.Henn@web.de>
  *
  * 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
  *
  * 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
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * The spi_parport is generated from the spi_butterfly driver, but with the
- * idea to isolate here only the SPI master controller part. Note that we
- * have not implement the full SPI functionality
+ * The spi_parport find its beginning in the spi_butterfly driver, but with the
+ * idea to isolate the SPI master controller part and move all additional SPI
+ * protocoll stuff into separate drivers.
+ * The most important settings can be given to the driver by module load parameter.
+ * Also the SPI-protocol driver can be configured by module load parameter. Make
+ * sure that all related SPI-protocol drivers are load before you load this SPI
+ * controller module!
+ * The SPI parport driver supports more than one SPI device. Therefore all module
+ * parameter can be given as an ARRAY with ',' as a delimiter. Starting from the
+ * first entry up to SPI_DEV_MAX devices will be handled. Therefore a successive
+ * chip selects will be reserved in the parport settings.
  *
  *
- * Some important settings can be given to the driver by module load parameter.
- * Also the refernced SPI-protocoll driver can be changed by a module load parameter.
+ * Naturally this driver should normally use its gpio_parport base driver. But just
+ * to prevent additional performace issues I decice to finish this spi_parport.
+ * Both drivers may gives you also the possibility to use them hardware shared on
+ * one parport device, as long as you never use GPIO outputs in the gpio_parport from
+ * the parport data port and move the SPI control signals in the spi_parport driver
+ * to the parport control port - and visa verse, although its not a real software
+ * shared functionality.
  */
 #include <linux/kernel.h>
 #include <linux/init.h>
  */
 #include <linux/kernel.h>
 #include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/interrupt.h>
 #include <linux/device.h>
 #include <linux/parport.h>
 #include <linux/device.h>
 #include <linux/parport.h>
+#include <linux/spinlock.h>
 
 #include <linux/sched.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/spi_bitbang.h>
 
 #include <linux/sched.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/spi_bitbang.h>
-//#include <linux/gpio.h>
 
 
-/*
- * spi_device
- * todo:
- * - clean up
- * - test spi modes
- * - make driver possible to share the parport with a gpio_parport.c
- */
-
-/* DATA output bits (pins 2..9 == D0..D7) */
-#define MODALIASLEN     10
-#define DRVNAME                "spi_parport"
-#define DRIVER_VERSION  "0.2"
-
-#define        spi_sck_bit     (1 << 0)                /* pin 2 */
-#define        spi_mosi_bit    (1 << 7)                /* pin 9 */
-
-#define        vcc_bits        ((1 << 6) | (1 << 5))   /* pins 7, 8 */
-
-/* STATUS input bits */
-#define        spi_miso_bit    PARPORT_STATUS_BUSY     /* pin 11 */
-
-/* CONTROL output bits */
-#define        spi_cs_bit      (1 << 1)                /* pin 3 */
+#ifdef CONFIG_SPI_PARPORT_EXCLUSIVE
+#define sCONFIG_SPI_PARPORT_EXCLUSIVE " CONFIG_SPI_PARPORT_EXCLUSIVE"
+#else
+#define sCONFIG_SPI_PARPORT_EXCLUSIVE ""
+#endif
 
 
-/*
- *   Parallel                
+/* ToDo
+ * 0. Setup of the parport mode must be done. Therefore we read the
+ *    supported modes: dev->dev->port->modes; Depending on the mode flags we change
+ *    the mode value in the dev->dev->port->base_hi+0x02; Register, which is the ECR
+ *    (extended control register). So first set to SPP (standard parport mode)
+ * 1. first setup all CS, because spi_new_device or spi_add_device will call
+ *    probe fkt of the SPI protocoll driver. This might initiate a SPI transfer
+ *    and triggers the SCLK, which is worse for other chips on the SPI-BUS using
+ *    the wrong CS polarity
+ * 2. use spi_add_device instead of spi_new_device. That's the only chance
+ *    to setup the also the SPI bits per word value, just before any transaction
+ *    might be initiated with the SPI probe fkt of the SPI protocol driver.
+ *    Setting this spi->bits_per_word value afterwards might be here too late.
+ *    Note that bits_per_word is normally has to be set normally by the protocoll
+ *    driver (e.g. a bad style of the lm70.c driver, which guess that the value is
+ *    setup afterwards)
+ *    Therefore we MUST support a mode, where NOTHING will be changed!
+ * 3. support better irq via array load parameter and values like AUTO, NONE. Additionally
+ *    don't free any interrupt, if no interrupt might be requested
+ * 4. output warning, if open mode, bits values after finish init
+ * 5. test spi modes
+ *
+ * Note: - SPI_LSB_FIRST is not yet supported and would need own txrx functions
+ *       - SPI_LOOP is supported, but requires tx before rx in own transfer functions
+ *       - SPI_3WIRE depends on signal routing, but is in principle possible
+ *         and therefore supported.
+ *       - SPI_CS_HIGH is supported
+ *       - Slow CPU frequencies are currently not supported. The
+ *         maximum Bit rate may be around 200 kBit/sec
+ *
+ * ToDo: - support also bits of the Control port
+ *       - support more than one SPI controller
+ *
+ *   Parallel
  *     Port      Direction  Register Name                   Signal
  *  -----------  ---------  -------- ---------------------- ------
  *   nStrobe  1     -->     nC0      PARPORT_CONTROL_STROBE
  *      D0    2     -->      D0      (1 << 0)                SCLK
  *     Port      Direction  Register Name                   Signal
  *  -----------  ---------  -------- ---------------------- ------
  *   nStrobe  1     -->     nC0      PARPORT_CONTROL_STROBE
  *      D0    2     -->      D0      (1 << 0)                SCLK
- *      D1    3     -->      D1      (1 << 1)               nCS
- *      D2    4     -->      D2      (1 << 2)                 --
- *      D3    5     -->      D3      (1 << 3)                 --
+ *      D1    3     -->      D1      (1 << 1)                MOSI
+ *      D2    4     -->      D2      (1 << 2)               nCS0
+ *      D3    5     -->      D3      (1 << 3)               nCS1
  *      D4    6     -->      D4      (1 << 4)                 --
  *      D5    7     -->      D5      (1 << 5)                 --
  *      D6    8     -->      D6      (1 << 6)                 --
  *      D4    6     -->      D4      (1 << 4)                 --
  *      D5    7     -->      D5      (1 << 5)                 --
  *      D6    8     -->      D6      (1 << 6)                 --
- *      D7    9     -->      D7      (1 << 7)                MOSI
+ *      D7    9     -->      D7      (1 << 7)                 --
  *   nAckn   10     <--      S6      PARPORT_STATUS_ACK
  *   nAckn   10     <--      S6      PARPORT_STATUS_ACK
- *    Busy   11     <--     nS7      PARPORT_STATUS_BUSY     MISO  
+ *    Busy   11     <--     nS7      PARPORT_STATUS_BUSY     MISO
  *    Paper  12     <--      S5      PARPORT_STATUS_PAPEROUT  --
  *    Sel    13     <--      S4      PARPORT_STATUS_SELECT    --
  *   nFeed   14     -->     nC1      PARPORT_CONTROL_AUTOFD   --
  *   nError  15     <--      S3      PARPORT_STATUS_ERROR     --
  *   nInit   16     -->      C2      PARPORT_CONTROL_INIT     --
  *    Paper  12     <--      S5      PARPORT_STATUS_PAPEROUT  --
  *    Sel    13     <--      S4      PARPORT_STATUS_SELECT    --
  *   nFeed   14     -->     nC1      PARPORT_CONTROL_AUTOFD   --
  *   nError  15     <--      S3      PARPORT_STATUS_ERROR     --
  *   nInit   16     -->      C2      PARPORT_CONTROL_INIT     --
- *   nSelIn  17     -->     nC3      PARPORT_CONTROL_SELECT   --  
+ *   nSelIn  17     -->     nC3      PARPORT_CONTROL_SELECT   --
  *     GND   25     --       --                              GND
  *     GND   25     --       --                              GND
+ *
+ * Signal:
+ * - SCLK : SPI Master Clock
+ * - MOSI : SPI Master Data Output, Slave Data Input
+ * - MISO : SPI Master Date Input, Slave Data Output
+ * - nCSx : SPI Chip Select x, default low active
+ *   Note: nCSx must be in consecutive bit order!
  */
 
 //*****************************************************************************
  */
 
 //*****************************************************************************
-// MODULE PARAMETERS 
+// MODULE PARAMETERS
 //*****************************************************************************
 //*****************************************************************************
-/* module parameters */
-static  char                   modalias[MODALIASLEN] = "gtm501l_spi";
-static  unsigned int            spi_mode = SPI_3WIRE | SPI_MODE_0;
-static  unsigned int            spi_bits = 16;
-
-static inline struct spi_parport *spidev_to_pp(struct spi_device *spi)
-{
-       return spi->controller_data;
-}
-
-
-typedef struct {
-       struct pardevice        *dev;     /* Parport device entry         */
-        struct parport         *port;    /* */
-       struct spi_bitbang      bitbang;
-        u8                     lastbyte; /* hold copy of parport to be faster */
-        struct spi_message      *msg;
-       struct spi_device       *spi_parport;
-       struct spi_board_info   info;
-        struct list_head        list;
-} spi_parport_struct;
-
+#define DRVNAME                        "spi_parport"   /* name of the driver */
+#define DRIVER_VERSION                 "0.2"   /* helps identifing different versions of that driver */
+
+#define SPI_PARPORT_SCLK            (1 << 0)   /* SPI Clock on parport D0 or pin 2 */
+#define SPI_PARPORT_MOSI            (1 << 1)   /* SPI MOSI on parport D1 or pin 3 */
+#define SPI_PARPORT_CS0                     (1 << 2)   /* first SPI chipselet on parport D2 or pin 4 */
+#define SPI_PARPORT_MISO  PARPORT_STATUS_BUSY  /* SPI MISO on parport Status Busy or pin 11 */
+
+#define SPI_PARPORT_STATUS_INV           0x80   /* invert internal bit 7 of status parport bit (Busy) */
+#define SPI_PARPORT_CONTROL_INV          0x0b   /* invert internal bit 0, 1 and 3 of control parport bits
+                                                  (Strobe, Autofd, Select) */
+
+#define SPI_PARPORT_MAX_SPEED     1*1000*1000   /* maximum allowed speed for any SPI device, currently not used */
+#define SPI_DEV_MAX                         4   /* maximum number of supported SPI devices and CS */
+#define SPI_MODALIAS_LEN                   32   /* spi protocol module alias name length, not defined in spi.h */
+
+/* Module load parameter */
+static  int irq=-1;                             /* force irq for parport pin 10 */
+static  char spi_modalias[SPI_DEV_MAX*(SPI_MODALIAS_LEN+1)]="";
+static  unsigned int spi_mode[SPI_DEV_MAX] = {SPI_MODE_0, SPI_MODE_0, SPI_MODE_0, SPI_MODE_0};
+static  int spi_mode_cnt = SPI_DEV_MAX;
+static  unsigned int spi_bits[SPI_DEV_MAX] = {8, 8, 8, 8};
+static  int spi_bits_cnt = SPI_DEV_MAX;
 
 struct spi_parport {
 
 struct spi_parport {
-       /* REVISIT ... for now, this must be first */
-       struct spi_bitbang      bitbang;
-
-       struct parport          *port;
-       struct pardevice        *pdev;
-
-        u8                     lastbyte; /* hold copy of parport to be faster */
-
-        struct spi_message      *msg;
-       struct spi_device       *spi_parport;
-       struct spi_board_info   info;
-        struct list_head        list;
+       struct spi_bitbang      bitbang;  /* SPI bitbang controller data, must be the first item */
+       struct pardevice        *dev;     /* parport device entry */
+        struct parport         *port;    /* parport port entry */
+        u8                     dcache;   /* parport data port mirror bits */
+        int                     irq;      /* parport interrupt */
+        struct list_head        list;    /* spi_parport host controller list */
 };
 
 };
 
+/* helps accessing the spi controller stucture */
+static inline struct spi_parport *spidev_to_pp(struct spi_device *spi)
+{
+        return spi->controller_data;
+}
 /*----------------------------------------------------------------------*/
 /*----------------------------------------------------------------------*/
+/* spi_parport to spi_bitbang interface functions */
 
 
-
-static inline void
-setsck(struct spi_device *spi, int is_on)
+/*
+** parport data bit output function
+*/
+static inline void spi_parport_data_set(struct spi_parport *pp, unsigned mask, int value)
 {
 {
-       struct spi_parport      *pp = spidev_to_pp(spi);
-       u8                      bit, byte = pp->lastbyte;
-
-       bit = spi_sck_bit;
-
-       if (is_on)
-               byte |= bit;
+        u8 byte = pp->dcache; // use old value from cache
+       if (value)
+               byte |= mask;
        else
        else
-               byte &= ~bit;
-       parport_write_data(pp->port, byte);
-       pp->lastbyte = byte;   /* use parport mirror to be faster */
+               byte &= ~mask;
+       pp->dcache = byte;   // restore cache
+       return parport_write_data(pp->port, byte);
 }
 
 }
 
-static inline void
-setmosi(struct spi_device *spi, int is_on)
+/*
+** set clock signal
+*/
+static inline void setsck(struct spi_device *spi, int value)
 {
 {
-       struct spi_parport      *pp = spidev_to_pp(spi);
-       u8                      bit, byte = pp->lastbyte;
-
-       bit = spi_mosi_bit;
+        struct spi_parport *pp = spidev_to_pp(spi);
+       return spi_parport_data_set(pp, SPI_PARPORT_SCLK, value);
+}
 
 
-       if (is_on)
-               byte |= bit;
-       else
-               byte &= ~bit;
-       parport_write_data(pp->port, byte);
-       pp->lastbyte = byte;   /* use parport mirror to be faster */
+/*
+** set MOSI signal
+*/
+static inline void setmosi(struct spi_device *spi, int value)
+{
+       struct spi_parport *pp = spidev_to_pp(spi);
+       return spi_parport_data_set(pp, SPI_PARPORT_MOSI, value);
 }
 
 }
 
-static inline int getmiso(struct spi_device *spi)
+/*----------------------------------------------------------------------*/
+/*
+** parport status bit input function
+*/
+static inline int spi_parport_status_get(struct spi_parport *pp, unsigned mask)
 {
 {
-       struct spi_parport      *pp = spidev_to_pp(spi);
-       int                     value;
-       u8                      bit;
+       u8 byte = SPI_PARPORT_STATUS_INV ^ parport_read_status(pp->port);
+       return ((byte & mask) != 0);
+}
 
 
-       bit = spi_miso_bit;
+/*
+** parport data bit input function, only need for loop back mode
+** Note: Neiter the dcache is here updated nor the value is read from that dcache
+**       to ensure similar system timing bahaviour as in standard MISO read mode.
+*/
+static inline int spi_parport_data_get(struct spi_parport *pp, unsigned mask)
+{
+        u8 byte = parport_read_data(pp->port);
+       return ((byte & mask) != 0);
+}
 
 
-       /* only STATUS_BUSY is NOT negated */
-       value = !(parport_read_status(pp->port) & bit);
-       return (bit == PARPORT_STATUS_BUSY) ? value : !value;
+/*
+** get MISO signal
+** supports also loop back mode in master controller
+*/
+static inline int getmiso(struct spi_device *spi)
+{
+        int value;
+       struct spi_parport *pp = spidev_to_pp(spi);
+       if (spi->mode & SPI_LOOP) {
+               /* Loop back mode, read direct from output port is possible, */
+               /* because setmosi will be done before getmiso in txrx */
+               value = spi_parport_data_get(pp, SPI_PARPORT_MOSI);
+       } else {
+               /* default MISO read */
+               value = spi_parport_status_get(pp, SPI_PARPORT_MISO);
+       }
+       return value;
 }
 
 }
 
+/*
+** set chipselects
+** sets corresponding CSx and CLK, depending on its polarity and mode
+*/
 static void spi_parport_chipselect(struct spi_device *spi, int value)
 {
 static void spi_parport_chipselect(struct spi_device *spi, int value)
 {
-       struct spi_parport      *pp = spidev_to_pp(spi);
-
-#if 0
+       struct spi_parport *pp = spidev_to_pp(spi);
        /* set default clock polarity */
        /* set default clock polarity */
-       if (value != BITBANG_CS_INACTIVE)
-               setsck(spi, spi->mode & SPI_CPOL);
-
-       /* here, value == "activate or not";
-        * most PARPORT_CONTROL_* bits are negated, so we must
-        * morph it to value == "bit value to write in control register"
-        */
-       if (spi_cs_bit == PARPORT_CONTROL_INIT)
-               value = !value;
-
-       parport_frob_control(pp->port, spi_cs_bit, value ? spi_cs_bit : 0);
-#else
-       u8                      bit, byte = pp->lastbyte;
-
-       bit = spi_cs_bit;
-
-       if (value)
-               byte &= ~bit;
-       else
-               byte |= bit;
-       parport_write_data(pp->port, byte);
-       pp->lastbyte = byte;   /* use parport mirror to be faster */
-#endif
+       if (value == BITBANG_CS_INACTIVE) {
+               /* Deselect the slaves on the SPI bus by programming CS to inactive state */
+               spi_parport_data_set(pp, SPI_PARPORT_CS0 << spi->chip_select, (spi->mode & SPI_CS_HIGH)==0);
+       } else if (value == BITBANG_CS_ACTIVE) {
+               /* Set the SPI clock phase and polarity */
+               setsck(spi, (spi->mode & SPI_CPOL)!=0);
+               /* Activate the chip select by programming CS to active state */
+               if ((spi->chip_select >= 0) && (spi->chip_select < SPI_DEV_MAX)) {
+                       spi_parport_data_set(pp, SPI_PARPORT_CS0 << spi->chip_select, (spi->mode & SPI_CS_HIGH)!=0);
+               } else {
+                       pr_err("%s: Use chip %sCS%d select out of range CS0 - CS%d\n",
+                              DRVNAME, ((spi->mode & SPI_CS_HIGH)!=0)?"":"/", spi->chip_select, SPI_DEV_MAX);
+               }
+       }
 }
 
 }
 
-// we do not wait, because parport is even slow, but if X > 10000, this would become important
-//#define      spidelay        ndelay
+/* use default TXRX bitbang interface functions from header file */
+#define        EXPAND_BITBANG_TXRX
+
+/* currently we omit any additional delay, because parport is even slow.
+   Naturally for X > 10000, a delay would become more important */
 #define        spidelay(X)     do{}while(0)
 
 #define        spidelay(X)     do{}while(0)
 
-#define        EXPAND_BITBANG_TXRX
 #include <linux/spi/spi_bitbang.h>
 
 #include <linux/spi/spi_bitbang.h>
 
-static u32
-spi_parport_txrx_word_mode0(struct spi_device *spi,
+static u32 spi_parport_txrx_word_mode0(struct spi_device *spi,
                unsigned nsecs,
                u32 word, u8 bits)
 {
        return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
 }
 
                unsigned nsecs,
                u32 word, u8 bits)
 {
        return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
 }
 
-static u32
-spi_parport_txrx_word_mode1(struct spi_device *spi,
+static u32 spi_parport_txrx_word_mode1(struct spi_device *spi,
                unsigned nsecs,
                u32 word, u8 bits)
 {
        return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits);
 }
 
                unsigned nsecs,
                u32 word, u8 bits)
 {
        return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits);
 }
 
-static u32
-spi_parport_txrx_word_mode2(struct spi_device *spi,
+static u32 spi_parport_txrx_word_mode2(struct spi_device *spi,
                unsigned nsecs,
                u32 word, u8 bits)
 {
        return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits);
 }
 
                unsigned nsecs,
                u32 word, u8 bits)
 {
        return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits);
 }
 
-static u32
-spi_parport_txrx_word_mode3(struct spi_device *spi,
+static u32 spi_parport_txrx_word_mode3(struct spi_device *spi,
                unsigned nsecs,
                u32 word, u8 bits)
 {
                unsigned nsecs,
                u32 word, u8 bits)
 {
@@ -244,226 +295,291 @@ spi_parport_txrx_word_mode3(struct spi_device *spi,
 }
 
 
 }
 
 
+/*----------------------------------------------------------------------*/
+/*
+** copies src to dest string up to count characters or until a character is found
+** which matches the delim string set. As the return value will be returned the next
+** address of src, which can be used for the next stnarr of NULL if the found string
+** was longer than count.
+*/
+const char *strnarr(char *dst, const char *src, const char *dlim, size_t count)
+{
+        int i;
+       for(i=0; i<count; i++) {
+               if (*src == '\0') {
+                       *dst='\0';
+                       return src;
+               }
+               if (strchr(dlim, *src)!=NULL) {
+                       *dst='\0';
+                       return ++src;
+               }
+               *dst++=*src++;
+       }
+        *--dst='\0'; /* add missing line end */
+       return NULL;
+}
+
+
+#if 0
+void parse_irqs(int nports, const char *irqstr, int irqval[])
+{
+  unsigned int i;
+  for (i=0; i<nports && irgstr; i++){
+    if (!strncmp(irqstr, "auto", 4))
+      irqval[i] = AUTO;
+    else if (!strncmp(irqstr, "none", 4))
+      irqval[i] = NONE;
+    else {
+      char *ep;
+      unsigned long r = simple_strtoul(irqstr, &ep, 0);
+      if (ep != irqstr)
+       irqval[i] = r;
+      else {
+       printk("... bad irq specifier '%s'\n", irqstr);
+      return;
+      }
+    }
+    irqstr = strchr(irqstr, ',');
+    if (irqstr) irqstr++;
+  }
+}
+#endif
+
+
+/*
+** spi parport registers SPI devices, defined by the given module load parameters.
+*/
+static int spi_parport_register_spi_devices(struct spi_parport *pp)
+{
+       const char *modalias_param=spi_modalias;
+       int i=0;
+
+       /* loop over all SPI device informations found in module parameter settings */
+       while ((modalias_param[0]!='\0') && (i<SPI_DEV_MAX)) {
+               struct spi_board_info chip;     /* parport SPI board description placeholder*/
+               struct spi_device *proxy;       /* just need to check spi device result and add additional settings */
+               modalias_param=strnarr(chip.modalias, modalias_param, " ,", sizeof(chip.modalias));
+               if (modalias_param==NULL) {
+                       pr_err("%s: %d given modalias name is too long! See only that '%s'!\n",  DRVNAME, i, chip.modalias);
+                       return -EINVAL;
+               }
+
+               /*
+                * The modalias name MUST match the device_driver name
+                * for the bus glue code to match and subsequently bind them.
+                */
+               chip.max_speed_hz = SPI_PARPORT_MAX_SPEED;
+               chip.chip_select = i;
+               chip.mode = spi_mode[i];
+               chip.irq = pp->irq;
+               chip.controller_data = pp;      /* enable access to primary controller data */
+               chip.platform_data = NULL;      /* data area of SPI protocol driver */
+               /* chip.bus_num = master->bus_num;      // not needed for spi_new_device */
+               i++; /* found new SPI protocol driver */
+
+               if (spi_mode_cnt < i) {
+                       pr_warning("%s: No spi mode parameter given for device %d with driver '%s'. "
+                                  "Will use default setting: %d\n",
+                                  DRVNAME, i, chip.modalias, chip.mode);
+               }
+               pr_info("%s: Will load protocol driver: '%s' on CS%d with mode 0x%02x!\n",
+                       DRVNAME, chip.modalias, chip.chip_select, chip.mode);
+
+               /*
+                * Register spi device from board info settings.
+                * Note that this will also call the probe inside the SPI protocall driver
+                */
+               proxy = spi_new_device(pp->bitbang.master, &chip);
+               if (!proxy) {
+                         pr_err("%s: spi_new_device failed!\n", DRVNAME);
+                         return -ENODEV;
+               }
+               if (spi_bits_cnt < i) {
+                       pr_warning("%s: No spi bits per word parameter given for device %d with driver '%s'. "
+                                  "Will use default setting: %d\n",
+                                  DRVNAME, i, chip.modalias, spi_bits[i-1]);
+               }
+               proxy->bits_per_word=spi_bits[i-1];
+               pr_info("%s: SPI device successfully registered at bus %s as device %s with %d bits/word\n", DRVNAME,
+                       proxy->dev.bus_id, chip.modalias, proxy->bits_per_word);
+       }
+       if (modalias_param[0]!='\0'){
+               pr_warning("%s: Found more modalias parameters of SPI devices than allowed (max: %d)!\n",
+                          DRVNAME, SPI_DEV_MAX);
+       }
+       return i;
+}
 
 /***************************************************************************
 
 /***************************************************************************
- *                   Parallel port probing routines                        *
+ *         Parallel port attaching and detaching  routines                 *
  ***************************************************************************/
 
 static LIST_HEAD(spi_parport_hosts);
 
  ***************************************************************************/
 
 static LIST_HEAD(spi_parport_hosts);
 
-
-static void spi_parport_attach(struct parport *p)
+/*
+** If attaching the driver to parport is done with exclusive access, 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 any drivers
+** first as long as you use exclusive access.
+*/
+static int __spi_parport_attach(struct parport *pb)
 {
 {
-       int                     status;
-       struct spi_parport      *pp;
-       struct spi_master       *master;
-       struct device           *dev;
+        struct spi_master *master; /* is only temporary need */
+       struct device *dev = pb->physport->dev;
+        struct spi_parport *pp;
+       int err;
 
 
-#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;
-#endif
-       /* REVISIT:  this just _assumes_ a spi_parport is there ... no probe,
-        * and no way to be selective about what it binds to.
-        */
+       if (dev == NULL) {
+               return -ENODEV;
+       }
 
 
+       /* spi_alloc_master gives zero initialized structure */
        master = spi_alloc_master(dev, sizeof *pp);
        master = spi_alloc_master(dev, sizeof *pp);
-       if (!master) {
-               status = -ENOMEM;
-               goto done;
+       if (master == NULL) {
+               return -ENOMEM;
        }
        }
+       master->bus_num = -1;       /* dynamic allocation of a bus number */
+       master->num_chipselect = SPI_DEV_MAX; /* setup maximum capability */
+
+       /* call the container_of fkt */
        pp = spi_master_get_devdata(master);
 
        pp = spi_master_get_devdata(master);
 
-       master->bus_num = -1;       /* dynamic alloc of a bus number */
-       master->num_chipselect = 1;
+       if (pp == NULL) {
+               err = -ENOMEM;
+               goto err_release_mem;
+       }
 
 
-       /*
-        * SPI and bitbang hookup
-         *
-        * use default setup(), cleanup(), and transfer() methods; and
-        * only bother implementing mode 0.  Start it later.
-        */
-       pp->bitbang.master = spi_master_get(master);
+        /* ----------------- SPI BitBang Hook ---------------- */
+       /* use default setup(), cleanup(), and transfer() methods */
+       pp->bitbang.master = spi_master_get(master); /* now pp->bitbang.master = master */
        pp->bitbang.chipselect = spi_parport_chipselect;
        pp->bitbang.txrx_word[SPI_MODE_0] = spi_parport_txrx_word_mode0;
        pp->bitbang.txrx_word[SPI_MODE_1] = spi_parport_txrx_word_mode1;
        pp->bitbang.txrx_word[SPI_MODE_2] = spi_parport_txrx_word_mode2;
        pp->bitbang.txrx_word[SPI_MODE_3] = spi_parport_txrx_word_mode3;
        pp->bitbang.chipselect = spi_parport_chipselect;
        pp->bitbang.txrx_word[SPI_MODE_0] = spi_parport_txrx_word_mode0;
        pp->bitbang.txrx_word[SPI_MODE_1] = spi_parport_txrx_word_mode1;
        pp->bitbang.txrx_word[SPI_MODE_2] = spi_parport_txrx_word_mode2;
        pp->bitbang.txrx_word[SPI_MODE_3] = spi_parport_txrx_word_mode3;
-       pp->bitbang.flags = spi_mode;  //############### get info from board info would be even better 
-       
-       /*
-        * parport hookup
-        */
-       pp->port = p;
-       //##### because we want to share with gpio_partport !!!!! 
-       pp->pdev = parport_register_device(p, DRVNAME,
-                       NULL, NULL, NULL,
-                                    0 /* FLAGS */, pp); //PARPORT_FLAG_EXCL, pp);
-       if (!pp->pdev) {
+       pp->bitbang.flags = SPI_CS_HIGH | SPI_3WIRE | SPI_LOOP; /* setup supported mode flags */
+
+        /* ----------------- ParPort Hook -------------------- */
+       /* 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
+       pp->dev = parport_register_device(pb, /* port */
+                                         DRVNAME, /* driver name */
+                                         NULL, /* preemption fkt */
+                                         NULL, /* wake-up fkt */
+                                         NULL, /* no interrupt handler used */
+                                          #ifdef CONFIG_SPI_PARPORT_EXCLUSIVE
+                                             PARPORT_FLAG_EXCL, /* flags */
+                                         #else
+                                             0, /* flags, 0 means used port shared */
+                                         #endif
+                                         pp /* handle */ );
+       if (pp->dev == NULL) {
                pr_err("%s: unable to register with parport\n", DRVNAME);
                pr_err("%s: unable to register with parport\n", DRVNAME);
-               status = -ENOMEM;
-               goto clean0;
+               err = -ENOMEM;
+               goto err_release_mem;
        }
 
        }
 
-       //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;
-
-       pp->lastbyte = 0;
-       parport_write_data(pp->port, pp->lastbyte);
-
-       /*
-        * Spi_Parport powerup, run firmware
-        */
-       pr_debug("%s: powerup Spi_Parport\n", DRVNAME);
-
-       //##### nCS setting depends on spi_mode ....
-       /* nCS for dataflash (this bit is inverted on output) */
-       parport_frob_control(pp->port, spi_cs_bit, 0);
-
-       /* stabilize power with spi_sck_bit clear (CPOL=0)
-        */
-       pp->lastbyte |= vcc_bits;
-       parport_write_data(pp->port, pp->lastbyte);
-       msleep(5);
-
-       /*
-        * Start SPI ... for now, hide that we're two physical busses.
-        */
-       status = spi_bitbang_start(&pp->bitbang);
-       if (status < 0) {
-               pr_warning("%s: spi_bitbang_start failed with status %d\n",
-                       DRVNAME, status);
-               goto clean2;
+       err = parport_claim(pp->dev);
+       if (err < 0) {
+               pr_err("%s: unable to claim parport\n", DRVNAME);
+               err = -ENODEV;
+               goto err_unregister_parport;
        }
 
        }
 
-       /*
-        * The modalias name MUST match the device_driver name
-        * for the bus glue code to match and subsequently bind them.
-        * We are binding to the generic drivers/hwmon/lm70.c device
-        * driver.
-        */
-       if (modalias[0]) {
-               pr_info("%s: Will load protocol driver: '%s'!\n",  DRVNAME, modalias);
-       } else goto clean2;
-
-       /* need to make this parameter loadable */
-       strcpy(pp->info.modalias, modalias);
-       pp->info.max_speed_hz = 15 * 1000 * 1000;
-       pp->info.chip_select = 0;                                   // 0: .. 1:
-       pp->info.mode = spi_mode;
-
-       /* Enable access to our primary data structure via
-        * the board info's (void *)controller_data.
-        */
-       pp->info.platform_data = NULL;                              // here we should add data structures for subsystem driver ???
-       pp->info.controller_data = pp;        /* save my structure for later use */
-       pp->spi_parport = spi_new_device(pp->bitbang.master, &pp->info);
-       if (pp->spi_parport)
-               pr_info("%s: SPI tty at %s\n", DRVNAME,
-                               pp->spi_parport->dev.bus_id);
-       else {
-               pr_warning("%s: spi_new_device failed\n", DRVNAME);
-               status = -ENODEV;
-               goto out_bitbang_stop;
+        /* ----------------- SPI ParPort init ---------------- */
+       /* cache initialisation just preventing changing unused bits */
+       pp->dcache = parport_read_data(pp->port);
+
+       if(irq >= 0) {
+                pp->irq = irq;
+               pr_info("%s: force usage IRQ %d\n", DRVNAME, pp->irq);
+               if(pp->port->irq == pp->irq) {
+                       /* we free the standard parport handler, because we want to use our own later */
+                       free_irq(pp->irq, pp->port);
+                       pr_info("%s: Note parport driver will claim later irq as already freed during its module unload\n",
+                               DRVNAME);
+               }
+       } else if(pp->port->irq != PARPORT_IRQ_NONE) {
+                pp->irq = pp->port->irq;
+               /* we free the standard parport handler, because we want to use our own later */
+               free_irq(pp->irq, pp->port);
+               pr_info("%s: will use IRQ %d exclusive\n", DRVNAME, pp->irq);
+               pr_info("%s: Note parport driver will claim later irq as already freed during its module unload\n", DRVNAME);
+       } else {
+               pp->irq = -ENXIO;
        }
        }
-       pp->spi_parport->bits_per_word=spi_bits;
-
-       /* get spi_bitbang_transfer either via global Symbol, or better 
-         * ask it from the driver structure
-         */
-       pp->msg = spi_message_alloc(1, GFP_KERNEL);
-       if (master) { /* the same address is also saved in pp->bitbang.master or pp->spi_parport->master */
-             struct spi_message *spi_msg;
-              spi_msg = pp->msg;
-             if (spi_msg) {
-                     // spi_message_init(spi_msg);
-                     pr_info("Alloc SPI message buffer\n");
-                     spi_msg->spi = pp->spi_parport;
-                     spi_msg->actual_length = 1;
-#if 0
-                     spi_msg->list_head ... Addresse der ersten SPI transfer struct
 
 
+       err = spi_bitbang_start(&pp->bitbang);
+       if (err < 0) {
+               pr_warning("%s: spi_bitbang_start failed with status %d\n",
+                       DRVNAME, err);
+               goto err_release_parport;
+       }
 
 
-                     spi_msg->tx_buf = ;
-                     spi_msg->len = 1;
-                     spi_msg->cs_change = 1;
-                     spi_msg->delay_usecs = 0;
-                     
-                     /* fill up message */
-                     master->transfer(pp->spi_parport, spi_msg);
-#endif
-             }
+        /* ----------------- SPI BoardInfo init -------------- */
+       err = spi_parport_register_spi_devices(pp);
+       if (err == 0) {
+               pr_err("%s: at least one protocol driver name is needed, non found!\n", DRVNAME);
+               err = -ENODEV;
+               goto err_spi_controller_stop;
+       } else if (err < 0) {
+               goto err_spi_controller_stop;
        }
        list_add_tail(&pp->list, &spi_parport_hosts);
        }
        list_add_tail(&pp->list, &spi_parport_hosts);
-       return;
+       return 0;
 
 
-out_bitbang_stop:
+ err_spi_controller_stop:
+       /* this will unregister child SPI devices too, if there any */
        spi_bitbang_stop(&pp->bitbang);
        spi_bitbang_stop(&pp->bitbang);
-clean2:
-       /* turn off VCC */
-       parport_write_data(pp->port, 0);
-
-       parport_release(pp->pdev);
-clean1:
-       parport_unregister_device(pp->pdev);
-clean0:
-       (void) spi_master_put(pp->bitbang.master);
-done:
-       pr_debug("%s: spi_parport probe, fail %d\n", DRVNAME, status);
+ err_release_parport:
+       parport_release(pp->dev);
+ err_unregister_parport:
+       parport_unregister_device(pp->dev);
+ err_release_mem:
+       spi_master_put(master); /* will release memory */
+       return err;
 }
 
 }
 
-static void spi_parport_detach(struct parport *p)
+
+static void spi_parport_attach(struct parport *pb)
 {
 {
-       struct spi_parport      *pp;
-       int                     status;
+        (void)__spi_parport_attach(pb);
+}
 
 
-       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;
-               }
+
+static int spi_parport_remove(struct spi_parport *pp)
+{
+        int err;
+
+       /* stop and unregisters child devices too */
+       err = spi_bitbang_stop(&pp->bitbang);
+       if (err < 0) {
+               pr_err("%s: bitbang stop error: %d\n", DRVNAME, err);
        }
        }
+       parport_release(pp->dev);
+        parport_unregister_device(pp->dev);
+       /* this frees also the pp struct */
+       spi_master_put(pp->bitbang.master);
+       return err;
 }
 
 }
 
-#if 0
 static void spi_parport_detach(struct parport *pb)
 {
 static void spi_parport_detach(struct parport *pb)
 {
-       spi_parport_struct *dev;
-       list_for_each_entry(dev, &spi_parport_hosts, list) {
-               if (dev->dev->port == pb) {
-                       list_del_init(&dev->list);
+       struct spi_parport *pp;
 
 
-                       spi_remove_host(dev->host);
-                       spi_host_put(dev->host);
-
-                       parport_release(dev->pdev);
-                       parport_unregister_device(dev->pdev);
-                       kfree(dev);
+       list_for_each_entry(pp, &spi_parport_hosts, list) {
+               if (pp->port == pb) {
+                       list_del_init(&pp->list);
+                       (void)spi_parport_remove(pp);
                        break;
                }
        }
 }
                        break;
                }
        }
 }
-#endif
 
 static struct parport_driver spi_parport_driver = {
        .name =         DRVNAME,
 
 static struct parport_driver spi_parport_driver = {
        .name =         DRVNAME,
@@ -484,16 +600,21 @@ static void __exit spi_parport_exit(void)
 }
 module_exit(spi_parport_exit);
 
 }
 module_exit(spi_parport_exit);
 
+MODULE_AUTHOR("Option Wireless");
 MODULE_DESCRIPTION("SPI master controller driver for Parport Adapter");
 MODULE_LICENSE("GPL");
 
 MODULE_DESCRIPTION("SPI master controller driver for Parport Adapter");
 MODULE_LICENSE("GPL");
 
-module_param_string(spi_pdrv, modalias, sizeof(modalias), S_IRUGO);
-MODULE_PARM_DESC(spi_pdrv, "spi protocol driver name");
+module_param(irq, uint, S_IRUGO);
+MODULE_PARM_DESC(irq, "Force IRQ for parport pin 10");
+
+module_param_string(pdrv, spi_modalias, sizeof(spi_modalias), S_IRUGO);
+MODULE_PARM_DESC(pdrv, "array of spi protocol driver names");
 
 
-module_param(spi_mode, uint, S_IRUGO);
-MODULE_PARM_DESC(spi_mode, "spi mode");
+module_param_array_named(mode, spi_mode, uint, &spi_mode_cnt, S_IRUGO);
+MODULE_PARM_DESC(mode, "array of spi modes for each driver");
 
 
-module_param(spi_bits, uint, S_IRUGO);
-MODULE_PARM_DESC(spi_bits, "spi bits per word");
+module_param_array_named(bits, spi_bits, uint, &spi_bits_cnt, S_IRUGO);
+MODULE_PARM_DESC(bits, "array of spi bits per word for each driver");
 
 
+MODULE_INFO(Flags, sCONFIG_SPI_PARPORT_EXCLUSIVE);
 MODULE_INFO(Version, DRIVER_VERSION);
 MODULE_INFO(Version, DRIVER_VERSION);