From 87f8b01f58993ec40b2939db5df02fad56fa07d9 Mon Sep 17 00:00:00 2001 From: Andre Marques Date: Mon, 27 Jul 2015 17:01:43 +0100 Subject: RTEMS GPIO API definition and implementation. Changes relative to the previous patch set: - Moved GPIO pin interrupts to rtems chains, instead of a local linked list; - Restructured the pin tracking structure, separating the interrupt information for each pin meaning that a pin without any interrupt enabled only requires 8 bytes, while keeping interrupt information (handling information, handler chain control, ...) requires 24 additional bytes (total of 32 bytes per pin with interrupts enabled); - Added support for 'parallel' pin function assignment, allowing the function assignment to be set for multiple pins in a single GPIO hardware call. If a BSP does not support this feature it becomes a sequence of individual calls per pin. Also added support for GPIO pin groupings, allowing to write and read byte data to a series of pins which behave as a single entity; - Added bank tracking structure to maintain the bank lock and bank level interrupt information (threaded/normal handling, interrupt counter); - Changed GPIO settings to BSP defined constants, reducing dynamic memory allocation; - Switched interrupt tasks for a rtems interrupt server, with the possibility of using normal interrupts (user handlers being called within ISR context). --- c/src/lib/libbsp/Makefile.am | 3 +- c/src/lib/libbsp/preinstall.am | 4 + c/src/lib/libbsp/shared/gpio.c | 1977 ++++++++++++++++++++++++++++++++ c/src/lib/libbsp/shared/include/gpio.h | 948 +++++++++++++++ 4 files changed, 2931 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 3cab4d7cfa..a039a98b8d 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 651f1360a2..bbcb7c5da0 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 0000000000..a87b39fa29 --- /dev/null +++ b/c/src/lib/libbsp/shared/gpio.c @@ -0,0 +1,1977 @@ +/** + * @file gpio.c + * + * @ingroup rtems_gpio + * + * @brief RTEMS GPIO API implementation. + */ + +/* + * Copyright (c) 2014-2015 Andre Marques + * + * 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 +#include +#include +#include +#include +#include + +/** + * @brief GPIO API mutex attributes. + */ +#define MUTEX_ATTRIBUTES ( \ + RTEMS_LOCAL \ + | RTEMS_PRIORITY \ + | RTEMS_BINARY_SEMAPHORE \ + | RTEMS_INHERIT_PRIORITY \ + | RTEMS_NO_PRIORITY_CEILING \ +) + +#define CREATE_LOCK(name, lock_id) rtems_semaphore_create( \ + name, \ + 1, \ + MUTEX_ATTRIBUTES, \ + 0, \ + lock_id \ +) + +#define ACQUIRE_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 ) + +/** + * @brief Object containing relevant information about a GPIO group. + * + * Encapsulates relevant data for a GPIO pin group. + */ +struct rtems_gpio_group +{ + rtems_chain_node node; + + uint32_t *digital_inputs; + uint32_t digital_input_bank; + uint32_t input_count; + + uint32_t *digital_outputs; + uint32_t digital_output_bank; + uint32_t output_count; + + uint32_t *bsp_speficifc_pins; + uint32_t bsp_specific_bank; + uint32_t bsp_specific_pin_count; + + rtems_id group_lock; +}; + +/** + * @brief Object containing relevant information to a list of user-defined + * interrupt handlers. + * + * Encapsulates relevant data for a GPIO interrupt handler. + */ +typedef struct +{ + rtems_chain_node node; + + /* User-defined ISR routine. */ + rtems_gpio_irq_state (*handler) (void *arg); + + /* User-defined arguments for the ISR routine. */ + void *arg; +} gpio_handler_node; + +/** + * @brief Object containing relevant information of a pin's interrupt + * configuration/state. + * + * Encapsulates relevant data of a GPIO pin interrupt state. + */ +typedef struct +{ + /* Currently active interrupt. */ + rtems_gpio_interrupt active_interrupt; + + /* ISR shared flag. */ + rtems_gpio_handler_flag handler_flag; + + /* Linked list of interrupt handlers. */ + rtems_chain_control handler_chain; + + /* Switch-deboucing information. */ + uint32_t debouncing_tick_count; + rtems_interval last_isr_tick; +} gpio_pin_interrupt_state; + +/** + * @brief Object containing information on a GPIO pin. + * + * Encapsulates relevant data about a GPIO pin. + */ +typedef struct +{ + rtems_gpio_function pin_function; + + /* GPIO pull resistor configuration. */ + rtems_gpio_pull_mode resistor_mode; + + /* If true inverts digital in/out applicational logic. */ + bool logic_invert; + + /* True if the pin is on a group. */ + bool on_group; + + /* Interrupt data for a pin. This field is NULL if no interrupt is enabled + * on the pin. */ + gpio_pin_interrupt_state *interrupt_state; +} gpio_pin; + +/** + * @brief Object containing relevant information regarding a GPIO bank state. + * + * Encapsulates relevant data for a GPIO bank. + */ +typedef struct +{ + uint32_t bank_number; + uint32_t interrupt_counter; + rtems_id lock; + + /* If TRUE the interrupts on the bank will be called + * by a rtems interrupt server, otherwise they will be handled + * in the normal ISR context. */ + bool threaded_interrupts; +} gpio_bank; + +static gpio_pin gpio_pin_state[BSP_GPIO_PIN_COUNT]; +static Atomic_Flag init_flag = ATOMIC_INITIALIZER_FLAG; +static gpio_bank gpio_bank_state[GPIO_BANK_COUNT]; +static Atomic_Uint threaded_interrupt_counter = ATOMIC_INITIALIZER_UINT(0); +static rtems_chain_control gpio_group; + +#define BANK_NUMBER(pin_number) pin_number / BSP_GPIO_PINS_PER_BANK +#define PIN_NUMBER(pin_number) pin_number % BSP_GPIO_PINS_PER_BANK + +static int debounce_switch(gpio_pin_interrupt_state *interrupt_state) +{ + rtems_interval time; + + time = rtems_clock_get_ticks_since_boot(); + + /* If not enough time has elapsed since last interrupt. */ + if ( + (time - interrupt_state->last_isr_tick) < + interrupt_state->debouncing_tick_count + ) { + return -1; + } + + interrupt_state->last_isr_tick = time; + + return 0; +} + +/* Returns the amount of pins in a bank. */ +static uint32_t get_bank_pin_count(uint32_t bank) +{ + /* If the current bank is the last bank, which may not be completely filled. */ + if ( bank == GPIO_BANK_COUNT - 1 ) { + return GPIO_LAST_BANK_PINS; + } + + return BSP_GPIO_PINS_PER_BANK; +} + +/* GPIO generic bank ISR. This may be called directly as response to an + * interrupt, or by the rtems interrupt server task if the GPIO bank + * uses threading interrupt handling. */ +static void generic_bank_isr(void *arg) +{ + gpio_pin_interrupt_state *interrupt_state; + rtems_chain_control *handler_list; + rtems_chain_node *node; + rtems_chain_node *next_node; + gpio_handler_node *isr_node; + rtems_vector_number vector; + uint32_t event_status; + uint32_t bank_number; + uint32_t bank_start_pin; + uint8_t handled_count; + uint8_t rv; + uint8_t i; + + bank_number = *((uint32_t*) arg); + + assert ( bank_number >= 0 && bank_number < GPIO_BANK_COUNT ); + + /* Calculate bank start address in the pin_state array. */ + bank_start_pin = bank_number * BSP_GPIO_PINS_PER_BANK; + + vector = rtems_gpio_bsp_get_vector(bank_number); + + /* If this bank does not use threaded interrupts we have to + * disable the vector. Otherwise the interrupt server does it. */ + if ( gpio_bank_state[bank_number].threaded_interrupts == false ) { + /* Prevents more interrupts from being generated on GPIO. */ + bsp_interrupt_vector_disable(vector); + } + + /* Obtains a 32-bit bitmask, with the pins currently reporting interrupts + * signaled with 1. */ + event_status = rtems_gpio_bsp_interrupt_line(vector); + + /* Iterates through the bitmask and calls the corresponding handler + * for active interrupts. */ + for ( i = 0; i < get_bank_pin_count(bank_number); ++i ) { + /* If active, wake the corresponding pin's ISR task. */ + if ( event_status & (1 << i) ) { + interrupt_state = gpio_pin_state[bank_start_pin + i].interrupt_state; + + assert ( interrupt_state != NULL ); + + handled_count = 0; + + if ( gpio_bank_state[bank_number].threaded_interrupts ) { + ACQUIRE_LOCK(gpio_bank_state[bank_number].lock); + } + + /* If this pin has the debouncing function attached, call it. */ + if ( interrupt_state->debouncing_tick_count > 0 ) { + rv = debounce_switch(interrupt_state); + + /* If the handler call was caused by a switch bounce, + * ignores and move on. */ + if ( rv < 0 ) { + if ( gpio_bank_state[bank_number].threaded_interrupts ) { + RELEASE_LOCK(gpio_bank_state[bank_number].lock); + } + + continue; + } + } + + handler_list = &interrupt_state->handler_chain; + + node = rtems_chain_first(handler_list); + + /* Iterate the ISR list. */ + while ( !rtems_chain_is_tail(handler_list, node) ) { + isr_node = (gpio_handler_node *) node; + + next_node = node->next; + + if ( (isr_node->handler)(isr_node->arg) == IRQ_HANDLED ) { + ++handled_count; + } + + node = next_node; + } + + /* If no handler assumed the interrupt, + * treat it as a spurious interrupt. */ + if ( handled_count == 0 ) { + bsp_interrupt_handler_default(rtems_gpio_bsp_get_vector(bank_number)); + } + + if ( gpio_bank_state[bank_number].threaded_interrupts ) { + RELEASE_LOCK(gpio_bank_state[bank_number].lock); + } + } + } + + if ( gpio_bank_state[bank_number].threaded_interrupts == false ) { + bsp_interrupt_vector_enable(vector); + } +} + +/* Verifies if all pins in the received pin array are from the same bank and + * have the defined GPIO function. Produces bitmask of the received pins. */ +static rtems_status_code get_pin_bitmask( + uint32_t *pins, + uint32_t pin_count, + uint32_t *bank_number, + uint32_t *bitmask, + rtems_gpio_function function +) { + uint32_t pin_number; + uint32_t bank; + uint8_t i; + + if ( pin_count < 1 ) { + return RTEMS_UNSATISFIED; + } + + *bitmask = 0; + + for ( i = 0; i < pin_count; ++i ) { + pin_number = pins[i]; + + if ( pin_number < 0 || pin_number >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + + if ( i == 0 ) { + *bank_number = bank; + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + } + else if ( bank != *bank_number ) { + RELEASE_LOCK(gpio_bank_state[*bank_number].lock); + + return RTEMS_UNSATISFIED; + } + + if ( + gpio_pin_state[pin_number].pin_function != function || + gpio_pin_state[pin_number].on_group + ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_NOT_CONFIGURED; + } + + *bitmask |= (1 << PIN_NUMBER(pin_number)); + } + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_SUCCESSFUL; +} + +static rtems_status_code check_same_bank_and_availability( + const rtems_gpio_pin_conf *pin_confs, + uint32_t pin_count, + uint32_t *bank_number, + uint32_t *pins +) { + uint32_t pin_number; + uint32_t bank; + uint8_t i; + + for ( i = 0; i < pin_count; ++i ) { + pin_number = pin_confs[i].pin_number; + + bank = BANK_NUMBER(pin_number); + + if ( i == 0 ) { + *bank_number = bank; + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + } + else if ( bank != *bank_number ) { + RELEASE_LOCK(gpio_bank_state[*bank_number].lock); + + return RTEMS_UNSATISFIED; + } + + if ( gpio_pin_state[pin_number].pin_function != NOT_USED ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_RESOURCE_IN_USE; + } + + pins[i] = PIN_NUMBER(pin_number); + } + + RELEASE_LOCK(gpio_bank_state[*bank_number].lock); + + return RTEMS_SUCCESSFUL; +} + +static rtems_status_code setup_resistor_and_interrupt_configuration( + uint32_t pin_number, + rtems_gpio_pull_mode pull_mode, + rtems_gpio_interrupt_configuration *interrupt_conf +) { + gpio_pin_interrupt_state *interrupt_state; + rtems_status_code sc; + uint32_t bank; + + sc = rtems_gpio_resistor_mode(pin_number, pull_mode); + + if ( sc != RTEMS_SUCCESSFUL ) { +#if defined(DEBUG) + printk("rtems_gpio_resistor_mode failed with status code %d\n", sc); +#endif + + return RTEMS_UNSATISFIED; + } + + if ( interrupt_conf != NULL ) { + bank = BANK_NUMBER(pin_number); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + sc = rtems_gpio_enable_interrupt( + pin_number, + interrupt_conf->active_interrupt, + interrupt_conf->handler_flag, + interrupt_conf->threaded_interrupts, + interrupt_conf->handler, + interrupt_conf->arg + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + +#if defined(DEBUG) + printk( + "rtems_gpio_enable_interrupt failed with status code %d\n", + sc + ); +#endif + + return RTEMS_UNSATISFIED; + } + + interrupt_state = gpio_pin_state[pin_number].interrupt_state; + + interrupt_state->debouncing_tick_count = + interrupt_conf->debounce_clock_tick_interval; + + interrupt_state->last_isr_tick = 0; + + RELEASE_LOCK(gpio_bank_state[bank].lock); + } + + return RTEMS_SUCCESSFUL; +} + +static rtems_status_code gpio_multi_select( + const rtems_gpio_pin_conf *pins, + uint8_t pin_count, + bool on_group +) { + rtems_status_code sc; + uint32_t pin_number; + uint32_t bank; + uint8_t i; + + /* If the BSP has multi select capabilities. */ +#ifdef BSP_GPIO_PINS_PER_SELECT_BANK + rtems_gpio_multiple_pin_select + pin_data[GPIO_SELECT_BANK_COUNT][BSP_GPIO_PINS_PER_SELECT_BANK]; + rtems_gpio_specific_data *bsp_data; + + /* Since each platform may have more than two functions to assign to a pin, + * each pin requires more than one bit in the selection register to + * properly assign a function to it. + * Therefore a selection bank (pin selection register) will support fewer pins + * than a regular bank, meaning that there will be more selection banks than + * regular banks, which have to be handled separately. + * + * This field records the select bank number relative to the GPIO bank. */ + uint32_t select_bank; + uint32_t bank_number; + uint32_t select_bank_counter[GPIO_SELECT_BANK_COUNT]; + uint32_t select_count; + uint32_t pin; + + if ( pin_count == 0 ) { + return RTEMS_SUCCESSFUL; + } + + for ( i = 0; i < GPIO_SELECT_BANK_COUNT; ++i ) { + select_bank_counter[i] = 0; + } + + for ( i = 0; i < pin_count; ++i ) { + pin_number = pins[i].pin_number; + + if ( pin_number < 0 || pin_number >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + if ( i == 0 ) { + bank_number = bank; + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + } + else if ( bank != bank_number ) { + RELEASE_LOCK(gpio_bank_state[bank_number].lock); + + return RTEMS_UNSATISFIED; + } + + /* If the pin is already being used returns with an error. */ + if ( gpio_pin_state[pin_number].pin_function != NOT_USED ) { + RELEASE_LOCK(gpio_bank_state[bank_number].lock); + + return RTEMS_RESOURCE_IN_USE; + } + + select_bank = (pin_number / BSP_GPIO_PINS_PER_SELECT_BANK) - + (bank * GPIO_SELECT_BANK_COUNT); + + select_count = select_bank_counter[select_bank]; + + pin_data[select_bank][select_count].pin_number = pin_number; + pin_data[select_bank][select_count].function = pins[i].function; + + if ( pins[i].function == BSP_SPECIFIC ) { + bsp_data = (rtems_gpio_specific_data *) pins[i].bsp_specific; + + if ( bsp_data == NULL ) { + RELEASE_LOCK(gpio_bank_state[bank_number].lock); + + return RTEMS_UNSATISFIED; + } + + pin_data[select_bank][select_count].io_function = bsp_data->io_function; + pin_data[select_bank][select_count].bsp_specific = bsp_data->pin_data; + } + else { + /* io_function takes a dummy value, as it will not be used. */ + pin_data[select_bank][select_count].io_function = 0; + pin_data[select_bank][select_count].bsp_specific = pins[i].bsp_specific; + } + + ++select_bank_counter[select_bank]; + } + + for ( i = 0; i < GPIO_SELECT_BANK_COUNT; ++i ) { + if ( select_bank_counter[i] == 0 ) { + continue; + } + + sc = rtems_gpio_bsp_multi_select( + pin_data[i], select_bank_counter[i], i + + (bank_number * GPIO_SELECT_BANK_COUNT) + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank_number].lock); + + return sc; + } + } + + for ( i = 0; i < pin_count; ++i ) { + pin_number = pins[i].pin_number; + + /* Fill other pin state information. */ + gpio_pin_state[pin_number].pin_function = pins[i].function; + gpio_pin_state[pin_number].logic_invert = pins[i].logic_invert; + gpio_pin_state[pin_number].on_group = on_group; + + sc = setup_resistor_and_interrupt_configuration( + pin_number, + pins[i].pull_mode, + pins[i].interrupt + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank_number].lock); + + return sc; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + if ( pins[i].function == DIGITAL_OUTPUT ) { + if ( pins[i].output_enabled == true ) { + sc = rtems_gpio_bsp_set(bank, pin); + } + else { + sc = rtems_gpio_bsp_clear(bank, pin); + } + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank_number].lock); + + return sc; + } + } + } + + RELEASE_LOCK(gpio_bank_state[bank_number].lock); + + return sc; + + /* If the BSP does not provide pin multi-selection, + * configures each pin sequentially. */ +#else + for ( i = 0; i < pin_count; ++i ) { + pin_number = pins[i].pin_number; + + if ( pin_number < 0 || pin_number >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + /* If the pin is already being used returns with an error. */ + if ( gpio_pin_state[pin_number].pin_function != NOT_USED ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_RESOURCE_IN_USE; + } + } + + for ( i = 0; i < pin_count; ++i ) { + sc = rtems_gpio_request_configuration(&pins[i]); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + + gpio_pin_state[pins[i].pin_number].on_group = on_group; + } + + return RTEMS_SUCCESSFUL; +#endif +} + +rtems_status_code rtems_gpio_initialize(void) +{ + rtems_status_code sc; + uint32_t i; + + if ( _Atomic_Flag_test_and_set(&init_flag, ATOMIC_ORDER_RELAXED) == true ) { + return RTEMS_SUCCESSFUL; + } + + for ( i = 0; i < GPIO_BANK_COUNT; ++i ) { + sc = CREATE_LOCK( + rtems_build_name('G', 'I', 'N', 'T'), + &gpio_bank_state[i].lock + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + + gpio_bank_state[i].bank_number = i; + gpio_bank_state[i].interrupt_counter = 0; + + /* The threaded_interrupts field is initialized during + * rtems_gpio_enable_interrupt(), as its value is never used before. */ + } + + for ( i = 0; i < BSP_GPIO_PIN_COUNT; ++i ) { + gpio_pin_state[i].pin_function = NOT_USED; + gpio_pin_state[i].resistor_mode = NO_PULL_RESISTOR; + gpio_pin_state[i].logic_invert = false; + gpio_pin_state[i].on_group = false; + gpio_pin_state[i].interrupt_state = NULL; + } + + /* Initialize GPIO groups chain. */ + rtems_chain_initialize_empty(&gpio_group); + + return RTEMS_SUCCESSFUL; +} + +rtems_gpio_group *rtems_gpio_create_pin_group(void) +{ + struct rtems_gpio_group *group; + + group = (struct rtems_gpio_group *) malloc(sizeof(struct rtems_gpio_group)); + + return group; +} + +rtems_status_code rtems_gpio_define_pin_group( + const rtems_gpio_group_definition *group_definition, + rtems_gpio_group *group +) { + rtems_status_code sc; + + if ( group_definition == NULL || group == NULL ) { + return RTEMS_UNSATISFIED; + } + + if ( + group_definition->input_count == 0 && + group_definition->output_count == 0 && + group_definition->bsp_specific_pin_count == 0 + ) { + return RTEMS_UNSATISFIED; + } + + group->input_count = group_definition->input_count; + + if ( group->input_count > 0 ) { + group->digital_inputs = + (uint32_t *) malloc(group->input_count * sizeof(uint32_t)); + + /* Evaluate if the pins that will constitute the group are available and + * that pins with the same function within the group all belong + * to the same pin group. */ + sc = check_same_bank_and_availability( + group_definition->digital_inputs, + group->input_count, + &group->digital_input_bank, + group->digital_inputs + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + } + else { + group->digital_inputs = NULL; + } + + group->output_count = group_definition->output_count; + + if ( group->output_count > 0 ) { + group->digital_outputs = + (uint32_t *) malloc(group->output_count * sizeof(uint32_t)); + + sc = check_same_bank_and_availability( + group_definition->digital_outputs, + group->output_count, + &group->digital_output_bank, + group->digital_outputs + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + } + else { + group->digital_outputs = NULL; + } + + group->bsp_specific_pin_count = group_definition->bsp_specific_pin_count; + + if ( group->bsp_specific_pin_count > 0 ) { + group->bsp_speficifc_pins = + (uint32_t *) malloc( + group->bsp_specific_pin_count * + sizeof(uint32_t) + ); + + sc = check_same_bank_and_availability( + group_definition->bsp_specifics, + group->bsp_specific_pin_count, + &group->bsp_specific_bank, + group->bsp_speficifc_pins + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + } + else { + group->bsp_speficifc_pins = NULL; + } + + /* Request the pins. */ + sc = gpio_multi_select( + group_definition->digital_inputs, + group_definition->input_count, + true + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + return RTEMS_UNSATISFIED; + } + + sc = gpio_multi_select( + group_definition->digital_outputs, + group_definition->output_count, + true + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + sc = rtems_gpio_release_multiple_pins( + group_definition->digital_inputs, + group_definition->input_count + ); + + assert ( sc == RTEMS_SUCCESSFUL ); + + return RTEMS_UNSATISFIED; + } + + sc = gpio_multi_select( + group_definition->bsp_specifics, + group_definition->bsp_specific_pin_count, + true + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + sc = rtems_gpio_release_multiple_pins( + group_definition->digital_inputs, + group_definition->input_count + ); + + assert ( sc == RTEMS_SUCCESSFUL ); + + sc = rtems_gpio_release_multiple_pins( + group_definition->digital_outputs, + group_definition->output_count + ); + + assert ( sc == RTEMS_SUCCESSFUL ); + + return RTEMS_UNSATISFIED; + } + + /* Create group lock. */ + sc = CREATE_LOCK(rtems_build_name('G', 'R', 'P', 'L'), &group->group_lock); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + + rtems_chain_append(&gpio_group, &group->node); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_write_group(uint32_t data, rtems_gpio_group *group) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + uint32_t set_bitmask; + uint32_t clear_bitmask; + uint32_t bank; + uint32_t pin; + uint8_t i; + + if ( group->output_count == 0 ) { + return RTEMS_NOT_DEFINED; + } + + bank = group->digital_output_bank; + + /* Acquire bank lock for the digital output pins. */ + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + /* Acquire group lock. */ + ACQUIRE_LOCK(group->group_lock); + + set_bitmask = 0; + clear_bitmask = 0; + + for ( i = 0; i < group->output_count; ++i ) { + pin = group->digital_outputs[i]; + + if ( (data & (1 << i)) == 0 ) { + clear_bitmask |= (1 << pin); + } + else { + set_bitmask |= (1 << pin); + } + } + + /* Set the logical highs. */ + if ( set_bitmask > 0 ) { + sc = rtems_gpio_bsp_multi_set(bank, set_bitmask); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(group->group_lock); + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return sc; + } + } + + /* Set the logical lows. */ + if ( clear_bitmask > 0 ) { + sc = rtems_gpio_bsp_multi_clear(bank, clear_bitmask); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(group->group_lock); + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return sc; + } + } + + RELEASE_LOCK(group->group_lock); + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_SUCCESSFUL; +} + +uint32_t rtems_gpio_read_group(rtems_gpio_group *group) +{ + uint32_t read_bitmask; + uint32_t bank; + uint32_t pin; + uint32_t rv; + uint8_t i; + + if ( group->input_count == 0 ) { + return 0xDEADBEEF; + } + + bank = group->digital_input_bank; + + /* Acquire bank lock for the digital input pins. */ + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + /* Acquire group lock. */ + ACQUIRE_LOCK(group->group_lock); + + read_bitmask = 0; + + for ( i = 0; i < group->input_count; ++i ) { + pin = group->digital_inputs[i]; + + read_bitmask |= (1 << pin); + } + + rv = rtems_gpio_bsp_multi_read(bank, read_bitmask); + + RELEASE_LOCK(gpio_bank_state[bank].lock); + RELEASE_LOCK(group->group_lock); + + return rv; +} + +rtems_status_code rtems_gpio_group_bsp_specific_operation( + rtems_gpio_group *group, + void *arg +) { + rtems_status_code sc; + uint32_t bank; + + if ( group->bsp_specific_pin_count == 0 ) { + return RTEMS_NOT_DEFINED; + } + + bank = group->bsp_specific_bank; + + /* Acquire bank lock for the BSP specific function pins. */ + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + /* Acquire group lock. */ + ACQUIRE_LOCK(group->group_lock); + + sc = rtems_gpio_bsp_specific_group_operation( + bank, + group->bsp_speficifc_pins, + group->bsp_specific_pin_count, + arg + ); + + RELEASE_LOCK(gpio_bank_state[bank].lock); + RELEASE_LOCK(group->group_lock); + + return sc; +} + +rtems_status_code rtems_gpio_multi_select( + const rtems_gpio_pin_conf *pins, + uint8_t pin_count +) { + return gpio_multi_select(pins, pin_count, false); +} + +rtems_status_code rtems_gpio_request_configuration( + const rtems_gpio_pin_conf *conf +) { + rtems_status_code sc; + + sc = rtems_gpio_request_pin( + conf->pin_number, + conf->function, + conf->output_enabled, + conf->logic_invert, + conf->bsp_specific + ); + + if ( sc != RTEMS_SUCCESSFUL ) { +#if defined(DEBUG) + printk("rtems_gpio_request_pin failed with status code %d\n",sc); +#endif + + return RTEMS_UNSATISFIED; + } + + return setup_resistor_and_interrupt_configuration( + conf->pin_number, + conf->pull_mode, + conf->interrupt + ); +} + +rtems_status_code rtems_gpio_update_configuration( + const rtems_gpio_pin_conf *conf +) { + rtems_gpio_interrupt_configuration *interrupt_conf; + gpio_pin_interrupt_state *interrupt_state; + rtems_status_code sc; + uint32_t bank; + + if ( conf->pin_number < 0 || conf->pin_number >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(conf->pin_number); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + /* If the pin is not being used returns with an error. */ + if ( gpio_pin_state[conf->pin_number].pin_function == NOT_USED ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_NOT_CONFIGURED; + } + + sc = rtems_gpio_resistor_mode(conf->pin_number, conf->pull_mode); + + if ( sc != RTEMS_SUCCESSFUL ) { +#if defined(DEBUG) + printk("rtems_gpio_resistor_mode failed with status code %d\n", sc); +#endif + + return RTEMS_UNSATISFIED; + } + + interrupt_conf = (rtems_gpio_interrupt_configuration *) conf->interrupt; + + interrupt_state = gpio_pin_state[conf->pin_number].interrupt_state; + + if ( interrupt_state != NULL ) { + sc = rtems_gpio_disable_interrupt(conf->pin_number); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + +#if defined(DEBUG) + printk( + "rtems_gpio_disable_interrupt failed with status code %d\n", + sc + ); +#endif + + return RTEMS_UNSATISFIED; + } + } + + if ( interrupt_conf != NULL ) { + sc = rtems_gpio_enable_interrupt( + conf->pin_number, + interrupt_conf->active_interrupt, + interrupt_conf->handler_flag, + interrupt_conf->threaded_interrupts, + interrupt_conf->handler, + interrupt_conf->arg + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + +#if defined(DEBUG) + printk( + "rtems_gpio_enable_interrupt failed with status code %d\n", + sc + ); +#endif + + return RTEMS_UNSATISFIED; + } + } + + if ( interrupt_conf != NULL && interrupt_state != NULL ) { + if ( + interrupt_conf->debounce_clock_tick_interval != + interrupt_state->debouncing_tick_count + ) { + interrupt_state->debouncing_tick_count = + interrupt_conf->debounce_clock_tick_interval; + + interrupt_state->last_isr_tick = 0; + } + } + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_multi_set( + uint32_t *pin_numbers, + uint32_t pin_count +) { + rtems_status_code sc; + uint32_t bitmask; + uint32_t bank; + + sc = get_pin_bitmask(pin_numbers, pin_count, &bank, &bitmask, DIGITAL_OUTPUT); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + sc = rtems_gpio_bsp_multi_set(bank, bitmask); + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return sc; +} + +rtems_status_code rtems_gpio_multi_clear( + uint32_t *pin_numbers, + uint32_t pin_count +) { + rtems_status_code sc; + uint32_t bitmask; + uint32_t bank; + + sc = get_pin_bitmask(pin_numbers, pin_count, &bank, &bitmask, DIGITAL_OUTPUT); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + sc = rtems_gpio_bsp_multi_clear(bank, bitmask); + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return sc; +} + +uint32_t rtems_gpio_multi_read( + uint32_t *pin_numbers, + uint32_t pin_count +) { + rtems_status_code sc; + uint32_t bitmask; + uint32_t bank; + uint32_t rv; + + sc = get_pin_bitmask(pin_numbers, pin_count, &bank, &bitmask, DIGITAL_INPUT); + + if ( sc != RTEMS_SUCCESSFUL ) { + return 0xDEADBEEF; + } + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + rv = rtems_gpio_bsp_multi_read(bank, bitmask); + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return rv; +} + +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 >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + if ( + gpio_pin_state[pin_number].pin_function != DIGITAL_OUTPUT || + gpio_pin_state[pin_number].on_group + ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + +#if defined(DEBUG) + printk("Can only set digital output pins\n"); +#endif + + return RTEMS_NOT_CONFIGURED; + } + + if ( gpio_pin_state[pin_number].logic_invert ) { + sc = rtems_gpio_bsp_clear(bank, pin); + } + else { + sc = rtems_gpio_bsp_set(bank, pin); + } + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + 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 >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + if ( + gpio_pin_state[pin_number].pin_function != DIGITAL_OUTPUT || + gpio_pin_state[pin_number].on_group + ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + +#if defined(DEBUG) + printk("Can only clear digital output pins\n"); +#endif + + return RTEMS_NOT_CONFIGURED; + } + + if ( gpio_pin_state[pin_number].logic_invert ) { + sc = rtems_gpio_bsp_set(bank, pin); + } + else { + sc = rtems_gpio_bsp_clear(bank, pin); + } + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return sc; +} + +uint8_t rtems_gpio_get_value(uint32_t pin_number) +{ + uint32_t bank; + uint32_t pin; + int rv; + + if ( pin_number < 0 || pin_number >= BSP_GPIO_PIN_COUNT ) { + return -1; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + if ( + gpio_pin_state[pin_number].pin_function != DIGITAL_INPUT || + gpio_pin_state[pin_number].on_group + ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + +#if defined(DEBUG) + printk("Can only read digital input pins\n"); +#endif + + return -1; + } + + rv = rtems_gpio_bsp_get_value(bank, pin); + + if ( gpio_pin_state[pin_number].logic_invert && rv > 0 ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return !rv; + } + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + 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 >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + /* If the pin is already being used returns with an error. */ + if ( gpio_pin_state[pin_number].pin_function != NOT_USED ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_RESOURCE_IN_USE; + } + + switch ( function ) { + case DIGITAL_INPUT: + sc = rtems_gpio_bsp_select_input(bank, pin, bsp_specific); + break; + case DIGITAL_OUTPUT: + sc = rtems_gpio_bsp_select_output(bank, pin, bsp_specific); + break; + case BSP_SPECIFIC: + bsp_data = (rtems_gpio_specific_data *) bsp_specific; + + if ( bsp_data == NULL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + 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(gpio_bank_state[bank].lock); + + return RTEMS_NOT_DEFINED; + } + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return sc; + } + + /* If the function was successfully assigned to the pin, + * record that information on the gpio_pin_state structure. */ + gpio_pin_state[pin_number].pin_function = function; + gpio_pin_state[pin_number].logic_invert = logic_invert; + + if ( function == DIGITAL_OUTPUT ) { + if ( output_enabled == true ) { + sc = rtems_gpio_bsp_set(bank, pin); + } + else { + sc = rtems_gpio_bsp_clear(bank, pin); + } + } + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + 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 >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + /* 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[pin_number].resistor_mode == mode && + mode != NO_PULL_RESISTOR + ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_SUCCESSFUL; + } + + sc = rtems_gpio_bsp_set_resistor_mode(bank, pin, mode); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return sc; + } + + gpio_pin_state[pin_number].resistor_mode = mode; + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_release_pin(uint32_t pin_number) +{ + gpio_pin_interrupt_state *interrupt_state; + rtems_status_code sc; + uint32_t bank; + + if ( pin_number < 0 || pin_number >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + interrupt_state = gpio_pin_state[pin_number].interrupt_state; + + /* If the pin has an enabled interrupt then remove the handler(s) + * and disable interrupts on that pin. */ + if ( interrupt_state != NULL ) { + sc = rtems_gpio_disable_interrupt(pin_number); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return sc; + } + } + + gpio_pin_state[pin_number].pin_function = NOT_USED; + gpio_pin_state[pin_number].resistor_mode = NO_PULL_RESISTOR; + gpio_pin_state[pin_number].logic_invert = false; + gpio_pin_state[pin_number].on_group = false; + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_release_configuration( + const rtems_gpio_pin_conf *conf +) { + if ( conf == NULL ) { + return RTEMS_UNSATISFIED; + } + + return rtems_gpio_release_pin(conf->pin_number); +} + +rtems_status_code rtems_gpio_release_multiple_pins( + const rtems_gpio_pin_conf *pins, + uint32_t pin_count +) { + rtems_status_code sc; + uint32_t i; + + if ( pins == NULL ) { + return RTEMS_UNSATISFIED; + } + + for ( i = 0; i < pin_count; ++i ) { + sc = rtems_gpio_release_pin(pins[i].pin_number); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + } + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_release_pin_group( + rtems_gpio_group *group +) { + rtems_status_code sc; + uint8_t i; + + ACQUIRE_LOCK(group->group_lock); + + sc = rtems_semaphore_flush(group->group_lock); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(group->group_lock); + + return sc; + } + + RELEASE_LOCK(group->group_lock); + + /* Deletes the group lock. */ + sc = rtems_semaphore_delete(group->group_lock); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + + /* Pin releasing. */ + for ( i = 0; i < group->input_count; ++i ) { + sc = rtems_gpio_release_pin(group->digital_inputs[i]); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + } + + if ( group->input_count > 0 ) { + free(group->digital_inputs); + } + + for ( i = 0; i < group->output_count; ++i ) { + sc = rtems_gpio_release_pin(group->digital_outputs[i]); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + } + + if ( group->output_count > 0 ) { + free(group->digital_outputs); + } + + for ( i = 0; i < group->bsp_specific_pin_count; ++i ) { + sc = rtems_gpio_release_pin(group->bsp_speficifc_pins[i]); + + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + } + + if ( group->bsp_specific_pin_count > 0 ) { + free(group->bsp_speficifc_pins); + } + + rtems_chain_extract(&group->node); + + free(group); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_debounce_switch(uint32_t pin_number, int ticks) +{ + gpio_pin_interrupt_state *interrupt_state; + uint32_t bank; + + if ( pin_number < 0 || pin_number >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + interrupt_state = gpio_pin_state[pin_number].interrupt_state; + + /* If no interrupt configuration is set for this pin, or if the pin is + * not set as a digital input, or the pin in on a group. */ + if ( + interrupt_state == NULL || + gpio_pin_state[pin_number].pin_function != DIGITAL_INPUT || + gpio_pin_state[pin_number].on_group + ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_NOT_CONFIGURED; + } + + interrupt_state->debouncing_tick_count = ticks; + interrupt_state->last_isr_tick = 0; + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + 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_pin_interrupt_state *interrupt_state; + gpio_handler_node *isr_node; + uint32_t bank; + + if ( pin_number < 0 || pin_number >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + interrupt_state = gpio_pin_state[pin_number].interrupt_state; + + /* If no interrupt configuration is set for this pin. */ + if ( interrupt_state == NULL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_NOT_CONFIGURED; + } + + /* If the current pin has no interrupt enabled + * then it does not need an handler. */ + if ( interrupt_state->active_interrupt == NONE ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_NOT_CONFIGURED; + } + /* If the pin already has an enabled interrupt but the installed handler + * is set as unique. */ + else if ( + interrupt_state->handler_flag == UNIQUE_HANDLER && + !rtems_chain_is_empty(&interrupt_state->handler_chain) + ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_TOO_MANY; + } + + /* Update the pin's ISR list. */ + isr_node = (gpio_handler_node *) malloc(sizeof(gpio_handler_node)); + + if ( isr_node == NULL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_NO_MEMORY; + } + + isr_node->handler = handler; + isr_node->arg = arg; + + rtems_chain_append(&interrupt_state->handler_chain, &isr_node->node); + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_enable_interrupt( + uint32_t pin_number, + rtems_gpio_interrupt interrupt, + rtems_gpio_handler_flag flag, + bool threaded_handling, + rtems_gpio_irq_state (*handler) (void *arg), + void *arg +) { + gpio_pin_interrupt_state *interrupt_state; + rtems_vector_number vector; + rtems_status_code sc; + uint32_t bank; + uint32_t pin; + + if ( pin_number < 0 || pin_number >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + vector = rtems_gpio_bsp_get_vector(bank); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + if ( + gpio_pin_state[pin_number].pin_function != DIGITAL_INPUT || + gpio_pin_state[pin_number].on_group + ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_NOT_CONFIGURED; + } + + /* If the bank already has at least one interrupt enabled on a pin, + * then new interrupts on this bank must follow the current + * threading policy. */ + if ( + gpio_bank_state[bank].interrupt_counter > 0 && + gpio_bank_state[bank].threaded_interrupts != threaded_handling + ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_RESOURCE_IN_USE; + } + + interrupt_state = gpio_pin_state[pin_number].interrupt_state; + + /* If an interrupt configuration is already in place for this pin. */ + if ( interrupt_state != NULL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_RESOURCE_IN_USE; + } + + gpio_pin_state[pin_number].interrupt_state = + (gpio_pin_interrupt_state *) malloc(sizeof(gpio_pin_interrupt_state)); + + if ( gpio_pin_state[pin_number].interrupt_state == NULL ) { + return RTEMS_NO_MEMORY; + } + + gpio_pin_state[pin_number].interrupt_state->active_interrupt = NONE; + gpio_pin_state[pin_number].interrupt_state->debouncing_tick_count = 0; + gpio_pin_state[pin_number].interrupt_state->last_isr_tick = 0; + + rtems_chain_initialize_empty( + &gpio_pin_state[pin_number].interrupt_state->handler_chain + ); + + interrupt_state = gpio_pin_state[pin_number].interrupt_state; + + interrupt_state->active_interrupt = interrupt; + interrupt_state->handler_flag = flag; + + /* Installs the interrupt handler on the GPIO pin + * tracking structure. */ + sc = rtems_gpio_interrupt_handler_install(pin_number, handler, arg); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_UNSATISFIED; + } + + if ( threaded_handling ) { + if ( + _Atomic_Load_uint(&threaded_interrupt_counter, ATOMIC_ORDER_RELAXED) == 0 + ) { + sc = rtems_interrupt_server_initialize( + INTERRUPT_SERVER_PRIORITY, + INTERRUPT_SERVER_STACK_SIZE, + INTERRUPT_SERVER_MODES, + INTERRUPT_SERVER_ATTRIBUTES, + NULL + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_UNSATISFIED; + } + } + + if ( gpio_bank_state[bank].interrupt_counter == 0 ) { + sc = rtems_interrupt_server_handler_install( + RTEMS_ID_NONE, + vector, + "GPIO_HANDLER", + RTEMS_INTERRUPT_UNIQUE, + (rtems_interrupt_handler) generic_bank_isr, + &gpio_bank_state[bank].bank_number + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_UNSATISFIED; + } + + _Atomic_Fetch_add_uint( + &threaded_interrupt_counter, + 1, + ATOMIC_ORDER_RELAXED + ); + } + } + else if ( gpio_bank_state[bank].interrupt_counter == 0 ) { + sc = rtems_interrupt_handler_install( + vector, + "GPIO_HANDLER", + RTEMS_INTERRUPT_UNIQUE, + (rtems_interrupt_handler) generic_bank_isr, + &gpio_bank_state[bank].bank_number + ); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_UNSATISFIED; + } + } + + sc = rtems_bsp_enable_interrupt(bank, pin, interrupt); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_UNSATISFIED; + } + + /* If this was the first interrupt enabled on this GPIO bank, + * record the threading policy. */ + if ( gpio_bank_state[bank].interrupt_counter == 0 ) { + gpio_bank_state[bank].threaded_interrupts = threaded_handling; + } + + ++gpio_bank_state[bank].interrupt_counter; + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + 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_pin_interrupt_state *interrupt_state; + rtems_chain_control *handler_list; + rtems_chain_node *node; + rtems_chain_node *next_node; + gpio_handler_node *isr_node; + uint32_t bank; + + if ( pin_number < 0 || pin_number >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + interrupt_state = gpio_pin_state[pin_number].interrupt_state; + + /* If no interrupt configuration is set for this pin. */ + if ( interrupt_state == NULL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_NOT_CONFIGURED; + } + + handler_list = &interrupt_state->handler_chain; + + node = rtems_chain_first(handler_list); + + /* If the first node is also the last handler for this pin, disables + * interrupts on this pin as there will be no handler to handle it. + * This also removes the remaining handler. */ + if ( rtems_chain_is_last(node) ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return rtems_gpio_disable_interrupt(pin_number); + } + + /* Iterate the ISR list. */ + while ( !rtems_chain_is_tail(handler_list, node) ) { + isr_node = (gpio_handler_node *) node; + + next_node = node->next; + + if ( isr_node->handler == handler && isr_node->arg == arg ) { + rtems_chain_extract(node); + + break; + } + + node = next_node; + } + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rtems_gpio_disable_interrupt(uint32_t pin_number) +{ + gpio_pin_interrupt_state *interrupt_state; + rtems_chain_control *handler_list; + rtems_chain_node *node; + rtems_chain_node *next_node; + rtems_vector_number vector; + rtems_status_code sc; + uint32_t bank; + uint32_t pin; + + if ( pin_number < 0 || pin_number >= BSP_GPIO_PIN_COUNT ) { + return RTEMS_INVALID_ID; + } + + bank = BANK_NUMBER(pin_number); + pin = PIN_NUMBER(pin_number); + + vector = rtems_gpio_bsp_get_vector(bank); + + ACQUIRE_LOCK(gpio_bank_state[bank].lock); + + interrupt_state = gpio_pin_state[pin_number].interrupt_state; + + /* If no interrupt configuration is set for this pin. */ + if ( interrupt_state == NULL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_NOT_CONFIGURED; + } + + sc = rtems_bsp_disable_interrupt(bank, pin, interrupt_state->active_interrupt); + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_UNSATISFIED; + } + + interrupt_state->active_interrupt = NONE; + + handler_list = &interrupt_state->handler_chain; + + node = rtems_chain_first(handler_list); + + /* Iterate the ISR list. */ + while ( !rtems_chain_is_tail(handler_list, node) ) { + next_node = node->next; + + rtems_chain_extract(node); + + node = next_node; + } + + /* If this is the last GPIO interrupt are left in this bank, + * removes the handler. */ + if ( gpio_bank_state[bank].interrupt_counter == 1 ) { + if ( gpio_bank_state[bank].threaded_interrupts ) { + sc = rtems_interrupt_server_handler_remove( + RTEMS_ID_NONE, + vector, + (rtems_interrupt_handler) generic_bank_isr, + &gpio_bank_state[bank].bank_number + ); + } + else { + sc = rtems_interrupt_handler_remove( + vector, + (rtems_interrupt_handler) generic_bank_isr, + &gpio_bank_state[bank].bank_number + ); + } + + if ( sc != RTEMS_SUCCESSFUL ) { + RELEASE_LOCK(gpio_bank_state[bank].lock); + + return RTEMS_UNSATISFIED; + } + } + + /* Free the pin's interrupt state structure. */ + free(interrupt_state); + + --gpio_bank_state[bank].interrupt_counter; + + if ( gpio_bank_state[bank].threaded_interrupts ) { + _Atomic_Fetch_sub_uint(&threaded_interrupt_counter, 1, ATOMIC_ORDER_RELAXED); + } + + RELEASE_LOCK(gpio_bank_state[bank].lock); + + 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 0000000000..b2deb1e09b --- /dev/null +++ b/c/src/lib/libbsp/shared/include/gpio.h @@ -0,0 +1,948 @@ +/** + * @file gpio.h + * + * @ingroup rtems_gpio + * + * @brief RTEMS GPIO API definition. + */ + +/* + * Copyright (c) 2014-2015 Andre Marques + * + * 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 +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if !defined(BSP_GPIO_PIN_COUNT) || !defined(BSP_GPIO_PINS_PER_BANK) + #error "BSP_GPIO_PIN_COUNT or BSP_GPIO_PINS_PER_BANK is not defined." +#endif + +#if BSP_GPIO_PIN_COUNT <= 0 || BSP_GPIO_PINS_PER_BANK <= 0 + #error "Invalid BSP_GPIO_PIN_COUNT or BSP_GPIO_PINS_PER_BANK." +#endif + +#if BSP_GPIO_PINS_PER_BANK > 32 + #error "Invalid BSP_GPIO_PINS_PER_BANK. Must be in the range of 1 to 32." +#endif + +#define GPIO_LAST_BANK_PINS BSP_GPIO_PIN_COUNT % BSP_GPIO_PINS_PER_BANK + +#if GPIO_LAST_BANK_PINS > 0 + #define GPIO_BANK_COUNT (BSP_GPIO_PIN_COUNT / BSP_GPIO_PINS_PER_BANK) + 1 +#else + #define GPIO_BANK_COUNT BSP_GPIO_PIN_COUNT / BSP_GPIO_PINS_PER_BANK + #define GPIO_LAST_BANK_PINS BSP_GPIO_PINS_PER_BANK +#endif + +#if defined(BSP_GPIO_PINS_PER_SELECT_BANK) && BSP_GPIO_PINS_PER_SELECT_BANK > 32 + #error "Invalid BSP_GPIO_PINS_PER_SELECT_BANK. Must under and including 32." +#elif defined(BSP_GPIO_PINS_PER_SELECT_BANK) <= 32 + #define GPIO_SELECT_BANK_COUNT \ + BSP_GPIO_PINS_PER_BANK / BSP_GPIO_PINS_PER_SELECT_BANK +#endif + +#define INTERRUPT_SERVER_PRIORITY 1 +#define INTERRUPT_SERVER_STACK_SIZE 2 * RTEMS_MINIMUM_STACK_SIZE +#define INTERRUPT_SERVER_MODES RTEMS_TIMESLICE | RTEMS_PREEMPT +#define INTERRUPT_SERVER_ATTRIBUTES RTEMS_DEFAULT_ATTRIBUTES + +/** + * @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 configurations + * 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 configuration information + * regarding interrupts. + */ +typedef struct +{ + rtems_gpio_interrupt active_interrupt; + + rtems_gpio_handler_flag handler_flag; + + bool threaded_interrupts; + + /* 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 debounce_clock_tick_interval; +} rtems_gpio_interrupt_configuration; + +/** + * @brief Object containing configuration information + * to request/update a GPIO pin. + */ +typedef struct +{ + /* Processor pin number. */ + 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; + + /* If true inverts digital in/out applicational logic. */ + bool logic_invert; + + /* Pin interrupt configuration. Should be NULL if not used. */ + rtems_gpio_interrupt_configuration *interrupt; + + /* Structure 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 structure. + * + * 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; + +/** + * @brief Object containing configuration information + * to assign GPIO functions to multiple pins + * at the same time. To be used by BSP code only. + */ +typedef struct +{ + /* Global GPIO pin number. */ + uint32_t pin_number; + + /* RTEMS GPIO pin function code. */ + rtems_gpio_function function; + + /* BSP specific function code. Only used if function == BSP_SPECIFIC */ + uint32_t io_function; + + /* BSP specific data. */ + void *bsp_specific; +} rtems_gpio_multiple_pin_select; + +/** + * @brief Object containing configuration information + * to request a GPIO pin group. + */ +typedef struct +{ + const rtems_gpio_pin_conf *digital_inputs; + uint32_t input_count; + + const rtems_gpio_pin_conf *digital_outputs; + uint32_t output_count; + + const rtems_gpio_pin_conf *bsp_specifics; + uint32_t bsp_specific_pin_count; +} rtems_gpio_group_definition; + +/** + * @brief Opaque type for a GPIO pin group. + */ +typedef struct rtems_gpio_group rtems_gpio_group; + +/** @} */ + +/** + * @name gpio Usage + * + * @{ + */ + +/** + * @brief Initializes the GPIO API. + * + * @retval RTEMS_SUCCESSFUL API successfully initialized. + * @retval * @see rtems_semaphore_create(). + */ +extern rtems_status_code rtems_gpio_initialize(void); + +/** + * @brief Instantiates a GPIO pin group. + * To define the group @see rtems_gpio_define_pin_group(). + * + * @retval rtems_gpio_group pointer. + */ +extern rtems_gpio_group *rtems_gpio_create_pin_group(void); + +/** + * @brief Requests a GPIO pin group configuration. + * + * @param[in] group_definition rtems_gpio_group_definition structure filled with + * the group pins configurations. + * @param[out] group Reference to the created group. + * + * @retval RTEMS_SUCCESSFUL Pin group was configured successfully. + * @retval RTEMS_UNSATISFIED @var group_definition or @var group is NULL, + * the @var pins are not from the same bank, + * no pins were defined or could not satisfy at + * least one given configuration. + * @retval RTEMS_RESOURCE_IN_USE At least one pin is already being used. + * @retval * @see rtems_semaphore_create(). + */ +extern rtems_status_code rtems_gpio_define_pin_group( + const rtems_gpio_group_definition *group_definition, + rtems_gpio_group *group +); + +/** + * @brief Writes a value to the group's digital outputs. The pins order + * is as defined in the group definition. + * + * @param[in] data Data to write/send. + * @param[in] group Reference to the group. + * + * @retval RTEMS_SUCCESSFUL Data successfully written. + * @retval RTEMS_NOT_DEFINED Group has no output pins. + * @retval RTEMS_UNSATISFIED Could not operate on at least one of the pins. + */ +extern rtems_status_code rtems_gpio_write_group( + uint32_t data, + rtems_gpio_group *group +); + +/** + * @brief Reads the value/level of the group's digital inputs. The pins order + * is as defined in the group definition. + * + * @param[in] group Reference to the group. + * + * @retval The function returns a 32-bit bitmask with the group's input pins + * current logical values. + * @retval 0xDEADBEEF Group has no input pins. + */ +extern uint32_t rtems_gpio_read_group(rtems_gpio_group *group); + +/** + * @brief Performs a BSP specific operation on a group of pins. The pins order + * is as defined in the group definition. + * + * @param[in] group Reference to the group. + * @param[in] arg Pointer to a BSP defined structure with BSP-specific + * data. This field is handled by the BSP. + * + * @retval RTEMS_SUCCESSFUL Operation completed with success. + * @retval RTEMS_NOT_DEFINED Group has no BSP specific pins, or the BSP does not + * support BSP specific operations for groups. + * @retval RTEMS_UNSATISFIED Could not operate on at least one of the pins. + */ +extern rtems_status_code rtems_gpio_group_bsp_specific_operation( + rtems_gpio_group *group, + void *arg +); + +/** + * @brief Requests a GPIO pin configuration. + * + * @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 satisfy the given configuration. + */ +extern rtems_status_code rtems_gpio_request_configuration( + const rtems_gpio_pin_conf *conf +); + +/** + * @brief Updates the current configuration of a GPIO pin . + * + * @param[in] conf rtems_gpio_pin_conf structure filled with the pin information + * and desired configurations. + * + * @retval RTEMS_SUCCESSFUL Pin configuration was updated successfully. + * @retval RTEMS_INVALID_ID Pin number is invalid. + * @retval RTEMS_NOT_CONFIGURED The pin is not being used. + * @retval RTEMS_UNSATISFIED Could not update the pin's configuration. + */ +extern rtems_status_code rtems_gpio_update_configuration( + const rtems_gpio_pin_conf *conf +); + +/** + * @brief Sets multiple output GPIO pins with the logical high. + * + * @param[in] pin_numbers Array with the GPIO pin numbers to set. + * @param[in] count Number of GPIO pins to set. + * + * @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 *pin_numbers, + uint32_t pin_count +); + +/** + * @brief Sets multiple output GPIO pins with the logical low. + * + * @param[in] pin_numbers Array with the GPIO pin numbers to clear. + * @param[in] count Number of GPIO pins to clear. + * + * @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 *pin_numbers, + uint32_t pin_count +); + +/** + * @brief Returns the value (level) of multiple GPIO input pins. + * + * @param[in] pin_numbers Array with the GPIO pin numbers to read. + * @param[in] count Number of GPIO pins to read. + * + * @retval Bitmask with the values of the corresponding pins. + * 0 for logical low and 1 for logical high. + * @retval 0xDEADBEEF Could not read at least one pin level. + */ +extern uint32_t rtems_gpio_multi_read( + uint32_t *pin_numbers, + uint32_t pin_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 uint8_t rtems_gpio_get_value(uint32_t pin_number); + +/** + * @brief Requests multiple GPIO pin configurations. If the BSP provides + * support for parallel selection each call to this function will + * result in a single call to the GPIO hardware, else each pin + * configuration will be done in individual and sequential calls. + * All pins must belong to the same GPIO bank. + * + * @param[in] pins Array of rtems_gpio_pin_conf structures filled with the pins + * information and desired configurations. All pins must belong + * to the same GPIO bank. + * @param[in] pin_count Number of pin configurations in the @var pins array. + * + * @retval RTEMS_SUCCESSFUL All pins were configured successfully. + * @retval RTEMS_INVALID_ID At least one pin number in the @var pins array + * is invalid. + * @retval RTEMS_RESOURCE_IN_USE At least one pin is already being used. + * @retval RTEMS_UNSATISFIED Could not satisfy at least one given configuration. + */ +extern rtems_status_code rtems_gpio_multi_select( + const rtems_gpio_pin_conf *pins, + uint8_t pin_count +); + +/** + * @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 handled by the BSP. + * + * @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, making it available to be used again. + * + * @param[in] pin_number GPIO pin number. + * + * @retval RTEMS_SUCCESSFUL Pin successfully disabled. + * @retval RTEMS_INVALID_ID Pin number is invalid. + * @retval * Could not disable an active interrupt on this pin, + * @see rtems_gpio_disable_interrupt(). + */ +extern rtems_status_code rtems_gpio_release_pin(uint32_t pin_number); + +/** + * @brief Releases a GPIO pin, making it available to be used again. + * + * @param[in] conf GPIO pin configuration to be released. + * + * @retval RTEMS_SUCCESSFUL Pin successfully disabled. + * @retval RTEMS_UNSATISFIED Pin configuration is NULL. + * @retval * @see rtems_gpio_release_pin(). + */ +extern rtems_status_code rtems_gpio_release_configuration( + const rtems_gpio_pin_conf *conf +); + +/** + * @brief Releases multiple GPIO pins, making them available to be used again. + * + * @param[in] pins Array of rtems_gpio_pin_conf structures. + * @param[in] pin_count Number of pin configurations in the @var pins array. + * + * @retval RTEMS_SUCCESSFUL Pins successfully disabled. + * @retval RTEMS_UNSATISFIED @var pins array is NULL. + * @retval * @see rtems_gpio_release_pin(). + */ +extern rtems_status_code rtems_gpio_release_multiple_pins( + const rtems_gpio_pin_conf *pins, + uint32_t pin_count +); + +/** + * @brief Releases a GPIO pin group, making the pins used available to be + * repurposed. + * + * @param[in] conf GPIO pin configuration to be released. + * + * @retval RTEMS_SUCCESSFUL Pins successfully disabled. + * @retval * @see rtems_gpio_release_pin(), @see rtems_semaphore_delete() or + * @see rtems_semaphore_flush(). + */ +extern rtems_status_code rtems_gpio_release_pin_group( + rtems_gpio_group *group +); + +/** + * @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 Debounce 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, + * or interrupts are not enabled for this pin. + */ +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_TOO_MANY The pin's current handler is set as unique. + * @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] flag Defines the uniqueness of the interrupt handler for the pin. + * @param[in] threaded_handling Defines if the handler should be called from a + * thread/task or from normal ISR contex. + * @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_NOT_CONFIGURED The received pin is not configured + * as a digital input, the pin is on a + * pin grouping. + * @retval RTEMS_RESOURCE_IN_USE The pin already has an enabled interrupt, + * or the handler threading policy does not match + * the bank's policy. + * @retval RTEMS_NO_MEMORY Could not store the pin's interrupt configuration. + */ +extern rtems_status_code rtems_gpio_enable_interrupt( + uint32_t pin_number, + rtems_gpio_interrupt interrupt, + rtems_gpio_handler_flag flag, + bool threaded_handling, + 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 RTEMS_NOT_CONFIGURED Pin has no active interrupts. + * @retval * @see rtems_gpio_disable_interrupt() + */ +extern 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_NOT_CONFIGURED Pin has no active interrupts. + * @retval RTEMS_UNSATISFIED Could not remove the current interrupt handler, + * could not recognize 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 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_gpio_bsp_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_gpio_bsp_multi_clear( + uint32_t bank, + uint32_t bitmask +); + +/** + * @brief Returns the value (level) of multiple GPIO input pins. + * This must be implemented by each BSP. + * + * @param[in] bank GPIO bank number. + * @param[in] bitmask Bitmask of GPIO pins to read in the given bank. + * + * @retval The function must return a bitmask with the values of the + * corresponding pins. 0 for logical low and 1 for logical high. + * @retval 0xDEADBEEF Could not read at least one pin level. + */ +extern uint32_t rtems_gpio_bsp_multi_read(uint32_t bank, uint32_t bitmask); + +/** + * @brief Performs a BSP specific operation on a group of pins. + * The implementation for this function may be omitted if the target + * does not support the feature, by returning RTEMS_NOT_DEFINED. + * + * @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_NOT_DEFINED The BSP does not support BSP specific operations + * for groups. + * @retval RTEMS_UNSATISFIED Could not clear at least one of the pins. + */ +extern rtems_status_code rtems_gpio_bsp_specific_group_operation( + uint32_t bank, + uint32_t *pins, + uint32_t pin_count, + void *arg +); + +/** + * @brief Assigns GPIO functions to all the given pins in a single register + * operation. + * The implementation for this function may be omitted if the target + * does not support the feature, by returning RTEMS_NOT_DEFINED. + * + * @param[in] pins Array of rtems_gpio_multiple_pin_select structures filled + * with the pins desired functions. All pins belong to the + * same select bank. + * @param[in] pin_count Number of pin configurations in the @var pins array. + * @param[in] select_bank Select bank number of the received pins. + * + * @retval RTEMS_SUCCESSFUL Functions were assigned successfully. + * @retval RTEMS_NOT_DEFINED The BSP does not support multiple pin function + * assignment. + * @retval RTEMS_UNSATISFIED Could not assign the functions to the pins. + */ +extern rtems_status_code rtems_gpio_bsp_multi_select( + rtems_gpio_multiple_pin_select *pins, + uint32_t pin_count, + uint32_t select_bank +); + +/** + * @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_gpio_bsp_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_gpio_bsp_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 uint8_t rtems_gpio_bsp_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 assigned successfully. + * @retval RTEMS_UNSATISFIED Could not assign the function to the pin. + */ +extern rtems_status_code rtems_gpio_bsp_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 assigned successfully. + * @retval RTEMS_UNSATISFIED Could not assign the function to the pin. + */ +extern rtems_status_code rtems_gpio_bsp_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 assigned 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_gpio_bsp_set_resistor_mode( + uint32_t bank, + uint32_t pin, + rtems_gpio_pull_mode mode +); + +/** + * @brief Reads and returns a vector/bank interrupt event line. + * The bitmask should indicate with a 1 if the corresponding pin + * as a pending interrupt, or 0 if otherwise. The function + * should clear the interrupt event line before returning. + * 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_gpio_bsp_interrupt_line(rtems_vector_number vector); + +/** + * @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_gpio_bsp_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 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] active_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 interrupt +); + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LIBBSP_SHARED_GPIO_H */ -- cgit v1.2.3