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