#ifndef _PLATFORM_UART_H
#define _PLATFORM_UART_H


#include <stdint.h>
#include <macro.h>


// <Description> <[Doc Page]>

// UART Base Addesses [141]
#define UART_0_ADDR 0x10010000L
#define UART_1_ADDR 0x10010000L

// UART Control Register Offsets [142]
#define UART_TXDATA_OFF 0x00    // Transmit data register
#define UART_RXDATA_OFF 0x04    // Receive data register
#define UART_TXCTRL_OFF 0x08    // Transmit control register
#define UART_RXCTRL_OFF 0x0C    // Receive control register
#define UART_IE_OFF     0x10    // UART interrupt enable
#define UART_IP_OFF     0x14    // UART interrupt pending
#define UART_DIV_OFF    0x18    // Baud rate divisor

#define UART_NUMBER 2
#define DEFAULT_BAUD 115200


// UART ids
typedef enum {
	UART0 = 0,
	UART1 = 1,
} uart_id;


/* Transmit Data Register (txdata) [142]
 * Writing to the txdata register enqueues the character contained in the data
 * field to the transmit FIFO if the FIFO is able to accept new entries. Reading
 * from txdata returns the current value of the full flag and zero in the data
 * field. The full flag indicates whether the transmit FIFO is able to accept
 * new entries; when set, writes to data are ignored. A RISC‐V amoor.w
 * instruction can be used to both read the full status and attempt to enqueue
 * data, with a non-zero return value indicating the character was not accepted.
 */
struct S_PACKED uart_txdata_reg {
	uint32_t data:8;
	uint32_t reserved:23;
	uint32_t full:1;
};
static_assert((sizeof(struct uart_txdata_reg) == sizeof(uint32_t)),
              "uart_txdata_reg is not the right size");

/* Receive Data Register (rxdata) [143]
 * Reading the rxdata register dequeues a character from the receive FIFO and
 * returns the value in the data field. The empty flag indicates if the receive
 * FIFO was empty; when set, the data field does not contain a valid character.
 * Writes to rxdata are ignored.
 */
struct S_PACKED uart_rxdata_reg {
	uint32_t data:8;
	uint32_t reserved:23;
	uint32_t empty:1;
};
static_assert((sizeof(struct uart_rxdata_reg) == sizeof(uint32_t)),
              "uart_rxdata_reg is not the right size");

/* Transmit Control Register (txctrl) [143]
 * The read-write txctrl register controls the operation of the transmit channel.
 * The txen bit con- trols whether the Tx channel is active. When cleared,
 * transmission of Tx FIFO contents is suppressed, and the txd pin is driven
 * high. The nstop field specifies the number of stop bits: 0 for one stop bit
 * and 1 for two stop bits. The txcnt field specifies the threshold at which the
 * Tx FIFO watermark interrupt triggers. The txctrl register is reset to 0.
 */
struct S_PACKED uart_txctrl_reg {
	uint32_t txen:1;
	uint32_t nstop:1;
	uint32_t reserved0:14;
	uint32_t txcnt:3;
	uint32_t reserved1:13;
};
static_assert((sizeof(struct uart_txctrl_reg) == sizeof(uint32_t)),
              "uart_txctrl_reg is not the right size");

/* Receive Control Register (rxctrl) [144]
 * The read-write rxctrl register controls the operation of the receive channel.
 * The rxen bit controls whether the Rx channel is active. When cleared, the
 * state of the rxd pin is ignored, and no characters will be enqueued into the
 * Rx FIFO. The rxcnt field specifies the threshold at which the Rx FIFO
 * watermark interrupt triggers. The rxctrl register is reset to 0. Characters
 * are enqueued when a zero (low) start bit is seen.
 */
struct S_PACKED uart_rxctrl_reg {
	uint32_t rxen:1;
	uint32_t reserved0:15;
	uint32_t rxcnt:3;
	uint32_t reserved1:13;
};
static_assert((sizeof(struct uart_rxctrl_reg) == sizeof(uint32_t)),
              "uart_rxctrl_reg is not the right size");

/* Interrupt Registers (ip and ie) [144]
 * The ip register is a read-only register indicating the pending interrupt
 * conditions, and the read- write ie register controls which UART interrupts
 * are enabled. ie is reset to 0. The txwm condition becomes raised when the
 * number of entries in the transmit FIFO is strictly less than the count
 * specified by the txcnt field of the txctrl register. The pending bit is
 * cleared when sufficient entries have been enqueued to exceed the watermark.
 * The rxwm condition becomes raised when the number of entries in the receive
 * FIFO is strictly greater than the count specified by the rxcnt field of the
 * rxctrl register. The pending bit is cleared when sufficient entries have been
 * dequeued to fall below the watermark.
 */
struct S_PACKED uart_ie_reg {
	uint32_t txwm:1;
	uint32_t rxwm:1;
	uint32_t reserved:30;
};
static_assert((sizeof(struct uart_ie_reg) == sizeof(uint32_t)),
              "uart_ie_reg is not the right size");

struct S_PACKED uart_ip_reg {
	uint32_t txwm:1;
	uint32_t rxwm:1;
	uint32_t reserved:30;
};
static_assert((sizeof(struct uart_ip_reg) == sizeof(uint32_t)),
              "uart_ip_reg is not the right size");

/* Baud Rate Divisor Register (div) [145]
 * The read-write, div_width-bit div register specifies the divisor used by baud
 * rate generation for both Tx and Rx channels. The relationship between the
 * input clock and baud rate is given by the following formula: The input clock
 * is the bus clock pclk. The reset value of the register is set to div_init,
 * which is tuned to provide a 115200 baud output out of reset given the
 * expected frequency of pclk. Table 85 shows divisors for some common core
 * clock rates and commonly used baud rates. Note that the table shows the
 * divide ratios, which are one greater than the value stored in the div
 * register. The receive channel is sampled at 16× the baud rate, and a majority
 * vote over 3 neighboring bits is used to determine the received value. For
 * this reason, the divisor must be ≥16 for a receive channel.
 */
struct S_PACKED uart_div_reg {
	uint32_t div:16;
	uint32_t reserved:16;
};
static_assert((sizeof(struct uart_div_reg) == sizeof(uint32_t)),
              "uart_div_reg is not the right size");


struct S_PACKED uart_memory_map {
	struct uart_txdata_reg txdata;
	struct uart_rxdata_reg rxdata;
	struct uart_txctrl_reg txctrl;
	struct uart_rxctrl_reg rxctrl;
	struct uart_ie_reg ie;
	struct uart_ip_reg ip;
	struct uart_div_reg div;
};
static_assert((sizeof(struct uart_memory_map) == sizeof(uint32_t[7])),
              "uart_memory_map is not the right size");


int uart_set_baudrate(uart_id id, int baud);
int uart_init(uart_id id, uint32_t baud, int stop_bits);
void uart_tx_byte(uart_id id, unsigned char b);
void uart_tx_full(uart_id id);
void uart_tx_buf(uart_id id, unsigned char *buf, unsigned int size);


#endif
