mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-30 10:31:02 +00:00
Attempts to use ccprinf() before both uart and usb consoles have been
initialized cause the device lock up. Luckily both console channels
are buffered (and usb console buffering is about to be greatly
improved), all what needs to be done is to hold on to the attempts to
start transmit interrupts until hardware has been initialized.
BRANCH=none
BUG=none
TEST=attempts to print something on the console early in the process
do not cause the device to lock up any more, and the printouts
show up where expected.
Change-Id: I16cd1fab79bceaf7c2334a955fdb6046d21ed550
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/268379
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-by: Sheng-liang Song <ssl@chromium.org>
157 lines
3.4 KiB
C
157 lines
3.4 KiB
C
/* Copyright (c) 2014 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.
|
|
*/
|
|
|
|
#include "clock.h"
|
|
#include "common.h"
|
|
#include "gpio.h"
|
|
#include "registers.h"
|
|
#include "system.h"
|
|
#include "task.h"
|
|
#include "uart.h"
|
|
#include "util.h"
|
|
|
|
static int done_uart_init_yet;
|
|
|
|
int uart_init_done(void)
|
|
{
|
|
return done_uart_init_yet;
|
|
}
|
|
|
|
void uart_tx_start(void)
|
|
{
|
|
if (!uart_init_done())
|
|
return;
|
|
|
|
/* If interrupt is already enabled, nothing to do */
|
|
if (GR_UART_ICTRL(0) & GC_UART_ICTRL_TX_MASK)
|
|
return;
|
|
|
|
/* Do not allow deep sleep while transmit in progress */
|
|
disable_sleep(SLEEP_MASK_UART);
|
|
|
|
/*
|
|
* Re-enable the transmit interrupt, then forcibly trigger the
|
|
* interrupt. This works around a hardware problem with the
|
|
* UART where the FIFO only triggers the interrupt when its
|
|
* threshold is _crossed_, not just met.
|
|
*/
|
|
/* TODO(crosbug.com/p/33819): Do we need this hack here? Find out. */
|
|
REG_WRITE_MLV(GR_UART_ICTRL(0), GC_UART_ICTRL_TX_MASK,
|
|
GC_UART_ICTRL_TX_LSB, 1);
|
|
task_trigger_irq(GC_IRQNUM_UART0_TXINT);
|
|
}
|
|
|
|
void uart_tx_stop(void)
|
|
{
|
|
/* Disable the TX interrupt */
|
|
REG_WRITE_MLV(GR_UART_ICTRL(0), GC_UART_ICTRL_TX_MASK,
|
|
GC_UART_ICTRL_TX_LSB, 0);
|
|
|
|
/* Re-allow deep sleep */
|
|
enable_sleep(SLEEP_MASK_UART);
|
|
}
|
|
|
|
int uart_tx_in_progress(void)
|
|
{
|
|
/* Transmit is in progress if the TX idle bit is not set */
|
|
return !(GR_UART_STATE(0) & GC_UART_STATE_TXIDLE_MASK);
|
|
}
|
|
|
|
void uart_tx_flush(void)
|
|
{
|
|
/* Wait until TX FIFO is idle. */
|
|
while (uart_tx_in_progress())
|
|
;
|
|
}
|
|
|
|
int uart_tx_ready(void)
|
|
{
|
|
/* True if the TX buffer is not completely full */
|
|
return !(GR_UART_STATE(0) & GC_UART_STATE_TX_MASK);
|
|
}
|
|
|
|
int uart_rx_available(void)
|
|
{
|
|
/* True if the RX buffer is not completely empty. */
|
|
return !(GR_UART_STATE(0) & GC_UART_STATE_RXEMPTY_MASK);
|
|
}
|
|
|
|
void uart_write_char(char c)
|
|
{
|
|
/* Wait for space in transmit FIFO. */
|
|
while (!uart_tx_ready())
|
|
;
|
|
|
|
GR_UART_WDATA(0) = c;
|
|
}
|
|
|
|
int uart_read_char(void)
|
|
{
|
|
return GR_UART_RDATA(0);
|
|
}
|
|
|
|
void uart_disable_interrupt(void)
|
|
{
|
|
task_disable_irq(GC_IRQNUM_UART0_TXINT);
|
|
task_disable_irq(GC_IRQNUM_UART0_RXINT);
|
|
}
|
|
|
|
void uart_enable_interrupt(void)
|
|
{
|
|
task_enable_irq(GC_IRQNUM_UART0_TXINT);
|
|
task_enable_irq(GC_IRQNUM_UART0_RXINT);
|
|
}
|
|
|
|
/**
|
|
* Interrupt handlers for UART0
|
|
*/
|
|
void uart_ec_tx_interrupt(void)
|
|
{
|
|
/* Clear transmit interrupt status */
|
|
GR_UART_ISTATECLR(0) = GC_UART_ISTATECLR_TX_MASK;
|
|
|
|
/* Fill output FIFO */
|
|
uart_process_output();
|
|
}
|
|
DECLARE_IRQ(GC_IRQNUM_UART0_TXINT, uart_ec_tx_interrupt, 1);
|
|
|
|
void uart_ec_rx_interrupt(void)
|
|
{
|
|
/* Clear receive interrupt status */
|
|
GR_UART_ISTATECLR(0) = GC_UART_ISTATECLR_RX_MASK;
|
|
|
|
/* Read input FIFO until empty */
|
|
uart_process_input();
|
|
}
|
|
DECLARE_IRQ(GC_IRQNUM_UART0_RXINT, uart_ec_rx_interrupt, 1);
|
|
|
|
void uart_init(void)
|
|
{
|
|
long long setting = (16 * (1 << UART_NCO_WIDTH) *
|
|
(long long)CONFIG_UART_BAUD_RATE / PCLK_FREQ);
|
|
|
|
/* turn on uart clock */
|
|
clock_enable_module(MODULE_UART, 1);
|
|
|
|
/* set frequency */
|
|
GR_UART_NCO(0) = setting;
|
|
|
|
/* Interrupt when RX fifo has anything, when TX fifo <= half empty */
|
|
/* Also reset (clear) both FIFOs */
|
|
GR_UART_FIFO(0) = 0x63;
|
|
|
|
/* TX enable, RX enable, HW flow control disabled, no loopback */
|
|
GR_UART_CTRL(0) = 0x03;
|
|
|
|
/* enable RX interrupts in block */
|
|
/* Note: doesn't do anything unless turned on in NVIC */
|
|
GR_UART_ICTRL(0) = 0x02;
|
|
|
|
/* Enable interrupts for UART0 only */
|
|
uart_enable_interrupt();
|
|
|
|
done_uart_init_yet = 1;
|
|
}
|