aboutsummaryrefslogtreecommitdiff
path: root/src/soc/ti/am335x/uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/ti/am335x/uart.c')
-rw-r--r--src/soc/ti/am335x/uart.c185
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);
+}