coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/ish/uart.c

263 lines
5.3 KiB
C

/* Copyright 2016 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.
*/
/* UART module for ISH */
#include "common.h"
#include "console.h"
#include "uart_defs.h"
#include "atomic.h"
#include "task.h"
#include "registers.h"
#include "uart.h"
#include "uart_defs.h"
#include "interrupts.h"
#include "system.h"
#define CPUTS(outstr) cputs(CC_LPC, outstr)
#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args)
#define CPRINTF(format, args...) cprintf(CC_LPC, format, ## args)
static const uint32_t baud_conf[][BAUD_TABLE_MAX] = {
{B9600, 9600},
{B57600, 57600},
{B115200, 115200},
{B921600, 921600},
{B2000000, 2000000},
{B3000000, 3000000},
{B3250000, 3250000},
{B3500000, 3500000},
{B4000000, 4000000},
{B19200, 19200},
};
static struct uart_ctx uart_ctx[UART_DEVICES] = {
{
.id = 0,
.base = UART0_BASE,
.input_freq = UART_ISH_INPUT_FREQ,
.addr_interval = UART_ISH_ADDR_INTERVAL,
.uart_state = UART_STATE_CG,
},
{
.id = 1,
.base = UART1_BASE,
.input_freq = UART_ISH_INPUT_FREQ,
.addr_interval = UART_ISH_ADDR_INTERVAL,
.uart_state = UART_STATE_CG,
},
{
.id = 2,
.base = UART2_BASE,
.input_freq = UART_ISH_INPUT_FREQ,
.addr_interval = UART_ISH_ADDR_INTERVAL,
.uart_state = UART_STATE_CG,
}
};
static int init_done;
int uart_init_done(void)
{
return init_done;
}
void uart_tx_start(void)
{
if (!IS_ENABLED(CONFIG_POLLING_UART)) {
if (IER(ISH_DEBUG_UART) & IER_TDRQ)
return;
/* Do not allow deep sleep while transmit in progress */
disable_sleep(SLEEP_MASK_UART);
IER(ISH_DEBUG_UART) |= IER_TDRQ;
}
}
void uart_tx_stop(void)
{
if (!IS_ENABLED(CONFIG_POLLING_UART)) {
/* Re-allow deep sleep */
enable_sleep(SLEEP_MASK_UART);
IER(ISH_DEBUG_UART) &= ~IER_TDRQ;
}
}
void uart_tx_flush(void)
{
if (!IS_ENABLED(CONFIG_POLLING_UART)) {
while (!(LSR(ISH_DEBUG_UART) & LSR_TEMT))
continue;
}
}
int uart_tx_ready(void)
{
return LSR(ISH_DEBUG_UART) & LSR_TEMT;
}
int uart_rx_available(void)
{
if (IS_ENABLED(CONFIG_POLLING_UART))
return 0;
return LSR(ISH_DEBUG_UART) & LSR_DR;
}
void uart_write_char(char c)
{
/* Wait till receiver is ready */
while (!uart_tx_ready())
continue;
THR(ISH_DEBUG_UART) = c;
}
int uart_read_char(void)
{
return RBR(ISH_DEBUG_UART);
}
void uart_ec_interrupt(void)
{
/* Read input FIFO until empty, then fill output FIFO */
uart_process_input();
uart_process_output();
}
#ifndef CONFIG_POLLING_UART
DECLARE_IRQ(ISH_DEBUG_UART_IRQ, uart_ec_interrupt);
#endif
static int uart_return_baud_rate_by_id(int baud_rate_id)
{
int i;
for (i = 0; i < ARRAY_SIZE(baud_conf); i++) {
if (baud_conf[i][BAUD_IDX] == baud_rate_id)
return baud_conf[i][BAUD_SPEED];
}
return -1;
}
static void uart_hw_init(enum UART_PORT id)
{
uint32_t divisor; /* baud rate divisor */
uint8_t mcr = 0;
uint8_t fcr = 0;
struct uart_ctx *ctx = &uart_ctx[id];
/* Calculate baud rate divisor */
divisor = (ctx->input_freq / ctx->baud_rate) >> 4;
MUL(ctx->id) = (divisor * ctx->baud_rate);
DIV(ctx->id) = (ctx->input_freq / 16);
PS(ctx->id) = 16;
/* Set the DLAB to access the baud rate divisor registers */
LCR(ctx->id) = LCR_DLAB;
DLL(ctx->id) = (divisor & 0xff);
DLH(ctx->id) = ((divisor >> 8) & 0xff);
/* 8 data bits, 1 stop bit, no parity, clear DLAB */
LCR(ctx->id) = LCR_8BIT_CHR;
if (ctx->client_flags & UART_CONFIG_HW_FLOW_CONTROL)
mcr = MCR_AUTO_FLOW_EN;
/* needs to be set regardless of flow control */
mcr |= MCR_INTR_ENABLE;
mcr |= (MCR_RTS | MCR_DTR);
MCR(ctx->id) = mcr;
fcr = FCR_FIFO_SIZE_64 | FCR_ITL_FIFO_64_BYTES_1;
/* configure FIFOs */
FCR(ctx->id) = (fcr | FCR_FIFO_ENABLE
| FCR_RESET_RX | FCR_RESET_TX);
/* enable UART unit */
ABR(ctx->id) = ABR_UUE;
/* clear the port */
RBR(ctx->id);
if (IS_ENABLED(CONFIG_POLLING_UART))
IER(ctx->id) = 0x00;
else
IER(ctx->id) = IER_RECV;
}
static void uart_stop_hw(enum UART_PORT id)
{
int i;
uint32_t fifo_len;
/* Manually clearing the fifo from possible noise.
* Entering D0i3 when fifo is not cleared may result in a hang.
*/
fifo_len = (FOR(id) & FOR_OCCUPANCY_MASK) >> FOR_OCCUPANCY_OFFS;
for (i = 0; i < fifo_len; i++)
(void)RBR(id);
/* No interrupts are enabled */
IER(id) = 0;
MCR(id) = 0;
/* Clear and disable FIFOs */
FCR(id) = (FCR_RESET_RX | FCR_RESET_TX);
/* Disable uart unit */
ABR(id) = 0;
}
static int uart_client_init(enum UART_PORT id, uint32_t baud_rate_id, int flags)
{
if ((uart_ctx[id].base == 0) || (id >= UART_DEVICES))
return UART_ERROR;
if (!bool_compare_and_swap_u32(&uart_ctx[id].is_open, 0, 1))
return UART_BUSY;
uart_ctx[id].baud_rate = uart_return_baud_rate_by_id(baud_rate_id);
if ((uart_ctx[id].baud_rate == -1) || (uart_ctx[id].baud_rate == 0))
uart_ctx[id].baud_rate = UART_DEFAULT_BAUD_RATE;
uart_ctx[id].client_flags = flags;
atomic_and(&uart_ctx[id].uart_state, ~UART_STATE_CG);
uart_hw_init(id);
return EC_SUCCESS;
}
static void uart_drv_init(void)
{
int i;
/* Disable UART */
for (i = 0; i < UART_DEVICES; i++)
uart_stop_hw(i);
/* Enable HSU global interrupts (DMA/U0/U1) and set PMEN bit
* to allow PMU to clock gate ISH
*/
HSU_REG_GIEN = (GIEN_DMA_EN | GIEN_UART0_EN
| GIEN_UART1_EN | GIEN_PWR_MGMT);
task_enable_irq(ISH_DEBUG_UART_IRQ);
}
void uart_init(void)
{
uart_drv_init();
uart_client_init(ISH_DEBUG_UART, B115200, 0);
init_done = 1;
}