spi_parport
[pub/spi-gpio-pp.git] / spi_parport.c
index 02e26e1..00c6b51 100644 (file)
@@ -1,7 +1,8 @@
 /*
- * spi_butterfly.c - parport-to-butterfly adapter
+ * spi_parport.c - SPI master controller driver based on the parallel port adapter
  *
  * Copyright (C) 2005 David Brownell
+ *               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
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * idea:
- * separate the butterfly driver into a hotpluggable SPI master controller
- * use a board description file, which loads the:
- * - the SPI master controller
- * - the SPI protocoll driver
- * call spi_new_device with 
-   - spi_master = butterfly parport driver (or parameter configurable)
-   - spi_board_info = infos about Mauro-Lite
-
+ * 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
+ *
+ * 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.
  */
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/spi_bitbang.h>
-
-
-
-/*
- * This uses SPI to talk with an "AVR Butterfly", which is a $US20 card
- * with a battery powered AVR microcontroller and lots of goodies.  You
- * can use GCC to develop firmware for this.
- *
- * See Documentation/spi/butterfly for information about how to build
- * and use this custom parallel port cable.
- */
+//#include <linux/gpio.h>
 
 /*
  * spi_device
  * todo:
  * - implement SPI mode 1,2,3 is yet missing
- * - prevent ugly handling of global butterfly structure
- * - use serial subsystem
- * - make stuff module configurable during runtime
+ * - 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 ...
+ * - 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_butterfly"
+#define DRVNAME                "spi_parport"
 #define DRIVER_VERSION  "0.2"
 
-#define        butterfly_nreset (1 << 1)               /* pin 3 */
-
 #define        spi_sck_bit     (1 << 0)                /* pin 2 */
 #define        spi_mosi_bit    (1 << 7)                /* pin 9 */
 
 #define        spi_miso_bit    PARPORT_STATUS_BUSY     /* pin 11 */
 
 /* CONTROL output bits */
-#define        spi_cs_bit      PARPORT_CONTROL_SELECT  /* pin 17 */
+#define        spi_cs_bit      (1 << 1)                /* pin 3 */
 
 /*
  *   Parallel                
- *     Port      Direction   Signal
- *  -----------  ---------  ------------
- *      D0    2     -->       SCLK
- *      D1    3     -->      nRESET
- *      D2    4     -->        --
- *      D3    5     -->        --
- *      D4    6     -->        --
- *      D5    7     -->        --
- *      D6    8     -->        --
- *      D7    9     -->       MOSI
- *     GND   25      -        GND
- *    Busy   11     <--       MISO  
- *    Paper  12     <--        --
- *    Select 13     <--        --
- *    Feed   14     -->        --
- *    Error  15     <--        --
- *    Init   16     -->        --
- *    Select 17     -->      nCS
+ *     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)                 --
+ *      D4    6     -->      D4      (1 << 4)                 --
+ *      D5    7     -->      D5      (1 << 5)                 --
+ *      D6    8     -->      D6      (1 << 6)                 --
+ *      D7    9     -->      D7      (1 << 7)                MOSI
+ *   nAckn   10     <--      S6      PARPORT_STATUS_ACK
+ *    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     --
+ *   nSelIn  17     -->     nC3      PARPORT_CONTROL_SELECT   --  
+ *     GND   25     --       --                              GND
  */
 
 //*****************************************************************************
 // Globals
 //*****************************************************************************
 /* module parameters */
-static  char                   modalias[MODALIASLEN] = "maurol";
+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 butterfly *spidev_to_pp(struct spi_device *spi)
+static inline struct spi_parport *spidev_to_pp(struct spi_device *spi)
 {
        return spi->controller_data;
 }
 
 
-struct butterfly {
+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;
+
+
+struct spi_parport {
        /* REVISIT ... for now, this must be first */
        struct spi_bitbang      bitbang;
 
        struct parport          *port;
-       struct pardevice        *pd;
+       struct pardevice        *pdev;
 
-        u8                     lastbyte; /* hold copy of partport to be faster */
+        u8                     lastbyte; /* hold copy of parport to be faster */
 
         struct spi_message      *msg;
-       struct spi_device       *butterfly;
+       struct spi_device       *spi_parport;
        struct spi_board_info   info;
 
 };
 
+static LIST_HEAD(spi_parport);
+
+/* REVISIT remove this ugly global and its "only one" limitation */
+static struct spi_parport *spi_parport;
+
 /*----------------------------------------------------------------------*/
 
 
 static inline void
 setsck(struct spi_device *spi, int is_on)
 {
-       struct butterfly        *pp = spidev_to_pp(spi);
+       struct spi_parport      *pp = spidev_to_pp(spi);
        u8                      bit, byte = pp->lastbyte;
 
        bit = spi_sck_bit;
@@ -146,7 +159,7 @@ setsck(struct spi_device *spi, int is_on)
 static inline void
 setmosi(struct spi_device *spi, int is_on)
 {
-       struct butterfly        *pp = spidev_to_pp(spi);
+       struct spi_parport      *pp = spidev_to_pp(spi);
        u8                      bit, byte = pp->lastbyte;
 
        bit = spi_mosi_bit;
@@ -161,7 +174,7 @@ setmosi(struct spi_device *spi, int is_on)
 
 static inline int getmiso(struct spi_device *spi)
 {
-       struct butterfly        *pp = spidev_to_pp(spi);
+       struct spi_parport      *pp = spidev_to_pp(spi);
        int                     value;
        u8                      bit;
 
@@ -172,9 +185,9 @@ static inline int getmiso(struct spi_device *spi)
        return (bit == PARPORT_STATUS_BUSY) ? value : !value;
 }
 
-static void butterfly_chipselect(struct spi_device *spi, int value)
+static void spi_parport_chipselect(struct spi_device *spi, int value)
 {
-       struct butterfly        *pp = spidev_to_pp(spi);
+       struct spi_parport      *pp = spidev_to_pp(spi);
 
 #if 0
        /* set default clock polarity */
@@ -192,7 +205,7 @@ static void butterfly_chipselect(struct spi_device *spi, int value)
 #else
        u8                      bit, byte = pp->lastbyte;
 
-       bit = butterfly_nreset;
+       bit = spi_cs_bit;
 
        if (value)
                byte &= ~bit;
@@ -203,40 +216,60 @@ static void butterfly_chipselect(struct spi_device *spi, int value)
 #endif
 }
 
-
-/* we only needed to implement one mode here, and choose SPI_MODE_0 */
-
-#define        spidelay(X)     do{}while(0)
+// we do not wait, because parport is even slow, but if X > 10000, this would become important
 //#define      spidelay        ndelay
+#define        spidelay(X)     do{}while(0)
 
 #define        EXPAND_BITBANG_TXRX
 #include <linux/spi/spi_bitbang.h>
 
 static u32
-butterfly_txrx_word_mode0(struct spi_device *spi,
+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);
 }
 
-/*----------------------------------------------------------------------*/
+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);
+}
 
-/* REVISIT remove this ugly global and its "only one" limitation */
-static struct butterfly *butterfly;
+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);
+}
+
+static u32
+spi_parport_txrx_word_mode3(struct spi_device *spi,
+               unsigned nsecs,
+               u32 word, u8 bits)
+{
+       return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits);
+}
+
+
+
+/*----------------------------------------------------------------------*/
 
-static void butterfly_attach(struct parport *p)
+static void spi_parport_attach(struct parport *p)
 {
-       struct pardevice        *pd;
        int                     status;
-       struct butterfly        *pp;
+       struct spi_parport      *pp;
        struct spi_master       *master;
        struct device           *dev = p->physport->dev;
 
-       if (butterfly || !dev)
+       if (spi_parport || !dev)
                return;
 
-       /* REVISIT:  this just _assumes_ a butterfly is there ... no probe,
+       /* REVISIT:  this just _assumes_ a spi_parport is there ... no probe,
         * and no way to be selective about what it binds to.
         */
 
@@ -257,24 +290,28 @@ static void butterfly_attach(struct parport *p)
         * only bother implementing mode 0.  Start it later.
         */
        pp->bitbang.master = spi_master_get(master);
-       pp->bitbang.chipselect = butterfly_chipselect;
-       pp->bitbang.txrx_word[SPI_MODE_0] = butterfly_txrx_word_mode0;
-       pp->bitbang.flags = SPI_3WIRE;              // ####################################
+       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;
-       pd = parport_register_device(p, DRVNAME,
+       //##### 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 (!pd) {
+       if (!pp->pdev) {
+               pr_err("%s: unable to register with parport\n", DRVNAME);
                status = -ENOMEM;
                goto clean0;
        }
-       pp->pd = pd;
 
-       status = parport_claim(pd);
+       status = parport_claim(pp->pdev);
        if (status < 0)
                goto clean1;
 
@@ -282,31 +319,20 @@ static void butterfly_attach(struct parport *p)
        parport_write_data(pp->port, pp->lastbyte);
 
        /*
-        * Butterfly reset, powerup, run firmware
+        * Spi_Parport powerup, run firmware
         */
-       pr_debug("%s: powerup/reset Butterfly\n", DRVNAME);
+       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 chip in reset (nRESET), and
-        * spi_sck_bit clear (CPOL=0)
+       /* stabilize power with spi_sck_bit clear (CPOL=0)
         */
        pp->lastbyte |= vcc_bits;
        parport_write_data(pp->port, pp->lastbyte);
        msleep(5);
 
-#if 0
-       /* take it out of reset; assume long reset delay */
-       pp->lastbyte |= butterfly_nreset;
-       parport_write_data(pp->port, pp->lastbyte);
-       msleep(10);
-
-       /* take reset as trigger signal ############# */
-       pp->lastbyte &= ~butterfly_nreset;
-       parport_write_data(pp->port, pp->lastbyte);
-#endif
-
        /*
         * Start SPI ... for now, hide that we're two physical busses.
         */
@@ -331,35 +357,35 @@ static void butterfly_attach(struct parport *p)
        strcpy(pp->info.modalias, modalias);
        pp->info.max_speed_hz = 15 * 1000 * 1000;
        pp->info.chip_select = 0;                                   // 0: .. 1:
-       pp->info.mode = SPI_3WIRE | SPI_MODE_0;                     // ################
+       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->butterfly = spi_new_device(pp->bitbang.master, &pp->info);
-       if (pp->butterfly)
+       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->butterfly->dev.bus_id);
+                               pp->spi_parport->dev.bus_id);
        else {
                pr_warning("%s: spi_new_device failed\n", DRVNAME);
                status = -ENODEV;
                goto out_bitbang_stop;
        }
-       //        pp->butterfly->bits_per_word=16;                             // ###############
+       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->butterfly->master */
+       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->butterfly;
+                     spi_msg->spi = pp->spi_parport;
                      spi_msg->actual_length = 1;
 #if 0
                      spi_msg->list_head ... Addresse der ersten SPI transfer struct
@@ -371,11 +397,11 @@ static void butterfly_attach(struct parport *p)
                      spi_msg->delay_usecs = 0;
                      
                      /* fill up message */
-                     master->transfer(pp->butterfly, spi_msg);
+                     master->transfer(pp->spi_parport, spi_msg);
 #endif
              }
        }
-       butterfly = pp;
+       spi_parport = pp;
        return;
 
 out_bitbang_stop:
@@ -384,27 +410,27 @@ clean2:
        /* turn off VCC */
        parport_write_data(pp->port, 0);
 
-       parport_release(pp->pd);
+       parport_release(pp->pdev);
 clean1:
-       parport_unregister_device(pd);
+       parport_unregister_device(pp->pdev);
 clean0:
        (void) spi_master_put(pp->bitbang.master);
 done:
-       pr_debug("%s: butterfly probe, fail %d\n", DRVNAME, status);
+       pr_debug("%s: spi_parport probe, fail %d\n", DRVNAME, status);
 }
 
-static void butterfly_detach(struct parport *p)
+static void spi_parport_detach(struct parport *p)
 {
-       struct butterfly        *pp;
+       struct spi_parport      *pp;
        int                     status;
 
        /* FIXME this global is ugly ... but, how to quickly get from
-        * the parport to the "struct butterfly" associated with it?
+        * the parport to the "struct spi_parport" associated with it?
         * "old school" driver-internal device lists?
         */
-       if (!butterfly || butterfly->port != p)
+       if (!spi_parport || spi_parport->port != p)
                return;
-       pp = butterfly;
+       pp = spi_parport;
        if (pp->msg) {
                spi_message_free(pp->msg);
                pr_info("Dealloc SPI message buffer\n");
@@ -417,37 +443,59 @@ static void butterfly_detach(struct parport *p)
        parport_write_data(pp->port, 0);
        msleep(10);
 
-       parport_release(pp->pd);
-       parport_unregister_device(pp->pd);
+       parport_release(pp->pdev);
+       parport_unregister_device(pp->pdev);
 
        (void) spi_master_put(pp->bitbang.master);
 
-       butterfly = NULL;
+       spi_parport = NULL;
 }
 
-static struct parport_driver butterfly_driver = {
+static void spi_parport_detach(struct parport *pb)
+{
+       ppa_struct *dev;
+       list_for_each_entry(dev, &ppa_hosts, 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);
+                       kfree(dev);
+                       break;
+               }
+       }
+}
+
+
+static struct parport_driver spi_parport_driver = {
        .name =         DRVNAME,
-       .attach =       butterfly_attach,
-       .detach =       butterfly_detach,
+       .attach =       spi_parport_attach,
+       .detach =       spi_parport_detach,
 };
 
 
-static int __init butterfly_init(void)
+static int __init spi_parport_init(void)
 {
-       return parport_register_driver(&butterfly_driver);
+       return parport_register_driver(&spi_parport_driver);
 }
-device_initcall(butterfly_init);
+device_initcall(spi_parport_init);
 
-static void __exit butterfly_exit(void)
+static void __exit spi_parport_exit(void)
 {
-       parport_unregister_driver(&butterfly_driver);
+       parport_unregister_driver(&spi_parport_driver);
 }
-module_exit(butterfly_exit);
+module_exit(spi_parport_exit);
 
-MODULE_DESCRIPTION("Parport Adapter driver for SPI tty Butterfly");
+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(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 bits per word");
+
 MODULE_INFO(Version, DRIVER_VERSION);