/*
- * 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;
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;
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;
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 */
#else
u8 bit, byte = pp->lastbyte;
- bit = butterfly_nreset;
+ bit = spi_cs_bit;
if (value)
byte &= ~bit;
#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.
*/
* 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;
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.
*/
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
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:
/* 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");
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);