From: Peter Henn Date: Thu, 1 Jan 2009 10:49:37 +0000 (+0100) Subject: spi_parport X-Git-Tag: v0.2~3 X-Git-Url: http://git.linex4red.de/pub/spi-gpio-pp.git/commitdiff_plain/e539cfa0af8f19cb21fca37079e4805935814ba5 spi_parport Start using list struct instead of ugly global struct. Add missing SPI mode functions. Note: The driver is just compiled and not tested! --- diff --git a/spi_parport.c b/spi_parport.c index 02e26e1..00c6b51 100644 --- a/spi_parport.c +++ b/spi_parport.c @@ -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 @@ -17,15 +18,12 @@ * 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 #include @@ -36,34 +34,28 @@ #include #include #include - - - -/* - * 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 /* * 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 */ @@ -73,64 +65,85 @@ #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 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);