summaryrefslogtreecommitdiff
path: root/chip/mt_scp/uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/mt_scp/uart.c')
-rw-r--r--chip/mt_scp/uart.c171
1 files changed, 171 insertions, 0 deletions
diff --git a/chip/mt_scp/uart.c b/chip/mt_scp/uart.c
new file mode 100644
index 0000000000..554a53cc1d
--- /dev/null
+++ b/chip/mt_scp/uart.c
@@ -0,0 +1,171 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* SCP UART module */
+
+#include "console.h"
+#include "registers.h"
+#include "serial_reg.h"
+#include "system.h"
+#include "task.h"
+#include "uart.h"
+#include "util.h"
+
+/* Console UART index */
+#define UARTN CONFIG_UART_CONSOLE
+#define UART_WAIT_US 50
+#define UART_IDLE_WAIT_US 500
+
+static uint8_t uart_done, tx_started;
+
+int uart_init_done(void)
+{
+ /*
+ * TODO: AP UART support
+ * When access AP UART port, wait for AP peripheral clock
+ */
+ return uart_done;
+}
+
+void uart_tx_start(void)
+{
+ tx_started = 1;
+
+ /* AP UART mode doesn't support interrupt */
+ if (UARTN >= SCP_UART_COUNT)
+ return;
+
+ if (UART_IER(UARTN) & UART_IER_THRI)
+ return;
+ disable_sleep(SLEEP_MASK_UART);
+ UART_IER(UARTN) |= UART_IER_THRI;
+}
+
+void uart_tx_stop(void)
+{
+ tx_started = 0;
+
+ /* AP UART mode doesn't support interrupt */
+ if (UARTN >= SCP_UART_COUNT)
+ return;
+
+ UART_IER(UARTN) &= ~UART_IER_THRI;
+ enable_sleep(SLEEP_MASK_UART);
+}
+
+void uart_tx_flush(void)
+{
+ while (!(UART_LSR(UARTN) & UART_LSR_TEMT))
+ usleep(uart_wait_us);
+}
+
+int uart_tx_ready(void)
+{
+ /* Check xmit FIFO empty */
+ return UART_LSR(UARTN) & UART_LSR_THRE;
+}
+
+int uart_rx_available(void)
+{
+ /* Check rcvr data ready */
+ return UART_LSR(UARTN) & UART_LSR_DR;
+}
+
+void uart_write_char(char c)
+{
+ while (!uart_tx_ready())
+ usleep(uart_wait_us);
+
+ UART_DATA(UARTN) = c;
+}
+
+int uart_read_char(void)
+{
+ return UART_DATA(UARTN);
+}
+
+void uart_process(void)
+{
+ uart_process_input();
+ uart_process_output();
+}
+
+#if (UARTN < SCP_UART_COUNT)
+void uart_interrupt(void)
+{
+ uint8_t ier;
+
+ task_clear_pending_irq(UART_IRQ(UARTN));
+ uart_process();
+ ier = UART_IER(UARTN);
+ UART_IER(UARTN) = 0;
+ UART_IER(UARTN) = ier;
+}
+DECLARE_IRQ(UART_IRQ(UARTN), uart_interrupt, 2);
+
+void uart_rx_interrupt(void)
+{
+ uint8_t ier;
+
+ task_clear_pending_irq(UART_RX_IRQ(UARTN));
+ SCP_INTC_UART_RX_IRQ &= ~(1 << UARTN);
+ uart_process();
+ ier = UART_IER(UARTN);
+ UART_IER(UARTN) = 0;
+ UART_IER(UARTN) = ier;
+ SCP_INTC_UART_RX_IRQ |= 1 << UARTN;
+}
+DECLARE_IRQ(UART_RX_IRQ(UARTN), uart_rx_interrupt, 2);
+#endif
+
+void uart_task(void)
+{
+#if (UARTN >= SCP_UART_COUNT)
+ while (1) {
+ if (uart_rx_available() || tx_started)
+ uart_process();
+ else
+ task_wait_event(UART_IDLE_WAIT_US);
+ }
+#endif
+}
+
+void uart_init(void)
+{
+ const uint32_t baud_rate = CONFIG_UART_BAUD_RATE;
+ const uint32_t uart_clock = 26000000;
+ const uint32_t div = DIV_ROUND_NEAREST(uart_clock, baud_rate * 16);
+
+ /* Init clock */
+#if UARTN == 0
+ SCP_CLK_GATE |= CG_UART_M | CG_UART_B | CG_UART_RSTN;
+#elif UARTN == 1
+ SCP_CLK_GATE |= CG_UART1_M | CG_UART1_B | CG_UART1_RSTN;
+#endif
+
+ /* Init and clear FIFO */
+ UART_FCR(UARTN) = UART_FCR_ENABLE_FIFO
+ | UART_FCR_CLEAR_RCVR
+ | UART_FCR_CLEAR_XMIT;
+ /* Line control: parity none, 8 bit, 1 stop bit */
+ UART_LCR(UARTN) = UART_LCR_WLEN8;
+ /* For baud rate <= 115200 */
+ UART_HIGHSPEED(UARTN) = 0;
+ /* DLAB = 1 and update DLL DLH */
+ UART_LCR(UARTN) |= UART_LCR_DLAB;
+ UART_DLL(UARTN) = div & 0xff;
+ UART_DLH(UARTN) = (div >> 8) & 0xff;
+ UART_LCR(UARTN) &= ~UART_LCR_DLAB;
+ UART_IER(UARTN) |= UART_IER_RDI;
+
+#if (UARTN < SCP_UART_COUNT)
+ task_enable_irq(UART_IRQ(UARTN));
+ task_enable_irq(UART_RX_IRQ(UARTN));
+ /* UART RX IRQ needs an extra enable */
+ SCP_INTC_UART_RX_IRQ |= 1 << UARTN;
+#endif
+ gpio_config_module(MODULE_UART, 1);
+ uart_done = 1;
+}