/**
* @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/chain.h>
#include <bsp/irq-generic.h>
#include <bsp/gpio.h>
#include <assert.h>
#include <stdlib.h>
/**
* @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 GPIO_INPUT_ERROR;
}
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 GPIO_INPUT_ERROR;
}
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;
}
int rtems_gpio_get_value(uint32_t pin_number)
{
uint32_t bank;
uint32_t pin;
uint32_t 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 ( rv == GPIO_INPUT_ERROR ) {
RELEASE_LOCK(gpio_bank_state[bank].lock);
return -1;
}
if ( gpio_pin_state[pin_number].logic_invert ) {
RELEASE_LOCK(gpio_bank_state[bank].lock);
return !rv;
}
RELEASE_LOCK(gpio_bank_state[bank].lock);
return rv > 0;
}
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_gpio_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;
}
/* If an interrupt configuration is already in place for this pin. */
if ( gpio_pin_state[pin_number].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;
}
interrupt_state = gpio_pin_state[pin_number].interrupt_state;
interrupt_state->active_interrupt = NONE;
interrupt_state->debouncing_tick_count = 0;
interrupt_state->last_isr_tick = 0;
rtems_chain_initialize_empty( &interrupt_state->handler_chain );
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 ) {
free(interrupt_state);
gpio_pin_state[pin_number].interrupt_state = NULL;
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_gpio_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_gpio_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;
}