Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 1 | /* |
| 2 | * SPDX-License-Identifier: BSD-3-Clause |
| 3 | * SPDX-FileCopyrightText: Copyright TF-RMM Contributors. |
| 4 | */ |
| 5 | |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 6 | #include <assert.h> |
| 7 | #include <console.h> |
| 8 | #include <errno.h> |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 9 | #include <mmio.h> |
| 10 | #include <pl011.h> |
| 11 | #include <utils_def.h> |
| 12 | |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 13 | /* PL011 Registers */ |
| 14 | #define UARTDR 0x00U |
| 15 | #define UARTECR 0x04U |
| 16 | #define UARTFR 0x18U |
| 17 | |
Jacob Man Chun Yiu | e143aea | 2025-02-26 16:11:18 +0000 | [diff] [blame^] | 18 | /* Transmit FIFO full */ |
| 19 | #define PL011_UARTFR_TXFF (U(1) << 5) |
| 20 | |
| 21 | #ifndef PL011_GENERIC_SBSA_UART |
| 22 | |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 23 | /* PL011 registers (out of the SBSA specification) */ |
| 24 | #define UARTIBRD 0x24U |
| 25 | #define UARTFBRD 0x28U |
| 26 | #define UARTLCR_H 0x2CU |
| 27 | #define UARTCR 0x30U |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 28 | /* Flag reg bits */ |
| 29 | |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 30 | /* Control reg bits */ |
| 31 | #define PL011_UARTCR_RXE (U(1) << 9) /* Receive enable */ |
| 32 | #define PL011_UARTCR_TXE (U(1) << 8) /* Transmit enable */ |
| 33 | #define PL011_UARTCR_UARTEN (U(1) << 0) /* UART Enable */ |
| 34 | |
| 35 | /* FIFO Enabled / No Parity / 8 Data bit / One Stop Bit */ |
| 36 | #define PL011_LINE_CONTROL (PL011_UARTLCR_H_FEN | PL011_UARTLCR_H_WLEN_8) |
| 37 | |
| 38 | /* Line Control Register Bits */ |
| 39 | #define PL011_UARTLCR_H_WLEN_8 (U(3) << 5) |
| 40 | #define PL011_UARTLCR_H_FEN (U(1) << 4) /* FIFOs Enable */ |
| 41 | |
Jacob Man Chun Yiu | e143aea | 2025-02-26 16:11:18 +0000 | [diff] [blame^] | 42 | #endif /* PL011_GENERIC_SBSA_UART */ |
| 43 | |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 44 | static inline void pl011_wait(uintptr_t base) |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 45 | { |
| 46 | /* Wait until there is room in the Tx FIFO */ |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 47 | while ((read32((void *)(base + UARTFR)) |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 48 | & PL011_UARTFR_TXFF) != 0U) { |
| 49 | /* Do nothing */ |
| 50 | } |
| 51 | } |
| 52 | |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 53 | static void writechar(uintptr_t base, int ch) |
| 54 | { |
| 55 | pl011_wait(base); |
| 56 | write8((uint8_t)ch, (void *)(base + UARTDR)); |
| 57 | } |
| 58 | |
| 59 | /* Serial output - called from console driver */ |
| 60 | /* coverity[misra_c_2012_rule_8_7_violation:SUPPRESS] */ |
Shruti Gupta | 0a0d4ee | 2024-04-22 21:37:25 +0100 | [diff] [blame] | 61 | static int pl011_putc(int c, const struct console *csl) |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 62 | { |
| 63 | assert(csl != NULL); |
| 64 | |
| 65 | if ((char)c == '\n') { |
| 66 | /* NOLINTNEXTLINE(google-readability-casting) */ |
| 67 | writechar(csl->base, (int)'\r'); |
| 68 | } |
| 69 | writechar(csl->base, c); |
| 70 | |
| 71 | return c; |
| 72 | } |
| 73 | |
| 74 | static struct console pl011_csl = { |
| 75 | .putc = pl011_putc, |
| 76 | .flush = NULL |
| 77 | }; |
| 78 | |
| 79 | /* Function that initializes PL011 for console output */ |
| 80 | int pl011_init(uintptr_t base_addr, |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 81 | unsigned int uart_clk, |
| 82 | unsigned int baud_rate) |
| 83 | { |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 84 | /* Check Base address, baud rate and UART clock for sanity */ |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 85 | if ((base_addr == 0UL) || (uart_clk == 0U) || |
| 86 | (baud_rate == 0U)) { |
| 87 | return -EINVAL; |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 88 | } |
Jacob Man Chun Yiu | e143aea | 2025-02-26 16:11:18 +0000 | [diff] [blame^] | 89 | #ifndef PL011_GENERIC_SBSA_UART |
| 90 | unsigned int div; |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 91 | |
| 92 | /* Disable UART before programming */ |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 93 | write32(0U, (void *)(base_addr + UARTCR)); |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 94 | |
AlexeiFedorov | 33656a3 | 2023-08-30 11:51:41 +0100 | [diff] [blame] | 95 | /* Program the baud rate */ |
| 96 | div = (uart_clk * 4U) / baud_rate; |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 97 | |
| 98 | /* IBRD = Divisor >> 6 */ |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 99 | write32(div >> 6, (void *)((base_addr) + UARTIBRD)); |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 100 | |
| 101 | /* FBRD = Divisor & 0x3F */ |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 102 | write32(div & 0x3fU, (void *)((base_addr) + UARTFBRD)); |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 103 | |
| 104 | /* Enable FIFO and set word length, parity and number of stop bits */ |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 105 | write32(PL011_LINE_CONTROL, (void *)((base_addr) + UARTLCR_H)); |
Jacob Man Chun Yiu | e143aea | 2025-02-26 16:11:18 +0000 | [diff] [blame^] | 106 | #endif /* PL011_GENERIC_SBSA_UART */ |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 107 | |
| 108 | /* Clear any pending errors */ |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 109 | write32(0U, (void *)((base_addr) + UARTECR)); |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 110 | |
Jacob Man Chun Yiu | e143aea | 2025-02-26 16:11:18 +0000 | [diff] [blame^] | 111 | #ifndef PL011_GENERIC_SBSA_UART |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 112 | /* Enable Tx, Rx, and UART overall */ |
| 113 | write32(PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN, |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 114 | (void *)((base_addr) + UARTCR)); |
Jacob Man Chun Yiu | e143aea | 2025-02-26 16:11:18 +0000 | [diff] [blame^] | 115 | #endif /* PL011_GENERIC_SBSA_UART */ |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 116 | |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 117 | pl011_csl.base = base_addr; |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 118 | |
Soby Mathew | 8218e08 | 2024-02-26 14:09:28 +0000 | [diff] [blame] | 119 | return console_register(&pl011_csl); |
Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame] | 120 | } |