Angel Pons | 8a3453f | 2020-04-02 23:48:19 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Jonathan Neuschäfer | 894e3a9 | 2018-04-19 16:23:54 +0200 | [diff] [blame] | 2 | |
Kyösti Mälkki | 13f6650 | 2019-03-03 08:01:05 +0200 | [diff] [blame] | 3 | #include <device/mmio.h> |
Jonathan Neuschäfer | 894e3a9 | 2018-04-19 16:23:54 +0200 | [diff] [blame] | 4 | #include <boot/coreboot_tables.h> |
| 5 | #include <console/uart.h> |
| 6 | #include <types.h> |
| 7 | |
| 8 | /* |
| 9 | * This is a driver for SiFive's own UART, documented in the FU540 manual: |
| 10 | * https://www.sifive.com/documentation/chips/freedom-u540-c000-manual/ |
| 11 | */ |
| 12 | |
| 13 | struct sifive_uart_registers { |
| 14 | uint32_t txdata; /* Transmit data register */ |
| 15 | uint32_t rxdata; /* Receive data register */ |
| 16 | uint32_t txctrl; /* Transmit control register */ |
| 17 | uint32_t rxctrl; /* Receive control register */ |
| 18 | uint32_t ie; /* UART interrupt enable */ |
| 19 | uint32_t ip; /* UART interrupt pending */ |
| 20 | uint32_t div; /* Baud rate divisor */ |
| 21 | } __packed; |
| 22 | |
| 23 | #define TXDATA_FULL BIT(31) |
| 24 | #define RXDATA_EMPTY BIT(31) |
| 25 | #define TXCTRL_TXEN BIT(0) |
| 26 | #define TXCTRL_NSTOP_SHIFT 1 |
| 27 | #define TXCTRL_NSTOP(x) (((x)-1) << TXCTRL_NSTOP_SHIFT) |
| 28 | #define TXCTRL_TXCNT_SHIFT 16 |
| 29 | #define TXCTRL_TXCNT(x) ((x) << TXCTRL_TXCNT_SHIFT) |
| 30 | #define RXCTRL_RXEN BIT(0) |
| 31 | #define RXCTRL_RXCNT_SHIFT 16 |
| 32 | #define RXCTRL_RXCNT(x) ((x) << RXCTRL_RXCNT_SHIFT) |
| 33 | #define IP_TXWM BIT(0) |
| 34 | #define IP_RXWM BIT(1) |
| 35 | |
Philipp Hug | 7524400 | 2018-07-07 21:34:31 +0200 | [diff] [blame] | 36 | static void sifive_uart_init(struct sifive_uart_registers *regs, int div) |
Jonathan Neuschäfer | 894e3a9 | 2018-04-19 16:23:54 +0200 | [diff] [blame] | 37 | { |
Philipp Hug | 7524400 | 2018-07-07 21:34:31 +0200 | [diff] [blame] | 38 | /* Configure the divisor */ |
| 39 | write32(®s->div, div); |
Jonathan Neuschäfer | 894e3a9 | 2018-04-19 16:23:54 +0200 | [diff] [blame] | 40 | |
| 41 | /* Enable transmission, one stop bit, transmit watermark at 1 */ |
| 42 | write32(®s->txctrl, TXCTRL_TXEN|TXCTRL_NSTOP(1)|TXCTRL_TXCNT(1)); |
| 43 | |
| 44 | /* Enable reception, receive watermark at 0 */ |
| 45 | write32(®s->rxctrl, RXCTRL_RXEN|RXCTRL_RXCNT(0)); |
| 46 | } |
| 47 | |
Felix Held | e3a1247 | 2020-09-11 15:47:09 +0200 | [diff] [blame] | 48 | void uart_init(unsigned int idx) |
Philipp Hug | 7524400 | 2018-07-07 21:34:31 +0200 | [diff] [blame] | 49 | { |
Maximilian Brune | a2eca49 | 2024-01-14 20:06:10 +0600 | [diff] [blame^] | 50 | /* |
| 51 | * according to FU540/FU740 manual: |
| 52 | * f_baud = f_in / (div + 1) |
| 53 | * <=> |
| 54 | * div = (f_in / f_baud) - 1 |
| 55 | */ |
| 56 | unsigned int div = uart_baudrate_divisor(get_uart_baudrate(), uart_platform_refclk(), |
| 57 | uart_input_clock_divider()); |
| 58 | div -= 1; |
| 59 | |
Philipp Hug | 7524400 | 2018-07-07 21:34:31 +0200 | [diff] [blame] | 60 | sifive_uart_init(uart_platform_baseptr(idx), div); |
| 61 | } |
| 62 | |
Jonathan Neuschäfer | 894e3a9 | 2018-04-19 16:23:54 +0200 | [diff] [blame] | 63 | static bool uart_can_tx(struct sifive_uart_registers *regs) |
| 64 | { |
| 65 | return !(read32(®s->txdata) & TXDATA_FULL); |
| 66 | } |
| 67 | |
Felix Held | e3a1247 | 2020-09-11 15:47:09 +0200 | [diff] [blame] | 68 | void uart_tx_byte(unsigned int idx, unsigned char data) |
Jonathan Neuschäfer | 894e3a9 | 2018-04-19 16:23:54 +0200 | [diff] [blame] | 69 | { |
| 70 | struct sifive_uart_registers *regs = uart_platform_baseptr(idx); |
| 71 | |
| 72 | while (!uart_can_tx(regs)) |
| 73 | ; /* TODO: implement a timeout */ |
| 74 | |
| 75 | write32(®s->txdata, data); |
| 76 | } |
| 77 | |
Felix Held | e3a1247 | 2020-09-11 15:47:09 +0200 | [diff] [blame] | 78 | void uart_tx_flush(unsigned int idx) |
Jonathan Neuschäfer | 894e3a9 | 2018-04-19 16:23:54 +0200 | [diff] [blame] | 79 | { |
| 80 | struct sifive_uart_registers *regs = uart_platform_baseptr(idx); |
| 81 | uint32_t ip; |
| 82 | |
| 83 | /* Use the TX watermark bit to find out if the TX FIFO is empty */ |
| 84 | do { |
| 85 | ip = read32(®s->ip); |
| 86 | } while (!(ip & IP_TXWM)); |
| 87 | } |
| 88 | |
Felix Held | e3a1247 | 2020-09-11 15:47:09 +0200 | [diff] [blame] | 89 | unsigned char uart_rx_byte(unsigned int idx) |
Jonathan Neuschäfer | 894e3a9 | 2018-04-19 16:23:54 +0200 | [diff] [blame] | 90 | { |
| 91 | struct sifive_uart_registers *regs = uart_platform_baseptr(idx); |
| 92 | uint32_t rxdata; |
| 93 | |
| 94 | do { |
| 95 | rxdata = read32(®s->rxdata); |
| 96 | } while (rxdata & RXDATA_EMPTY); |
| 97 | |
| 98 | return rxdata & 0xff; |
| 99 | } |
| 100 | |
| 101 | unsigned int uart_input_clock_divider(void) |
| 102 | { |
| 103 | /* |
| 104 | * The SiFive UART handles oversampling internally. The divided clock |
| 105 | * is the baud clock. |
| 106 | */ |
| 107 | return 1; |
| 108 | } |
| 109 | |
Arthur Heymans | 9948c52 | 2022-10-24 14:37:40 +0200 | [diff] [blame] | 110 | enum cb_err fill_lb_serial(struct lb_serial *serial) |
Jonathan Neuschäfer | 894e3a9 | 2018-04-19 16:23:54 +0200 | [diff] [blame] | 111 | { |
Arthur Heymans | 9948c52 | 2022-10-24 14:37:40 +0200 | [diff] [blame] | 112 | return CB_ERR; |
Jonathan Neuschäfer | 894e3a9 | 2018-04-19 16:23:54 +0200 | [diff] [blame] | 113 | /* TODO */ |
| 114 | } |