mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/esp32/gpio_ll: fix & cleanup
- `gpio_ll_toggle()` now is race-free - avoid using a look up table but branch to the two different registers in the `gpio_ll*()` functions - in most cases the GPIO port is a compile time constant and the dead branch is eliminated by the optimizer, making this vastly more efficient - some MCUs do only have a single port, in which case `GPIO_PORT_NUM(port)` is known to return `0` even if `port` is not known, resulting in one of the branch being eliminated as dead branch no matter what - in case it really is unknown at compile time which port to work on, the branch can still be implemented efficiently by the compiler e.g. using a conditional move; likely more efficient than fetching a value from the look up table.
This commit is contained in:
parent
c2bd865060
commit
dd585f9e9d
@ -22,8 +22,9 @@
|
||||
#define GPIO_LL_ARCH_H
|
||||
|
||||
#include "gpio_arch.h"
|
||||
#include "irq.h"
|
||||
#include "soc/gpio_reg.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/gpio_struct.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -31,64 +32,90 @@ extern "C" {
|
||||
|
||||
#ifndef DOXYGEN /* hide implementation specific details from Doxygen */
|
||||
|
||||
#define GPIO_PORT(num) ((gpio_port_t)(&_esp32_ports[num]))
|
||||
#define GPIO_PORT_NUM(port) (((_esp32_port_t*)port == _esp32_ports) ? 0 : 1)
|
||||
|
||||
/* GPIO port descriptor type */
|
||||
typedef struct {
|
||||
volatile uint32_t *out; /* address of GPIO_OUT/GPIO_OUT1 */
|
||||
volatile uint32_t *out_w1ts; /* address of GPIO_OUT_W1TS/GPIO_OUT1_W1TC */
|
||||
volatile uint32_t *out_w1tc; /* address of GPIO_OUT_W1TC/GPIO_OUT1_W1TC */
|
||||
volatile uint32_t *in; /* address of GPIO_IN/GPIO_IN1 */
|
||||
volatile uint32_t *enable; /* address of GPIO_ENABLE/GPIO_ENABLE1 */
|
||||
volatile uint32_t *enable_w1ts; /* address of GPIO_ENABLE_W1TS/GPIO_ENABLE1_W1TS */
|
||||
volatile uint32_t *enable_w1tc; /* address of GPIO_ENABLE_W1TC/GPIO_ENABLE1_W1TC */
|
||||
volatile uint32_t *status_w1tc; /* address of GPIO_STATUS_W1TC/GPIO_STATUS1_W1TC */
|
||||
} _esp32_port_t;
|
||||
|
||||
/* GPIO port table */
|
||||
extern const _esp32_port_t _esp32_ports[];
|
||||
#define GPIO_PORT(num) (num)
|
||||
#if GPIO_PORT_NUMOF > 1
|
||||
# define GPIO_PORT_NUM(port) (port)
|
||||
#else
|
||||
# define GPIO_PORT_NUM(port) 0
|
||||
#endif
|
||||
|
||||
static inline uword_t gpio_ll_read(gpio_port_t port)
|
||||
{
|
||||
static_assert(GPIO_PORT_NUMOF < 3);
|
||||
volatile uword_t *in = (uint32_t *)GPIO_IN_REG;
|
||||
/* return 0 for unconfigured pins, the current level at the pin otherwise */
|
||||
const _esp32_port_t *p = (_esp32_port_t *)port;
|
||||
return *p->in;
|
||||
#if GPIO_PORT_NUM > 1
|
||||
if (GPIO_PORT_NUM(port) != 0) {
|
||||
in = (uint32_t *)GPIO_IN1_REG;
|
||||
}
|
||||
#endif
|
||||
|
||||
return *in;
|
||||
}
|
||||
|
||||
static inline uword_t gpio_ll_read_output(gpio_port_t port)
|
||||
{
|
||||
/* return output register bits */
|
||||
const _esp32_port_t *p = (_esp32_port_t *)port;
|
||||
return *p->out;
|
||||
static_assert(GPIO_PORT_NUMOF < 3);
|
||||
volatile uword_t *out = (uint32_t *)GPIO_OUT_REG;
|
||||
#if GPIO_PORT_NUM > 1
|
||||
if (GPIO_PORT_NUM(port) != 0) {
|
||||
out = (uint32_t *)GPIO_OUT1_REG;
|
||||
}
|
||||
#endif
|
||||
|
||||
return *out;
|
||||
}
|
||||
|
||||
static inline void gpio_ll_set(gpio_port_t port, uword_t mask)
|
||||
{
|
||||
/* set output register bits for configured pins in the mask */
|
||||
const _esp32_port_t *p = (_esp32_port_t *)port;
|
||||
*p->out_w1ts = mask;
|
||||
static_assert(GPIO_PORT_NUMOF < 3);
|
||||
volatile uword_t *out_w1ts = (uint32_t *)GPIO_OUT_W1TS_REG;
|
||||
if (GPIO_PORT_NUM(port) != 0) {
|
||||
#if GPIO_PORT_NUM > 1
|
||||
out_w1ts = (uint32_t)GPIO_OUT1_W1TS;
|
||||
#endif
|
||||
}
|
||||
|
||||
*out_w1ts = mask;
|
||||
}
|
||||
|
||||
static inline void gpio_ll_clear(gpio_port_t port, uword_t mask)
|
||||
{
|
||||
/* clear output register bits for configured pins in the mask */
|
||||
const _esp32_port_t *p = (_esp32_port_t *)port;
|
||||
*p->out_w1tc = mask;
|
||||
static_assert(GPIO_PORT_NUMOF < 3);
|
||||
volatile uword_t *out_w1tc = (uint32_t *)GPIO_OUT_W1TC_REG;
|
||||
if (GPIO_PORT_NUM(port) != 0) {
|
||||
#if GPIO_PORT_NUM > 1
|
||||
out_w1tc = (uint32_t)GPIO_OUT1_W1TC;
|
||||
#endif
|
||||
}
|
||||
|
||||
*out_w1tc = mask;
|
||||
}
|
||||
|
||||
static inline void gpio_ll_toggle(gpio_port_t port, uword_t mask)
|
||||
{
|
||||
/* toggle output register bits for configured pins in the mask */
|
||||
const _esp32_port_t *p = (_esp32_port_t *)port;
|
||||
*p->out ^= mask;
|
||||
static_assert(GPIO_PORT_NUMOF < 3);
|
||||
volatile uword_t *out = (uint32_t *)GPIO_OUT_REG;
|
||||
#if GPIO_PORT_NUM > 1
|
||||
if (GPIO_PORT_NUM(port) != 0) {
|
||||
out = (uint32_t *)GPIO_OUT1_REG;
|
||||
}
|
||||
#endif
|
||||
unsigned irq_state = irq_disable();
|
||||
*out ^= mask;
|
||||
irq_restore(irq_state);
|
||||
}
|
||||
|
||||
static inline void gpio_ll_write(gpio_port_t port, uword_t value)
|
||||
{
|
||||
/* write output register bits for configured pins in the mask */
|
||||
const _esp32_port_t *p = (_esp32_port_t *)port;
|
||||
*p->out = value;
|
||||
static_assert(GPIO_PORT_NUMOF < 3);
|
||||
volatile uword_t *out = (uint32_t *)GPIO_OUT_REG;
|
||||
#if GPIO_PORT_NUM > 1
|
||||
if (GPIO_PORT_NUM(port) != 0) {
|
||||
out = (uint32_t *)GPIO_OUT1_REG;
|
||||
}
|
||||
#endif
|
||||
*out = value;
|
||||
}
|
||||
|
||||
static inline gpio_port_t gpio_get_port(gpio_t pin)
|
||||
@ -108,15 +135,11 @@ static inline gpio_port_t gpio_port_pack_addr(void *addr)
|
||||
|
||||
static inline void * gpio_port_unpack_addr(gpio_port_t port)
|
||||
{
|
||||
const _esp32_port_t *p = (_esp32_port_t *)port;
|
||||
if (port <= 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return NULL if port is one of the port descriptors in GPIO port table */
|
||||
#if GPIO_PORT_NUMOF > 1
|
||||
return ((p == &_esp32_ports[0]) ||
|
||||
(p == &_esp32_ports[1])) ? NULL : (void *)port;
|
||||
#else
|
||||
return (p == _esp32_ports) ? NULL : (void *)port;
|
||||
#endif
|
||||
return (void *)port;
|
||||
}
|
||||
|
||||
static inline bool is_gpio_port_num_valid(uint_fast8_t num)
|
||||
|
@ -34,7 +34,7 @@
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "hal/gpio_types.h"
|
||||
#include "gpio_ll_arch.h"
|
||||
#include "soc/gpio_reg.h"
|
||||
#include "soc/gpio_struct.h"
|
||||
|
||||
#include "esp_idf_api/gpio.h"
|
||||
|
||||
@ -55,35 +55,9 @@ extern bool _gpio_pin_pd[GPIO_PIN_NUMOF];
|
||||
|
||||
static gpio_conf_t _gpio_conf[GPIO_PIN_NUMOF] = { };
|
||||
|
||||
const _esp32_port_t _esp32_ports[GPIO_PORT_NUMOF] = {
|
||||
{
|
||||
.out = (uint32_t *)GPIO_OUT_REG,
|
||||
.out_w1ts = (uint32_t *)GPIO_OUT_W1TS_REG,
|
||||
.out_w1tc = (uint32_t *)GPIO_OUT_W1TC_REG,
|
||||
.in = (uint32_t *)GPIO_IN_REG,
|
||||
.enable = (uint32_t *)GPIO_ENABLE_REG,
|
||||
.enable_w1ts = (uint32_t *)GPIO_ENABLE_W1TS_REG,
|
||||
.enable_w1tc = (uint32_t *)GPIO_ENABLE_W1TC_REG,
|
||||
.status_w1tc = (uint32_t *)GPIO_STATUS_W1TC_REG,
|
||||
},
|
||||
#if GPIO_PORT_NUMOF > 1
|
||||
{
|
||||
.out = (uint32_t *)GPIO_OUT1_REG,
|
||||
.out_w1ts = (uint32_t *)GPIO_OUT1_W1TS_REG,
|
||||
.out_w1tc = (uint32_t *)GPIO_OUT1_W1TC_REG,
|
||||
.in = (uint32_t *)GPIO_IN1_REG,
|
||||
.enable = (uint32_t *)GPIO_ENABLE1_REG,
|
||||
.enable_w1ts = (uint32_t *)GPIO_ENABLE1_W1TS_REG,
|
||||
.enable_w1tc = (uint32_t *)GPIO_ENABLE1_W1TC_REG,
|
||||
.status_w1tc = (uint32_t *)GPIO_STATUS1_W1TC_REG,
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
int gpio_ll_init(gpio_port_t port, uint8_t pin, gpio_conf_t conf)
|
||||
{
|
||||
assert(port);
|
||||
assert(GPIO_PORT_NUM(port) < GPIO_PORT_NUMOF);
|
||||
assert(is_gpio_port_num_valid(port));
|
||||
assert(pin < GPIO_PORT_PIN_NUMOF(port));
|
||||
|
||||
gpio_t gpio = GPIO_PIN(GPIO_PORT_NUM(port), pin);
|
||||
|
@ -55,9 +55,8 @@ extern void IRAM gpio_int_handler(void* arg);
|
||||
int gpio_ll_irq(gpio_port_t port, uint8_t pin, gpio_irq_trig_t trig,
|
||||
gpio_ll_cb_t cb, void *arg)
|
||||
{
|
||||
assert(port);
|
||||
assert(is_gpio_port_num_valid(port));
|
||||
assert(arg);
|
||||
assert(GPIO_PORT_NUM(port) < GPIO_PORT_NUMOF);
|
||||
assert(pin < GPIO_PORT_PIN_NUMOF(port));
|
||||
|
||||
unsigned state = irq_disable();
|
||||
@ -138,13 +137,20 @@ void gpio_ll_irq_unmask(gpio_port_t port, uint8_t pin)
|
||||
|
||||
void gpio_ll_irq_unmask_and_clear(gpio_port_t port, uint8_t pin)
|
||||
{
|
||||
_esp32_port_t *p = (_esp32_port_t *)port;
|
||||
gpio_t gpio = GPIO_PIN(GPIO_PORT_NUM(port), pin);
|
||||
|
||||
DEBUG("%s gpio=%u port=%u pin=%u\n",
|
||||
__func__, gpio, GPIO_PORT_NUM(port), pin);
|
||||
|
||||
*p->status_w1tc = BIT(pin);
|
||||
volatile uint32_t *status_w1tc = (uint32_t *)GPIO_STATUS_W1TC_REG;
|
||||
#if GPIO_PORT_NUMOF > 1
|
||||
if (GPIO_PORT_NUM(port) != 0) {
|
||||
status_w1tc = (uint32_t *)GPIO_STATUS1_W1TC_REG;
|
||||
}
|
||||
#endif
|
||||
|
||||
*status_w1tc = BIT(pin);
|
||||
|
||||
if (esp_idf_gpio_intr_enable(gpio) == ESP_OK) {
|
||||
gpio_int_enabled_table[gpio] = true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user