112 lines
2.7 KiB
C
112 lines
2.7 KiB
C
/**
|
|
* \file
|
|
* \brief Kernel serial driver for the Xilinx Zynq7000-series UART
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2016, ETH Zurich.
|
|
* All rights reserved.
|
|
*
|
|
* This file is distributed under the terms in the attached LICENSE file.
|
|
* If you do not find this file, copies can be found by writing to:
|
|
* ETH Zurich D-INFK, Universitaetstr 6, CH-8092 Zurich. Attn: Systems Group.
|
|
*/
|
|
|
|
#include <kernel.h>
|
|
|
|
#include <arm.h>
|
|
#include <dev/zynq7/zynq_uart_dev.h>
|
|
#include <paging_kernel_arch.h>
|
|
#include <arch/arm/platform.h>
|
|
#include <serial.h>
|
|
#include <zynq_uart.h>
|
|
#include <maps/zynq7_map.h>
|
|
|
|
/* Serial console and debugger interfaces. */
|
|
static zynq_uart_t ports[ZYNQ_UART_MAX_PORTS];
|
|
|
|
static void zynq_uart_hw_init(zynq_uart_t *uart);
|
|
|
|
#define MSG(port, format, ...) \
|
|
printk( LOG_NOTE, "ZYNQ serial[%d]: "format, port, ## __VA_ARGS__ )
|
|
|
|
/* XXX - rename this. */
|
|
errval_t
|
|
serial_early_init(unsigned port) {
|
|
assert(port < ZYNQ_UART_MAX_PORTS);
|
|
|
|
zynq_uart_initialize(&ports[port], (mackerel_addr_t)platform_uart_base[port]);
|
|
|
|
/* Ensure the transmitter is enabled. */
|
|
zynq_uart_CR_tx_dis_wrf(&ports[port], 0);
|
|
zynq_uart_CR_tx_en_wrf(&ports[port], 1);
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
void
|
|
zynq_uart_init(unsigned port, lvaddr_t base, bool initialize_hw) {
|
|
assert(port < ZYNQ_UART_MAX_PORTS);
|
|
zynq_uart_initialize(&ports[port], (mackerel_addr_t) base);
|
|
if(initialize_hw) zynq_uart_hw_init(&ports[port]);
|
|
}
|
|
|
|
/*
|
|
* Initialise Zynq UART
|
|
* Zynq TRM S19.3.1
|
|
*/
|
|
static void
|
|
zynq_uart_hw_init(zynq_uart_t *uart) {
|
|
/* Disable all interrupts. */
|
|
zynq_uart_IDR_rawwr(uart, 0);
|
|
|
|
/* Clear all interrupts. */
|
|
zynq_uart_ISR_rawwr(uart, 0xffffffff);
|
|
|
|
/* Trigger an interrupt on a single byte. */
|
|
zynq_uart_RXWM_RTRIG_wrf(uart, 1);
|
|
|
|
/* Enable RX trigger interrupt. */
|
|
zynq_uart_IER_rtrig_wrf(uart, 1);
|
|
|
|
/* Enable receiver. */
|
|
zynq_uart_CR_rx_dis_wrf(uart, 0);
|
|
zynq_uart_CR_rx_en_wrf(uart, 1);
|
|
}
|
|
|
|
/**
|
|
* \brief Prints a single character to a serial port.
|
|
*/
|
|
void
|
|
serial_putchar(unsigned port, char c) {
|
|
assert(port <= ZYNQ_UART_MAX_PORTS);
|
|
zynq_uart_t *uart = &ports[port];
|
|
|
|
/* Wait until FIFO can hold more characters. */
|
|
while(zynq_uart_SR_TXFULL_rdf(uart));
|
|
|
|
/* Write character. */
|
|
zynq_uart_FIFO_FIFO_wrf(uart, c);
|
|
}
|
|
|
|
/**
|
|
* \brief Reads a single character from the default serial port.
|
|
*/
|
|
char
|
|
serial_getchar(unsigned port) {
|
|
assert(port <= ZYNQ_UART_MAX_PORTS);
|
|
zynq_uart_t *uart = &ports[port];
|
|
|
|
/* Drain the FIFO. */
|
|
char c= zynq_uart_FIFO_FIFO_rdf(uart);
|
|
while(!zynq_uart_SR_RXEMPTY_rdf(uart)) {
|
|
c= zynq_uart_FIFO_FIFO_rdf(uart);
|
|
}
|
|
|
|
/* Clear the RXTRIG interrupt. */
|
|
zynq_uart_ISR_rtrig_wrf(uart, 1);
|
|
|
|
/* Return the character. */
|
|
return c;
|
|
}
|