/* 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.
 */

/* USART driver for Chrome EC */

#include "clock.h"
#include "console.h"
#include "common.h"
#include "dma.h"
#include "hooks.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "uart.h"
#include "util.h"

#define CPUTS(outstr) cputs(CC_SYSTEM, outstr)
#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args)
#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)


static int ever_sent;    /* if we ever sent byte to TXD? */
static int init_done;    /* Initialization done? */
static int should_stop;  /* Last TX control action */

int uart_init_done(void)
{
	return init_done;
}

void uart_tx_start(void)
{
	disable_sleep(SLEEP_MASK_UART);
	should_stop = 0;
	NRF51_UART_INTENSET = (1 << NRF55_UART_TXDRDY_BIT);
	task_trigger_irq(NRF51_PERID_USART);
}

void uart_tx_stop(void)
{
	NRF51_UART_INTENCLR = (1 << NRF55_UART_TXDRDY_BIT);
	should_stop = 1;
	enable_sleep(SLEEP_MASK_UART);
}

int uart_tx_ready(void)
{
	/*
	 * nRF51 design is NOT tx-empty style. Instead, it is if a byte is
	 * ever transmitted from TxD. This means NRF51_UART_TXDRDY is always
	 * 0 after reset. So, we use 'ever_sent' to send the first byte.
	 */
	return NRF51_UART_TXDRDY || (!ever_sent);
}

int uart_rx_available(void)
{
	return NRF51_UART_RXDRDY;
}

void uart_tx_flush(void)
{
	while (!uart_tx_ready())
		;
}

void uart_write_char(char c)
{
	ever_sent = 1;
	NRF51_UART_TXDRDY = 0;
	NRF51_UART_TXD = c;
	NRF51_UART_STARTTX = 1;
}

int uart_read_char(void)
{
	NRF51_UART_RXDRDY = 0;
	return NRF51_UART_RXD;
}

/* Interrupt handler for console USART */
void uart_interrupt(void)
{
#ifndef CONFIG_UART_RX_DMA
	/*
	 * Read input FIFO until empty.  DMA-based receive does this from a
	 * hook in the UART buffering module.
	 */
	uart_process_input();
#endif

	/* Fill output FIFO */
	uart_process_output();

#ifndef CONFIG_UART_TX_DMA
	if (!should_stop)
		NRF51_UART_INTENSET = (1 << NRF55_UART_TXDRDY_BIT);
#endif  /* CONFIG_UART_TX_DMA */

}
DECLARE_IRQ(NRF51_PERID_USART, uart_interrupt, 2);


void uart_init(void)
{
	NRF51_UART_PSELTXD = NRF51_UART_TX_PIN;   /* GPIO Port for Tx */
	NRF51_UART_PSELRXD = NRF51_UART_RX_PIN;   /* GPIO Port for Rx */
	NRF51_UART_CONFIG = 0;    /* disable HW flow control, no parity bit */
	NRF51_UART_BAUDRATE = 0x01d7e000;  /* 115200 */
	NRF51_UART_ENABLE = 0x4;  /* Enable UART */

	task_enable_irq(NRF51_PERID_USART);

	NRF51_UART_INTENSET = (1 << NRF55_UART_RXDRDY_BIT);
	NRF51_UART_STARTRX = 1;

	init_done = 1;
}
