mbed-os/targets/TARGET_ONSEMI/TARGET_NCS36510/gpio_irq_api.c

247 lines
7.8 KiB
C

/**
******************************************************************************
* @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
* <h1> Reference document(s) </h1>
* <p>
* Reference document: IPC7203 APB GPIO Design Specification v1.2</a>
* </p>
* @endinternal
*
* <h1> Functional description (internal) </h1>
* <p>
* 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.
* </p>
*
* <h1> Use of GPIO driver in SW </h1>
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
*/
#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