diff --git a/drivers/discrete/Kconfig b/drivers/discrete/Kconfig index 787c6e541af..4a25c727037 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 ae5c117d8a1..5651641d987 100644 --- a/drivers/discrete/pca9555.c +++ b/drivers/discrete/pca9555.c @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -435,22 +436,7 @@ static int pca9555_multireadbuf(FAR struct ioexpander_dev_s *dev, #endif -#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; -} +#ifdef CONFIG_PCA9555_INT_ENABLE /**************************************************************************** * Name: pca9555_irqworker @@ -461,32 +447,77 @@ 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, bits; + FAR struct pca9555_dev_s *pca = (FAR struct pca9555_dev_s*)arg; - /* Get the set of pending GPIO interrupts */ - - /* Look at each pin */ - - for (pin = 0; pin < PCA9555_GPIO_NPINS; pin++) + /* read inputs */ + ret = I2C_WRITEREAD(pca->i2c, &addr, 1, buf, 2); + if( ret != OK) { - /* 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. - */ - + 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 */ + 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 *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 + * 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(&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. + */ + + pca->config->enable(pca->config, FALSE); + return work_queue(HPWORK, &pca->dev.work, pca9555_irqworker, (FAR void *)pca, 0); +} + #endif /**************************************************************************** @@ -536,6 +567,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 +577,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); +#ifdef CONFIG_PCA9555_INT_ENABLE + 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 2586937c83c..638180e2919 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,19 +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" */ - pca9555_handler_t handlers[PCA9555_GPIO_NPINS]; /* GPIO "interrupt handlers" */ -#endif }; #endif /* CONFIG_IOEXPANDER && CONFIG_IOEXPANDER_PCA9555 */ diff --git a/include/nuttx/discrete/ioexpander.h b/include/nuttx/discrete/ioexpander.h index 609961d8959..9adbb065d38 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 diff --git a/include/nuttx/discrete/pca9555.h b/include/nuttx/discrete/pca9555.h index fea1bdb3d44..5f71c02dbdc 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 };