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