/** ****************************************************************************** * @file gpio_irq_api.c * @brief Implementation of a GPIO irq handlers * @internal * @author ON Semiconductor * $Rev: * $Date: 2015-11-04 $ ****************************************************************************** * Copyright 2016 Semiconductor Components Industries LLC (d/b/a “ON Semiconductor”). * All rights reserved. This software and/or documentation is licensed by ON Semiconductor * under limited terms and conditions. The terms and conditions pertaining to the software * and/or documentation are available at http://www.onsemi.com/site/pdf/ONSEMI_T&C.pdf * (“ON Semiconductor Standard Terms and Conditions of Sale, Section 8 Software”) and * if applicable the software license agreement. Do not use this software and/or * documentation unless you have carefully read and you agree to the limited terms and * conditions. By using this software and/or documentation, you agree to the limited * terms and conditions. * * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. * ON SEMICONDUCTOR SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. * @endinternal * * @ingroup gpio * * @details * * @internal *

Reference document(s)

*

* Reference document: IPC7203 APB GPIO Design Specification v1.2 *

* @endinternal * *

Functional description (internal)

*

* Each GPIO line can be independently programmed as an input or an output. Separate Set * and Clear registers are provided since it is likely that different software tasks may be * servicing different I/O signals. Inputs are synchronized to the system clock * through a pair of flip-flops. Each input can be programmed * to cause an interrupt to be generated. The interrupt can be programmed to be level-sensitive * or edge-sensitive and the level (high or low) or edge (rising, falling or either) that causes * the interrupt can be selected. Interrupts can be individually enabled or disabled. * Level-sensitive interrupts stay asserted until the interrupting condition is cleared. * Edge-triggered interrupts are cleared by writing to the GPIO interrupt clear register. *

* *

Use of GPIO driver in SW

*

* The user of the GPIO driver should set the pin as GPIO, using crossbar. * Init the GPIO and configure the mode and direction.This will return a device pointer. One device controls all GPIO's. It is not * needed nor supported to create a device per GPIO. * Next, the user should call the fGpioOpen function with the device and options as paramter. *

*

* Use the device driver fGpioIoctl function to change the behavior of the GPIO's and to register an * interrupt handler for each IO that has an interrupt enabled. There is one interrupt for all GPIO's. * The GPIO driver will look up what IO caused the interrupt and call the respective interrupt handler. *

*/ #include "gpio.h" /* Include files from the mbed-hal layer */ #include "gpio_irq_api.h" #include "device.h" #if DEVICE_INTERRUPTIN /* Handler for the GPIO pin */ static gpio_irq_handler irq_handler; static uint32_t gpioIds[NUMBER_OF_GPIO] = {0}; /** Main GPIO IRQ handler called from vector table handler * * @param gpioBase The GPIO register base address * @return void */ void fGpioHandler(void) { uint8_t index; uint32_t active_interrupts = 0; gpio_irq_event event = IRQ_NONE; GpioReg_pt gpioBase; /* Enable the GPIO clock which may have been switched off by other drivers */ CLOCK_ENABLE(CLOCK_GPIO); gpioBase = GPIOREG; /** - Store all active interrupts */ active_interrupts = gpioBase->R_IRQ_W_CLEAR; for (index=0; index < NUMBER_OF_GPIO; index++) { /* Check the pin for which IRQ is raised */ if ((active_interrupts >> index) & 0x01) { /* Check if it is edge triggered and clear the interrupt */ if ((gpioBase->IRQ_EDGE >> index) & 0x01) { if ((gpioBase->IRQ_POLARITY_SET >> index) &0x01) { /* Edge triggered high */ event = IRQ_RISE; } else { /* Edge triggered low */ event = IRQ_FALL; } } gpioBase->IRQ_CLEAR = (0x1 << index); /* Call the handler registered to the pin */ irq_handler(gpioIds[index], event); } } } /** Initialize the GPIO IRQ pin * * @param obj The GPIO object to initialize * @param pin The GPIO pin name * @param handler The handler to be attached to GPIO IRQ * @param id The object ID * @return -1 if pin is NC, 0 otherwise */ int gpio_irq_init(gpio_irq_t *obj, PinName pin, gpio_irq_handler handler, uint32_t id) { /* If Pin is not connected; then return -1 */ if (pin == NC) { return(-1); } /* Store the pin for which handler is registered */ obj->pin = pin; obj->pinMask = (0x1 << pin); /* Store the ID, this is required by registered handler function */ gpioIds[pin] = id; /* Enable the GPIO clock which may have been switched off by other drivers */ CLOCK_ENABLE(CLOCK_GPIO); /* Initialize the GPIO membase */ obj->GPIOMEMBASE = GPIOREG; /* Set default values for the pin interrupt */ obj->GPIOMEMBASE->W_IN = obj->pinMask; obj->GPIOMEMBASE->IRQ_EDGE = obj->pinMask; obj->GPIOMEMBASE->IRQ_POLARITY_SET = obj->pinMask; /* Register the handler for this pin */ irq_handler = handler; /* Enable interruption associated with the gpio */ NVIC_ClearPendingIRQ(Gpio_IRQn); NVIC_EnableIRQ(Gpio_IRQn); return(0); } /** Release the GPIO IRQ PIN * * @param obj The gpio object */ void gpio_irq_free(gpio_irq_t *obj) { /* Enable the GPIO clock which may have been switched off by other drivers */ CLOCK_ENABLE(CLOCK_GPIO); /* Disable IRQs to indicate that it is now free */ obj->GPIOMEMBASE->IRQ_ENABLE_CLEAR = obj->pinMask; gpioIds[obj->pin] = 0; } /** Enable/disable pin IRQ event * * @param obj The GPIO object * @param event The GPIO IRQ event * @param enable The enable flag */ void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable) { /* Enable the GPIO clock which may have been switched off by other drivers */ CLOCK_ENABLE(CLOCK_GPIO); obj->GPIOMEMBASE->IRQ_EDGE = obj->pinMask; switch(event) { case IRQ_RISE: /* Enable rising edge */ obj->GPIOMEMBASE->IRQ_POLARITY_SET = obj->pinMask; break; case IRQ_FALL: /* Enable falling edge */ obj->GPIOMEMBASE->IRQ_POLARITY_CLEAR = obj->pinMask; break; default: /* No event is set */ break; } /* Enable the IRQ based on enable parameter */ if (enable) { obj->GPIOMEMBASE->IRQ_ENABLE_SET = obj->pinMask; } else { obj->GPIOMEMBASE->IRQ_ENABLE_CLEAR = obj->pinMask; } } /** Enable GPIO IRQ * * This is target dependent, as it might enable the entire port or just a pin * @param obj The GPIO object */ void gpio_irq_enable(gpio_irq_t *obj) { /* Enable the GPIO clock which may have been switched off by other drivers */ CLOCK_ENABLE(CLOCK_GPIO); obj->GPIOMEMBASE->IRQ_ENABLE_SET = obj->pinMask; } /** Disable GPIO IRQ * * This is target dependent, as it might disable the entire port or just a pin * @param obj The GPIO object */ void gpio_irq_disable(gpio_irq_t *obj) { /* Enable the GPIO clock which may have been switched off by other drivers */ CLOCK_ENABLE(CLOCK_GPIO); obj->GPIOMEMBASE->IRQ_ENABLE_CLEAR = obj->pinMask; } #endif //DEVICE_INTERRUPTIN