1 //
2 // low-level driver for 16550a UART.
3 //
4
5 #include "types.h"
6 #include "param.h"
7 #include "memlayout.h"
8 #include "riscv.h"
9 #include "spinlock.h"
10 #include "sleeplock.h"
11 #include "proc.h"
12 #include "defs.h"
13
14 // the UART control registers are memory-mapped
15 // at address UART0. this macro returns the
16 // address of one of the registers.
17 #define Reg(reg) ((volatile unsigned char *)(UART0 + (reg)))
18
19 #define ReadReg(reg) (*(Reg(reg)))
20 #define WriteReg(reg, v) (*(Reg(reg)) = (v))
21
22 // the UART control registers.
23 // some have different meanings for read vs write.
24 // see http://byterunner.com/16550.html
25 #define RHR 0 // receive holding register (for input bytes)
26 #define THR 0 // transmit holding register (for output bytes)
27 #define IER 1 // interrupt enable register
28 #define IER_RX_ENABLE (1 << 0) // receiver interrupts
29 #define IER_TX_ENABLE (1 << 1) // transmit interrupts
30 #define FCR 2 // FIFO control register
31 #define FCR_FIFO_ENABLE (1 << 0)
32 #define FCR_FIFO_CLEAR (3 << 1) // clear the content of the two FIFOs
33 #define ISR 2 // interrupt status register
34 #define LCR 3 // line control register
35 #define LCR_EIGHT_BITS (3 << 0)
36 #define LCR_BAUD_LATCH (1 << 7) // special mode to set baud rate
37 #define LSR 5 // line status register
38 #define LSR_RX_READY (1 << 0) // input is waiting to be read from RHR
39 #define LSR_TX_IDLE (1 << 5) // THR can accept another character to send
40
41 // for sending threads to serialize their writes
42 static struct sleeplock tx_lock;
43 static int tx_chan; // &tx_chan is the "wait channel"
44
45 extern volatile int panicking; // from printk.c
46 extern volatile int panicked; // from printk.c
47
48 void
49 uartinit(void)
50 {
51 // disable interrupts.
52 WriteReg(IER, 0x00);
53
54 // special mode to set baud rate.
55 WriteReg(LCR, LCR_BAUD_LATCH);
56
57 // LSB for baud rate of 38.4K.
58 WriteReg(0, 0x03);
59
60 // MSB for baud rate of 38.4K.
61 WriteReg(1, 0x00);
62
63 // leave set-baud mode,
64 // and set word length to 8 bits, no parity.
65 WriteReg(LCR, LCR_EIGHT_BITS);
66
67 // reset and enable FIFOs.
68 WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
69
70 // enable transmit and receive interrupts.
71 WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);
72
73 initsleeplock(&tx_lock, "uart");
74 }
75
76 // transmit buf[] to the uart. it blocks if the
77 // uart is busy, so it cannot be called from
78 // interrupts, only from write() system calls.
79 void
80 uartwrite(char buf[], int n)
81 {
82 acquiresleep(&tx_lock);
83
84 int i = 0;
85 while (i < n) {
86 sleep_prepare(&tx_chan);
87 if (ReadReg(LSR) & LSR_TX_IDLE) {
88 WriteReg(THR, buf[i]);
89 i += 1;
90 } else {
91 sleep();
92 }
93 }
94
95 releasesleep(&tx_lock);
96 }
97
98 // write a byte to the uart without using
99 // interrupts, for use by kernel printk() and
100 // to echo characters. it spins waiting for the uart's
101 // output register to be empty.
102 void
103 uartputc_sync(int c)
104 {
105 if (panicking == 0)
106 push_off();
107
108 if (panicked) {
109 for (;;)
110 ;
111 }
112
113 // wait for UART to set Transmit Holding Empty in LSR.
114 while ((ReadReg(LSR) & LSR_TX_IDLE) == 0)
115 ;
116 WriteReg(THR, c);
117
118 if (panicking == 0)
119 pop_off();
120 }
121
122 // try to read one input character from the UART.
123 // return -1 if none is waiting.
124 static int
125 uartgetc(void)
126 {
127 // is input ready?
128 if (ReadReg(LSR) & LSR_RX_READY) {
129 return ReadReg(RHR);
130 } else {
131 return -1;
132 }
133 }
134
135 // handle a uart interrupt, raised because input has
136 // arrived, or the uart is ready for more output, or
137 // both. called from devintr().
138 void
139 uartintr(void)
140 {
141 ReadReg(ISR); // acknowledge the interrupt
142
143 if (ReadReg(LSR) & LSR_TX_IDLE) {
144 // UART finished transmitting; wake up sending thread.
145 wakeup(&tx_chan);
146 }
147
148 // read and process incoming characters, if any.
149 while (1) {
150 int c = uartgetc();
151 if (c == -1)
152 break;
153 consoleintr(c);
154 }
155 }