--- c/src/lib/libbsp/Makefile.am | 3 +- c/src/lib/libbsp/preinstall.am | 4 + c/src/lib/libbsp/shared/gpio.c | 1083 ++++++++++++++++++++++++++++++++ c/src/lib/libbsp/shared/include/gpio.h | 610 ++++++++++++++++++ 4 files changed, 1699 insertions(+), 1 deletion(-) create mode 100644 c/src/lib/libbsp/shared/gpio.c create mode 100644 c/src/lib/libbsp/shared/include/gpio.h
diff --git a/c/src/lib/libbsp/Makefile.am b/c/src/lib/libbsp/Makefile.am index 3cab4d7..a039a98 100644 --- a/c/src/lib/libbsp/Makefile.am +++ b/c/src/lib/libbsp/Makefile.am @@ -9,7 +9,7 @@ EXTRA_DIST = MERGE.PROCEDURE bsp.am EXTRA_DIST += shared/bootcard.c shared/bspclean.c \ shared/bsplibc.c shared/bsppost.c shared/console-polled.c \ shared/console.c shared/gnatinstallhandler.c shared/sbrk.c \ - shared/tod.c + shared/tod.c shared/gpio.c EXTRA_DIST += shared/vmeUniverse/vmeUniverse.c \ shared/vmeUniverse/vmeUniverse.h \ shared/vmeUniverse/vmeUniverseDMA.h \ @@ -35,6 +35,7 @@ include_bsp_HEADERS = include_bsp_HEADERS += shared/include/default-initial-extension.h include_bsp_HEADERS += shared/include/fatal.h include_bsp_HEADERS += shared/include/console-termios.h +include_bsp_HEADERS += shared/include/gpio.h include $(srcdir)/preinstall.am include $(top_srcdir)/automake/subdirs.am diff --git a/c/src/lib/libbsp/preinstall.am b/c/src/lib/libbsp/preinstall.am index 651f136..bbcb7c5 100644 --- a/c/src/lib/libbsp/preinstall.am +++ b/c/src/lib/libbsp/preinstall.am @@ -30,3 +30,7 @@ $(PROJECT_INCLUDE)/bsp/console-termios.h: shared/include/console-termios.h $(PRO $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/console-termios.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/console-termios.h +$(PROJECT_INCLUDE)/bsp/gpio.h: shared/include/gpio.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/gpio.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/gpio.h + diff --git a/c/src/lib/libbsp/shared/gpio.c b/c/src/lib/libbsp/shared/gpio.c new file mode 100644 index 0000000..12cdb29 --- /dev/null +++ b/c/src/lib/libbsp/shared/gpio.c @@ -0,0 +1,1083 @@ +/** + * @file gpio.c + * + * @ingroup rtems_gpio + * + * @brief RTEMS GPIO API implementation. + */ + +/* + * Copyright (c) 2014-2015 Andre Marques <andre.lousa.marques at gmail.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems/score/atomic.h> +#include <rtems/status-checks.h> +#include <bsp/irq-generic.h> +#include <bsp/gpio.h> + +#include <assert.h> +#include <stdlib.h> + +/** + * @brief GPIO API mutex atributes. + */ +#define MUTEX_ATRIBUTES \ + ( RTEMS_LOCAL \ + | RTEMS_PRIORITY \ + | RTEMS_BINARY_SEMAPHORE \ + | RTEMS_INHERIT_PRIORITY \ + | RTEMS_NO_PRIORITY_CEILING \ + ) + +#define AQUIRE_LOCK(m) assert( rtems_semaphore_obtain(m, \ + RTEMS_WAIT, \ + RTEMS_NO_TIMEOUT \ + ) == RTEMS_SUCCESSFUL ) + +#define RELEASE_LOCK(m) assert( rtems_semaphore_release(m) == RTEMS_SUCCESSFUL ) + +/*Encapsulates relevant data for a GPIO interrupt handler. */ +typedef struct _gpio_handler_list +{ + struct _gpio_handler_list *next_isr; + + /* User-defined ISR routine. */ + rtems_gpio_irq_state (*handler) (void *arg); + + /* User-defined arguments for the ISR routine. */ + void *arg; +} gpio_handler_list; + +/* Encapsulates relevant data about a GPIO pin. */ +typedef struct +{ + uint32_t bank_number; + uint32_t pin_number; + + rtems_gpio_function pin_function; + + /* Type of event which will trigger an interrupt. */ + rtems_gpio_interrupt enabled_interrupt; + + /* Id of the task that will be calling the user-defined ISR handlers + * for this pin. */ + rtems_id task_id; + + /* ISR shared flag. */ + rtems_gpio_handler_flag handler_flag; + + /* Linked list of interrupt handlers. */ + gpio_handler_list *handler_list; + + /* GPIO input (pull resistor) pin mode. */ + rtems_gpio_pull_mode resistor_mode; + + /* If true inverts digital in/out logic. */ + int logic_invert; + + /* Switch-deboucing information. */ + int debouncing_tick_count; + rtems_interval last_isr_tick; +} gpio_pin; + +static gpio_pin** gpio_pin_state; +static Atomic_Flag init_flag = ATOMIC_INITIALIZER_FLAG; +static uint32_t gpio_count; +static uint32_t bank_count; +static uint32_t pins_per_bank; +static uint32_t odd_bank_pins; +static uint32_t* interrupt_counter; +static rtems_id* bank_lock; + +#define BANK_NUMBER(pin_number) pin_number / pins_per_bank +#define PIN_NUMBER(pin_number) pin_number % pins_per_bank + +static int debounce_switch(gpio_pin* gpio) +{ + rtems_interval time; + + time = rtems_clock_get_ticks_since_boot(); + + /* If not enough time has elapsed since last interrupt. */ + if ( (time - gpio->last_isr_tick) < gpio->debouncing_tick_count ) { + return -1; + } + + gpio->last_isr_tick = time; + + return 0; +} + +static rtems_task generic_handler_task(rtems_task_argument arg) +{ + gpio_handler_list *handler_list; + rtems_event_set out; + gpio_pin* gpio; + uint32_t bank; + int handled_count; + int rv; + + gpio = (gpio_pin*) arg; + + bank = gpio->bank_number; + + while ( true ) { + handled_count = 0; + + /* Wait for interrupt event. This is sent by the bank's generic_isr handler. */ + rtems_event_receive(RTEMS_EVENT_1, RTEMS_EVENT_ALL | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, + &out); + + AQUIRE_LOCK(bank_lock[bank]); + + /* If this pin has the debouncing function attached, call it. */ + if ( gpio->debouncing_tick_count > 0 ) { + rv = debounce_switch(gpio); + + /* If the handler call was caused by a switch bounce, ignores and move on. */ + if ( rv < 0 ) { + RELEASE_LOCK(bank_lock[bank]); + + continue; + } + + /* Record the current clock tick. */ + gpio->last_isr_tick = rtems_clock_get_ticks_since_boot(); + } + + handler_list = gpio->handler_list; + + /* Iterate the ISR list. */ + while ( handler_list != NULL ) { + if ( (handler_list->handler)(handler_list->arg) == IRQ_HANDLED ) { + ++handled_count; + } + + handler_list = handler_list->next_isr; + } + + /* If no handler assumed the interrupt, treat it as a spurious interrupt. */ + if ( handled_count == 0 ) { + bsp_interrupt_handler_default(rtems_bsp_gpio_get_vector(bank)); + } + + RELEASE_LOCK(bank_lock[bank]); + } +} + +static void generic_isr(void* arg) +{ + rtems_vector_number vector; + uint32_t event_status; + uint32_t bank_pin_count; + gpio_pin *gpio; + uint32_t bank_number; + int i; + + gpio = (gpio_pin *) arg; + + /* Get the bank/vector number of a pin (e.g.: 0) from this bank. */ + bank_number = gpio[0].bank_number; + + vector = rtems_bsp_gpio_get_vector(bank_number); + + /* If the current bank is an odd last bank (i.e.: not completely filled). */ + if ( odd_bank_pins > 0 && bank_number == bank_count - 1 ) { + bank_pin_count = odd_bank_pins; + } + else { + bank_pin_count = pins_per_bank; + } + + /* Prevents more interrupts from being generated on GPIO. */ + bsp_interrupt_vector_disable(vector); + + /* Ensure that interrupts are disabled in this vector, before checking + * the interrupt line. */ + RTEMS_COMPILER_MEMORY_BARRIER(); + + /* Obtains a 32-bit bitmask, with the pins currently reporting interrupts + * signaled with 1. */ + event_status = rtems_bsp_gpio_interrupt_line(vector); + + /* Iterates through the bitmask and calls the corresponding handler + * for active interrupts. */ + for ( i = 0; i < bank_pin_count; ++i ) { + /* If active, wake the corresponding pin's ISR task. */ + if ( event_status & (1 << i) ) { + rtems_event_send(gpio[i].task_id, RTEMS_EVENT_1); + } + } + + /* Clear all active events. */ + rtems_bsp_gpio_clear_interrupt_line(vector, event_status); + + /* Ensure that the interrupt line is cleared before re-activating + * the interrupts on this vector. */ + RTEMS_COMPILER_MEMORY_BARRIER(); + + bsp_interrupt_vector_enable(vector); +} + +static uint32_t get_pin_bitmask(uint32_t count, va_list args, uint32_t* bank_number, rtems_status_code* sc) +{ + uint32_t bank; + uint32_t pin; + uint32_t bitmask; + uint32_t pin_number; + int i; + + if ( count < 1 ) { + *sc = RTEMS_UNSATISFIED; + + return 0; + } + + bitmask = 0; + + for ( i = 0; i < count; ++i ) { + pin_number = va_arg(args, uint32_t); + + if ( pin_number < 0 || pin_number >= gpio_count ) { + *sc = RTEMS_INVALID_ID; + + return 0; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + if ( i == 0 ) { + *bank_number = bank; + + AQUIRE_LOCK(bank_lock[bank]); + } + else if ( bank != *bank_number ) { + *sc = RTEMS_UNSATISFIED; + + RELEASE_LOCK(bank_lock[*bank_number]); + + return 0; + } + + if ( gpio_pin_state[bank][pin].pin_function != DIGITAL_OUTPUT ) { + *sc = RTEMS_NOT_CONFIGURED; + + RELEASE_LOCK(bank_lock[bank]); + + return 0; + } + + bitmask |= (1 << PIN_NUMBER(pin_number)); + } + + *sc = RTEMS_SUCCESSFUL; + + RELEASE_LOCK(bank_lock[bank]); + + return bitmask; +} + +rtems_status_code rtems_gpio_initialize(void) +{ + rtems_status_code sc; + rtems_gpio_layout layout; + int bank_pins; + int pin; + int bank; + int i; + + if ( _Atomic_Flag_test_and_set(&init_flag, ATOMIC_ORDER_RELAXED) == true ) { + return RTEMS_SUCCESSFUL; + } + + layout = rtems_bsp_gpio_initialize(); + + gpio_count = layout.pin_count; + pins_per_bank = layout.pins_per_bank; + + bank_count = gpio_count / pins_per_bank; + + /* Account for remaining pins after filling the last bank. */ + odd_bank_pins = gpio_count % pins_per_bank; + + if ( odd_bank_pins > 0 ) { + ++bank_count; + } + + /* Create GPIO bank mutexes. */ + bank_lock = (rtems_id*) malloc(bank_count * sizeof(rtems_id)); + + for ( i = 0; i < bank_count; ++i ) { + sc = rtems_semaphore_create(rtems_build_name('G', 'I', 'N', 'T'), 1, MUTEX_ATRIBUTES, 0, &bank_lock[i]); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + } + + /* Create GPIO pin state matrix. */ + gpio_pin_state = (gpio_pin**) malloc(bank_count * sizeof(gpio_pin*)); + + for ( i = 0; i < bank_count; ++i ) { + if ( i == bank_count - 1 ) { + bank_pins = odd_bank_pins; + } + else { + bank_pins = pins_per_bank; + } + + gpio_pin_state[i] = (gpio_pin*) malloc(bank_pins * sizeof(gpio_pin)); + } + + /* Creates an interrupt counter per pin bank. */ + interrupt_counter = (uint32_t*) calloc(bank_count, sizeof(uint32_t)); + + for ( i = 0; i < gpio_count; ++i ) { + bank = BANK_NUMBER(i); + pin = PIN_NUMBER(i); + + gpio_pin_state[bank][pin].bank_number = bank; + gpio_pin_state[bank][pin].pin_number = pin; + gpio_pin_state[bank][pin].pin_function = NOT_USED; + gpio_pin_state[bank][pin].enabled_interrupt = NONE; + gpio_pin_state[bank][pin].task_id = RTEMS_ID_NONE; + gpio_pin_state[bank][pin].handler_list = NULL; + gpio_pin_state[bank][pin].debouncing_tick_count = 0; + gpio_pin_state[bank][pin].last_isr_tick = 0; + } + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_request_conf(rtems_gpio_pin_conf* conf) +{ + rtems_gpio_interrupt_conf* interrupt_conf; + rtems_status_code sc; + bool new_request; + uint32_t bank; + uint32_t pin; + + new_request = false; + + sc = rtems_gpio_request_pin(conf->pin_number, conf->function, conf->output_enabled, conf->logic_invert, conf->bsp_specific); + + if ( sc == RTEMS_SUCCESSFUL ) { + new_request = true; + } + /* If the pin is being used, then this function call is an update call. + * If not, an error occurred. */ + else if ( sc != RTEMS_RESOURCE_IN_USE ) { + RTEMS_SYSLOG_ERROR("rtems_gpio_request_pin failed with status code %d\n", sc); + + return RTEMS_UNSATISFIED; + } + + sc = rtems_gpio_resistor_mode(conf->pin_number, conf->pull_mode); + + if ( sc != RTEMS_SUCCESSFUL ) { + RTEMS_SYSLOG_ERROR("rtems_gpio_resistor_mode failed with status code %d\n", sc); + + return RTEMS_UNSATISFIED; + } + + interrupt_conf = (rtems_gpio_interrupt_conf*) conf->interrupt; + + if ( interrupt_conf != NULL ) { + bank = BANK_NUMBER(conf->pin_number); + pin = PIN_NUMBER(conf->pin_number); + + AQUIRE_LOCK(bank_lock[bank]); + + if ( interrupt_conf->enabled_interrupt != gpio_pin_state[bank][pin].enabled_interrupt ) { + if ( new_request == false ) { + sc = rtems_gpio_disable_interrupt(conf->pin_number); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(bank_lock[bank]); + + RTEMS_SYSLOG_ERROR("rtems_gpio_disable_interrupt failed with status code %d\n", sc); + + return RTEMS_UNSATISFIED; + } + } + + sc = rtems_gpio_enable_interrupt(conf->pin_number, + interrupt_conf->enabled_interrupt, + interrupt_conf->handler_flag, + interrupt_conf->handler, + interrupt_conf->arg); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(bank_lock[bank]); + + RTEMS_SYSLOG_ERROR("rtems_gpio_enable_interrupt failed with status code %d\n", sc); + + return RTEMS_UNSATISFIED; + } + + } + + if ( interrupt_conf->clock_tick_interval != gpio_pin_state[bank][pin].debouncing_tick_count ) { + gpio_pin_state[bank][pin].debouncing_tick_count = interrupt_conf->clock_tick_interval; + gpio_pin_state[bank][pin].last_isr_tick = 0; + } + + RELEASE_LOCK(bank_lock[bank]); + } + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_multi_set(uint32_t count, ...) +{ + rtems_status_code sc; + uint32_t bank; + uint32_t bitmask; + va_list ap; + + va_start(ap, count); + + bitmask = get_pin_bitmask(count, ap, &bank, &sc); + + if ( sc != RTEMS_SUCCESSFUL ) { + RTEMS_SYSLOG_ERROR("error parsing function arguments\n"); + + return sc; + } + + va_end(ap); + + AQUIRE_LOCK(bank_lock[bank]); + + sc = rtems_bsp_gpio_multi_set(bank, bitmask); + + RELEASE_LOCK(bank_lock[bank]); + + return sc; +} + +rtems_status_code rtems_gpio_multi_clear(uint32_t count, ...) +{ + rtems_status_code sc; + uint32_t bank; + uint32_t bitmask; + va_list ap; + + va_start(ap, count); + + bitmask = get_pin_bitmask(count, ap, &bank, &sc); + + if ( sc != RTEMS_SUCCESSFUL ) { + RTEMS_SYSLOG_ERROR("error parsing function arguments\n"); + + return sc; + } + + va_end(ap); + + AQUIRE_LOCK(bank_lock[bank]); + + sc = rtems_bsp_gpio_multi_clear(bank, bitmask); + + RELEASE_LOCK(bank_lock[bank]); + + return sc; +} + +rtems_status_code rtems_gpio_set(uint32_t pin_number) +{ + rtems_status_code sc; + uint32_t bank; + uint32_t pin; + + if ( pin_number < 0 || pin_number >= gpio_count ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + AQUIRE_LOCK(bank_lock[bank]); + + if ( gpio_pin_state[bank][pin].pin_function != DIGITAL_OUTPUT ) { + RELEASE_LOCK(bank_lock[bank]); + + RTEMS_SYSLOG_ERROR("Can only set digital output pins\n"); + + return RTEMS_NOT_CONFIGURED; + } + + if ( gpio_pin_state[bank][pin].logic_invert ) { + sc = rtems_bsp_gpio_clear(bank, pin); + } + else { + sc = rtems_bsp_gpio_set(bank, pin); + } + + RELEASE_LOCK(bank_lock[bank]); + + return sc; +} + +rtems_status_code rtems_gpio_clear(uint32_t pin_number) +{ + rtems_status_code sc; + uint32_t bank; + uint32_t pin; + + if ( pin_number < 0 || pin_number >= gpio_count ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + AQUIRE_LOCK(bank_lock[bank]); + + if ( gpio_pin_state[bank][pin].pin_function != DIGITAL_OUTPUT ) { + RELEASE_LOCK(bank_lock[bank]); + + RTEMS_SYSLOG_ERROR("Can only clear digital output pins\n"); + + return RTEMS_NOT_CONFIGURED; + } + + if ( gpio_pin_state[bank][pin].logic_invert ) { + sc = rtems_bsp_gpio_set(bank, pin); + } + else { + sc = rtems_bsp_gpio_clear(bank, pin); + } + + RELEASE_LOCK(bank_lock[bank]); + + return sc; +} + +int rtems_gpio_get_value(uint32_t pin_number) +{ + uint32_t bank; + uint32_t pin; + int rv; + + if ( pin_number < 0 || pin_number >= gpio_count ) { + return -1; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + AQUIRE_LOCK(bank_lock[bank]); + + if ( gpio_pin_state[bank][pin].pin_function != DIGITAL_INPUT) { + RELEASE_LOCK(bank_lock[bank]); + + RTEMS_SYSLOG_ERROR("Can only read digital input pins\n"); + + return -1; + } + + rv = rtems_bsp_gpio_get_value(bank, pin); + + if ( gpio_pin_state[bank][pin].logic_invert && rv > 0 ) { + RELEASE_LOCK(bank_lock[bank]); + + return !rv; + } + + RELEASE_LOCK(bank_lock[bank]); + + return ( rv > 0 ) ? 1 : rv; +} + +rtems_status_code rtems_gpio_request_pin(uint32_t pin_number, rtems_gpio_function function, bool output_enabled, bool logic_invert, void* bsp_specific) +{ + rtems_gpio_specific_data* bsp_data; + rtems_status_code sc = RTEMS_SUCCESSFUL; + uint32_t bank; + uint32_t pin; + + if ( pin_number < 0 || pin_number >= gpio_count ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + AQUIRE_LOCK(bank_lock[bank]); + + /* If the pin is already being used returns with an error. */ + if ( gpio_pin_state[bank][pin].pin_function != NOT_USED ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_RESOURCE_IN_USE; + } + + switch ( function ) { + case DIGITAL_INPUT: + sc = rtems_bsp_gpio_select_input(bank, pin, bsp_specific); + break; + case DIGITAL_OUTPUT: + sc = rtems_bsp_gpio_select_output(bank, pin, bsp_specific); + break; + case BSP_SPECIFIC: + bsp_data = (rtems_gpio_specific_data*) bsp_specific; + + if ( bsp_data == NULL ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_UNSATISFIED; + } + + sc = rtems_bsp_select_specific_io(bank, pin, bsp_data->io_function, bsp_data->pin_data); + break; + case NOT_USED: + default: + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_NOT_DEFINED; + } + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(bank_lock[bank]); + + return sc; + } + + /* If the function was successfuly assigned to the pin, + * record that information on the gpio_pin_state structure. */ + gpio_pin_state[bank][pin].pin_function = function; + gpio_pin_state[bank][pin].logic_invert = logic_invert; + + if ( function == DIGITAL_OUTPUT ) { + if ( output_enabled == true ) { + sc = rtems_bsp_gpio_set(bank, pin); + } + else { + sc = rtems_bsp_gpio_clear(bank, pin); + } + } + + RELEASE_LOCK(bank_lock[bank]); + + return sc; +} + +rtems_status_code rtems_gpio_resistor_mode(uint32_t pin_number, rtems_gpio_pull_mode mode) +{ + rtems_status_code sc; + uint32_t bank; + uint32_t pin; + + if ( pin_number < 0 || pin_number >= gpio_count ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + AQUIRE_LOCK(bank_lock[bank]); + + /* If the desired actuation mode is already set, silently exits. + * The NO_PULL_RESISTOR is a special case, as some platforms have + * pull-up resistors enabled on startup, so this state may have to + * be reinforced in the hardware. */ + if ( gpio_pin_state[bank][pin].resistor_mode == mode && mode != NO_PULL_RESISTOR ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_SUCCESSFUL; + } + + sc = rtems_bsp_gpio_set_resistor_mode(bank, pin, mode); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(bank_lock[bank]); + + return sc; + } + + gpio_pin_state[bank][pin].resistor_mode = mode; + + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_release_pin(uint32_t pin_number) +{ + rtems_status_code sc; + gpio_pin* gpio; + uint32_t bank; + uint32_t pin; + + if ( pin_number < 0 || pin_number >= gpio_count ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + AQUIRE_LOCK(bank_lock[bank]); + + gpio = &gpio_pin_state[bank][pin]; + + /* If the pin has an enabled interrupt then remove the handler(s), + * and disable the interrupts on that pin. */ + if ( gpio->enabled_interrupt != NONE ) { + sc = rtems_gpio_disable_interrupt(pin_number); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(bank_lock[bank]); + + return sc; + } + } + + gpio->pin_function = NOT_USED; + gpio->task_id = RTEMS_ID_NONE; + gpio->debouncing_tick_count = 0; + gpio->last_isr_tick = 0; + + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_debounce_switch(uint32_t pin_number, int ticks) +{ + uint32_t bank; + uint32_t pin; + + if ( pin_number < 0 || pin_number >= gpio_count ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + AQUIRE_LOCK(bank_lock[bank]); + + if ( gpio_pin_state[bank][pin].pin_function != DIGITAL_INPUT ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_NOT_CONFIGURED; + } + + gpio_pin_state[bank][pin].debouncing_tick_count = ticks; + gpio_pin_state[bank][pin].last_isr_tick = rtems_clock_get_ticks_per_second(); + + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_interrupt_handler_install( +uint32_t pin_number, +rtems_gpio_irq_state (*handler) (void *arg), +void *arg +) +{ + gpio_handler_list *isr_node; + gpio_pin* gpio; + uint32_t bank; + uint32_t pin; + + if ( pin_number < 0 || pin_number >= gpio_count ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + AQUIRE_LOCK(bank_lock[bank]); + + gpio = &gpio_pin_state[bank][pin]; + + /* If the current pin has no interrupt enabled + * then it does not need an handler. */ + if ( gpio->enabled_interrupt == NONE ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_NOT_CONFIGURED; + } + /* If the pin already has an enabled interrupt but the installed handler + * is set as unique. */ + else if ( gpio->handler_flag == UNIQUE_HANDLER && gpio->handler_list != NULL ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_RESOURCE_IN_USE; + } + + /* Update the pin's ISR list. */ + isr_node = (gpio_handler_list *) malloc(sizeof(gpio_handler_list)); + + if ( isr_node == NULL ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_NO_MEMORY; + } + + isr_node->handler = handler; + isr_node->arg = arg; + + if ( gpio->handler_flag == SHARED_HANDLER ) { + isr_node->next_isr = gpio->handler_list; + } + else { + isr_node->next_isr = NULL; + } + + gpio->handler_list = isr_node; + + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_enable_interrupt( +uint32_t pin_number, +rtems_gpio_interrupt interrupt, +rtems_gpio_handler_flag flag, +rtems_gpio_irq_state (*handler) (void *arg), +void *arg +) +{ + rtems_vector_number vector; + rtems_status_code sc; + gpio_pin* gpio; + uint32_t bank; + uint32_t pin; + + if ( pin_number < 0 || pin_number >= gpio_count ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + vector = rtems_bsp_gpio_get_vector(bank); + + AQUIRE_LOCK(bank_lock[bank]); + + gpio = &gpio_pin_state[bank][pin]; + + if ( gpio->pin_function != DIGITAL_INPUT ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_NOT_CONFIGURED; + } + + /* If trying to enable the same type of interrupt on the same pin, or if the pin + * already has an enabled interrupt. */ + if ( interrupt == gpio->enabled_interrupt || gpio->enabled_interrupt != NONE ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_RESOURCE_IN_USE; + } + + /* Creates and starts a new task which will call the corresponding + * user-defined handlers for this pin. */ + sc = rtems_task_create(rtems_build_name('G', 'P', 'I', 'O'), + 5, + RTEMS_MINIMUM_STACK_SIZE * 2, + RTEMS_NO_TIMESLICE, + RTEMS_DEFAULT_ATTRIBUTES, + &gpio->task_id); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_UNSATISFIED; + } + + sc = rtems_task_start(gpio->task_id, generic_handler_task, (rtems_task_argument) gpio); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(bank_lock[bank]); + + sc = rtems_task_delete(gpio->task_id); + + assert( sc == RTEMS_SUCCESSFUL ); + + return RTEMS_UNSATISFIED; + } + + gpio->enabled_interrupt = interrupt; + gpio->handler_flag = flag; + + /* Installs the interrupt handler. */ + sc = rtems_gpio_interrupt_handler_install(pin_number, handler, arg); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(bank_lock[bank]); + + sc = rtems_task_delete(gpio->task_id); + + assert( sc == RTEMS_SUCCESSFUL ); + + return RTEMS_UNSATISFIED; + } + + /* If the generic ISR has not been yet installed for this bank, installs it. + * This ISR will be responsible for calling the handler tasks, + * which in turn will call the user-defined interrupt handlers.*/ + if ( interrupt_counter[bank] == 0 ) { + sc = rtems_interrupt_handler_install(vector, + "GPIO_HANDLER", + RTEMS_INTERRUPT_UNIQUE, + (rtems_interrupt_handler) generic_isr, + gpio_pin_state[bank]); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_UNSATISFIED; + } + } + + sc = rtems_bsp_enable_interrupt(bank, pin, interrupt); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_UNSATISFIED; + } + + ++interrupt_counter[bank]; + + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_interrupt_handler_remove( +uint32_t pin_number, +rtems_gpio_irq_state (*handler) (void *arg), +void *arg +) +{ + gpio_handler_list *isr_node, *next_node; + gpio_pin* gpio; + uint32_t bank; + uint32_t pin; + + if ( pin_number < 0 || pin_number >= gpio_count ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + AQUIRE_LOCK(bank_lock[bank]); + + gpio = &gpio_pin_state[bank][pin]; + + isr_node = gpio->handler_list; + + if ( isr_node != NULL ) { + if ( isr_node->handler == handler && isr_node->arg == arg ) { + gpio->handler_list = isr_node->next_isr; + + free(isr_node); + } + else { + while ( isr_node->next_isr != NULL ) { + if ( (isr_node->next_isr)->handler == handler && (isr_node->next_isr)->arg == arg ) { + next_node = (isr_node->next_isr)->next_isr; + + free(isr_node->next_isr); + + isr_node->next_isr = next_node; + } + } + } + } + + /* If the removed handler was the last for this pin, disables further + * interrupts on this pin. */ + if ( gpio->handler_list == NULL ) { + RELEASE_LOCK(bank_lock[bank]); + + return rtems_gpio_disable_interrupt(pin_number); + } + + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_disable_interrupt(uint32_t pin_number) +{ + gpio_handler_list *isr_node; + rtems_vector_number vector; + rtems_status_code sc; + gpio_pin* gpio; + uint32_t bank; + uint32_t pin; + + if ( pin_number < 0 || pin_number >= gpio_count ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + vector = rtems_bsp_gpio_get_vector(bank); + + AQUIRE_LOCK(bank_lock[bank]); + + gpio = &gpio_pin_state[bank][pin]; + + if ( interrupt_counter[bank] == 0 || gpio->enabled_interrupt == NONE ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_SUCCESSFUL; + } + + sc = rtems_bsp_disable_interrupt(bank, pin, gpio->enabled_interrupt); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_UNSATISFIED; + } + + gpio->enabled_interrupt = NONE; + + while ( gpio->handler_list != NULL ) { + isr_node = gpio->handler_list; + + gpio->handler_list = isr_node->next_isr; + + free(isr_node); + } + + sc = rtems_task_delete(gpio->task_id); + + assert( sc == RTEMS_SUCCESSFUL ); + + --interrupt_counter[bank]; + + /* If no GPIO interrupts are left in this bank, removes the handler. */ + if ( interrupt_counter[bank] == 0 ) { + sc = rtems_interrupt_handler_remove(vector, + (rtems_interrupt_handler) generic_isr, + gpio_pin_state[bank]); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_UNSATISFIED; + } + } + + RELEASE_LOCK(bank_lock[bank]); + + return RTEMS_SUCCESSFUL; +} diff --git a/c/src/lib/libbsp/shared/include/gpio.h b/c/src/lib/libbsp/shared/include/gpio.h new file mode 100644 index 0000000..6a84747 --- /dev/null +++ b/c/src/lib/libbsp/shared/include/gpio.h @@ -0,0 +1,610 @@ +/** + * @file gpio.h + * + * @ingroup rtems_gpio + * + * @brief RTEMS GPIO API definition. + */ + +/* + * Copyright (c) 2014-2015 Andre Marques <andre.lousa.marques at gmail.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifndef LIBBSP_SHARED__GPIO_H +#define LIBBSP_SHARED__GPIO_H + +#include <rtems.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @name GPIO data structures + * + * @{ + */ + +/** + * @brief The set of possible configurations for a GPIO pull-up resistor. + * + * Enumerated type to define the possible pull-up resistor configuratons + * for a GPIO pin. + */ +typedef enum +{ + PULL_UP = 1, + PULL_DOWN, + NO_PULL_RESISTOR +} rtems_gpio_pull_mode; + +/** + * @brief The set of possible functions a pin can have. + * + * Enumerated type to define a pin function. + */ +typedef enum +{ + DIGITAL_INPUT = 0, + DIGITAL_OUTPUT, + BSP_SPECIFIC, + NOT_USED +} rtems_gpio_function; + +/** + * @brief The set of possible interrupts a GPIO pin can generate. + * + * Enumerated type to define a GPIO pin interrupt. + */ +typedef enum +{ + FALLING_EDGE = 0, + RISING_EDGE, + LOW_LEVEL, + HIGH_LEVEL, + BOTH_EDGES, + BOTH_LEVELS, + NONE +} rtems_gpio_interrupt; + +/** + * @brief The set of possible handled states an user-defined interrupt + * handler can return. + * + * Enumerated type to define an interrupt handler handled state. + */ +typedef enum +{ + IRQ_HANDLED, + IRQ_NONE +} rtems_gpio_irq_state; + +/** + * @brief The set of flags to specify an user-defined interrupt handler + * uniqueness on a GPIO pin. + * + * Enumerated type to define an interrupt handler shared flag. + */ +typedef enum +{ + SHARED_HANDLER, + UNIQUE_HANDLER +} rtems_gpio_handler_flag; + +/** + * @brief Object containing relevant information for assigning a BSP specific + * function to a pin. + * + * Encapsulates relevant data for a BSP specific GPIO function. + */ +typedef struct +{ + /* The bsp defined function code. */ + uint32_t io_function; + + void* pin_data; +} rtems_gpio_specific_data; + +/** + * @brief Object containing relevant information about a BSP's GPIO layout. + */ +typedef struct +{ + /* Total number of GPIO pins. */ + uint32_t pin_count; + + /* Number of pins per bank. The last bank may be smaller, + * depending on the total number of pins. */ + uint32_t pins_per_bank; +} rtems_gpio_layout; + +/** + * @brief Object containing configuration information + * regarding interrupts. + */ +typedef struct +{ + rtems_gpio_interrupt enabled_interrupt; + + rtems_gpio_handler_flag handler_flag; + + /* Interrupt handler function. */ + rtems_gpio_irq_state (*handler) (void *arg); + + /* Interrupt handler function arguments. */ + void *arg; + + /* Software switch debounce settings. It should contain the amount of clock + * ticks that must pass between interrupts to ensure that the interrupt + * was not caused by a switch bounce. If set to 0 this feature is disabled . */ + uint32_t clock_tick_interval; +} rtems_gpio_interrupt_conf; + +/** + * @brief Object containing configuration information + * to request/update a GPIO pin. + */ +typedef struct +{ + uint32_t pin_number; + rtems_gpio_function function; + + /* Pull resistor setting. */ + rtems_gpio_pull_mode pull_mode; + + /* If digital out pin, set to TRUE to set the pin to logical high, + * or FALSE for logical low. If not a digital out then this + * is ignored. */ + bool output_enabled; + bool logic_invert; + + /* Pin interrupt configuration. Should be NULL if not used. */ + rtems_gpio_interrupt_conf* interrupt; + + /* Struct with bsp specific data, to use during the pin request. + * If function == BSP_SPECIFIC this should have a pointer to + * a rtems_gpio_specific_data struct. + * + * If not this field may be NULL. This is passed to the bsp function so any bsp specific data + * can be passed to it through this pointer. */ + void* bsp_specific; +} rtems_gpio_pin_conf; + +/** @} */ + +/** + * @name gpio Usage + * + * @{ + */ + +/** + * @brief Initializes the GPIO API. + * + * @retval RTEMS_SUCCESSFUL API successfully initialized. + * @retval * For other error code see @rtems_semaphore_create(). + */ +extern rtems_status_code rtems_gpio_initialize(void); + +/** + * @brief Requests a GPIO pin configuration from the API. + * It may be used either to request a new pin, or to update the + * configuration/functions of a pin currently in use. + * + * @param[in] conf rtems_gpio_pin_conf structure filled with the pin information + * and desired configurations. + * + * @retval RTEMS_SUCCESSFUL Pin was configured successfully. + * @retval RTEMS_UNSATISFIED Could not request/update the pin's configuration. + */ +extern rtems_status_code rtems_gpio_request_conf(rtems_gpio_pin_conf* conf); + +/** + * @brief Sets multiple output GPIO pins with the logical high. + * + * @param[in] count Number of GPIO pins to set. + * @param[in] ... Comma-separated list of GPIO pin numbers. + * + * @retval RTEMS_SUCCESSFUL All pins were set successfully. + * @retval RTEMS_INVALID_ID At least one pin number is invalid. + * @retval RTEMS_NOT_CONFIGURED At least one of the received pins + * is not configured as a digital output. + * @retval RTEMS_UNSATISFIED Could not set the GPIO pins. + */ +extern rtems_status_code rtems_gpio_multi_set(uint32_t count, ...); + +/** + * @brief Sets multiple output GPIO pins with the logical low. + * + * @param[in] count Number of GPIO pins to clear. + * @param[in] ... Comma-separated list of GPIO pin numbers. + * + * @retval RTEMS_SUCCESSFUL All pins were cleared successfully. + * @retval RTEMS_INVALID_ID At least one pin number is invalid. + * @retval RTEMS_NOT_CONFIGURED At least one of the received pins + * is not configured as a digital output. + * @retval RTEMS_UNSATISFIED Could not clear the GPIO pins. + */ +extern rtems_status_code rtems_gpio_multi_clear(uint32_t count, ...); + +/** + * @brief Sets an output GPIO pin with the logical high. + * + * @param[in] pin_number GPIO pin number. + * + * @retval RTEMS_SUCCESSFUL Pin was set successfully. + * @retval RTEMS_INVALID_ID Pin number is invalid. + * @retval RTEMS_NOT_CONFIGURED The received pin is not configured + * as a digital output. + * @retval RTEMS_UNSATISFIED Could not set the GPIO pin. + */ +extern rtems_status_code rtems_gpio_set(uint32_t pin_number); + +/** + * @brief Sets an output GPIO pin with the logical low. + * + * @param[in] pin_number GPIO pin number. + * + * @retval RTEMS_SUCCESSFUL Pin was cleared successfully. + * @retval RTEMS_INVALID_ID Pin number is invalid. + * @retval RTEMS_NOT_CONFIGURED The received pin is not configured + * as a digital output. + * @retval RTEMS_UNSATISFIED Could not clear the GPIO pin. + */ +extern rtems_status_code rtems_gpio_clear(uint32_t pin_number); + +/** + * @brief Returns the value (level) of a GPIO input pin. + * + * @param[in] pin_number GPIO pin number. + * + * @retval The function returns 0 or 1 depending on the pin current + * logical value. + * @retval -1 Pin number is invalid, or not a digital input pin. + */ +extern int rtems_gpio_get_value(uint32_t pin_number); + +/** + * @brief Assigns a certain function to a GPIO pin. + * + * @param[in] pin_number GPIO pin number. + * @param[in] function The new function for the pin. + * @param[in] output_enabled If TRUE and @var function is DIGITAL_OUTPUT, + * then the pin is set with the logical high. + * Otherwise it is set with logical low. + * @param[in] logic_invert Reverses the digital I/O logic for DIGITAL_INPUT + * and DIGITAL_OUTPUT pins. + * @param[in] bsp_specific Pointer to a bsp defined structure with bsp-specific + * data. This field is not handled by the API. + * + * @retval RTEMS_SUCCESSFUL Pin was configured successfully. + * @retval RTEMS_INVALID_ID Pin number is invalid. + * @retval RTEMS_RESOURCE_IN_USE The received pin is already being used. + * @retval RTEMS_UNSATISFIED Could not assign the GPIO function. + * @retval RTEMS_NOT_DEFINED GPIO function not defined, or NOT_USED. + */ +extern rtems_status_code rtems_gpio_request_pin(uint32_t pin_number, rtems_gpio_function function, bool output_enable, bool logic_invert, void* bsp_specific); + +/** + * @brief Configures a single GPIO pin pull resistor. + * + * @param[in] pin_number GPIO pin number. + * @param[in] mode The pull resistor mode. + * + * @retval RTEMS_SUCCESSFUL Pull resistor successfully configured. + * @retval RTEMS_INVALID_ID Pin number is invalid. + * @retval RTEMS_UNSATISFIED Could not set the pull mode. + */ +extern rtems_status_code rtems_gpio_resistor_mode(uint32_t pin_number, rtems_gpio_pull_mode mode); + +/** + * @brief Releases a GPIO pin from the API, making it available to be used + * again. + * + * @param[in] pin_number GPIO pin number. + * + * @retval RTEMS_SUCCESSFUL Pin successfully disabled on the API. + * @retval RTEMS_INVALID_ID Pin number is invalid. + * @retval * Could not disable an ative interrupt on this pin, + * @see rtems_gpio_disable_interrupt(). + */ +extern rtems_status_code rtems_gpio_release_pin(uint32_t pin_number); + +/** + * @brief Attaches a debouncing function to a given pin/switch. + * Debouncing is done by requiring a certain number of clock ticks to + * pass between interrupts. Any interrupt fired too close to the last + * will be ignored as it is probably the result of an involuntary + * switch/button bounce after being released. + * + * @param[in] pin_number GPIO pin number. + * @param[in] ticks Minimum number of clock ticks that must pass between + * interrupts so it can be considered a legitimate + * interrupt. + * + * @retval RTEMS_SUCCESSFUL De-bounce function successfully attached to the pin. + * @retval RTEMS_INVALID_ID Pin number is invalid. + * @retval RTEMS_NOT_CONFIGURED The current pin is not configured as a digital + * input, hence it can not be connected to a switch. + */ +extern rtems_status_code rtems_gpio_debounce_switch(uint32_t pin_number, int ticks); + +/** + * @brief Connects a new user-defined interrupt handler to a given pin. + * + * @param[in] pin_number GPIO pin number. + * @param[in] handler Pointer to a function that will be called every time + * the enabled interrupt for the given pin is generated. + * This function must return information about its + * handled/unhandled state. + * @param[in] arg Void pointer to the arguments of the user-defined handler. + * + * @retval RTEMS_SUCCESSFUL Handler successfully connected to this pin. + * @retval RTEMS_NO_MEMORY Could not connect more user-defined handlers to + * the given pin. + * @retval RTEMS_NOT_CONFIGURED The given pin has no interrupt configured. + * @retval RTEMS_INVALID_ID Pin number is invalid. + * @retval RTEMS_RESOURCE_IN_USE The current user-defined handler for this pin + * is unique. + */ +extern rtems_status_code rtems_gpio_interrupt_handler_install( +uint32_t pin_number, +rtems_gpio_irq_state (*handler) (void *arg), +void *arg +); + +/** + * @brief Enables interrupts to be generated on a given GPIO pin. + * When fired that interrupt will call the given handler. + * + * @param[in] pin_number GPIO pin number. + * @param[in] interrupt Type of interrupt to enable for the pin. + * @param[in] handler Pointer to a function that will be called every time + * @var interrupt is generated. This function must return + * information about its handled/unhandled state. + * @param[in] arg Void pointer to the arguments of the user-defined handler. + * + * @retval RTEMS_SUCCESSFUL Interrupt successfully enabled for this pin. + * @retval RTEMS_UNSATISFIED Could not install the GPIO ISR, create/start + * the handler task, or enable the interrupt + * on the pin. + * @retval RTEMS_INVALID_ID Pin number is invalid. + * @retval RTEMS_RESOURCE_IN_USE The pin already has an enabled interrupt. + */ +extern rtems_status_code rtems_gpio_enable_interrupt( +uint32_t pin_number, +rtems_gpio_interrupt interrupt, +rtems_gpio_handler_flag flag, +rtems_gpio_irq_state (*handler) (void *arg), +void *arg +); + +/** + * @brief Disconnects an user-defined interrupt handler from the given pin. + * If in the end there are no more user-defined handlers connected + * to the pin, interrupts are disabled on the given pin. + * + * @param[in] pin_number GPIO pin number. + * @param[in] handler Pointer to the user-defined handler + * @param[in] arg Void pointer to the arguments of the user-defined handler. + * + * @retval RTEMS_SUCCESSFUL Handler successfully disconnected from this pin. + * @retval RTEMS_INVALID_ID Pin number is invalid. + * @retval * @see rtems_gpio_disable_interrupt() + */ +rtems_status_code rtems_gpio_interrupt_handler_remove( +uint32_t pin_number, +rtems_gpio_irq_state (*handler) (void *arg), +void *arg +); + +/** + * @brief Stops interrupts from being generated on a given GPIO pin + * and removes the corresponding handler. + * + * @param[in] pin_number GPIO pin number. + * + * @retval RTEMS_SUCCESSFUL Interrupt successfully disabled for this pin. + * @retval RTEMS_INVALID_ID Pin number is invalid. + * @retval RTEMS_UNSATISFIED Could not remove the current interrupt handler, + * could not recognise the current active interrupt + * on this pin or could not disable interrupts on + * this pin. + */ +extern rtems_status_code rtems_gpio_disable_interrupt(uint32_t pin_number); + +/** + * @brief Defines the GPIO pin layout. This must be implemented by each BSP. + */ +extern rtems_gpio_layout rtems_bsp_gpio_initialize(void); + +/** + * @brief Sets multiple output GPIO pins with the logical high. This must be implemented + * by each BSP. + * + * @param[in] bank GPIO bank number. + * @param[in] bitmask Bitmask of GPIO pins to set in the given bank. + * + * @retval RTEMS_SUCCESSFUL All pins were set successfully. + * @retval RTEMS_UNSATISFIED Could not set at least one of the pins. + */ +extern rtems_status_code rtems_bsp_gpio_multi_set(uint32_t bank, uint32_t bitmask); + +/** + * @brief Sets multiple output GPIO pins with the logical low. This must be implemented + * by each BSP. + * + * @param[in] bank GPIO bank number. + * @param[in] bitmask Bitmask of GPIO pins to clear in the given bank. + * + * @retval RTEMS_SUCCESSFUL All pins were cleared successfully. + * @retval RTEMS_UNSATISFIED Could not clear at least one of the pins. + */ +extern rtems_status_code rtems_bsp_gpio_multi_clear(uint32_t bank, uint32_t bitmask); + +/** + * @brief Sets an output GPIO pin with the logical high. This must be implemented + * by each BSP. + * + * @param[in] bank GPIO bank number. + * @param[in] pin GPIO pin number within the given bank. + * + * @retval RTEMS_SUCCESSFUL Pin was set successfully. + * @retval RTEMS_UNSATISFIED Could not set the given pin. + */ +extern rtems_status_code rtems_bsp_gpio_set(uint32_t bank, uint32_t pin); + +/** + * @brief Sets an output GPIO pin with the logical low. This must be implemented + * by each BSP. + * + * @param[in] bank GPIO bank number. + * @param[in] pin GPIO pin number within the given bank. + * + * @retval RTEMS_SUCCESSFUL Pin was cleared successfully. + * @retval RTEMS_UNSATISFIED Could not clear the given pin. + */ +extern rtems_status_code rtems_bsp_gpio_clear(uint32_t bank, uint32_t pin); + +/** + * @brief Returns the value (level) of a GPIO input pin. This must be implemented + * by each BSP. + * + * @param[in] bank GPIO bank number. + * @param[in] pin GPIO pin number within the given bank. + * + * @retval The function must return 0 or 1 depending on the pin current + * logical value. + * @retval -1 Could not read the pin level. + */ +extern int rtems_bsp_gpio_get_value(uint32_t bank, uint32_t pin); + +/** + * @brief Assigns the digital input function to the given pin. + * This must be implemented by each BSP. + * + * @param[in] bank GPIO bank number. + * @param[in] pin GPIO pin number within the given bank. + * @param[in] bsp_specific Pointer to a bsp defined structure with bsp-specific + * data. + * + * @retval RTEMS_SUCCESSFUL Function was asssigned successfully. + * @retval RTEMS_UNSATISFIED Could not assign the function to the pin. + */ +extern rtems_status_code rtems_bsp_gpio_select_input(uint32_t bank, uint32_t pin, void* bsp_specific); + +/** + * @brief Assigns the digital output function to the given pin. + * This must be implemented by each BSP. + * + * @param[in] bank GPIO bank number. + * @param[in] pin GPIO pin number within the given bank. + * @param[in] bsp_specific Pointer to a bsp defined structure with bsp-specific + * data. + * + * @retval RTEMS_SUCCESSFUL Function was asssigned successfully. + * @retval RTEMS_UNSATISFIED Could not assign the function to the pin. + */ +extern rtems_status_code rtems_bsp_gpio_select_output(uint32_t bank, uint32_t pin, void* bsp_specific); + +/** + * @brief Assigns a bsp specific function to the given pin. + * This must be implemented by each BSP. + * + * @param[in] bank GPIO bank number. + * @param[in] pin GPIO pin number within the given bank. + * @param[in] function Bsp defined GPIO function. + * @param[in] pin_data Pointer to a bsp defined structure with bsp-specific + * data. + * + * @retval RTEMS_SUCCESSFUL Function was asssigned successfully. + * @retval RTEMS_UNSATISFIED Could not assign the function to the pin. + */ +extern rtems_status_code rtems_bsp_select_specific_io(uint32_t bank, uint32_t pin, uint32_t function, void* pin_data); + +/** + * @brief Configures a single GPIO pin pull resistor. + * This must be implemented by each BSP. + * + * @param[in] bank GPIO bank number. + * @param[in] pin GPIO pin number within the given bank. + * @param[in] mode The pull resistor mode. + * + * @retval RTEMS_SUCCESSFUL Pull resistor successfully configured. + * @retval RTEMS_UNSATISFIED Could not set the pull mode. + */ +extern rtems_status_code rtems_bsp_gpio_set_resistor_mode(uint32_t bank, uint32_t pin, rtems_gpio_pull_mode mode); + +/** + * @brief Reads and returns a vector/bank interrupt event line. + * This must be implemented by each BSP. + * + * @param[in] vector GPIO vector/bank. + * + * @retval Bitmask (max 32-bit) representing a GPIO bank, where a bit set + * indicates an active interrupt on that pin. + */ +extern uint32_t rtems_bsp_gpio_interrupt_line(rtems_vector_number vector); + +/** + * @brief Clears a vector/bank interrupt event line. + * This must be implemented by each BSP. + * + * @param[in] vector GPIO vector/bank. + * @param[in] event_status Bitmask with the processed interrupts on the given + * vector. This bitmask is the same as calculated in + * @see rtems_bsp_gpio_interrupt_line(). + */ +extern void rtems_bsp_gpio_clear_interrupt_line(rtems_vector_number vector, uint32_t event_status); + +/** + * @brief Calculates a vector number for a given GPIO bank. + * This must be implemented by each BSP. + * + * @param[in] bank GPIO bank number. + * + * @retval The corresponding rtems_vector_number. + */ +extern rtems_vector_number rtems_bsp_gpio_get_vector(uint32_t bank); + +/** + * @brief Enables interrupts to be generated on a given GPIO pin. + * This must be implemented by each BSP. + * + * @param[in] bank GPIO bank number. + * @param[in] pin GPIO pin number within the given bank. + * @param[in] interrupt Type of interrupt to enable for the pin. + * + * @retval RTEMS_SUCCESSFUL Interrupt successfully enabled for this pin. + * @retval RTEMS_UNSATISFIED Could not enable the interrupt on the pin. + */ +extern rtems_status_code rtems_bsp_enable_interrupt(uint32_t bank, uint32_t pin, rtems_gpio_interrupt interrupt); + +/** + * @brief Disables interrupt on hardware. This must be implemented by each BSP. + */ + +/** + * @brief Stops interrupts from being generated on a given GPIO pin. + * This must be implemented by each BSP. + * + * @param[in] bank GPIO bank number. + * @param[in] pin GPIO pin number within the given bank. + * @param[in] enabled_interrupt Interrupt type currently active on this pin. + * + * @retval RTEMS_SUCCESSFUL Interrupt successfully disabled for this pin. + * @retval RTEMS_UNSATISFIED Could not disable interrupts on this pin. + */ +extern rtems_status_code rtems_bsp_disable_interrupt(uint32_t bank, uint32_t pin, rtems_gpio_interrupt enabled_interrupt); + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LIBBSP_SHARED__GPIO_H */ -- 2.0.5 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel