driver: serial: uart_stm32: Calculate suitable PRESCALER value

Current driver set a fixed prescaler value for the lpuart
that caused certain baudrate configurations to fail due to
LPUARTDIV overflow the LPUART_BRR register.

This PR attempt to calculate a suitable PRESCALER for the
selected baudrate, throws error and return if it couldn't get
an optimal one.

Signed-off-by: Yong Cong Sin <yongcong.sin@gmail.com>
diff --git a/drivers/serial/uart_stm32.c b/drivers/serial/uart_stm32.c
index 0dfbad9..67ca825 100644
--- a/drivers/serial/uart_stm32.c
+++ b/drivers/serial/uart_stm32.c
@@ -51,6 +51,34 @@
 #define UART_STRUCT(dev)					\
 	((USART_TypeDef *)(DEV_CFG(dev))->uconf.base)
 
+#if HAS_LPUART_1
+#ifdef USART_PRESC_PRESCALER
+uint32_t lpuartdiv_calc(const uint64_t clock_rate, const uint16_t presc_idx,
+			const uint32_t baud_rate)
+{
+	uint64_t lpuartdiv;
+
+	lpuartdiv = clock_rate / LPUART_PRESCALER_TAB[presc_idx];
+	lpuartdiv *= LPUART_LPUARTDIV_FREQ_MUL;
+	lpuartdiv += baud_rate / 2;
+	lpuartdiv /= baud_rate;
+
+	return (uint32_t)lpuartdiv;
+}
+#else
+uint32_t lpuartdiv_calc(const uint64_t clock_rate, const uint32_t baud_rate)
+{
+	uint64_t lpuartdiv;
+
+	lpuartdiv = clock_rate * LPUART_LPUARTDIV_FREQ_MUL;
+	lpuartdiv += baud_rate / 2;
+	lpuartdiv /= baud_rate;
+
+	return (uint32_t)lpuartdiv;
+}
+#endif /* USART_PRESC_PRESCALER */
+#endif /* HAS_LPUART_1 */
+
 #define TIMEOUT 1000
 
 #ifdef CONFIG_PM
@@ -92,13 +120,39 @@
 		return;
 	}
 
-
 #if HAS_LPUART_1
 	if (IS_LPUART_INSTANCE(UartInstance)) {
+		uint32_t lpuartdiv;
+#ifdef USART_PRESC_PRESCALER
+		uint8_t presc_idx;
+		uint32_t presc_val;
+
+		for (presc_idx = 0; presc_idx < ARRAY_SIZE(LPUART_PRESCALER_TAB); presc_idx++) {
+			lpuartdiv = lpuartdiv_calc(clock_rate, presc_idx, baud_rate);
+			if (lpuartdiv >= LPUART_BRR_MIN_VALUE && lpuartdiv <= LPUART_BRR_MASK) {
+				break;
+			}
+		}
+
+		if (presc_idx == ARRAY_SIZE(LPUART_PRESCALER_TAB)) {
+			LOG_ERR("Unable to set %s to %d", dev->name, baud_rate);
+			return;
+		}
+
+		presc_val = presc_idx << USART_PRESC_PRESCALER_Pos;
+
+		LL_LPUART_SetPrescaler(UartInstance, presc_val);
+#else
+		lpuartdiv = lpuartdiv_calc(clock_rate, baud_rate);
+		if (lpuartdiv < LPUART_BRR_MIN_VALUE || lpuartdiv > LPUART_BRR_MASK) {
+			LOG_ERR("Unable to set %s to %d", dev->name, baud_rate);
+			return;
+		}
+#endif /* USART_PRESC_PRESCALER */
 		LL_LPUART_SetBaudRate(UartInstance,
 				      clock_rate,
 #ifdef USART_PRESC_PRESCALER
-				      LL_USART_PRESCALER_DIV1,
+				      presc_val,
 #endif
 				      baud_rate);
 	} else {