1/**
2 * \file
3 * \brief Kernel serial driver for the Xilinx Zynq7000-series UART
4 */
5
6/*
7 * Copyright (c) 2016, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Universitaetstr 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <kernel.h>
16
17#include <arm.h>
18#include <dev/zynq7/zynq_uart_dev.h>
19#include <paging_kernel_arch.h>
20#include <platform.h>
21#include <serial.h>
22#include <zynq_uart.h>
23#include <maps/zynq7_map.h>
24
25/* Serial console and debugger interfaces. */
26static zynq_uart_t ports[ZYNQ_UART_MAX_PORTS];
27
28static void zynq_uart_hw_init(zynq_uart_t *uart);
29
30#define MSG(port, format, ...) \
31    printk( LOG_NOTE, "ZYNQ serial[%d]: "format, port, ## __VA_ARGS__ )
32
33/* XXX - rename this. */
34errval_t
35serial_early_init(unsigned port) {
36    assert(port < ZYNQ_UART_MAX_PORTS);
37
38    zynq_uart_initialize(&ports[port], (mackerel_addr_t)uart_base[port]);
39
40    /* Ensure the transmitter is enabled. */
41    zynq_uart_CR_tx_dis_wrf(&ports[port], 0);
42    zynq_uart_CR_tx_en_wrf(&ports[port], 1);
43
44    return SYS_ERR_OK;
45}
46
47void
48zynq_uart_init(unsigned port, lvaddr_t base, bool initialize_hw) {
49    assert(port < ZYNQ_UART_MAX_PORTS);
50    zynq_uart_initialize(&ports[port], (mackerel_addr_t) base);
51    if(initialize_hw) zynq_uart_hw_init(&ports[port]);
52}
53
54/*
55 * Initialise Zynq UART
56 * Zynq TRM S19.3.1
57 */
58static void
59zynq_uart_hw_init(zynq_uart_t *uart) {
60    /* Disable all interrupts. */
61    zynq_uart_IDR_rawwr(uart, 0);
62
63    /* Clear all interrupts. */
64    zynq_uart_ISR_rawwr(uart, 0xffffffff);
65
66    /* Trigger an interrupt on a single byte. */
67    zynq_uart_RXWM_RTRIG_wrf(uart, 1);
68
69    /* Enable RX trigger interrupt. */
70    zynq_uart_IER_rtrig_wrf(uart, 1);
71
72    /* Enable receiver. */
73    zynq_uart_CR_rx_dis_wrf(uart, 0);
74    zynq_uart_CR_rx_en_wrf(uart, 1);
75}
76
77/**
78 * \brief Prints a single character to a serial port.
79 */
80void
81serial_putchar(unsigned port, char c) {
82    assert(port <= ZYNQ_UART_MAX_PORTS);
83    zynq_uart_t *uart = &ports[port];
84
85    /* Wait until FIFO can hold more characters. */
86    while(zynq_uart_SR_TXFULL_rdf(uart));
87
88    /* Write character. */
89    zynq_uart_FIFO_FIFO_wrf(uart, c);
90}
91
92/**
93 * \brief Reads a single character from the default serial port.
94 */
95char
96serial_getchar(unsigned port) {
97    assert(port <= ZYNQ_UART_MAX_PORTS);
98    zynq_uart_t *uart = &ports[port];
99
100    /* Drain the FIFO. */
101    char c= zynq_uart_FIFO_FIFO_rdf(uart);
102    while(!zynq_uart_SR_RXEMPTY_rdf(uart)) {
103        c= zynq_uart_FIFO_FIFO_rdf(uart);
104    }
105
106    /* Clear the RXTRIG interrupt. */
107    zynq_uart_ISR_rtrig_wrf(uart, 1);
108
109    /* Return the character. */
110    return c;
111}
112