diff options
Diffstat (limited to 'src/soc/ti/am335x/uart.c')
-rw-r--r-- | src/soc/ti/am335x/uart.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/soc/ti/am335x/uart.c b/src/soc/ti/am335x/uart.c new file mode 100644 index 0000000000..90095d47d9 --- /dev/null +++ b/src/soc/ti/am335x/uart.c @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <types.h> +#include <console/uart.h> +#include <device/mmio.h> +#include <boot/coreboot_tables.h> +#include <soc/ti/am335x/uart.h> + +#define EFR_ENHANCED_EN (1 << 4) +#define FCR_FIFO_EN (1 << 0) +#define MCR_TCR_TLR (1 << 6) +#define SYSC_SOFTRESET (1 << 1) +#define SYSS_RESETDONE (1 << 0) + +#define LSR_RXFIFOE (1 << 0) +#define LSR_TXFIFOE (1 << 5) + +/* + * Initialise the serial port with the given baudrate divisor. The settings + * are always 8 data bits, no parity, 1 stop bit, no start bits. + */ +static void am335x_uart_init(struct am335x_uart *uart, uint16_t div) +{ + uint16_t lcr_orig, efr_orig, mcr_orig; + + /* reset the UART */ + write16(&uart->sysc, uart->sysc | SYSC_SOFTRESET); + while (!(read16(&uart->syss) & SYSS_RESETDONE)) + ; + + /* 1. switch to register config mode B */ + lcr_orig = read16(&uart->lcr); + write16(&uart->lcr, 0xbf); + + /* + * 2. Set EFR ENHANCED_EN bit. To access this bit, registers must + * be in TCR_TLR submode, meaning EFR[4] = 1 and MCR[6] = 1. + */ + efr_orig = read16(&uart->efr); + write16(&uart->efr, efr_orig | EFR_ENHANCED_EN); + + /* 3. Switch to register config mode A */ + write16(&uart->lcr, 0x80); + + /* 4. Enable register submode TCR_TLR to access the UARTi.UART_TLR */ + mcr_orig = read16(&uart->mcr); + write16(&uart->mcr, mcr_orig | MCR_TCR_TLR); + + /* 5. Enable the FIFO. For now we'll ignore FIFO triggers and DMA */ + write16(&uart->fcr, FCR_FIFO_EN); + + /* 6. Switch to configuration mode B */ + write16(&uart->lcr, 0xbf); + /* Skip steps 7 and 8 (setting up FIFO triggers for DMA) */ + + /* 9. Restore original EFR value */ + write16(&uart->efr, efr_orig); + + /* 10. Switch to config mode A */ + write16(&uart->lcr, 0x80); + + /* 11. Restore original MCR value */ + write16(&uart->mcr, mcr_orig); + + /* 12. Restore original LCR value */ + write16(&uart->lcr, lcr_orig); + + /* Protocol, baud rate and interrupt settings */ + + /* 1. Disable UART access to DLL and DLH registers */ + write16(&uart->mdr1, read16(&uart->mdr1) | 0x7); + + /* 2. Switch to config mode B */ + write16(&uart->lcr, 0xbf); + + /* 3. Enable access to IER[7:4] */ + write16(&uart->efr, efr_orig | EFR_ENHANCED_EN); + + /* 4. Switch to operational mode */ + write16(&uart->lcr, 0x0); + + /* 5. Clear IER */ + write16(&uart->ier, 0x0); + + /* 6. Switch to config mode B */ + write16(&uart->lcr, 0xbf); + + /* 7. Set dll and dlh to the desired values (table 19-25) */ + write16(&uart->dlh, (div >> 8)); + write16(&uart->dll, (div & 0xff)); + + /* 8. Switch to operational mode to access ier */ + write16(&uart->lcr, 0x0); + + /* 9. Clear ier to disable all interrupts */ + write16(&uart->ier, 0x0); + + /* 10. Switch to config mode B */ + write16(&uart->lcr, 0xbf); + + /* 11. Restore efr */ + write16(&uart->efr, efr_orig); + + /* 12. Set protocol formatting 8n1 (8 bit data, no parity, 1 stop bit) */ + write16(&uart->lcr, 0x3); + + /* 13. Load the new UART mode */ + write16(&uart->mdr1, 0x0); +} + +/* + * Read a single byte from the serial port. Returns 1 on success, 0 + * otherwise. When the function is successful, the character read is + * written into its argument c. + */ +static unsigned char am335x_uart_rx_byte(struct am335x_uart *uart) +{ + while (!(read16(&uart->lsr) & LSR_RXFIFOE)); + + return read8(&uart->rhr); +} + +/* + * Output a single byte to the serial port. + */ +static void am335x_uart_tx_byte(struct am335x_uart *uart, unsigned char data) +{ + while (!(read16(&uart->lsr) & LSR_TXFIFOE)); + + return write8(&uart->thr, data); +} + +unsigned int uart_platform_refclk(void) +{ + return 48000000; +} + +uintptr_t uart_platform_base(int idx) +{ + const unsigned int bases[] = { + 0x44e09000, 0x48022000, 0x48024000, + 0x481a6000, 0x481a8000, 0x481aa000 + }; + if (idx < ARRAY_SIZE(bases)) + return bases[idx]; + return 0; +} + +void uart_init(int idx) +{ + struct am335x_uart *uart = uart_platform_baseptr(idx); + uint16_t div = (uint16_t) uart_baudrate_divisor( + get_uart_baudrate(), uart_platform_refclk(), 16); + am335x_uart_init(uart, div); +} + +unsigned char uart_rx_byte(int idx) +{ + struct am335x_uart *uart = uart_platform_baseptr(idx); + return am335x_uart_rx_byte(uart); +} + +void uart_tx_byte(int idx, unsigned char data) +{ + struct am335x_uart *uart = uart_platform_baseptr(idx); + am335x_uart_tx_byte(uart, data); +} + +void uart_tx_flush(int idx) +{ +} + +void uart_fill_lb(void *data) +{ + struct lb_serial serial; + serial.type = LB_SERIAL_TYPE_MEMORY_MAPPED; + serial.baseaddr = uart_platform_base(CONFIG_UART_FOR_CONSOLE); + serial.baud = get_uart_baudrate(); + serial.regwidth = 2; + serial.input_hertz = uart_platform_refclk(); + serial.uart_pci_addr = CONFIG_UART_PCI_ADDR; + lb_add_serial(&serial, data); + + lb_add_console(LB_TAG_CONSOLE_SERIAL8250MEM, data); +} |