From 8242600e5e52baf8683263c750ff54a9e5d44b42 Mon Sep 17 00:00:00 2001 From: Sebastien Lorquet Date: Fri, 13 Nov 2015 16:28:46 +0100 Subject: [PATCH 1/2] WIP for pca9555 irq --- drivers/discrete/pca9555.c | 79 +++++++++++++++++--------------- drivers/discrete/pca9555.h | 1 - include/nuttx/discrete/pca9555.h | 3 -- 3 files changed, 42 insertions(+), 41 deletions(-) diff --git a/drivers/discrete/pca9555.c b/drivers/discrete/pca9555.c index ae5c117d8a..da8a385d6b 100644 --- a/drivers/discrete/pca9555.c +++ b/drivers/discrete/pca9555.c @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -437,21 +438,6 @@ static int pca9555_multireadbuf(FAR struct ioexpander_dev_s *dev, #ifndef CONFIG_PCA9555_INT_DISABLE -/**************************************************************************** - * Name: pca9555_gpioworker - * - * Description: - * See include/nuttx/discrete/ioexpander.h - * - ****************************************************************************/ - -static int pca9555_attach(FAR struct ioexpander_dev_s *dev, uint8_t pin, - ioexpander_handler_t handler) -{ - FAR struct pca9555_dev_s *pca = (FAR struct pca9555_dev_s *)dev; - return 0; -} - /**************************************************************************** * Name: pca9555_irqworker * @@ -461,32 +447,46 @@ static int pca9555_attach(FAR struct ioexpander_dev_s *dev, uint8_t pin, * ****************************************************************************/ -static void pca9555_irqworker(FAR struct pca9555_dev_s *priv) +static void pca9555_irqworker(void *arg) { - uint8_t regval; - uint8_t pinmask; - int pin; + uint8_t addr = PCA9555_REG_INPUT; + uint8_t buf[2]; + int ret; + FAR struct pca9555_dev_s *dev = (FAR struct pca9555_dev_s*)arg; - /* Get the set of pending GPIO interrupts */ + /* read inputs */ + ret = I2C_WRITEREAD(dev->i2c, &addr, 1, buf, 2); + dbg("> %02X %02X\n",buf[0],buf[1]); - /* Look at each pin */ - - for (pin = 0; pin < PCA9555_GPIO_NPINS; pin++) - { - /* Check if we have a handler for this interrupt (there should - * be one) - */ - - /* Interrupt is pending... dispatch the interrupt to the - * callback - */ - - /* Clear the pending GPIO interrupt by writing a '1' to the - * pin position in the status register. - */ - - } + /* re-enable */ + dev->config->enable(dev->config, TRUE); } + +static int pca9555_interrupt(int irq, FAR void *context) +{ + /* To support multiple devices, + * retrieve the priv structure using the irq number */ + + register FAR struct pca9555_dev_s *dev = &g_pca9555; + + /* In complex environments, we cannot do I2C transfers from the interrupt + * handler because semaphores are probably used to lock the I2C bus. In + * this case, we will defer processing to the worker thread. This is also + * much kinder in the use of system resources and is, therefore, probably + * a good thing to do in any event. + */ + + DEBUGASSERT(work_available(&dev->work)); + + /* Notice that further GPIO interrupts are disabled until the work is + * actually performed. This is to prevent overrun of the worker thread. + * Interrupts are re-enabled in pca9555_irqworker() when the work is completed. + */ + + dev->config->enable(dev->config, FALSE); + return work_queue(HPWORK, &dev->work, pca9555_irqworker, (FAR void *)dev, 0); +} + #endif /**************************************************************************** @@ -536,6 +536,7 @@ FAR struct ioexpander_dev_s *pca9555_initialize(FAR struct i2c_dev_s *i2cdev, pcadev->i2c = i2cdev; pcadev->dev.ops = &g_pca9555_ops; + pcadev->config = config; /* Set the I2C address and frequency. REVISIT: This logic would be * insufficient if we share the I2C bus with any other devices that also @@ -545,6 +546,10 @@ FAR struct ioexpander_dev_s *pca9555_initialize(FAR struct i2c_dev_s *i2cdev, I2C_SETADDRESS(i2cdev, config->address, 7); I2C_SETFREQUENCY(i2cdev, config->frequency); +#ifndef CONFIG_PCA9555_INT_DISABLE + pcadev->config->attach(pcadev->config, pca9555_interrupt); + pcadev->config->enable(pcadev->config, TRUE); +#endif return &pcadev->dev; } diff --git a/drivers/discrete/pca9555.h b/drivers/discrete/pca9555.h index 2586937c83..4819e0f880 100644 --- a/drivers/discrete/pca9555.h +++ b/drivers/discrete/pca9555.h @@ -134,7 +134,6 @@ struct pca9555_dev_s #ifndef CONFIG_PCA9555_INT_DISABLE struct work_s work; /* Supports the interrupt handling "bottom half" */ - pca9555_handler_t handlers[PCA9555_GPIO_NPINS]; /* GPIO "interrupt handlers" */ #endif }; diff --git a/include/nuttx/discrete/pca9555.h b/include/nuttx/discrete/pca9555.h index fea1bdb3d4..5f71c02dbd 100644 --- a/include/nuttx/discrete/pca9555.h +++ b/include/nuttx/discrete/pca9555.h @@ -63,7 +63,6 @@ struct pca9555_config_s */ #ifndef CONFIG_PCA9555_INT_DISABLE -/* IRQ support TODO */ #ifdef CONFIG_PCA9555_MULTIPLE int irq; /* IRQ number received by interrupt handler. */ @@ -75,12 +74,10 @@ struct pca9555_config_s * * attach - Attach the PCA9555 interrupt handler to the GPIO interrupt * enable - Enable or disable the GPIO interrupt - * clear - Acknowledge/clear any pending GPIO interrupt */ CODE int (*attach)(FAR struct pca9555_config_s *state, xcpt_t isr); CODE void (*enable)(FAR struct pca9555_config_s *state, bool enable); - CODE void (*clear)(FAR struct pca9555_config_s *state); #endif }; From 826aadbce8cb570e36dfefa882aae4b7ee855835 Mon Sep 17 00:00:00 2001 From: Sebastien Lorquet Date: Tue, 17 Nov 2015 14:09:43 +0100 Subject: [PATCH 2/2] Include support for PCA9555 interrupt driven IO changes detection. Currently using a signal, and a single notified task. signal handling support moved to generic IO expander header (not pca specific) --- drivers/discrete/Kconfig | 30 ++++++++++------ drivers/discrete/pca9555.c | 53 +++++++++++++++++++++++------ drivers/discrete/pca9555.h | 16 ++------- include/nuttx/discrete/ioexpander.h | 12 +++++++ 4 files changed, 76 insertions(+), 35 deletions(-) diff --git a/drivers/discrete/Kconfig b/drivers/discrete/Kconfig index 787c6e541a..4a25c72703 100644 --- a/drivers/discrete/Kconfig +++ b/drivers/discrete/Kconfig @@ -12,13 +12,6 @@ menuconfig IOEXPANDER if IOEXPANDER -config IOEXPANDER_MULTIPIN - bool "Support multi-pin access routines" - default n - ---help--- - This settings enable the definition of routines for - optimized simultaneous access to multiple pins. - config IOEXPANDER_PCA9555 bool "PCA9555 I2C IO expander" default n @@ -34,13 +27,28 @@ config PCA9555_MULTIPLE ---help--- Can be defined to support multiple PCA9555 devices on board. -config PCA9555_INT_DISABLE - bool "Disable PCA9555 Interrupt Support" - default y +config PCA9555_INT_ENABLE + bool "Enable PCA9555 Interrupt Support" + default n + select IOEXPANDER_INT_ENABLE ---help--- - Disable driver interrupt functionality + Enable driver interrupt functionality endif # IOEXPANDER_PCA9555 + +config IOEXPANDER_INT_ENABLE + bool + default y if PCA9555_INT_ENABLE + ---help--- + This is the global INT supported flag for io expanders + +config IOEXPANDER_MULTIPIN + bool "Support multi-pin access routines" + default n + ---help--- + This settings enable the definition of routines for + optimized simultaneous access to multiple pins. + endif # IOEXPANDER config USERLED diff --git a/drivers/discrete/pca9555.c b/drivers/discrete/pca9555.c index da8a385d6b..5651641d98 100644 --- a/drivers/discrete/pca9555.c +++ b/drivers/discrete/pca9555.c @@ -436,7 +436,7 @@ static int pca9555_multireadbuf(FAR struct ioexpander_dev_s *dev, #endif -#ifndef CONFIG_PCA9555_INT_DISABLE +#ifdef CONFIG_PCA9555_INT_ENABLE /**************************************************************************** * Name: pca9555_irqworker @@ -451,23 +451,54 @@ static void pca9555_irqworker(void *arg) { uint8_t addr = PCA9555_REG_INPUT; uint8_t buf[2]; - int ret; - FAR struct pca9555_dev_s *dev = (FAR struct pca9555_dev_s*)arg; + int ret, bits; + FAR struct pca9555_dev_s *pca = (FAR struct pca9555_dev_s*)arg; /* read inputs */ - ret = I2C_WRITEREAD(dev->i2c, &addr, 1, buf, 2); - dbg("> %02X %02X\n",buf[0],buf[1]); + ret = I2C_WRITEREAD(pca->i2c, &addr, 1, buf, 2); + if( ret != OK) + { + return; + } + bits = (buf[0]<<8) | buf[1]; + + + /* if signal PID is registered, enqueue signal. */ + if(pca->dev.sigpid) + { +#ifdef CONFIG_CAN_PASS_STRUCTS + union sigval value; + value.sival_int = bits; + ret = sigqueue(pca->dev.sigpid, pca->dev.sigval, value); +#else + ret = sigqueue(pca->dev.sigpid, pca->dev.sigval, (FAR void*)bits); +#endif + dbg("pca signal %04X (sig %d to pid %d)\n",bits, pca->dev.sigval, pca->dev.sigpid); + } + else + { + dbg("no handler registered\n"); + } /* re-enable */ - dev->config->enable(dev->config, TRUE); + pca->config->enable(pca->config, TRUE); } +/**************************************************************************** + * Name: pca9555_interrupt + * + * Description: + * Handle GPIO interrupt events (this function executes in the + * context of the interrupt). + * + ****************************************************************************/ + static int pca9555_interrupt(int irq, FAR void *context) { /* To support multiple devices, * retrieve the priv structure using the irq number */ - register FAR struct pca9555_dev_s *dev = &g_pca9555; + register FAR struct pca9555_dev_s *pca = &g_pca9555; /* In complex environments, we cannot do I2C transfers from the interrupt * handler because semaphores are probably used to lock the I2C bus. In @@ -476,15 +507,15 @@ static int pca9555_interrupt(int irq, FAR void *context) * a good thing to do in any event. */ - DEBUGASSERT(work_available(&dev->work)); + DEBUGASSERT(work_available(&pca->dev.work)); /* Notice that further GPIO interrupts are disabled until the work is * actually performed. This is to prevent overrun of the worker thread. * Interrupts are re-enabled in pca9555_irqworker() when the work is completed. */ - dev->config->enable(dev->config, FALSE); - return work_queue(HPWORK, &dev->work, pca9555_irqworker, (FAR void *)dev, 0); + pca->config->enable(pca->config, FALSE); + return work_queue(HPWORK, &pca->dev.work, pca9555_irqworker, (FAR void *)pca, 0); } #endif @@ -546,7 +577,7 @@ FAR struct ioexpander_dev_s *pca9555_initialize(FAR struct i2c_dev_s *i2cdev, I2C_SETADDRESS(i2cdev, config->address, 7); I2C_SETFREQUENCY(i2cdev, config->frequency); -#ifndef CONFIG_PCA9555_INT_DISABLE +#ifdef CONFIG_PCA9555_INT_ENABLE pcadev->config->attach(pcadev->config, pca9555_interrupt); pcadev->config->enable(pcadev->config, TRUE); #endif diff --git a/drivers/discrete/pca9555.h b/drivers/discrete/pca9555.h index 4819e0f880..638180e291 100644 --- a/drivers/discrete/pca9555.h +++ b/drivers/discrete/pca9555.h @@ -48,7 +48,6 @@ #include #include -#include #include #include @@ -95,12 +94,6 @@ #error "CONFIG_I2C is required by PCA9555" #endif -#ifndef CONFIG_PCA9555_INT_DISABLE -#ifndef CONFIG_SCHED_WORKQUEUE -#error "Work queue support required. CONFIG_SCHED_WORKQUEUE must be selected." -#endif -#endif - #define PCA9555_MAXDEVS 8 /* I2C frequency */ @@ -123,18 +116,15 @@ struct pca9555_dev_s { - struct ioexpander_dev_s dev; /* Nested structure to allow casting as public gpio expander. */ + struct ioexpander_dev_s dev; /* Nested structure to allow casting as public gpio expander. */ #ifdef CONFIG_PCA9555_MULTIPLE - FAR struct pca9555_dev_s *flink; /* Supports a singly linked list of drivers */ + FAR struct pca9555_dev_s * flink; /* Supports a singly linked list of drivers */ #endif FAR struct pca9555_config_s *config; /* Board configuration data */ - FAR struct i2c_dev_s *i2c; /* Saved I2C driver instance */ + FAR struct i2c_dev_s * i2c; /* Saved I2C driver instance */ -#ifndef CONFIG_PCA9555_INT_DISABLE - struct work_s work; /* Supports the interrupt handling "bottom half" */ -#endif }; #endif /* CONFIG_IOEXPANDER && CONFIG_IOEXPANDER_PCA9555 */ diff --git a/include/nuttx/discrete/ioexpander.h b/include/nuttx/discrete/ioexpander.h index 609961d895..9adbb065d3 100644 --- a/include/nuttx/discrete/ioexpander.h +++ b/include/nuttx/discrete/ioexpander.h @@ -41,9 +41,16 @@ ****************************************************************************/ #include +#include #if defined(CONFIG_IOEXPANDER) +#ifndef CONFIG_PCA9555_INT_DISABLE +#ifndef CONFIG_SCHED_WORKQUEUE +#error "Work queue support required. CONFIG_SCHED_WORKQUEUE must be selected." +#endif +#endif + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -249,6 +256,11 @@ struct ioexpander_ops_s struct ioexpander_dev_s { FAR const struct ioexpander_ops_s *ops; +#ifdef CONFIG_IOEXPANDER_INT_ENABLE + struct work_s work; /* Supports the interrupt handling "bottom half" */ + int sigpid; /* PID to be signaled in case of interrupt */ + int sigval; /* signal to be sent in case of interrupt */ +#endif }; #endif //CONFIG_IOEXPANDER