/* Copyright 2020 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 "csr.h" #include "system.h" #include "uart.h" #include "uart_regs.h" #include "util.h" /* * UARTN == 0, SCP UART0 * UARTN == 1, SCP UART1 * UARTN == 2, AP UART1 */ #define UARTN CONFIG_UART_CONSOLE #define UART_IDLE_WAIT_US 500 #define UART_INTC_GROUP 12 static uint8_t init_done, tx_started; 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); uart_init_pinmux(); /* 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 start */ UART_LCR(UARTN) |= UART_LCR_DLAB; UART_DLL(UARTN) = div & 0xff; UART_DLH(UARTN) = (div >> 8) & 0xff; UART_LCR(UARTN) &= ~UART_LCR_DLAB; /* DLAB end */ /* Enable received data interrupt */ UART_IER(UARTN) |= UART_IER_RDI; #if (UARTN < SCP_UART_COUNT) task_enable_irq(UART_TX_IRQ(UARTN)); task_enable_irq(UART_RX_IRQ(UARTN)); #endif init_done = 1; } int uart_init_done(void) { return init_done; } void uart_tx_flush(void) { while (!(UART_LSR(UARTN) & UART_LSR_TEMT)) ; } int uart_tx_ready(void) { return UART_LSR(UARTN) & UART_LSR_THRE; } int uart_rx_available(void) { return UART_LSR(UARTN) & UART_LSR_DR; } void uart_write_char(char c) { while (!uart_tx_ready()) ; UART_THR(UARTN) = c; } int uart_read_char(void) { return UART_RBR(UARTN); } void uart_tx_start(void) { tx_started = 1; if (UART_IER(UARTN) & UART_IER_THRI) return; disable_sleep(SLEEP_MASK_UART); UART_IER(UARTN) |= UART_IER_THRI; } void uart_tx_stop(void) { /* * Workaround for b/157541273. * Don't unset the THRI flag unless we are in the UART ISR. * * Note: * MICAUSE denotes current INTC group number. */ if (in_interrupt_context() && read_csr(CSR_VIC_MICAUSE) != UART_INTC_GROUP) return; tx_started = 0; UART_IER(UARTN) &= ~UART_IER_THRI; enable_sleep(SLEEP_MASK_UART); } static void uart_process(void) { uart_process_input(); uart_process_output(); } #if (UARTN < SCP_UART_COUNT) static void uart_irq_handler(void) { extern volatile int ec_int; switch (ec_int) { case UART_TX_IRQ(UARTN): uart_process(); task_clear_pending_irq(ec_int); break; case UART_RX_IRQ(UARTN): uart_process(); SCP_CORE0_INTC_UART_RX_IRQ(UARTN) = BIT(0); asm volatile ("fence.i" ::: "memory"); task_clear_pending_irq(ec_int); break; } } DECLARE_IRQ(UART_INTC_GROUP, uart_irq_handler, 0); #else #ifndef HAS_TASK_APUART #error "APUART task hasn't defined in ec.tasklist." #endif void uart_task(void) { while (1) { if (uart_rx_available() || tx_started) uart_process(); else task_wait_event(UART_IDLE_WAIT_US); } } #endif