You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
280 lines
7.4 KiB
280 lines
7.4 KiB
/* |
|
* Copyright (c) 2022 SEAL AG |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT nuvoton_numicro_gpio |
|
|
|
#include <errno.h> |
|
#include <stdint.h> |
|
#include <zephyr/device.h> |
|
#include <zephyr/devicetree.h> |
|
#include <zephyr/irq.h> |
|
#include <zephyr/drivers/gpio.h> |
|
#include <zephyr/drivers/gpio/gpio_utils.h> |
|
#include <zephyr/dt-bindings/gpio/numicro-gpio.h> |
|
#include <NuMicro.h> |
|
|
|
#define MODE_PIN_SHIFT(pin) ((pin) * 2) |
|
#define MODE_MASK(pin) (3 << MODE_PIN_SHIFT(pin)) |
|
#define DINOFF_PIN_SHIFT(pin) ((pin) + 16) |
|
#define DINOFF_MASK(pin) (1 << DINOFF_PIN_SHIFT(pin)) |
|
#define PUSEL_PIN_SHIFT(pin) ((pin) * 2) |
|
#define PUSEL_MASK(pin) (3 << PUSEL_PIN_SHIFT(pin)) |
|
|
|
#define PORT_PIN_MASK 0xFFFF |
|
|
|
struct gpio_numicro_config { |
|
/* gpio_driver_config needs to be first */ |
|
struct gpio_driver_config common; |
|
GPIO_T *regs; |
|
}; |
|
|
|
struct gpio_numicro_data { |
|
/* gpio_driver_data needs to be first */ |
|
struct gpio_driver_data common; |
|
/* port ISR callback routine address */ |
|
sys_slist_t callbacks; |
|
#ifdef CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT |
|
/* |
|
* backup of the INTEN register. |
|
* The higher half is RHIEN for whether rising trigger is enabled, and |
|
* the lower half is FLIEN for whether falling trigger is enabled. |
|
*/ |
|
uint32_t interrupt_en_reg_bak; |
|
#endif /* CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT */ |
|
}; |
|
|
|
static int gpio_numicro_configure(const struct device *dev, |
|
gpio_pin_t pin, gpio_flags_t flags) |
|
{ |
|
const struct gpio_numicro_config *cfg = dev->config; |
|
GPIO_T * const regs = cfg->regs; |
|
|
|
uint32_t mode; |
|
uint32_t debounce_enable = 0; |
|
uint32_t schmitt_enable = 0; |
|
uint32_t disable_input_path = 0; |
|
uint32_t bias = GPIO_PUSEL_DISABLE; |
|
|
|
/* Pin mode */ |
|
if ((flags & GPIO_OUTPUT) != 0) { |
|
/* Output */ |
|
|
|
if ((flags & GPIO_SINGLE_ENDED) != 0) { |
|
if ((flags & GPIO_LINE_OPEN_DRAIN) != 0) { |
|
mode = GPIO_MODE_OPEN_DRAIN; |
|
} else { |
|
/* Output can't be open source */ |
|
return -ENOTSUP; |
|
} |
|
} else { |
|
mode = GPIO_MODE_OUTPUT; |
|
} |
|
} else if ((flags & GPIO_INPUT) != 0) { |
|
/* Input */ |
|
|
|
mode = GPIO_MODE_INPUT; |
|
|
|
if ((flags & NUMICRO_GPIO_INPUT_DEBOUNCE) != 0) { |
|
debounce_enable = 1; |
|
} |
|
|
|
if ((flags & NUMICRO_GPIO_INPUT_SCHMITT) != 0) { |
|
schmitt_enable = 1; |
|
} |
|
} else { |
|
/* Deactivated: Analog */ |
|
|
|
mode = GPIO_MODE_INPUT; |
|
disable_input_path = 1; |
|
} |
|
|
|
/* Bias */ |
|
if ((flags & GPIO_OUTPUT) != 0 || (flags & GPIO_INPUT) != 0) { |
|
if ((flags & GPIO_PULL_UP) != 0) { |
|
bias = GPIO_PUSEL_PULL_UP; |
|
} else if ((flags & GPIO_PULL_DOWN) != 0) { |
|
bias = GPIO_PUSEL_PULL_DOWN; |
|
} |
|
} |
|
|
|
regs->MODE = (regs->MODE & ~MODE_MASK(pin)) | |
|
(mode << MODE_PIN_SHIFT(pin)); |
|
regs->DBEN = (regs->DBEN & ~BIT(pin)) | (debounce_enable << pin); |
|
regs->SMTEN = (regs->SMTEN & ~BIT(pin)) | (schmitt_enable << pin); |
|
regs->DINOFF = (regs->DINOFF & ~DINOFF_MASK(pin)) | |
|
(disable_input_path << DINOFF_PIN_SHIFT(pin)); |
|
regs->PUSEL = (regs->PUSEL & ~PUSEL_MASK(pin)) | |
|
(bias << PUSEL_PIN_SHIFT(pin)); |
|
|
|
return 0; |
|
} |
|
|
|
static int gpio_numicro_port_get_raw(const struct device *dev, uint32_t *value) |
|
{ |
|
const struct gpio_numicro_config *cfg = dev->config; |
|
|
|
*value = cfg->regs->PIN & PORT_PIN_MASK; |
|
|
|
return 0; |
|
} |
|
|
|
static int gpio_numicro_port_set_masked_raw(const struct device *dev, |
|
uint32_t mask, |
|
uint32_t value) |
|
{ |
|
const struct gpio_numicro_config *cfg = dev->config; |
|
|
|
cfg->regs->DATMSK = ~mask; |
|
cfg->regs->DOUT = value; |
|
|
|
return 0; |
|
} |
|
|
|
static int gpio_numicro_port_set_bits_raw(const struct device *dev, |
|
uint32_t mask) |
|
{ |
|
const struct gpio_numicro_config *cfg = dev->config; |
|
|
|
cfg->regs->DATMSK = ~mask; |
|
cfg->regs->DOUT = PORT_PIN_MASK; |
|
|
|
return 0; |
|
} |
|
|
|
static int gpio_numicro_port_clear_bits_raw(const struct device *dev, |
|
uint32_t mask) |
|
{ |
|
const struct gpio_numicro_config *cfg = dev->config; |
|
|
|
cfg->regs->DATMSK = ~mask; |
|
cfg->regs->DOUT = 0; |
|
|
|
return 0; |
|
} |
|
|
|
static int gpio_numicro_port_toggle_bits(const struct device *dev, uint32_t mask) |
|
{ |
|
const struct gpio_numicro_config *cfg = dev->config; |
|
|
|
cfg->regs->DATMSK = 0; |
|
cfg->regs->DOUT ^= mask; |
|
|
|
return 0; |
|
} |
|
|
|
static int gpio_numicro_pin_interrupt_configure(const struct device *dev, |
|
gpio_pin_t pin, enum gpio_int_mode mode, |
|
enum gpio_int_trig trig) |
|
{ |
|
const struct gpio_numicro_config *cfg = dev->config; |
|
#ifdef CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT |
|
struct gpio_numicro_data *data = dev->data; |
|
#endif /* CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT */ |
|
uint32_t int_type = 0; |
|
uint32_t int_level = 0; |
|
uint32_t int_level_mask = BIT(pin) | BIT(pin + 16); |
|
|
|
#ifdef CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT |
|
if (mode == GPIO_INT_MODE_DISABLE_ONLY) { |
|
cfg->regs->INTEN &= ~(BIT(pin) | BIT(pin + 16)); |
|
return 0; |
|
} else if (mode == GPIO_INT_MODE_ENABLE_ONLY) { |
|
cfg->regs->INTEN |= data->interrupt_en_reg_bak & (BIT(pin) | BIT(pin + 16)); |
|
return 0; |
|
} |
|
#endif /* CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT */ |
|
|
|
if (mode != GPIO_INT_MODE_DISABLED) { |
|
int_type = (mode == GPIO_INT_MODE_LEVEL) ? 1 : 0; |
|
|
|
switch (trig) { |
|
case GPIO_INT_TRIG_LOW: |
|
int_level = BIT(pin); |
|
break; |
|
case GPIO_INT_TRIG_HIGH: |
|
int_level = BIT(pin + 16); |
|
break; |
|
case GPIO_INT_TRIG_BOTH: |
|
int_level = BIT(pin) | BIT(pin + 16); |
|
break; |
|
default: |
|
return -EINVAL; |
|
} |
|
} |
|
|
|
cfg->regs->INTTYPE = (cfg->regs->INTTYPE & ~BIT(pin)) | (int_type << pin); |
|
cfg->regs->INTEN = (cfg->regs->INTEN & ~int_level_mask) | int_level; |
|
#ifdef CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT |
|
data->interrupt_en_reg_bak = cfg->regs->INTEN; |
|
#endif /* CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT */ |
|
|
|
return 0; |
|
} |
|
|
|
static int gpio_numicro_manage_callback(const struct device *dev, |
|
struct gpio_callback *callback, |
|
bool set) |
|
{ |
|
struct gpio_numicro_data *data = dev->data; |
|
|
|
return gpio_manage_callback(&data->callbacks, callback, set); |
|
} |
|
|
|
static void gpio_numicro_isr(const struct device *dev) |
|
{ |
|
const struct gpio_numicro_config *cfg = dev->config; |
|
struct gpio_numicro_data *data = dev->data; |
|
uint32_t int_status; |
|
|
|
int_status = cfg->regs->INTSRC; |
|
|
|
/* Clear the port interrupts */ |
|
cfg->regs->INTSRC = int_status; |
|
|
|
gpio_fire_callbacks(&data->callbacks, dev, int_status); |
|
} |
|
|
|
static DEVICE_API(gpio, gpio_numicro_driver_api) = { |
|
.pin_configure = gpio_numicro_configure, |
|
.port_get_raw = gpio_numicro_port_get_raw, |
|
.port_set_masked_raw = gpio_numicro_port_set_masked_raw, |
|
.port_set_bits_raw = gpio_numicro_port_set_bits_raw, |
|
.port_clear_bits_raw = gpio_numicro_port_clear_bits_raw, |
|
.port_toggle_bits = gpio_numicro_port_toggle_bits, |
|
.pin_interrupt_configure = gpio_numicro_pin_interrupt_configure, |
|
.manage_callback = gpio_numicro_manage_callback, |
|
}; |
|
|
|
#define GPIO_NUMICRO_INIT(n) \ |
|
static int gpio_numicro_port##n##_init(const struct device *dev)\ |
|
{ \ |
|
IRQ_CONNECT(DT_INST_IRQN(n), \ |
|
DT_INST_IRQ(n, priority), \ |
|
gpio_numicro_isr, \ |
|
DEVICE_DT_INST_GET(n), 0); \ |
|
irq_enable(DT_INST_IRQN(n)); \ |
|
return 0; \ |
|
} \ |
|
\ |
|
static struct gpio_numicro_data gpio_numicro_port##n##_data; \ |
|
\ |
|
static const struct gpio_numicro_config gpio_numicro_port##n##_config = {\ |
|
.common = { \ |
|
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),\ |
|
}, \ |
|
.regs = (GPIO_T *)DT_INST_REG_ADDR(n), \ |
|
}; \ |
|
\ |
|
DEVICE_DT_INST_DEFINE(n, \ |
|
gpio_numicro_port##n##_init, \ |
|
NULL, \ |
|
&gpio_numicro_port##n##_data, \ |
|
&gpio_numicro_port##n##_config, \ |
|
PRE_KERNEL_1, \ |
|
CONFIG_GPIO_INIT_PRIORITY, \ |
|
&gpio_numicro_driver_api); |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(GPIO_NUMICRO_INIT)
|
|
|