mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-05-05 15:06:58 +00:00
This patch allows to disable the serial port directly from Kconfig. This used to only be possible from the command-line (by passing "uart=disabled"). To disable the serial port via Kconfig, simply unselect the "Serial IO type" option. The default options do not seem to have any effect anymore (and were confusing anyway) so they are removed and the serial port is enabled for various platforms in their corresponding *.config file. Tracked-On: #1481 Signed-off-by: Geoffroy Van Cutsem <geoffroy.vancutsem@intel.com>
193 lines
4.5 KiB
C
193 lines
4.5 KiB
C
/*
|
|
* Copyright (C) 2018 Intel Corporation. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <hypervisor.h>
|
|
#include "uart16550.h"
|
|
|
|
#if defined(CONFIG_SERIAL_PIO_BASE)
|
|
static bool serial_port_mapped = true;
|
|
static bool uart_enabled = true;
|
|
#define UART_BASE_ADDRESS CONFIG_SERIAL_PIO_BASE
|
|
#elif defined(CONFIG_SERIAL_MMIO_BASE)
|
|
static bool serial_port_mapped;
|
|
static bool uart_enabled = true;
|
|
#define UART_BASE_ADDRESS CONFIG_SERIAL_MMIO_BASE
|
|
#else
|
|
static bool serial_port_mapped;
|
|
static bool uart_enabled;
|
|
#define UART_BASE_ADDRESS 0UL
|
|
#endif
|
|
|
|
typedef uint32_t uart_reg_t;
|
|
|
|
static uint64_t uart_base_address;
|
|
|
|
static spinlock_t uart_rx_lock;
|
|
static spinlock_t uart_tx_lock;
|
|
|
|
/**
|
|
* @pre uart_enabled == true
|
|
*/
|
|
static inline uint32_t uart16550_read_reg(uint64_t base, uint16_t reg_idx)
|
|
{
|
|
if (serial_port_mapped) {
|
|
return pio_read8((uint16_t)base + reg_idx);
|
|
} else {
|
|
return mmio_read32((void *)((uint32_t *)hpa2hva(base) +
|
|
reg_idx));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @pre uart_enabled == true
|
|
*/
|
|
static inline void uart16550_write_reg(uint64_t base,
|
|
uint32_t val, uint16_t reg_idx)
|
|
{
|
|
if (serial_port_mapped) {
|
|
pio_write8((uint8_t)val, (uint16_t)base + reg_idx);
|
|
} else {
|
|
mmio_write32(val, (void *)((uint32_t *)hpa2hva(base) +
|
|
reg_idx));
|
|
}
|
|
}
|
|
|
|
static void uart16550_calc_baud_div(uint32_t ref_freq,
|
|
uint32_t *baud_div_ptr, uint32_t baud_rate_arg)
|
|
{
|
|
uint32_t baud_rate = baud_rate_arg;
|
|
uint32_t baud_multiplier = baud_rate < BAUD_460800 ? 16U : 13U;
|
|
|
|
if (baud_rate == 0U) {
|
|
baud_rate = BAUD_115200;
|
|
}
|
|
*baud_div_ptr = ref_freq / (baud_multiplier * baud_rate);
|
|
}
|
|
|
|
/**
|
|
* @pre uart_enabled == true
|
|
*/
|
|
static void uart16550_set_baud_rate(uint32_t baud_rate)
|
|
{
|
|
uint32_t baud_div, duart_clock = UART_CLOCK_RATE;
|
|
uart_reg_t temp_reg;
|
|
|
|
/* Calculate baud divisor */
|
|
uart16550_calc_baud_div(duart_clock, &baud_div, baud_rate);
|
|
|
|
/* Enable DLL and DLM registers for setting the Divisor */
|
|
temp_reg = uart16550_read_reg(uart_base_address, UART16550_LCR);
|
|
temp_reg |= LCR_DLAB;
|
|
uart16550_write_reg(uart_base_address, temp_reg, UART16550_LCR);
|
|
|
|
/* Write the appropriate divisor value */
|
|
uart16550_write_reg(uart_base_address,
|
|
((baud_div >> 8U) & 0xFFU), UART16550_DLM);
|
|
uart16550_write_reg(uart_base_address,
|
|
(baud_div & 0xFFU), UART16550_DLL);
|
|
|
|
/* Disable DLL and DLM registers */
|
|
temp_reg &= ~LCR_DLAB;
|
|
uart16550_write_reg(uart_base_address, temp_reg, UART16550_LCR);
|
|
}
|
|
|
|
void uart16550_init(void)
|
|
{
|
|
if (!uart_enabled) {
|
|
return;
|
|
}
|
|
if (uart_base_address == 0UL) {
|
|
uart_base_address = UART_BASE_ADDRESS;
|
|
}
|
|
spinlock_init(&uart_rx_lock);
|
|
spinlock_init(&uart_tx_lock);
|
|
/* Enable TX and RX FIFOs */
|
|
uart16550_write_reg(uart_base_address,
|
|
FCR_FIFOE | FCR_RFR | FCR_TFR, UART16550_FCR);
|
|
|
|
/* Set-up data bits / parity / stop bits. */
|
|
uart16550_write_reg(uart_base_address,
|
|
(LCR_WL8 | LCR_NB_STOP_BITS_1 | LCR_PARITY_NONE),
|
|
UART16550_LCR);
|
|
|
|
/* Disable interrupts (we use polling) */
|
|
uart16550_write_reg(uart_base_address,
|
|
UART_IER_DISABLE_ALL, UART16550_IER);
|
|
|
|
/* Set baud rate */
|
|
uart16550_set_baud_rate(BAUD_115200);
|
|
|
|
/* Data terminal ready + Request to send */
|
|
uart16550_write_reg(uart_base_address,
|
|
MCR_RTS | MCR_DTR, UART16550_MCR);
|
|
}
|
|
|
|
char uart16550_getc(void)
|
|
{
|
|
char ret = -1;
|
|
|
|
if (!uart_enabled) {
|
|
return ret;
|
|
}
|
|
|
|
spinlock_obtain(&uart_rx_lock);
|
|
|
|
/* If a character has been received, read it */
|
|
if ((uart16550_read_reg(uart_base_address, UART16550_LSR) & LSR_DR)
|
|
== LSR_DR) {
|
|
/* Read a character */
|
|
ret = uart16550_read_reg(uart_base_address, UART16550_RBR);
|
|
|
|
}
|
|
spinlock_release(&uart_rx_lock);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @pre uart_enabled == true
|
|
*/
|
|
static void uart16550_putc(char c)
|
|
{
|
|
uint8_t temp;
|
|
uint32_t reg;
|
|
|
|
/* Ensure there are no further Transmit buffer write requests */
|
|
do {
|
|
reg = uart16550_read_reg(uart_base_address, UART16550_LSR);
|
|
} while ((reg & LSR_THRE) == 0U || (reg & LSR_TEMT) == 0U);
|
|
|
|
temp = (uint8_t)c;
|
|
/* Transmit the character. */
|
|
uart16550_write_reg(uart_base_address, (uint32_t)temp, UART16550_THR);
|
|
}
|
|
|
|
size_t uart16550_puts(const char *buf, uint32_t len)
|
|
{
|
|
uint32_t i;
|
|
if (!uart_enabled) {
|
|
return len;
|
|
}
|
|
spinlock_obtain(&uart_tx_lock);
|
|
for (i = 0U; i < len; i++) {
|
|
/* Transmit character */
|
|
uart16550_putc(*buf);
|
|
if (*buf == '\n') {
|
|
/* Append '\r', no need change the len */
|
|
uart16550_putc('\r');
|
|
}
|
|
buf++;
|
|
}
|
|
spinlock_release(&uart_tx_lock);
|
|
return len;
|
|
}
|
|
|
|
void uart16550_set_property(bool enabled, bool port_mapped, uint64_t base_addr)
|
|
{
|
|
uart_enabled = enabled;
|
|
serial_port_mapped = port_mapped;
|
|
uart_base_address = base_addr;
|
|
}
|