diff --git a/cpu/stm32_common/Makefile.features b/cpu/stm32_common/Makefile.features
index 13c07298d718c6cd4de6ec6b068ea1397c437db7..f54d184a87ca0feda3d027924b8116dc89422e3c 100644
--- a/cpu/stm32_common/Makefile.features
+++ b/cpu/stm32_common/Makefile.features
@@ -1,6 +1,7 @@
 FEATURES_PROVIDED += periph_cpuid
 FEATURES_PROVIDED += periph_gpio periph_gpio_irq
 FEATURES_PROVIDED += puf_sram
+FEATURES_PROVIDED += periph_uart_modecfg
 
 ifneq (,$(filter $(BOARDS_WITHOUT_HWRNG),$(BOARD)))
   FEATURES_PROVIDED := $(filter-out periph_hwrng,$(FEATURES_PROVIDED))
diff --git a/cpu/stm32_common/include/periph_cpu_common.h b/cpu/stm32_common/include/periph_cpu_common.h
index b1f83a032f1e7fc026b7f2d659f06f234812e405..fd97fb25a2735b977c13baa1391235305d41aa37 100644
--- a/cpu/stm32_common/include/periph_cpu_common.h
+++ b/cpu/stm32_common/include/periph_cpu_common.h
@@ -370,6 +370,56 @@ typedef enum {
     STM32_LPUART,           /**< STM32 Low-power UART (LPUART) module type */
 } uart_type_t;
 
+/**
+ * @brief   Invalid UART mode mask
+ *
+ * This mask is also used to force data_bits_t to be uint32_t type
+ * since it may be assigned a uint32_t variable in uart_mode
+ */
+#define UART_INVALID_MODE   (0x8000000)
+
+/**
+ * @brief   Override parity values
+ * @{
+ */
+#define HAVE_UART_PARITY_T
+typedef enum {
+   UART_PARITY_NONE = 0,                               /**< no parity */
+   UART_PARITY_EVEN = USART_CR1_PCE,                   /**< even parity */
+   UART_PARITY_ODD = (USART_CR1_PCE | USART_CR1_PS),   /**< odd parity */
+   UART_PARITY_MARK = UART_INVALID_MODE | 4,           /**< not supported */
+   UART_PARITY_SPACE = UART_INVALID_MODE  | 5          /**< not supported */
+} uart_parity_t;
+/** @} */
+
+/**
+ * @brief   Override data bits length values
+ * @{
+ */
+#define HAVE_UART_DATA_BITS_T
+typedef enum {
+    UART_DATA_BITS_5 = UART_INVALID_MODE | 1,   /**< not supported */
+    UART_DATA_BITS_6 = UART_INVALID_MODE | 2,   /**< not supported unless parity is set */
+#if defined(USART_CR1_M1)
+    UART_DATA_BITS_7 = USART_CR1_M1,            /**< 7 data bits */
+#else
+    UART_DATA_BITS_7 = UART_INVALID_MODE | 3,   /**< not supported unless parity is set */
+#endif
+    UART_DATA_BITS_8 = 0,                       /**< 8 data bits */
+} uart_data_bits_t;
+/** @} */
+
+/**
+ * @brief   Override stop bits length values
+ * @{
+ */
+#define HAVE_UART_STOP_BITS_T
+typedef enum {
+   UART_STOP_BITS_1 = 0,                  /**< 1 stop bit */
+   UART_STOP_BITS_2 = USART_CR2_STOP_1,   /**< 2 stop bits */
+} uart_stop_bits_t;
+/** @} */
+
 /**
  * @brief   Structure for UART configuration data
  */
diff --git a/cpu/stm32_common/periph/uart.c b/cpu/stm32_common/periph/uart.c
index 135830051791c90c541487b701c6927fae039094..7a932b088cc413ca97bd64d1ad5c3a9e590987b8 100644
--- a/cpu/stm32_common/periph/uart.c
+++ b/cpu/stm32_common/periph/uart.c
@@ -53,8 +53,15 @@
 
 /**
  * @brief   Allocate memory to store the callback functions
+ *
+ * Extend standard uart_isr_ctx_t with data_mask field. This is needed
+ * in order to mask parity bit.
  */
-static uart_isr_ctx_t isr_ctx[UART_NUMOF];
+static struct {
+    uart_rx_cb_t rx_cb;   /**< data received interrupt callback */
+    void *arg;            /**< argument to both callback routines */
+    uint8_t data_mask;    /**< mask applied to the data register */
+} isr_ctx[UART_NUMOF];
 
 static inline USART_TypeDef *dev(uart_t uart)
 {
@@ -105,8 +112,9 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
     assert(uart < UART_NUMOF);
 
     /* save ISR context */
-    isr_ctx[uart].rx_cb = rx_cb;
-    isr_ctx[uart].arg   = arg;
+    isr_ctx[uart].rx_cb     = rx_cb;
+    isr_ctx[uart].arg       = arg;
+    isr_ctx[uart].data_mask = 0xFF;
 
     uart_init_pins(uart, rx_cb);
 
@@ -154,6 +162,53 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
     return UART_OK;
 }
 
+#ifdef MODULE_PERIPH_UART_MODECFG
+int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity,
+              uart_stop_bits_t stop_bits)
+{
+    assert(uart < UART_NUMOF);
+
+    isr_ctx[uart].data_mask = 0xFF;
+
+    if (parity) {
+        switch (data_bits) {
+            case UART_DATA_BITS_6:
+                data_bits = UART_DATA_BITS_7;
+                isr_ctx[uart].data_mask = 0x3F;
+                break;
+            case UART_DATA_BITS_7:
+                data_bits = UART_DATA_BITS_8;
+                isr_ctx[uart].data_mask = 0x7F;
+                break;
+            case UART_DATA_BITS_8:
+                data_bits = USART_CR1_M;
+                break;
+            default:
+                return UART_NOMODE;
+        }
+    }
+    if ((data_bits & UART_INVALID_MODE) || (parity & UART_INVALID_MODE)) {
+        return UART_NOMODE;
+    }
+
+#ifdef USART_CR1_M1
+    if (!(dev(uart)->ISR & USART_ISR_TC)) {
+        return UART_INTERR;
+    }
+    dev(uart)->CR1 &= ~(USART_CR1_UE | USART_CR1_TE);
+    dev(uart)->CR1 &= ~USART_CR1_M1;
+#endif
+
+    dev(uart)->CR2 &= ~USART_CR2_STOP;
+    dev(uart)->CR1 &= ~(USART_CR1_PS | USART_CR1_PCE | USART_CR1_M);
+
+    dev(uart)->CR2 |= stop_bits;
+    dev(uart)->CR1 |= (USART_CR1_UE | USART_CR1_TE | data_bits | parity);
+
+    return UART_OK;
+}
+#endif /* MODULE_PERIPH_UART_MODECFG */
+
 static inline void uart_init_usart(uart_t uart, uint32_t baudrate)
 {
     uint16_t mantissa;
@@ -301,7 +356,8 @@ static inline void irq_handler(uart_t uart)
     uint32_t status = dev(uart)->ISR;
 
     if (status & USART_ISR_RXNE) {
-        isr_ctx[uart].rx_cb(isr_ctx[uart].arg, (uint8_t)dev(uart)->RDR);
+        isr_ctx[uart].rx_cb(isr_ctx[uart].arg,
+                            (uint8_t)dev(uart)->RDR & isr_ctx[uart].data_mask);
     }
     if (status & USART_ISR_ORE) {
         dev(uart)->ICR |= USART_ICR_ORECF;    /* simply clear flag on overrun */
@@ -312,7 +368,8 @@ static inline void irq_handler(uart_t uart)
     uint32_t status = dev(uart)->SR;
 
     if (status & USART_SR_RXNE) {
-        isr_ctx[uart].rx_cb(isr_ctx[uart].arg, (uint8_t)dev(uart)->DR);
+        isr_ctx[uart].rx_cb(isr_ctx[uart].arg,
+                            (uint8_t)dev(uart)->DR & isr_ctx[uart].data_mask);
     }
     if (status & USART_SR_ORE) {
         /* ORE is cleared by reading SR and DR sequentially */