From: Peter Henn Date: Thu, 1 Jan 2009 10:59:10 +0000 (+0100) Subject: Add gpio_parport and gpio_test X-Git-Tag: v0.2~2 X-Git-Url: http://git.linex4red.de/pub/spi-gpio-pp.git/commitdiff_plain/813458776ee097275c377f19853773b39dab193a Add gpio_parport and gpio_test Correct some compile errors in spi_parport. Note: spi_parport just compiles and is not yet tested functional. Getting more familiar with GPIO handling using the GPIO lib of kernel with the GPIO controller driver and gpio_test driver. Both drivers gpio_parport and spi_parport should work later close together or should share the same parport device. --- diff --git a/Makefile b/Makefile index f490819..42afee9 100644 --- a/Makefile +++ b/Makefile @@ -8,17 +8,22 @@ # If CONFIG_SPI_PARPORT isn't set, we'll assume the user has never configured # their kernel to include this module and set up some defaults. # -# NOTE: If you have previously added the SPI_TTY project to your kernel -# and configured it for inclusion, these settings will be -# overridden by your kernel configuration. +ifndef CONFIG_GPIO_PARPORT +EXTERNAL_BUILD=y +CONFIG_GPIO_PARPORT=m +# CONFIG_GPIO_PARPORT_MUTEX is not set +# CONFIG_GPIO_PARPORT_EXCLUSIVE is not set +CONFIG_GPIO_PARPORT_NOCLAIME=y +endif + ifndef CONFIG_SPI_PARPORT EXTERNAL_BUILD=y CONFIG_SPI_PARPORT=m -CONFIG_SPI_PARPORT_DEBUG=n +# CONFIG_SPI_PARPORT_EXCLUSIVE is not set endif -ifeq ($(CONFIG_SPI_PARPORT_DEBUG),y) +ifeq ($(CONFIG_SPI_HSO_DEBUG),y) EXTRA_CFLAGS += -g -Wa,-adhlms=$@.lst endif @@ -28,7 +33,9 @@ endif list-m := +list-$(CONFIG_GPIO_PARPORT) += gpio_parport gpio_test list-$(CONFIG_SPI_PARPORT) += spi_parport +obj-$(CONFIG_GPIO_PARPORT) += gpio_parport.o gpio_test.o obj-$(CONFIG_SPI_PARPORT) += spi_parport.o # @@ -40,8 +47,22 @@ ifneq ($(KERNELRELEASE),) EXTRA_CFLAGS += -DCONFIG_PM ifeq ($(EXTERNAL_BUILD),y) -ifeq ($(CONFIG_SPI_PARPORT_DEBUG),y) -EXTRA_CFLAGS += -DCONFIG_SPI_PARPORT_DEBUG +ifeq ($(CONFIG_GPIO_PARPORT_MUTEX),y) + EXTRA_CFLAGS += -DCONFIG_GPIO_PARPORT_MUTEX +endif +ifeq ($(CONFIG_GPIO_PARPORT_EXCLUSIVE),y) + EXTRA_CFLAGS += -DCONFIG_GPIO_PARPORT_EXCLUSIVE +endif +ifeq ($(CONFIG_GPIO_PARPORT_NOCLAIME),y) + EXTRA_CFLAGS += -DCONFIG_GPIO_PARPORT_NOCLAIME +endif + +ifeq ($(CONFIG_SPI_PARPORT_EXCLUSIVE),y) + EXTRA_CFLAGS += -DCONFIG_SPI_PARPORT_EXCLUSIVE +endif + +ifeq ($(CONFIG_SPI_HSO_DEBUG),y) + EXTRA_CFLAGS += -DCONFIG_SPI_HSO_DEBUG endif endif @@ -105,7 +126,7 @@ else # We've got a kernel with seperate output, copy the config, and use O= endif install: modules - modprobe -r spi_parport || true + for mod; $(list-m); do modprobe -r $mod || true; done install -d $(INSTALLDIR) install -m 644 -c $(addsuffix .ko,$(list-m)) $(INSTALLDIR) /sbin/depmod -a ${KVER} diff --git a/gpio_parport.c b/gpio_parport.c new file mode 100644 index 0000000..73a16b1 --- /dev/null +++ b/gpio_parport.c @@ -0,0 +1,512 @@ +/* + * gpio_parport.c - GPIO master controller driver based on the parallel port adapter + * + * Copyright (C) 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * The gpio_parport driver uses the gpio framwork started with kernel 2.6.25. + * It is designed as a PC reference GPIO driver just for testing on Intel based + * PC architectures. Therefore the whole functionality of a parport used as + * GPIO was never in focus. We initial need only one dedicated output and one + * dedicated input, but with interrupt support. + * + * The driver uses caches, which saves the output status, because the IO output + * need long time on x86 architecture. The caches will be setup with the initial + * values read from those ports during driver startup. If you are using the parport + * in shared operation mode, the caches has to be updated too. + */ +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_GPIO_PARPORT_MUTEX +#include +#define sCONFIG_GPIO_PARPORT_MUTEX "CONFIG_GPIO_PARPORT_MUTEX" +#else +#include +#define sCONFIG_GPIO_PARPORT_MUTEX "" +#endif + + +/* 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 + * - Add sysfs Debug functionality + * - Support PM functions like resume, suspend + * + * Parallel + * Port Direction Register Name Signal + * ----------- --------- -------- ---------------------- ------ + * nStrobe 1 --> nC0 PARPORT_CONTROL_STROBE nGPO08 + * D0 2 --> D0 (1 << 0) GPO00 + * D1 3 --> D1 (1 << 1) GPO01 + * D2 4 --> D2 (1 << 2) GPO02 + * D3 5 --> D3 (1 << 3) GPO03 + * D4 6 --> D4 (1 << 4) GPO04 + * D5 7 --> D5 (1 << 5) GPO05 + * 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 + * Paper 12 <-- S5 PARPORT_STATUS_PAPEROUT GPI14 + * Sel 13 <-- S4 PARPORT_STATUS_SELECT GPI13 + * nFeed 14 --> nC1 PARPORT_CONTROL_AUTOFD nGPO09 + * nError 15 <-- S3 PARPORT_STATUS_ERROR GPI12 + * nInit 16 --> C2 PARPORT_CONTROL_INIT GPO10 + * nSelIn 17 --> nC3 PARPORT_CONTROL_SELECT nGPO11 + * GND 25 -- -- GND + * + * Signal: + * - GPOx : general purpose output only + * - GPIx : general purpose input only + * - GPIx* : interrupt in + * - nGPXx : signal is inverted + */ + +//***************************************************************************** +// MODULE PARAMETERS +//***************************************************************************** +#define DRVNAME "gpio_parport" /* name of the driver */ +#define DRIVER_VERSION "0.2" /* helps identifing different versions of that driver */ + +#define GPIO_PARPORT_CTRL_PORT_BITS 8 /* starting with bit 8 use the parport control port */ +#define GPIO_PARPORT_STAT_PORT_BITS 12 /* starting with bit 12 use the parport status port */ +#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 */ + + +static int modparam_gpiobase = -1 /* dynamic assingment */; + +typedef struct { + struct pardevice *dev; /* parport device entry */ + struct parport *port; /* parport port entry */ + u8 dcache; /* parport data port mirror bits */ + u8 ccache; /* parport control port mirror bits */ + struct gpio_chip gpio; /* parport GPIO seen as a chip */ + int irq; /* parport interrupt */ + #ifdef CONFIG_GPIO_PARPORT_MUTEX + struct mutex lock; /* prevent double accessing bits */ + #else + spinlock_t lock; /* prevent double accessing bits */ + #endif + struct list_head list; /* gpio_parport host controller list */ +} gpio_pp_struct; + + +/*----------------------------------------------------------------------*/ +/* +** use the lock and unlock macros to get rid of the bad #ifdef +** stuff in the rest of the code. +*/ +#ifdef CONFIG_GPIO_PARPORT_MUTEX +#define gpio_parport_lock(lock, flags) \ + do { \ + mutex_lock(lock); \ + } while (0) +#else +#define gpio_parport_lock(lock, flags) \ + do { \ + spin_lock_irqsave(lock, flags); \ + } while (0) +#endif + +#ifdef CONFIG_GPIO_PARPORT_MUTEX +#define gpio_parport_unlock(lock, flags) \ + do { \ + mutex_unlock(lock); \ + } while (0) +#else +#define gpio_parport_unlock(lock, flags) \ + do { \ + spin_unlock_irqrestore(lock, flags); \ + } while (0) +#endif + + +/*----------------------------------------------------------------------*/ +/* +** parport data bit input function +*/ +static inline int gpio_parport_data_get(gpio_pp_struct *pp, unsigned mask) +{ + // good idea to update also the cache + pp->dcache = parport_read_data(pp->port); + return ((pp->dcache & mask) != 0); +} + +/* +** parport control bit input function +*/ +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); + return ((pp->ccache & mask) != 0); +} + +/* +** parport status bit input function +*/ +static inline int gpio_parport_status_get(gpio_pp_struct *pp, unsigned mask) +{ + unsigned int cache = parport_read_status(pp->port); + return ((cache & mask) != 0); +} + + +/* +** general parport read bit function +*/ +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 */ + unsigned long flags; + + if (offset < GPIO_PARPORT_CTRL_PORT_BITS) { + u8 mask = 1 << offset; + gpio_parport_lock(&pp->lock, flags); + status = gpio_parport_data_get(pp, mask); + gpio_parport_unlock(&pp->lock, flags); + } else if (offset < GPIO_PARPORT_STAT_PORT_BITS) { + u8 mask = 1 << (offset - GPIO_PARPORT_CTRL_PORT_BITS); + gpio_parport_lock(&pp->lock, flags); + status = gpio_parport_ctrl_get(pp, mask); + gpio_parport_unlock(&pp->lock, flags); + } else if (offset < GPIO_PARPORT_NR_GPIOS) { + u8 mask = 1 << (offset - GPIO_PARPORT_STAT_PORT_BITS + GPIO_PARPORT_STAT_PORT_OFF); + gpio_parport_lock(&pp->lock, flags); + status = gpio_parport_status_get(pp, mask); + gpio_parport_unlock(&pp->lock, flags); + } + return status; +} + + +/*----------------------------------------------------------------------*/ +/* +** parport data bit output function +*/ +static inline void gpio_parport_data_set(gpio_pp_struct *pp, unsigned mask, int value) +{ + u8 byte = pp->dcache; // use old value from cache + if (value) + byte |= mask; + else + byte &= ~mask; + pp->dcache = byte; // restore cache + return parport_write_data(pp->port, byte); +} + +/* +** parport control bit output function +** most PARPORT_CONTROL_* bits are negated, but they are currently _not_ corrected +** to keep it as simple as possible +*/ +static inline void gpio_parport_ctrl_set(gpio_pp_struct *pp, unsigned mask, int value) +{ + u8 byte = pp->ccache; // use old value from cache + if (value) + byte |= mask; + else + byte &= ~mask; + pp->ccache = byte; // restore cache + return parport_write_control(pp->port, byte); +} + +/* +** general parport set bit output function +*/ +static void gpio_parport_set(struct gpio_chip *gpio, unsigned offset, int value) +{ + gpio_pp_struct *pp = container_of(gpio, gpio_pp_struct, gpio); + unsigned long flags; + + if (offset < GPIO_PARPORT_CTRL_PORT_BITS) { + u8 mask = 1 << offset; + gpio_parport_lock(&pp->lock, flags); + gpio_parport_data_set(pp, mask, value); + gpio_parport_unlock(&pp->lock, flags); + } else if (offset < GPIO_PARPORT_STAT_PORT_BITS) { + u8 mask = 1 << (offset - GPIO_PARPORT_CTRL_PORT_BITS); + gpio_parport_lock(&pp->lock, flags); + gpio_parport_ctrl_set(pp, mask, value); + gpio_parport_unlock(&pp->lock, flags); + } +} + +/*----------------------------------------------------------------------*/ +/* +** general parport direction input funtion +** we support only the parport control bits as dedicated input bits +*/ +static int gpio_parport_direction_input(struct gpio_chip * gpio, unsigned offset) +{ + if (offset < GPIO_PARPORT_STAT_PORT_BITS) { + return -ENOSYS; + } else if (offset < GPIO_PARPORT_NR_GPIOS) { + gpio_pp_struct *pp = container_of(gpio, gpio_pp_struct, gpio); + unsigned long flags; + gpio_parport_lock(&pp->lock, flags); + if ((GPIO_PARPORT_INTERRUPT == offset) && (pp->irq >= 0)) { + parport_enable_irq(pp->port); + } + gpio_parport_unlock(&pp->lock, flags); + return 0; + } + return -ENODEV; +} + +/* +** general parport direction output function +** we support only data bits and control bits as dedicated output bits +*/ +static int gpio_parport_direction_output(struct gpio_chip * gpio, unsigned offset, int value) +{ + gpio_pp_struct *pp = container_of(gpio, gpio_pp_struct, gpio); + unsigned long flags; + + if (offset < GPIO_PARPORT_CTRL_PORT_BITS) { + u8 mask = 1 << offset; + gpio_parport_lock(&pp->lock, flags); + gpio_parport_data_set(pp, mask, value); + gpio_parport_unlock(&pp->lock, flags); + return 0; + } else if (offset < GPIO_PARPORT_STAT_PORT_BITS) { + u8 mask = 1 << (offset - GPIO_PARPORT_CTRL_PORT_BITS); + gpio_parport_lock(&pp->lock, flags); + gpio_parport_ctrl_set(pp, mask, value); + gpio_parport_unlock(&pp->lock, flags); + return 0; + } else if (offset < GPIO_PARPORT_NR_GPIOS) { + return -ENOSYS; + } + return -ENODEV; +} + +/*----------------------------------------------------------------------*/ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) +/* +** parport interrupt support only for acknoledge on GPI15 interrupt +*/ +static int gpio_parport_gpio_to_irq(struct gpio_chip * gpio, unsigned offset) +{ + gpio_pp_struct *pp = container_of(gpio, gpio_pp_struct, gpio); + + if (GPIO_PARPORT_INTERRUPT == offset) { + return pp->irq; + } else { + return -ENOSYS; + } +} +#endif + +/*----------------------------------------------------------------------*/ +/* +** gpio_parport setup function +** add all entries into the GPIO framework +*/ +static void gpio_parport_gpio_setup(gpio_pp_struct *pp) +{ + struct gpio_chip *c = &pp->gpio; + c->label = (char *)pp->port->name; + c->owner = THIS_MODULE; + c->direction_input = gpio_parport_direction_input; + c->get = gpio_parport_get; + c->direction_output = gpio_parport_direction_output; + c->set = gpio_parport_set; + c->dbg_show = NULL; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)) + c->request = NULL; + c->free = NULL; + c->to_irq = gpio_parport_gpio_to_irq; +#endif + c->base = modparam_gpiobase; + c->ngpio = GPIO_PARPORT_NR_GPIOS; + #ifdef CONFIG_GPIO_PARPORT_MUTEX + c->can_sleep = 1; + c->dev = pp->dev; + #else + c->can_sleep = 0; + #endif +} + +/*************************************************************************** + * Parallel port attaching and detaching routines * + ***************************************************************************/ + +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. +*/ +static int __gpio_parport_attach(struct parport *pb) +{ + gpio_pp_struct *pp; + int err; + + /* we need a zero initialized structure here */ + pp = kzalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) + return -ENOMEM; + + #ifdef CONFIG_GPIO_PARPORT_MUTEX + mutex_init(&pp->lock); + #else + spin_lock_init(&pp->lock); + #endif + + /* ----------------- ParPort Hooks ------------------- */ + /* 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 + /* interrupt handler will not be here registered. + But an interrupt handler can be later registered to IRQ pin */ + pp->dev = parport_register_device(pb, /* port */ + DRVNAME, /* driver name */ + NULL, /* preemption fkt */ + NULL, /* wake-up fkt */ + NULL, /* interrupt handler */ + PARPORT_FLAG_EXCL, /* flags */ + pp /* handle */ ); + if (pp->dev == NULL) { + pr_err("%s: unable to register with parport\n", DRVNAME); + err = -ENOMEM; + goto err_release_mem; + } + + err = parport_claim(pp->dev); + if (err < 0) { + pr_err("%s: unable to claim parport\n", DRVNAME); + err = -ENODEV; + goto err_unregister_parport; + } + + /* ------------- 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; + /* 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); + } else { + pp->irq = -ENXIO; + } + + /* ----------------- GPIO init ------------------- */ + gpio_parport_gpio_setup(pp); + err = gpiochip_add(&pp->gpio); + if (err != 0) { + pr_err("%s: Failed to register GPIOs\n", DRVNAME); + goto err_release_parport; + } + + pr_info("%s: Abusing Parport for GPIO %d to %d, labled as %s\n", + DRVNAME, pp->gpio.base, pp->gpio.base + GPIO_PARPORT_NR_GPIOS - 1, pp->gpio.label); + + list_add_tail(&pp->list, &gpio_parport_hosts); + return 0; + + err_release_parport: + parport_release(pp->dev); + err_unregister_parport: + parport_unregister_device(pp->dev); + err_release_mem: + kfree(pp); + return err; +} + + + +static void gpio_parport_attach(struct parport *pb) +{ + (void)__gpio_parport_attach(pb); +} + + +static int gpio_parport_remove(gpio_pp_struct *pp) +{ + int err; + if (pp->irq >= 0) { + parport_disable_irq(pp->port); + } + err = gpiochip_remove(&pp->gpio); + if (err < 0) + pr_err("%s: gpio_remove error: %d\n", DRVNAME, err); + parport_release(pp->dev); + parport_unregister_device(pp->dev); + //ToDo: Check the unregister return value + return err; +} + +static void gpio_parport_detach(struct parport *pb) +{ + gpio_pp_struct *pp; + + list_for_each_entry(pp, &gpio_parport_hosts, list) { + if (pp->dev->port == pb) { + (void)gpio_parport_remove(pp); + list_del_init(&pp->list); + kfree(pp); + break; + } + } +} + +static struct parport_driver gpio_parport_driver = { + .name = DRVNAME, + .attach = gpio_parport_attach, + .detach = gpio_parport_detach, +}; + + +static int __init gpio_parport_init(void) +{ + return parport_register_driver(&gpio_parport_driver); +} +device_initcall(gpio_parport_init); + +static void __exit gpio_parport_exit(void) +{ + parport_unregister_driver(&gpio_parport_driver); +} +module_exit(gpio_parport_exit); + +MODULE_AUTHOR("Option Wireless"); +MODULE_DESCRIPTION("GPIO master controller driver for Parport Adapter"); +MODULE_LICENSE("GPL"); + +MODULE_INFO(Flags, sCONFIG_GPIO_PARPORT_MUTEX); +MODULE_INFO(Version, DRIVER_VERSION); diff --git a/gpio_test.c b/gpio_test.c new file mode 100644 index 0000000..90d0892 --- /dev/null +++ b/gpio_test.c @@ -0,0 +1,174 @@ +/* + * gpio_test.c - GPIO test driver + * + * Copyright (C) 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * It just triggers a specified GPIO line setup the output + */ +#include +#include +#include +#include +#include + + +//***************************************************************************** +// MODULE PARAMETERS +//***************************************************************************** +#define DRVNAME "gpio_test" /* name of the driver */ +#define DRIVER_VERSION "0.1" /* helps identifing different versions of that driver */ +#define LABEL NULL + +static unsigned int gpi; +static unsigned int gpo; +static unsigned int val; +static unsigned int irq = -1; + +typedef struct { + int out_cansleep; + int in_cansleep; + int irq; + int cnt; +} gpio_t_struct; + +static gpio_t_struct gt; + +static irqreturn_t gpio_test_irq_handler(int irq, void *dev_id) +{ + + // todo use dev_id to get pointer to own driver structure data; + // container can be memory init with GPK_KERNEL + if (irq == gt.irq) { + gt.cnt++; + val = !val; + if (!gt.in_cansleep) { + pr_info("%s: GPIO #%d in value %d with counter %d\n", DRVNAME, gpi, gpio_get_value(gpi), gt.cnt); + } + if (!gt.out_cansleep) { + gpio_set_value(gpo, !val); + } + } + return IRQ_HANDLED; +} + +/* +** just set here the value +*/ +static int __init gpio_test_init(void) +{ + int err; + err = gpio_is_valid(gpo); + if (err<0) { + pr_err("%s: GPIO #%d for output is not usable\n", DRVNAME, gpo); + return err; + } + //ToDo: Test, how a text might be used as LABEL + err = gpio_request(gpo, LABEL); + if (err<0) { + pr_err("%s: GPIO #%d for output is can not be requested\n", DRVNAME, gpo); + return err; + } + gt.out_cansleep = gpio_cansleep(gpo); + pr_info("%s: GPIO #%d for output %s sleep\n", DRVNAME, gpo, ((gt.out_cansleep>0) ? "can" : "will not")); + err = gpio_direction_output(gpo, val); + if (err<0) { + pr_err("%s: GPIO #%d output direction is not possible\n", DRVNAME, gpo); + gpio_free(gpo); + return err; + } + + err = gpio_is_valid(gpi); + if (err<0) { + pr_err("%s: GPIO #%d for inout is not usable\n", DRVNAME, gpi); + gpio_free(gpo); + return err; + } + err = gpio_request(gpi, LABEL); + if (err<0) { + pr_err("%s: GPIO #%d for input is can not be requested\n", DRVNAME, gpi); + gpio_free(gpo); + return err; + } + gt.in_cansleep = gpio_cansleep(gpi); + pr_info("%s: GPIO #%d for input %s sleep\n", DRVNAME, gpi, ((gt.in_cansleep>0) ? "can" : "will not")); + gt.irq = gpio_to_irq(gpi); + if (gt.irq<0) { + pr_info("%s: GPIO #%d IRQ is not supported\n", DRVNAME, gpi); + gt.irq = irq; + if (gt.irq >= 0) { + pr_info("%s: we force interrupt to IRQ %d\n", DRVNAME, gt.irq); + } + } else { + pr_info("%s: GPIO #%d IRQ %d is supported\n", DRVNAME, gpi, gt.irq); + } + /* gpio_direction_output must be call from task context, we use insmod as task context here */ + err = gpio_direction_input(gpi); + if (err<0) { + pr_err("%s: GPIO #%d input direction is not possible\n", DRVNAME, gpi); + gpio_free(gpo); + gpio_free(gpi); + return err; + } + gt.cnt=0; + /* register irq handler */ + // ToDo: Test with other flag types: IRQF_SHARED, IRQF_TRIGGER_* + if (gt.irq >= 0) { + err = request_irq(gt.irq, gpio_test_irq_handler, IRQF_TRIGGER_NONE, "gpio_test_handler", NULL); + if (err<0) { + pr_err("%s: GPIO #%d IRQ %d can not be requested, got %d\n", DRVNAME, gpi, gt.irq, err); + gt.irq = -1; + } + } + return 0; +} + +device_initcall(gpio_test_init); + +/* +** just restore here the old value +*/ +static void __exit gpio_test_exit(void) +{ + if (gt.out_cansleep) { + gpio_set_value_cansleep(gpo, !val); + } else { + gpio_set_value(gpo, !val); + } + if (gt.irq >= 0) free_irq(gt.irq, NULL); + gpio_free(gpo); + gpio_free(gpi); +} +module_exit(gpio_test_exit); + +module_param(gpo, uint, S_IRUGO); +MODULE_PARM_DESC(gpo, "GPIO output address or number"); + +module_param(gpi, uint, S_IRUGO); +MODULE_PARM_DESC(gpi, "GPIO input address or number"); + +module_param(val, bool, S_IRUGO); +MODULE_PARM_DESC(val, "GPIO output value 0 or 1"); + +module_param(irq, uint, S_IRUGO); +MODULE_PARM_DESC(irq, "GPIO interrupt number"); + + +MODULE_AUTHOR("Option Wireless"); +MODULE_DESCRIPTION("GPIO output test"); +MODULE_LICENSE("GPL"); + +MODULE_INFO(Version, DRIVER_VERSION); diff --git a/spi_parport.c b/spi_parport.c index 00c6b51..0c2b67e 100644 --- a/spi_parport.c +++ b/spi_parport.c @@ -39,15 +39,8 @@ /* * spi_device * todo: - * - implement SPI mode 1,2,3 is yet missing - * - 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 ... + * - clean up + * - test spi modes * - make driver possible to share the parport with a gpio_parport.c */ @@ -92,7 +85,7 @@ */ //***************************************************************************** -// Globals +// MODULE PARAMETERS //***************************************************************************** /* module parameters */ static char modalias[MODALIASLEN] = "gtm501l_spi"; @@ -113,7 +106,7 @@ typedef struct { struct spi_message *msg; struct spi_device *spi_parport; struct spi_board_info info; - struct list_head list; + struct list_head list; } spi_parport_struct; @@ -129,14 +122,9 @@ struct spi_parport { struct spi_message *msg; struct spi_device *spi_parport; struct spi_board_info info; - + struct list_head list; }; -static LIST_HEAD(spi_parport); - -/* REVISIT remove this ugly global and its "only one" limitation */ -static struct spi_parport *spi_parport; - /*----------------------------------------------------------------------*/ @@ -257,18 +245,28 @@ spi_parport_txrx_word_mode3(struct spi_device *spi, -/*----------------------------------------------------------------------*/ +/*************************************************************************** + * Parallel port probing routines * + ***************************************************************************/ + +static LIST_HEAD(spi_parport_hosts); + static void spi_parport_attach(struct parport *p) { int status; struct spi_parport *pp; struct spi_master *master; - struct device *dev = p->physport->dev; + struct device *dev; +#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. */ @@ -311,6 +309,7 @@ static void spi_parport_attach(struct parport *p) goto clean0; } + //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; @@ -401,7 +400,7 @@ static void spi_parport_attach(struct parport *p) #endif } } - spi_parport = pp; + list_add_tail(&pp->list, &spi_parport_hosts); return; out_bitbang_stop: @@ -424,48 +423,47 @@ static void spi_parport_detach(struct parport *p) struct spi_parport *pp; int status; - /* FIXME this global is ugly ... but, how to quickly get from - * the parport to the "struct spi_parport" associated with it? - * "old school" driver-internal device lists? - */ - if (!spi_parport || spi_parport->port != p) - return; - pp = spi_parport; - if (pp->msg) { - spi_message_free(pp->msg); - pr_info("Dealloc SPI message buffer\n"); + 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; + } } - - /* 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); - - spi_parport = NULL; } +#if 0 static void spi_parport_detach(struct parport *pb) { - ppa_struct *dev; - list_for_each_entry(dev, &ppa_hosts, list) { + spi_parport_struct *dev; + list_for_each_entry(dev, &spi_parport_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); + + spi_remove_host(dev->host); + spi_host_put(dev->host); + + parport_release(dev->pdev); + parport_unregister_device(dev->pdev); kfree(dev); break; } } } - +#endif static struct parport_driver spi_parport_driver = { .name = DRVNAME, @@ -496,6 +494,6 @@ 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_PARM_DESC(spi_bits, "spi bits per word"); MODULE_INFO(Version, DRIVER_VERSION);