mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-08 16:41:55 +00:00
AP and EC UART now use the CCD V1 capabilities to determine when access is allowed. Transmit to AP and EC can be toggled independently from read access to output from those UARTs. Note that disabling read access disables both transmit and receive. That is, it's not possible to set a UART where transmit is allowed but receive isn't. Why would you want to do that, anyway? See go/cr50-ccd-wp for more information. BUG=b:62537474 BRANCH=cr50 TEST=manual with CR50_DEV=1 ccdoops ccdset cr50fullconsole always -> so we can use ccd command for testing ccd -> AP RX+TX, EC RX+TX ccdset uartecrx unlesslocked ccdset uartectx ifopened ccdset uartaprx always ccdset uartaptx unlesslocked ccdunlock ccd -> AP RX+TX, EC RX ccdlock ccd -> AP RX, EC disabled ccdoops ccdset cr50fullconsole always ccd -> AP RX+TX, EC RX+TX ccdset uartaprx ifopened ccdlock ccd -> AP disabled, EC RX Change-Id: I55db5897bb52cd60658ab221eadf5c59fc86744a Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/595196 Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
228 lines
4.9 KiB
C
228 lines
4.9 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 "uart_bitbang.h"
|
|
#include "util.h"
|
|
|
|
#define USE_UART_INTERRUPTS (!(defined(CONFIG_CUSTOMIZED_RO) && \
|
|
defined(SECTION_IS_RO)))
|
|
|
|
struct uartn_interrupts {
|
|
int tx_int;
|
|
int rx_int;
|
|
};
|
|
static struct uartn_interrupts interrupt[] = {
|
|
{GC_IRQNUM_UART0_TXINT, GC_IRQNUM_UART0_RXINT},
|
|
{GC_IRQNUM_UART1_TXINT, GC_IRQNUM_UART1_RXINT},
|
|
{GC_IRQNUM_UART2_TXINT, GC_IRQNUM_UART2_RXINT},
|
|
};
|
|
|
|
struct uartn_function_ptrs uartn_funcs[3] = {
|
|
{
|
|
_uartn_rx_available,
|
|
_uartn_write_char,
|
|
_uartn_read_char,
|
|
},
|
|
|
|
{
|
|
_uartn_rx_available,
|
|
_uartn_write_char,
|
|
_uartn_read_char,
|
|
},
|
|
|
|
{
|
|
_uartn_rx_available,
|
|
_uartn_write_char,
|
|
_uartn_read_char,
|
|
},
|
|
};
|
|
|
|
void uartn_tx_start(int uart)
|
|
{
|
|
if (!uart_init_done())
|
|
return;
|
|
|
|
/* If interrupt is already enabled, nothing to do */
|
|
if (GR_UART_ICTRL(uart) & 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(uart), GC_UART_ICTRL_TX_MASK,
|
|
GC_UART_ICTRL_TX_LSB, 1);
|
|
task_trigger_irq(interrupt[uart].tx_int);
|
|
}
|
|
|
|
void uartn_tx_stop(int uart)
|
|
{
|
|
/* Disable the TX interrupt */
|
|
REG_WRITE_MLV(GR_UART_ICTRL(uart), GC_UART_ICTRL_TX_MASK,
|
|
GC_UART_ICTRL_TX_LSB, 0);
|
|
|
|
/* Re-allow deep sleep */
|
|
enable_sleep(SLEEP_MASK_UART);
|
|
}
|
|
|
|
int uartn_tx_in_progress(int uart)
|
|
{
|
|
/* Transmit is in progress unless the TX FIFO is empty and idle. */
|
|
return !(GR_UART_STATE(uart) & (GC_UART_STATE_TXIDLE_MASK |
|
|
GC_UART_STATE_TXEMPTY_MASK));
|
|
}
|
|
|
|
void uartn_tx_flush(int uart)
|
|
{
|
|
timestamp_t ts;
|
|
int i;
|
|
|
|
/* Wait until TX FIFO is idle. */
|
|
while (uartn_tx_in_progress(uart))
|
|
;
|
|
/*
|
|
* Even when uartn_tx_in_progress() returns false, the chip seems to
|
|
* be still trasmitting, resetting at this point results in an eaten
|
|
* last symbol. Let's just wait some time (required to transmit 10
|
|
* bits at 115200 baud).
|
|
*/
|
|
ts = get_time(); /* Start time. */
|
|
for (i = 0; i < 1000; i++) /* Limit it in case timer is not running. */
|
|
if ((get_time().val - ts.val) > ((1000000 * 10) / 115200))
|
|
return;
|
|
}
|
|
|
|
int uartn_tx_ready(int uart)
|
|
{
|
|
/* True if the TX buffer is not completely full */
|
|
return !(GR_UART_STATE(uart) & GC_UART_STATE_TX_MASK);
|
|
}
|
|
|
|
int _uartn_rx_available(int uart)
|
|
{
|
|
/* True if the RX buffer is not completely empty. */
|
|
return !(GR_UART_STATE(uart) & GC_UART_STATE_RXEMPTY_MASK);
|
|
}
|
|
|
|
int uartn_rx_available(int uart)
|
|
{
|
|
return uartn_funcs[uart]._rx_available(uart);
|
|
}
|
|
|
|
void _uartn_write_char(int uart, char c)
|
|
{
|
|
/* Wait for space in transmit FIFO. */
|
|
while (!uartn_tx_ready(uart))
|
|
;
|
|
|
|
GR_UART_WDATA(uart) = c;
|
|
}
|
|
|
|
void uartn_write_char(int uart, char c)
|
|
{
|
|
uartn_funcs[uart]._write_char(uart, c);
|
|
}
|
|
|
|
int _uartn_read_char(int uart)
|
|
{
|
|
return GR_UART_RDATA(uart);
|
|
}
|
|
|
|
int uartn_read_char(int uart)
|
|
{
|
|
return uartn_funcs[uart]._read_char(uart);
|
|
}
|
|
|
|
#ifdef CONFIG_UART_BITBANG
|
|
int _uart_bitbang_rx_available(int uart)
|
|
{
|
|
if (uart_bitbang_is_enabled(uart))
|
|
return uart_bitbang_is_char_available(uart);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void _uart_bitbang_write_char(int uart, char c)
|
|
{
|
|
if (uart_bitbang_is_enabled(uart))
|
|
uart_bitbang_write_char(uart, c);
|
|
}
|
|
|
|
int _uart_bitbang_read_char(int uart)
|
|
{
|
|
if (uart_bitbang_is_enabled(uart))
|
|
return uart_bitbang_read_char(uart);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* defined(CONFIG_UART_BITBANG) */
|
|
|
|
void uartn_disable_interrupt(int uart)
|
|
{
|
|
task_disable_irq(interrupt[uart].tx_int);
|
|
task_disable_irq(interrupt[uart].rx_int);
|
|
}
|
|
|
|
void uartn_enable_interrupt(int uart)
|
|
{
|
|
task_enable_irq(interrupt[uart].tx_int);
|
|
task_enable_irq(interrupt[uart].rx_int);
|
|
}
|
|
|
|
|
|
void uartn_enable(int uart)
|
|
{
|
|
/* Enable TX and RX. Disable HW flow control and loopback. */
|
|
GR_UART_CTRL(uart) = 0x03;
|
|
}
|
|
|
|
/* Disable TX, RX, HW flow control, and loopback */
|
|
void uartn_disable(int uart)
|
|
{
|
|
GR_UART_CTRL(uart) = 0;
|
|
}
|
|
|
|
int uartn_is_enabled(int uart)
|
|
{
|
|
return !!(GR_UART_CTRL(uart) & 0x03);
|
|
}
|
|
|
|
void uartn_init(int uart)
|
|
{
|
|
long long setting = (16 * (1 << UART_NCO_WIDTH) *
|
|
(long long)CONFIG_UART_BAUD_RATE / PCLK_FREQ);
|
|
|
|
/* set frequency */
|
|
GR_UART_NCO(uart) = setting;
|
|
|
|
/*
|
|
* Interrupt when RX fifo has anything, when TX fifo <= half
|
|
* empty and reset (clear) both FIFOs
|
|
*/
|
|
GR_UART_FIFO(uart) = 0x63;
|
|
|
|
/* enable RX interrupts in block */
|
|
/* Note: doesn't do anything unless turned on in NVIC */
|
|
GR_UART_ICTRL(uart) = 0x02;
|
|
|
|
#if USE_UART_INTERRUPTS
|
|
/* Enable interrupts for UART */
|
|
uartn_enable_interrupt(uart);
|
|
#endif
|
|
}
|