X-Git-Url: http://git.linex4red.de/pub/spi-gpio-pp.git/blobdiff_plain/64f1eafb27e338f0c16677692df232c84866cc2a..refs/heads/master:/spi_parport.c?ds=sidebyside diff --git a/spi_parport.c b/spi_parport.c index 82c71b2..f88d635 100644 --- a/spi_parport.c +++ b/spi_parport.c @@ -55,26 +55,10 @@ #endif /* ToDo - * 0. Setup of the parport mode must be done. Therefore we read the + * 1. 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 @@ -95,8 +79,8 @@ * 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) -- + * D4 6 --> D4 (1 << 4) nCS2 + * D5 7 --> D5 (1 << 5) nCS3 * D6 8 --> D6 (1 << 6) -- * D7 9 --> D7 (1 << 7) -- * nAckn 10 <-- S6 PARPORT_STATUS_ACK @@ -121,7 +105,7 @@ // MODULE PARAMETERS //***************************************************************************** #define DRVNAME "spi_parport" /* name of the driver */ -#define DRIVER_VERSION "0.2" /* helps identifing different versions of that driver */ +#define DRIVER_VERSION "0.3" /* helps identifying 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 */ @@ -136,20 +120,21 @@ #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; +static int spi_irq[SPI_DEV_MAX] = {-1, -1, -1, -1}; +static int spi_irq_cnt = SPI_DEV_MAX; struct spi_parport { 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 */ }; @@ -161,9 +146,9 @@ static inline struct spi_parport *spidev_to_pp(struct spi_device *spi) /*----------------------------------------------------------------------*/ /* spi_parport to spi_bitbang interface functions */ -/* -** parport data bit output function -*/ +/** + * parport data bit output function + */ static inline void spi_parport_data_set(struct spi_parport *pp, unsigned mask, int value) { u8 byte = pp->dcache; // use old value from cache @@ -175,18 +160,18 @@ static inline void spi_parport_data_set(struct spi_parport *pp, unsigned mask, i return parport_write_data(pp->port, byte); } -/* -** set clock signal -*/ +/** + * set clock signal + */ static inline void setsck(struct spi_device *spi, int value) { struct spi_parport *pp = spidev_to_pp(spi); return spi_parport_data_set(pp, SPI_PARPORT_SCLK, value); } -/* -** set MOSI signal -*/ +/** + * set MOSI signal + */ static inline void setmosi(struct spi_device *spi, int value) { struct spi_parport *pp = spidev_to_pp(spi); @@ -194,30 +179,30 @@ static inline void setmosi(struct spi_device *spi, int value) } /*----------------------------------------------------------------------*/ -/* -** parport status bit input function -*/ +/** + * parport status bit input function + */ static inline int spi_parport_status_get(struct spi_parport *pp, unsigned mask) { u8 byte = SPI_PARPORT_STATUS_INV ^ parport_read_status(pp->port); return ((byte & mask) != 0); } -/* -** 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. -*/ +/** + * parport data bit input function, only need for loop back mode + * Note: Neither the dcache is here updated nor the value is read from that dcache + * to ensure similar system timing behavior 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); } -/* -** get MISO signal -** supports also loop back mode in master controller -*/ +/** + * get MISO signal + * supports also loop back mode in master controller + */ static inline int getmiso(struct spi_device *spi) { int value; @@ -233,10 +218,10 @@ static inline int getmiso(struct spi_device *spi) return value; } -/* -** set chipselects -** sets corresponding CSx and CLK, depending on its polarity and mode -*/ +/** + * set chipselects + * sets corresponding CSx and CLK, depending on its polarity and mode + */ static void spi_parport_chipselect(struct spi_device *spi, int value) { struct spi_parport *pp = spidev_to_pp(spi); @@ -296,12 +281,59 @@ static u32 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. -*/ +#define param_check_irq(name, p) /* define own irq type */ +#define PARAM_IRQ_VAL_NONE -1 /* value, if 'none' irq is used */ +#define PARAM_IRQ_VAL_AUTO -2 /* value, if 'auto' irq is used */ +#define PARAM_IRQ_NAME_NONE "none" /* string, if no irq is used */ +#define PARAM_IRQ_NAME_AUTO "auto" /* string, if irq is automatically determined by the parport irq line */ + +/** + * special irq parameter type + * The irq type allows setting of irq numbers in the range of all positive integer values + * and additionally the values "none" and "auto". These extra values will be converted into + * corresponding negative values -1 and -2. As exception is also the value -1 allowed, which + * will be seen as "none". + */ +int param_set_irq(const char *pbuf, struct kernel_param *kp) +{ + int number; + + if (0 == strncmp(pbuf, PARAM_IRQ_NAME_NONE, sizeof(PARAM_IRQ_NAME_NONE))) { + number = PARAM_IRQ_VAL_NONE; + } else if (0 == strncmp(pbuf, PARAM_IRQ_NAME_AUTO, sizeof(PARAM_IRQ_NAME_AUTO))) { + number = PARAM_IRQ_VAL_AUTO; + } else { + char *ep; + number = simple_strtol(pbuf, &ep, 0); + if (ep != pbuf) { + if ((0 > number) && (PARAM_IRQ_VAL_NONE != number)) + return -EINVAL; + } else + return -EINVAL; + } + *(int *)kp->arg = number; + return 0; +} + +int param_get_irq(char *pbuf, struct kernel_param *kp) +{ + int value = *(int *)kp->arg; + if (PARAM_IRQ_VAL_NONE == value) + return sprintf((char *)pbuf, "%s", PARAM_IRQ_NAME_NONE); + else if (PARAM_IRQ_VAL_AUTO == value) + return sprintf((char *)pbuf, "%s", PARAM_IRQ_NAME_AUTO); + else if (0 <= value) + return sprintf((char *)pbuf, "%d", value); + else + return -EINVAL; +} + +/** + * 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; @@ -321,94 +353,119 @@ const char *strnarr(char *dst, const char *src, const char *dlim, size_t count) } -#if 0 -void parse_irqs(int nports, const char *irqstr, int irqval[]) -{ - unsigned int i; - for (i=0; iirq; - 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); - } + for (i=0; (modalias_param[0]!='\0') && (ibitbang.master); /* kzalloc memory and add device to master controller */ + if (NULL == spi[i]) { + while (i > 0) { + spi_dev_put(spi[--i]); /* free all spi devices from former spi_alloc_device */ + } + return -ENOMEM; + } + + /* + * first copy the modalias parameter + */ + modalias_param=strnarr(spi[i]->modalias, modalias_param, " ,", sizeof(spi[i]->modalias)); + if (NULL == spi[i]->modalias) { + pr_err("%s: %d given modalias name is too long! See only that '%s'!\n", + DRVNAME, i, spi[i]->modalias); + while (i >= 0) { + spi_dev_put(spi[i--]); /* free all spi devices */ + } + return -EINVAL; + } + + /* + * now setup the rest as it would be done by + * the spi_new_device from a given board_info + * structure. + */ + spi[i]->max_speed_hz = SPI_PARPORT_MAX_SPEED; + spi[i]->chip_select = i; + spi[i]->master = pp->bitbang.master; + spi[i]->controller_data = pp; /* enable access to primary controller data */ + spi[i]->controller_state = NULL; /* init state like in new_spi_device */ + spi[i]->mode = spi_mode[i]; + spi[i]->irq = spi_irq[i]; + spi[i]->bits_per_word = spi_bits[i]; /* must normally be done by protocol driver */ + spi[i]->dev.platform_data = (void *)NULL; /* data area of SPI protocol driver, + hot plug drivers must do that itself */ + /* + * first only initialize the chipselect + * for that spi device to set it inactive + */ + spi_parport_chipselect(spi[i], BITBANG_CS_INACTIVE); + if (spi_mode_cnt < (i+1)) { + pr_warning("%s: No spi mode parameter given for device %d with driver '%s'. " + "Will use default setting: %d\n", + DRVNAME, i, spi[i]->modalias, spi[i]->mode); + } + } + /* additional parameter checks and possibly logged warnings */ if (modalias_param[0]!='\0'){ - pr_warning("%s: Found more modalias parameters of SPI devices than allowed (max: %d)!\n", - DRVNAME, SPI_DEV_MAX); + pr_warning("%s: Found more modalias parameters of SPI devices than allowed (max: %d)!\n", + DRVNAME, SPI_DEV_MAX); + } + if (spi_mode_cnt > i) { + pr_warning("%s: Found further %d mode parameters than SPI devices used or no SPI mode defined!\n", + DRVNAME, spi_mode_cnt-i); } - return i; + /* + * Now all spi device structures are setup and all used + * chipselects are initialized. We will now add the + * spi devices for probing and usage. We use the same + * ordering! + */ + max = i; /* remember the last value */ + for (i=0; iirq) { + pr_info("%s: SPI device successfully registered at bus %s as device %s with mode 0x%x," + " %d bits/word, irq %d\n", DRVNAME, spi[i]->dev.bus_id, spi[i]->modalias, + spi[i]->mode, spi[i]->bits_per_word, spi[i]->irq); + } else { + pr_info("%s: SPI device successfully registered at bus %s as device %s with mode 0x%x," + " %d bits/word, no irq\n", DRVNAME, spi[i]->dev.bus_id, spi[i]->modalias, + spi[i]->mode, spi[i]->bits_per_word); + } + } + return i; } /*************************************************************************** @@ -417,18 +474,19 @@ static int spi_parport_register_spi_devices(struct spi_parport *pp) static LIST_HEAD(spi_parport_hosts); -/* -** 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. -*/ +/** + * 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) { struct spi_master *master; /* is only temporary need */ struct device *dev = pb->physport->dev; struct spi_parport *pp; int err; + int i, use_irq; if (dev == NULL) { return -ENODEV; @@ -492,26 +550,34 @@ static int __spi_parport_attach(struct parport *pb) } /* ----------------- SPI ParPort init ---------------- */ - /* cache initialisation just preventing changing unused bits */ + /* cache initialization 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; + /* + * loop over all irq module parameter, which must be exchanged by + * the parport irq, if value requires PARAM_IRQ_VAL_AUTO + */ + for (use_irq = 0, i=0; i < spi_irq_cnt; i++) { + if (PARAM_IRQ_VAL_AUTO == spi_irq[i]) { + if (PARPORT_IRQ_NONE != pp->port->irq) { + spi_irq[i] = pp->port->irq; /* use parport irq */ + use_irq = 1; + } else { + pr_warning("%s: Can not auto resolve IRQ, because parport has no valid IRQ\n", + DRVNAME); + break; /* further replacement makes no sense */ + } + } else if (PARAM_IRQ_VAL_NONE != spi_irq[i]) { + if ((PARPORT_IRQ_NONE != pp->port->irq) && (spi_irq[i] == pp->port->irq)) + use_irq = 1; + } + } + if (use_irq) { + /* we free the standard parport handler, because we want to use our own later */ + free_irq(pp->port->irq, pp->port); + pr_info("%s: will use IRQ %d exclusive\n", DRVNAME, pp->port->irq); + pr_info("%s: Note parport driver will claim later irq as already freed during its module unload\n", + DRVNAME); } err = spi_bitbang_start(&pp->bitbang); @@ -604,8 +670,8 @@ MODULE_AUTHOR("Option Wireless"); MODULE_DESCRIPTION("SPI master controller driver for Parport Adapter"); MODULE_LICENSE("GPL"); -module_param(irq, uint, S_IRUGO); -MODULE_PARM_DESC(irq, "Force IRQ for parport pin 10"); +module_param_array_named(irq, spi_irq, irq, &spi_irq_cnt, S_IRUGO); +MODULE_PARM_DESC(irq, "array of irq numbers for each driver"); module_param_string(pdrv, spi_modalias, sizeof(spi_modalias), S_IRUGO); MODULE_PARM_DESC(pdrv, "array of spi protocol driver names");