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>
This commit is contained in:
Yong Cong Sin 2021-10-22 00:02:29 +08:00 committed by Christopher Friedt
commit a6ebcddc54

View file

@ -51,6 +51,34 @@ LOG_MODULE_REGISTER(uart_stm32);
#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 @@ static inline void uart_stm32_set_baudrate(const struct device *dev,
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 {