diff --git a/drivers/include/periph/gpio_ll.h b/drivers/include/periph/gpio_ll.h index cd7c21f918..ec2c708dac 100644 --- a/drivers/include/periph/gpio_ll.h +++ b/drivers/include/periph/gpio_ll.h @@ -69,13 +69,13 @@ #ifndef PERIPH_GPIO_LL_H #define PERIPH_GPIO_LL_H -#include +#include #include #include #include "architecture.h" -#include "periph_cpu.h" #include "periph/gpio.h" +#include "periph_cpu.h" #ifdef __cplusplus extern "C" { @@ -706,6 +706,47 @@ static inline uword_t gpio_ll_prepare_write(gpio_port_t port, uword_t mask, } #endif +/** + * @brief Turn GPIO pins specified by the bitmask @p outputs to outputs + * + * @param[in] port GPIO port to modify + * @param[in] outputs Bitmask specifying the GPIO pins to set in output + * mode + * @pre The feature `gpio_ll_switch_dir` is available + * @pre Each affected GPIO pin is either configured as input or as + * push-pull output. + * + * @note This is a makeshift solution to implement bit-banging of + * bidirectional protocols on less sophisticated GPIO peripherals + * that do not support open drain mode. + * @warning Use open drain mode instead, if supported. + */ +static inline void gpio_ll_switch_dir_output(gpio_port_t port, uword_t outputs); + +/** + * @brief Turn GPIO pins specified by the bitmask @p inputs to inputs + * + * @param[in] port GPIO port to modify + * @param[in] inputs Bitmask specifying the GPIO pins to set in input + * mode + * @pre The feature `gpio_ll_switch_dir` is available + * @pre Each affected GPIO pin is either configured as input or as + * push-pull output. + * + * @warning The state of the output register may be intermixed with the + * input configuration. Specifically, on AVR the output register + * enables/disables the internal pull up, on SAM0 MCUs the output + * register controls the pull resistor direction (if the pull + * resistor is enabled). Hence, the bits in the output + * register of the pins switched to input should be restored + * just after this call. + * @note This is a makeshift solution to implement bit-banging of + * bidirectional protocols on less sophisticated GPIO peripherals + * that do not support open drain mode. + * @warning Use open drain mode instead, if supported. + */ +static inline void gpio_ll_switch_dir_input(gpio_port_t port, uword_t inputs); + /** * @brief Perform a masked write operation on the I/O register of the port * @@ -786,6 +827,39 @@ static inline gpio_port_t gpio_port_pack_addr(void *addr); */ static inline void * gpio_port_unpack_addr(gpio_port_t port); +#ifndef DOXYGEN +#if !MODULE_PERIPH_GPIO_LL_SWITCH_DIR +static inline void gpio_ll_switch_dir_output(gpio_port_t port, uword_t outputs) +{ + (void)port; + (void)outputs; + /* Hack: If this function is only used guarded by some + * + * if (IS_USED(MODULE_PERIPH_GPIO_LL_SWITCH_DIR)) { + * ... + * } + * + * as intended, all calls to the following fake function will be optimized + * due to the elimination of dead branches. If used incorrectly, a linking + * failure will be the result. The error message will not be ideal, but a + * compile time error is much better than a runtime error. + */ + extern void gpio_ll_switch_dir_output_used_but_feature_gpio_ll_switch_dir_is_not_provided(void); + gpio_ll_switch_dir_output_used_but_feature_gpio_ll_switch_dir_is_not_provided(); +} + +static inline void gpio_ll_switch_dir_input(gpio_port_t port, uword_t inputs) +{ + (void)port; + (void)inputs; + /* Same hack as above */ + extern void gpio_ll_switch_dir_input_used_but_feature_gpio_ll_switch_dir_is_not_provided(void); + gpio_ll_switch_dir_input_used_but_feature_gpio_ll_switch_dir_is_not_provided(); +} + +#endif /* !MODULE_PERIPH_GPIO_LL_SWITCH_DIR */ +#endif /* !DOXYGEN */ + #ifdef __cplusplus } #endif diff --git a/drivers/periph_common/Makefile.dep b/drivers/periph_common/Makefile.dep index cf0a84f9ff..9d164a9944 100644 --- a/drivers/periph_common/Makefile.dep +++ b/drivers/periph_common/Makefile.dep @@ -1,14 +1,15 @@ # Always use hardware features, if available ifneq (,$(filter periph_gpio_ll%,$(USEMODULE))) FEATURES_OPTIONAL += periph_gpio_ll_disconnect - FEATURES_OPTIONAL += periph_gpio_ll_irq_level_triggered_high FEATURES_OPTIONAL += periph_gpio_ll_input_pull_down FEATURES_OPTIONAL += periph_gpio_ll_input_pull_keep FEATURES_OPTIONAL += periph_gpio_ll_input_pull_up + FEATURES_OPTIONAL += periph_gpio_ll_irq_level_triggered_high FEATURES_OPTIONAL += periph_gpio_ll_irq_level_triggered_low FEATURES_OPTIONAL += periph_gpio_ll_irq_unmask FEATURES_OPTIONAL += periph_gpio_ll_open_drain FEATURES_OPTIONAL += periph_gpio_ll_open_drain_pull_up FEATURES_OPTIONAL += periph_gpio_ll_open_source FEATURES_OPTIONAL += periph_gpio_ll_open_source_pull_down + FEATURES_OPTIONAL += periph_gpio_ll_switch_dir endif