1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2018 Jonathan Neuschäfer
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <device/mmio.h>
#include <boot/coreboot_tables.h>
#include <console/uart.h>
#include <types.h>
/*
* This is a driver for SiFive's own UART, documented in the FU540 manual:
* https://www.sifive.com/documentation/chips/freedom-u540-c000-manual/
*/
struct sifive_uart_registers {
uint32_t txdata; /* Transmit data register */
uint32_t rxdata; /* Receive data register */
uint32_t txctrl; /* Transmit control register */
uint32_t rxctrl; /* Receive control register */
uint32_t ie; /* UART interrupt enable */
uint32_t ip; /* UART interrupt pending */
uint32_t div; /* Baud rate divisor */
} __packed;
#define TXDATA_FULL BIT(31)
#define RXDATA_EMPTY BIT(31)
#define TXCTRL_TXEN BIT(0)
#define TXCTRL_NSTOP_SHIFT 1
#define TXCTRL_NSTOP(x) (((x)-1) << TXCTRL_NSTOP_SHIFT)
#define TXCTRL_TXCNT_SHIFT 16
#define TXCTRL_TXCNT(x) ((x) << TXCTRL_TXCNT_SHIFT)
#define RXCTRL_RXEN BIT(0)
#define RXCTRL_RXCNT_SHIFT 16
#define RXCTRL_RXCNT(x) ((x) << RXCTRL_RXCNT_SHIFT)
#define IP_TXWM BIT(0)
#define IP_RXWM BIT(1)
static void sifive_uart_init(struct sifive_uart_registers *regs, int div)
{
/* Configure the divisor */
write32(®s->div, div);
/* Enable transmission, one stop bit, transmit watermark at 1 */
write32(®s->txctrl, TXCTRL_TXEN|TXCTRL_NSTOP(1)|TXCTRL_TXCNT(1));
/* Enable reception, receive watermark at 0 */
write32(®s->rxctrl, RXCTRL_RXEN|RXCTRL_RXCNT(0));
}
void uart_init(int idx)
{
unsigned int div;
div = uart_baudrate_divisor(get_uart_baudrate(),
uart_platform_refclk(), uart_input_clock_divider());
sifive_uart_init(uart_platform_baseptr(idx), div);
}
static bool uart_can_tx(struct sifive_uart_registers *regs)
{
return !(read32(®s->txdata) & TXDATA_FULL);
}
void uart_tx_byte(int idx, unsigned char data)
{
struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
while (!uart_can_tx(regs))
; /* TODO: implement a timeout */
write32(®s->txdata, data);
}
void uart_tx_flush(int idx)
{
struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
uint32_t ip;
/* Use the TX watermark bit to find out if the TX FIFO is empty */
do {
ip = read32(®s->ip);
} while (!(ip & IP_TXWM));
}
unsigned char uart_rx_byte(int idx)
{
struct sifive_uart_registers *regs = uart_platform_baseptr(idx);
uint32_t rxdata;
do {
rxdata = read32(®s->rxdata);
} while (rxdata & RXDATA_EMPTY);
return rxdata & 0xff;
}
unsigned int uart_input_clock_divider(void)
{
/*
* The SiFive UART handles oversampling internally. The divided clock
* is the baud clock.
*/
return 1;
}
#ifndef __PRE_RAM__
void uart_fill_lb(void *data)
{
/* TODO */
}
#endif
|