/** * @file * * @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 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; }