drivers: gpio: Smartbond: add support for both edge triggers

Hardware supports only one edge for GPIO interrupt.
This adds software implementation that hides hardware restriction.

With this change user code can use GPIO_INT_TRIG_BOTH and does not
have to do it in application code.

Signed-off-by: Jerzy Kasenberg <jerzy.kasenberg@codecoup.pl>
This commit is contained in:
Jerzy Kasenberg 2023-08-22 13:39:54 +02:00 committed by Carles Cufí
commit a9b44e08df

View file

@ -56,6 +56,8 @@ struct gpio_smartbond_wkup_regs {
struct gpio_smartbond_data {
/* gpio_driver_data needs to be first */
struct gpio_driver_data common;
/* Pins that are configured for both edges (handled by software) */
gpio_port_pins_t both_edges_pins;
sys_slist_t callbacks;
};
@ -184,12 +186,30 @@ static int gpio_smartbond_port_toggle_bits(const struct device *dev,
return 0;
}
static void gpio_smartbond_arm_next_edge_interrupt(const struct device *dev,
uint32_t pin_mask)
{
const struct gpio_smartbond_config *config = dev->config;
uint32_t pin_value;
do {
pin_value = config->data_regs->data & pin_mask;
if (pin_value) {
config->wkup_regs->pol |= pin_mask;
} else {
config->wkup_regs->pol &= ~pin_mask;
}
} while (pin_value != (config->data_regs->data & pin_mask));
}
static int gpio_smartbond_pin_interrupt_configure(const struct device *dev,
gpio_pin_t pin,
enum gpio_int_mode mode,
enum gpio_int_trig trig)
{
const struct gpio_smartbond_config *config = dev->config;
struct gpio_smartbond_data *data = dev->data;
uint32_t pin_mask = BIT(pin);
/* Not supported by hardware */
if (mode == GPIO_INT_MODE_LEVEL) {
@ -202,16 +222,15 @@ static int gpio_smartbond_pin_interrupt_configure(const struct device *dev,
} else {
if (trig == GPIO_INT_TRIG_BOTH) {
/* Not supported by hardware */
return -ENOTSUP;
}
if (trig == GPIO_INT_TRIG_HIGH) {
config->wkup_regs->pol &= ~BIT(pin);
data->both_edges_pins |= pin_mask;
gpio_smartbond_arm_next_edge_interrupt(dev, pin_mask);
} else if (trig == GPIO_INT_TRIG_HIGH) {
config->wkup_regs->pol &= ~pin_mask;
} else {
config->wkup_regs->pol |= BIT(pin);
config->wkup_regs->pol |= pin_mask;
}
config->wkup_regs->sel |= BIT(pin);
config->wkup_regs->sel |= pin_mask;
}
return 0;
@ -230,10 +249,21 @@ static void gpio_smartbond_isr(const struct device *dev)
const struct gpio_smartbond_config *config = dev->config;
struct gpio_smartbond_data *data = dev->data;
uint32_t stat;
uint32_t two_edge_triggered;
WAKEUP->WKUP_RESET_IRQ_REG = WAKEUP_WKUP_RESET_IRQ_REG_WKUP_IRQ_RST_Msk;
stat = config->wkup_regs->status;
two_edge_triggered = stat & data->both_edges_pins;
while (two_edge_triggered) {
int pos = find_lsb_set(two_edge_triggered) - 1;
two_edge_triggered &= ~BIT(pos);
/* Re-arm for other edge */
gpio_smartbond_arm_next_edge_interrupt(dev, BIT(pos));
}
config->wkup_regs->clear = stat;
gpio_fire_callbacks(&data->callbacks, dev, stat);