drivers: gpio: nrf: Add mask to track GPIOTE usage

So far, register state was used to determine if GPIOTE channel is busy.
This leads to issues if channel is used in more customized way after
allocation. In particular, if it temporarly disabled since disabled
channel is treated as available and can be allocated to another user.

Added additional mask which tracks allocated channels. After allocation
user can reconfigure the channel.

Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
Krzysztof Chruscinski 2020-05-14 09:03:04 +02:00 committed by Carles Cufí
parent 77bc550746
commit 1089b91ba2

View file

@ -10,6 +10,9 @@
#include "gpio_utils.h"
/* Mask holding information about which channels are allocated. */
static atomic_t gpiote_alloc_mask;
struct gpio_nrfx_data {
/* gpio_driver_data needs to be first */
struct gpio_driver_data common;
@ -47,10 +50,13 @@ static inline const struct gpio_nrfx_cfg *get_port_cfg(struct device *port)
return port->config_info;
}
static int gpiote_channel_alloc(uint32_t abs_pin, nrf_gpiote_polarity_t polarity)
static int gpiote_channel_alloc(atomic_t *mask, uint32_t abs_pin,
nrf_gpiote_polarity_t polarity)
{
for (uint8_t channel = 0; channel < GPIOTE_CH_NUM; ++channel) {
if (!nrf_gpiote_te_is_enabled(NRF_GPIOTE, channel)) {
atomic_val_t prev = atomic_or(mask, BIT(channel));
if ((prev & BIT(channel)) == 0) {
nrf_gpiote_event_t evt =
offsetof(NRF_GPIOTE_Type, EVENTS_IN[channel]);
@ -66,7 +72,10 @@ static int gpiote_channel_alloc(uint32_t abs_pin, nrf_gpiote_polarity_t polarity
return -ENODEV;
}
static void gpiote_channel_free(uint32_t abs_pin)
/* Function checks if given pin does not have already enabled GPIOTE event and
* disables it.
*/
static void gpiote_pin_cleanup(atomic_t *mask, uint32_t abs_pin)
{
uint32_t intenset = nrf_gpiote_int_enable_check(NRF_GPIOTE,
NRF_GPIOTE_INT_IN_MASK);
@ -74,6 +83,7 @@ static void gpiote_channel_free(uint32_t abs_pin)
for (size_t i = 0; i < GPIOTE_CH_NUM; i++) {
if ((nrf_gpiote_event_pin_get(NRF_GPIOTE, i) == abs_pin)
&& (intenset & BIT(i))) {
atomic_set_bit_to(mask, i, 0);
nrf_gpiote_event_disable(NRF_GPIOTE, i);
nrf_gpiote_int_disable(NRF_GPIOTE, BIT(i));
return;
@ -97,7 +107,7 @@ static int gpiote_pin_int_cfg(struct device *port, uint32_t pin)
uint32_t abs_pin = NRF_GPIO_PIN_MAP(cfg->port_num, pin);
int res = 0;
gpiote_channel_free(abs_pin);
gpiote_pin_cleanup(&gpiote_alloc_mask, abs_pin);
nrf_gpio_cfg_sense_set(abs_pin, NRF_GPIO_PIN_NOSENSE);
/* Pins trigger interrupts only if pin has been configured to do so
@ -116,7 +126,8 @@ static int gpiote_pin_int_cfg(struct device *port, uint32_t pin)
pol = NRF_GPIOTE_POLARITY_HITOLO;
}
res = gpiote_channel_alloc(abs_pin, pol);
res = gpiote_channel_alloc(&gpiote_alloc_mask,
abs_pin, pol);
} else {
/* For level triggering we use sense mechanism. */
uint32_t sense = sense_for_pin(data, pin);