diff --git a/cpu/gd32v/Kconfig b/cpu/gd32v/Kconfig index 4caf73dc7a470..3575d7e20a2e3 100644 --- a/cpu/gd32v/Kconfig +++ b/cpu/gd32v/Kconfig @@ -13,6 +13,7 @@ config CPU_FAM_GD32V select HAS_PERIPH_CLIC select HAS_PERIPH_GPIO select HAS_PERIPH_GPIO_IRQ + select HAS_PERIPH_GPIO_LL select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE diff --git a/cpu/gd32v/Makefile.features b/cpu/gd32v/Makefile.features index d7a4e16adcb35..3ec55f29a0e94 100644 --- a/cpu/gd32v/Makefile.features +++ b/cpu/gd32v/Makefile.features @@ -4,6 +4,7 @@ FEATURES_PROVIDED += arch_nuclei FEATURES_PROVIDED += periph_clic FEATURES_PROVIDED += periph_gpio FEATURES_PROVIDED += periph_gpio_irq +FEATURES_PROVIDED += periph_gpio_ll FEATURES_PROVIDED += periph_rtc FEATURES_PROVIDED += periph_rtt FEATURES_PROVIDED += periph_timer diff --git a/cpu/gd32v/include/gpio_ll_arch.h b/cpu/gd32v/include/gpio_ll_arch.h new file mode 100644 index 0000000000000..5b7fd25bc1ab9 --- /dev/null +++ b/cpu/gd32v/include/gpio_ll_arch.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2023 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_gd32v + * @ingroup drivers_periph_gpio_ll + * @{ + * + * @file + * @brief GPIO Low-level API implementation for the GD32V GPIO peripheral + * + * @author Gunar Schorcht + */ + +#ifndef GPIO_LL_ARCH_H +#define GPIO_LL_ARCH_H + +#include "architecture.h" +#include "periph/gpio_ll.h" +#include "periph_cpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DOXYGEN /* hide implementation specific details from Doxygen */ + +/** + * @brief Get a GPIO port by number + */ +#define GPIO_PORT(num) (GPIOA_BASE + ((num) << 10)) + +/** + * @brief Get a GPIO port number by gpio_t value + */ +#define GPIO_PORT_NUM(port) (((port) - GPIOA_BASE) >> 10) + +static inline uword_t gpio_ll_read(gpio_port_t port) +{ + return ((GPIO_Type *)port)->ISTAT; +} + +static inline uword_t gpio_ll_read_output(gpio_port_t port) +{ + return ((GPIO_Type *)port)->OCTL; +} + +static inline void gpio_ll_set(gpio_port_t port, uword_t mask) +{ + ((GPIO_Type *)port)->BOP = mask; +} + +static inline void gpio_ll_clear(gpio_port_t port, uword_t mask) +{ + ((GPIO_Type *)port)->BOP = mask << 16; +} + +static inline void gpio_ll_toggle(gpio_port_t port, uword_t mask) +{ + unsigned irq_state = irq_disable(); + ((GPIO_Type *)port)->OCTL ^= mask; + irq_restore(irq_state); +} + +static inline void gpio_ll_write(gpio_port_t port, uword_t value) +{ + ((GPIO_Type *)port)->OCTL = value; +} + +static inline gpio_port_t gpio_get_port(gpio_t pin) +{ + return pin & 0xfffffff0LU; +} + +static inline uint8_t gpio_get_pin_num(gpio_t pin) +{ + return pin & 0xfLU; +} + +static inline gpio_port_t gpio_port_pack_addr(void *addr) +{ + return (gpio_port_t)addr; +} + +static inline void * gpio_port_unpack_addr(gpio_port_t port) +{ + if (port < GPIOA_BASE) { + return (void *)port; + } + + return NULL; +} + +static inline bool is_gpio_port_num_valid(uint_fast8_t num) +{ + return num < 5; +} + +#endif /* DOXYGEN */ + +#ifdef __cplusplus +} +#endif + +#endif /* GPIO_LL_ARCH_H */ +/** @} */ diff --git a/cpu/gd32v/include/periph_cpu.h b/cpu/gd32v/include/periph_cpu.h index fd353c8476898..2e7cadc57d95e 100644 --- a/cpu/gd32v/include/periph_cpu.h +++ b/cpu/gd32v/include/periph_cpu.h @@ -183,6 +183,36 @@ void gpio_init_af(gpio_t pin, gpio_af_t af); */ void gpio_init_analog(gpio_t pin); +/* Hide this from Doxygen to avoid merging implementation details into + * public view on type */ +#ifndef DOXYGEN + +#define HAVE_GPIO_PULL_STRENGTH_T +typedef enum { + GPIO_PULL_WEAKEST = 0, + GPIO_PULL_WEAK = 0, + GPIO_PULL_STRONG = 0, + GPIO_PULL_STRONGEST = 0 +} gpio_pull_strength_t; + +#define HAVE_GPIO_DRIVE_STRENGTH_T +typedef enum { + GPIO_DRIVE_WEAKEST = 0, + GPIO_DRIVE_WEAK = 0, + GPIO_DRIVE_STRONG = 0, + GPIO_DRIVE_STRONGEST = 0 +} gpio_drive_strength_t; + +#define HAVE_GPIO_SLEW_T +typedef enum { + GPIO_SLEW_SLOWEST = 0, + GPIO_SLEW_SLOW = 1, + GPIO_SLEW_FAST = 2, + GPIO_SLEW_FASTEST = 2, +} gpio_slew_t; + +#endif DOXYGEN + /** * @brief Available number of ADC devices */ diff --git a/cpu/gd32v/periph/gpio_ll.c b/cpu/gd32v/periph/gpio_ll.c new file mode 100644 index 0000000000000..e5554a51b2f7d --- /dev/null +++ b/cpu/gd32v/periph/gpio_ll.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2023 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup cpu_gd32v + * @ingroup drivers_periph_gpio_ll + * @{ + * + * @file + * @brief GPIO Low-level API implementation for the GD32V GPIO peripheral + * + * @author Gunar Schorcht + * @} + */ + +#include +#include + +#include "cpu.h" +#include "bitarithm.h" +#include "periph/gpio_ll.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#define GPIO_PORT_NUMOF 5 + +uint16_t pin_used[GPIO_PORT_NUMOF] = {}; + +int gpio_ll_init(gpio_port_t port, uint8_t pin, const gpio_conf_t *conf) +{ + if ((conf->pull == GPIO_PULL_KEEP) || + (conf->state == GPIO_OUTPUT_OPEN_SOURCE) || + ((conf->state != GPIO_INPUT) && (conf->pull != GPIO_FLOATING))) { + return -ENOTSUP; + } + + unsigned state = irq_disable(); + + periph_clk_en(APB2, (RCU_APB2EN_PAEN_Msk << GPIO_PORT_NUM(port))); + + volatile uint32_t *ctrl = (pin < 8) ? &((GPIO_Type *)port)->CTL0 + : &((GPIO_Type *)port)->CTL1; + volatile uint32_t *octl = &((GPIO_Type *)port)->OCTL; + unsigned pos = ((pin % 8) * 4); + + /* reset configuration CTLx[1:0], MDx[1:0] (analogue, input mode) */ + *ctrl &= ~(0xf << pos); + + switch (conf->state) { + case GPIO_DISCONNECT: + *ctrl |= 0x1 << (pos + 2); + pin_used[GPIO_PORT_NUM(port)] &= ~(1 << pin); + if (pin_used[GPIO_PORT_NUM(port)] == 0) { + periph_clk_dis(APB2, (RCU_APB2EN_PAEN_Msk << GPIO_PORT_NUM(port))); + } + break; + case GPIO_INPUT: + pin_used[GPIO_PORT_NUM(port)] |= 1 << pin; + if (conf->pull == GPIO_FLOATING) { + *ctrl |= 0x1 << (pos + 2); + } + else { + *ctrl |= 0x2 << (pos + 2); + if (conf->pull == GPIO_PULL_UP) { + *octl |= 1 << pin; + } + else { + *octl &= ~(1 << pin); + } + } + break; + case GPIO_OUTPUT_PUSH_PULL: + case GPIO_OUTPUT_OPEN_DRAIN: + pin_used[GPIO_PORT_NUM(port)] |= 1 << pin; + *ctrl |= (conf->slew_rate + 1) << pos; + *ctrl |= (conf->state == GPIO_OUTPUT_OPEN_DRAIN ? 0x1 : 0x0) << (pos + 2); + if (conf->initial_value) { + gpio_ll_set(port, 1UL << pin); + } + else { + gpio_ll_clear(port, 1UL << pin); + } + break; + default: + irq_restore(state); + return -ENOTSUP; + } + + irq_restore(state); + + return 0; +} + +void gpio_ll_query_conf(gpio_conf_t *dest, gpio_port_t port, uint8_t pin) +{ + assert(dest); + + unsigned state = irq_disable(); + + memset(dest, 0, sizeof(*dest)); + + volatile uint32_t *ctrl_reg = (pin < 8) ? &((GPIO_Type *)port)->CTL0 + : &((GPIO_Type *)port)->CTL1; + + unsigned pos = ((pin % 8) * 4); + + uint32_t mode = (*ctrl_reg >> pos) & 0x3; + uint32_t ctrl = (*ctrl_reg >> (pos + 2)) & 0x3; + + if (mode == 0) { + dest->state = GPIO_INPUT; + switch (ctrl) { + case 0: + dest->state = GPIO_USED_BY_PERIPHERAL; + break; + case 1: + dest->pull = GPIO_FLOATING; + break; + case 2: + dest->pull = (((GPIO_Type *)port)->OCTL & (1UL << pin)) ? GPIO_PULL_UP + : GPIO_PULL_DOWN; + break; + default: + break; + } + } + else { + dest->slew_rate = mode - 1; + dest->pull = GPIO_FLOATING; + switch (ctrl) { + case 0: + dest->state = GPIO_OUTPUT_PUSH_PULL; + break; + case 1: + dest->state = GPIO_OUTPUT_OPEN_DRAIN; + break; + default: + dest->state = GPIO_USED_BY_PERIPHERAL; + break; + } + } + + if (dest->state == GPIO_INPUT) { + dest->initial_value = (gpio_ll_read(port) >> pin) & 1UL; + } + else { + dest->initial_value = (gpio_ll_read_output(port) >> pin) & 1UL; + } + irq_restore(state); +}