-
Notifications
You must be signed in to change notification settings - Fork 184
/
uart.c
120 lines (108 loc) · 3.41 KB
/
uart.c
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
#include "types.h"
#include "platform.h"
/*
* The UART control registers are memory-mapped at address UART0.
* This macro returns the address of one of the registers.
*/
#define UART_REG(reg) ((volatile uint8_t *)(UART0 + reg))
/*
* Reference
* [1]: TECHNICAL DATA ON 16550, http://byterunner.com/16550.html
*/
/*
* UART control registers map. see [1] "PROGRAMMING TABLE"
* note some are reused by multiple functions
* 0 (write mode): THR/DLL
* 1 (write mode): IER/DLM
*/
#define RHR 0 // Receive Holding Register (read mode)
#define THR 0 // Transmit Holding Register (write mode)
#define DLL 0 // LSB of Divisor Latch (write mode)
#define IER 1 // Interrupt Enable Register (write mode)
#define DLM 1 // MSB of Divisor Latch (write mode)
#define FCR 2 // FIFO Control Register (write mode)
#define ISR 2 // Interrupt Status Register (read mode)
#define LCR 3 // Line Control Register
#define MCR 4 // Modem Control Register
#define LSR 5 // Line Status Register
#define MSR 6 // Modem Status Register
#define SPR 7 // ScratchPad Register
/*
* POWER UP DEFAULTS
* IER = 0: TX/RX holding register interrupts are both disabled
* ISR = 1: no interrupt penting
* LCR = 0
* MCR = 0
* LSR = 60 HEX
* MSR = BITS 0-3 = 0, BITS 4-7 = inputs
* FCR = 0
* TX = High
* OP1 = High
* OP2 = High
* RTS = High
* DTR = High
* RXRDY = High
* TXRDY = Low
* INT = Low
*/
/*
* LINE STATUS REGISTER (LSR)
* LSR BIT 0:
* 0 = no data in receive holding register or FIFO.
* 1 = data has been receive and saved in the receive holding register or FIFO.
* ......
* LSR BIT 5:
* 0 = transmit holding register is full. 16550 will not accept any data for transmission.
* 1 = transmitter hold register (or FIFO) is empty. CPU can load the next character.
* ......
*/
#define LSR_RX_READY (1 << 0)
#define LSR_TX_IDLE (1 << 5)
#define uart_read_reg(reg) (*(UART_REG(reg)))
#define uart_write_reg(reg, v) (*(UART_REG(reg)) = (v))
void uart_init()
{
/* disable interrupts. */
uart_write_reg(IER, 0x00);
/*
* Setting baud rate. Just a demo here if we care about the divisor,
* but for our purpose [QEMU-virt], this doesn't really do anything.
*
* Notice that the divisor register DLL (divisor latch least) and DLM (divisor
* latch most) have the same base address as the receiver/transmitter and the
* interrupt enable register. To change what the base address points to, we
* open the "divisor latch" by writing 1 into the Divisor Latch Access Bit
* (DLAB), which is bit index 7 of the Line Control Register (LCR).
*
* Regarding the baud rate value, see [1] "BAUD RATE GENERATOR PROGRAMMING TABLE".
* We use 38.4K when 1.8432 MHZ crystal, so the corresponding value is 3.
* And due to the divisor register is two bytes (16 bits), so we need to
* split the value of 3(0x0003) into two bytes, DLL stores the low byte,
* DLM stores the high byte.
*/
uint8_t lcr = uart_read_reg(LCR);
uart_write_reg(LCR, lcr | (1 << 7));
uart_write_reg(DLL, 0x03);
uart_write_reg(DLM, 0x00);
/*
* Continue setting the asynchronous data communication format.
* - number of the word length: 8 bits
* - number of stop bits:1 bit when word length is 8 bits
* - no parity
* - no break control
* - disabled baud latch
*/
lcr = 0;
uart_write_reg(LCR, lcr | (3 << 0));
}
int uart_putc(char ch)
{
while ((uart_read_reg(LSR) & LSR_TX_IDLE) == 0);
return uart_write_reg(THR, ch);
}
void uart_puts(char *s)
{
while (*s) {
uart_putc(*s++);
}
}