uboot/drivers/serial/serial_stm32.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
   4 * Author(s): Vikas Manocha, <vikas.manocha@st.com> for STMicroelectronics.
   5 */
   6
   7#define LOG_CATEGORY UCLASS_SERIAL
   8
   9#include <common.h>
  10#include <clk.h>
  11#include <dm.h>
  12#include <log.h>
  13#include <reset.h>
  14#include <serial.h>
  15#include <watchdog.h>
  16#include <asm/io.h>
  17#include <asm/arch/stm32.h>
  18#include <dm/device_compat.h>
  19#include <linux/bitops.h>
  20#include <linux/delay.h>
  21#include "serial_stm32.h"
  22#include <dm/device_compat.h>
  23
  24static void _stm32_serial_setbrg(fdt_addr_t base,
  25                                 struct stm32_uart_info *uart_info,
  26                                 u32 clock_rate,
  27                                 int baudrate)
  28{
  29        bool stm32f4 = uart_info->stm32f4;
  30        u32 int_div, mantissa, fraction, oversampling;
  31
  32        int_div = DIV_ROUND_CLOSEST(clock_rate, baudrate);
  33
  34        if (int_div < 16) {
  35                oversampling = 8;
  36                setbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_OVER8);
  37        } else {
  38                oversampling = 16;
  39                clrbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_OVER8);
  40        }
  41
  42        mantissa = (int_div / oversampling) << USART_BRR_M_SHIFT;
  43        fraction = int_div % oversampling;
  44
  45        writel(mantissa | fraction, base + BRR_OFFSET(stm32f4));
  46}
  47
  48static int stm32_serial_setbrg(struct udevice *dev, int baudrate)
  49{
  50        struct stm32x7_serial_plat *plat = dev_get_plat(dev);
  51
  52        _stm32_serial_setbrg(plat->base, plat->uart_info,
  53                             plat->clock_rate, baudrate);
  54
  55        return 0;
  56}
  57
  58static int stm32_serial_setconfig(struct udevice *dev, uint serial_config)
  59{
  60        struct stm32x7_serial_plat *plat = dev_get_plat(dev);
  61        bool stm32f4 = plat->uart_info->stm32f4;
  62        u8 uart_enable_bit = plat->uart_info->uart_enable_bit;
  63        u32 cr1 = plat->base + CR1_OFFSET(stm32f4);
  64        u32 config = 0;
  65        uint parity = SERIAL_GET_PARITY(serial_config);
  66        uint bits = SERIAL_GET_BITS(serial_config);
  67        uint stop = SERIAL_GET_STOP(serial_config);
  68
  69        /*
  70         * only parity config is implemented, check if other serial settings
  71         * are the default one.
  72         * (STM32F4 serial IP didn't support parity setting)
  73         */
  74        if (bits != SERIAL_8_BITS || stop != SERIAL_ONE_STOP || stm32f4)
  75                return -ENOTSUPP; /* not supported in driver*/
  76
  77        clrbits_le32(cr1, USART_CR1_RE | USART_CR1_TE | BIT(uart_enable_bit));
  78        /* update usart configuration (uart need to be disable)
  79         * PCE: parity check enable
  80         * PS : '0' : Even / '1' : Odd
  81         * M[1:0] = '00' : 8 Data bits
  82         * M[1:0] = '01' : 9 Data bits with parity
  83         */
  84        switch (parity) {
  85        default:
  86        case SERIAL_PAR_NONE:
  87                config = 0;
  88                break;
  89        case SERIAL_PAR_ODD:
  90                config = USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0;
  91                break;
  92        case SERIAL_PAR_EVEN:
  93                config = USART_CR1_PCE | USART_CR1_M0;
  94                break;
  95        }
  96
  97        clrsetbits_le32(cr1,
  98                        USART_CR1_PCE | USART_CR1_PS | USART_CR1_M1 |
  99                        USART_CR1_M0,
 100                        config);
 101        setbits_le32(cr1, USART_CR1_RE | USART_CR1_TE | BIT(uart_enable_bit));
 102
 103        return 0;
 104}
 105
 106static int stm32_serial_getc(struct udevice *dev)
 107{
 108        struct stm32x7_serial_plat *plat = dev_get_plat(dev);
 109        bool stm32f4 = plat->uart_info->stm32f4;
 110        fdt_addr_t base = plat->base;
 111        u32 isr = readl(base + ISR_OFFSET(stm32f4));
 112
 113        if ((isr & USART_ISR_RXNE) == 0)
 114                return -EAGAIN;
 115
 116        if (isr & (USART_ISR_PE | USART_ISR_ORE | USART_ISR_FE)) {
 117                if (!stm32f4)
 118                        setbits_le32(base + ICR_OFFSET,
 119                                     USART_ICR_PCECF | USART_ICR_ORECF |
 120                                     USART_ICR_FECF);
 121                else
 122                        readl(base + RDR_OFFSET(stm32f4));
 123                return -EIO;
 124        }
 125
 126        return readl(base + RDR_OFFSET(stm32f4));
 127}
 128
 129static int _stm32_serial_putc(fdt_addr_t base,
 130                              struct stm32_uart_info *uart_info,
 131                              const char c)
 132{
 133        bool stm32f4 = uart_info->stm32f4;
 134
 135        if ((readl(base + ISR_OFFSET(stm32f4)) & USART_ISR_TXE) == 0)
 136                return -EAGAIN;
 137
 138        writel(c, base + TDR_OFFSET(stm32f4));
 139
 140        return 0;
 141}
 142
 143static int stm32_serial_putc(struct udevice *dev, const char c)
 144{
 145        struct stm32x7_serial_plat *plat = dev_get_plat(dev);
 146
 147        return _stm32_serial_putc(plat->base, plat->uart_info, c);
 148}
 149
 150static int stm32_serial_pending(struct udevice *dev, bool input)
 151{
 152        struct stm32x7_serial_plat *plat = dev_get_plat(dev);
 153        bool stm32f4 = plat->uart_info->stm32f4;
 154        fdt_addr_t base = plat->base;
 155
 156        if (input)
 157                return readl(base + ISR_OFFSET(stm32f4)) &
 158                        USART_ISR_RXNE ? 1 : 0;
 159        else
 160                return readl(base + ISR_OFFSET(stm32f4)) &
 161                        USART_ISR_TXE ? 0 : 1;
 162}
 163
 164static void _stm32_serial_init(fdt_addr_t base,
 165                               struct stm32_uart_info *uart_info)
 166{
 167        bool stm32f4 = uart_info->stm32f4;
 168        u8 uart_enable_bit = uart_info->uart_enable_bit;
 169
 170        /* Disable uart-> enable fifo -> enable uart */
 171        clrbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_RE | USART_CR1_TE |
 172                     BIT(uart_enable_bit));
 173        if (uart_info->has_fifo)
 174                setbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_FIFOEN);
 175        setbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_RE | USART_CR1_TE |
 176                     BIT(uart_enable_bit));
 177}
 178
 179static int stm32_serial_probe(struct udevice *dev)
 180{
 181        struct stm32x7_serial_plat *plat = dev_get_plat(dev);
 182        struct clk clk;
 183        struct reset_ctl reset;
 184        int ret;
 185
 186        plat->uart_info = (struct stm32_uart_info *)dev_get_driver_data(dev);
 187
 188        ret = clk_get_by_index(dev, 0, &clk);
 189        if (ret < 0)
 190                return ret;
 191
 192        ret = clk_enable(&clk);
 193        if (ret) {
 194                dev_err(dev, "failed to enable clock\n");
 195                return ret;
 196        }
 197
 198        ret = reset_get_by_index(dev, 0, &reset);
 199        if (!ret) {
 200                reset_assert(&reset);
 201                udelay(2);
 202                reset_deassert(&reset);
 203        }
 204
 205        plat->clock_rate = clk_get_rate(&clk);
 206        if (!plat->clock_rate) {
 207                clk_disable(&clk);
 208                return -EINVAL;
 209        };
 210
 211        _stm32_serial_init(plat->base, plat->uart_info);
 212
 213        return 0;
 214}
 215
 216static const struct udevice_id stm32_serial_id[] = {
 217        { .compatible = "st,stm32-uart", .data = (ulong)&stm32f4_info},
 218        { .compatible = "st,stm32f7-uart", .data = (ulong)&stm32f7_info},
 219        { .compatible = "st,stm32h7-uart", .data = (ulong)&stm32h7_info},
 220        {}
 221};
 222
 223static int stm32_serial_of_to_plat(struct udevice *dev)
 224{
 225        struct stm32x7_serial_plat *plat = dev_get_plat(dev);
 226
 227        plat->base = dev_read_addr(dev);
 228        if (plat->base == FDT_ADDR_T_NONE)
 229                return -EINVAL;
 230
 231        return 0;
 232}
 233
 234static const struct dm_serial_ops stm32_serial_ops = {
 235        .putc = stm32_serial_putc,
 236        .pending = stm32_serial_pending,
 237        .getc = stm32_serial_getc,
 238        .setbrg = stm32_serial_setbrg,
 239        .setconfig = stm32_serial_setconfig
 240};
 241
 242U_BOOT_DRIVER(serial_stm32) = {
 243        .name = "serial_stm32",
 244        .id = UCLASS_SERIAL,
 245        .of_match = of_match_ptr(stm32_serial_id),
 246        .of_to_plat = of_match_ptr(stm32_serial_of_to_plat),
 247        .plat_auto      = sizeof(struct stm32x7_serial_plat),
 248        .ops = &stm32_serial_ops,
 249        .probe = stm32_serial_probe,
 250#if !CONFIG_IS_ENABLED(OF_CONTROL)
 251        .flags = DM_FLAG_PRE_RELOC,
 252#endif
 253};
 254
 255#ifdef CONFIG_DEBUG_UART_STM32
 256#include <debug_uart.h>
 257static inline struct stm32_uart_info *_debug_uart_info(void)
 258{
 259        struct stm32_uart_info *uart_info;
 260
 261#if defined(CONFIG_STM32F4)
 262        uart_info = &stm32f4_info;
 263#elif defined(CONFIG_STM32F7)
 264        uart_info = &stm32f7_info;
 265#else
 266        uart_info = &stm32h7_info;
 267#endif
 268        return uart_info;
 269}
 270
 271static inline void _debug_uart_init(void)
 272{
 273        fdt_addr_t base = CONFIG_DEBUG_UART_BASE;
 274        struct stm32_uart_info *uart_info = _debug_uart_info();
 275
 276        _stm32_serial_init(base, uart_info);
 277        _stm32_serial_setbrg(base, uart_info,
 278                             CONFIG_DEBUG_UART_CLOCK,
 279                             CONFIG_BAUDRATE);
 280}
 281
 282static inline void _debug_uart_putc(int c)
 283{
 284        fdt_addr_t base = CONFIG_DEBUG_UART_BASE;
 285        struct stm32_uart_info *uart_info = _debug_uart_info();
 286
 287        while (_stm32_serial_putc(base, uart_info, c) == -EAGAIN)
 288                ;
 289}
 290
 291DEBUG_UART_FUNCS
 292#endif
 293