1 /*
2  * Copyright (c) 2014-2015 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 #include "include/dev/uart/pl011.h"
9 
10 #include <assert.h>
11 #include <lk/trace.h>
12 #include <lib/cbuf.h>
13 #include <lib/io.h>
14 #include <dev/uart.h>
15 #include <kernel/thread.h>
16 #include <platform/interrupts.h>
17 
18 // PL011 UART driver
19 
20 // PL011 registers
21 enum pl011_regs {
22     UART_DR    = 0x00,
23     UART_RSR   = 0x04,
24     UART_TFR   = 0x18,
25     UART_ILPR  = 0x20,
26     UART_IBRD  = 0x24,
27     UART_FBRD  = 0x28,
28     UART_LCRH  = 0x2c,
29     UART_CR    = 0x30,
30     UART_IFLS  = 0x34,
31     UART_IMSC  = 0x38,
32     UART_TRIS  = 0x3c,
33     UART_TMIS  = 0x40,
34     UART_ICR   = 0x44,
35     UART_DMACR = 0x48
36 };
37 
38 // Control register bits that are used in the driver
39 #define UART_TFR_RXFE (1<<4)
40 #define UART_TFR_TXFF (1<<5)
41 #define UART_TFR_RXFF (1<<6)
42 #define UART_TFR_TXFE (1<<7)
43 
44 #define UART_IMSC_RXIM (1<<4)
45 
46 #define UART_TMIS_RXMIS (1<<4)
47 
48 #define UART_CR_UARTEN (1<<0)
49 #define UART_CR_TXEN (1<<8)
50 #define UART_CR_RXEN (1<<9)
51 
52 // TODO: have these be configurable
53 #define RXBUF_SIZE 32
54 #define NUM_UART 1
55 
56 struct pl011_struct {
57     struct pl011_config config;
58 
59     cbuf_t uart_rx_buf;
60 };
61 
62 static struct pl011_struct uart[NUM_UART];
63 
write_uart_reg(uintptr_t base,size_t offset,uint32_t val)64 static inline void write_uart_reg(uintptr_t base, size_t offset, uint32_t val) {
65     mmio_write32((uint32_t *)(base + offset), val);
66 }
67 
read_uart_reg(uintptr_t base,size_t offset)68 static inline uint32_t read_uart_reg(uintptr_t base, size_t offset) {
69     return mmio_read32((uint32_t *)(base + offset));
70 }
71 
set_uart_reg_bits(uintptr_t base,size_t offset,uint32_t bits)72 static inline void set_uart_reg_bits(uintptr_t base, size_t offset, uint32_t bits) {
73     write_uart_reg(base, offset, read_uart_reg(base, offset) | bits);
74 }
75 
clear_uart_reg_bits(uintptr_t base,size_t offset,uint32_t bits)76 static inline void clear_uart_reg_bits(uintptr_t base, size_t offset, uint32_t bits) {
77     write_uart_reg(base, offset, read_uart_reg(base, offset) & ~bits);
78 }
79 
uart_to_ptr(unsigned int n)80 static inline uintptr_t uart_to_ptr(unsigned int n) {
81     DEBUG_ASSERT(n < NUM_UART);
82     return uart[n].config.base;
83 }
84 
uart_irq(void * arg)85 static enum handler_return uart_irq(void *arg) {
86     struct pl011_struct *u = (struct pl011_struct *)arg;
87 
88     /* read interrupt status and mask */
89     uint32_t isr = read_uart_reg(u->config.base, UART_TMIS);
90 
91     bool resched = false;
92     if (isr & UART_TMIS_RXMIS) { // rxmis
93         cbuf_t *rxbuf = &uart->uart_rx_buf;
94 
95         /* while fifo is not empty, read chars out of it */
96         while ((read_uart_reg(u->config.base, UART_TFR) & UART_TFR_RXFE) == 0) {
97 #if CONSOLE_HAS_INPUT_BUFFER
98             if (u->config.flag & PL011_FLAG_DEBUG_UART) {
99                 char c = read_uart_reg(u->config.base, UART_DR);
100                 cbuf_write_char(&console_input_cbuf, c, false);
101             } else
102 #endif
103             {
104                 /* if we're out of rx buffer, mask the irq instead of handling it */
105                 if (cbuf_space_avail(rxbuf) == 0) {
106                     clear_uart_reg_bits(u->config.base, UART_IMSC, UART_IMSC_RXIM); // !rxim
107                     break;
108                 }
109 
110                 char c = read_uart_reg(u->config.base, UART_DR);
111                 cbuf_write_char(rxbuf, c, false);
112             }
113 
114             resched = true;
115         }
116     }
117 
118     return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
119 }
120 
pl011_init(int port)121 void pl011_init(int port) {
122     uintptr_t base = uart_to_ptr(port);
123 
124     // create circular buffer to hold received data
125     cbuf_initialize(&uart[port].uart_rx_buf, RXBUF_SIZE);
126 
127     // assumes interrupts are contiguous
128     register_int_handler(uart[port].config.irq, &uart_irq, (void *)&uart[port]);
129 
130     // clear all irqs
131     write_uart_reg(base, UART_ICR, 0x3ff);
132 
133     // set fifo trigger level
134     write_uart_reg(base, UART_IFLS, 0); // 1/8 rxfifo, 1/8 txfifo
135 
136     // enable rx interrupt
137     write_uart_reg(base, UART_IMSC, UART_IMSC_RXIM); // rxim
138 
139     // enable receive
140     set_uart_reg_bits(base, UART_CR, UART_CR_RXEN); // rxen
141 
142     // enable interrupt
143     unmask_interrupt(uart[port].config.irq);
144 }
145 
pl011_init_early(int port,const struct pl011_config * config)146 void pl011_init_early(int port, const struct pl011_config *config) {
147     if (port >= NUM_UART) {
148         return;
149     }
150 
151     // TODO: validate config
152     uart[port].config = *config;
153 
154     write_uart_reg(uart[port].config.base, UART_CR, UART_CR_TXEN | UART_CR_UARTEN); // tx_enable, uarten
155 }
156 
uart_putc(int port,char c)157 int uart_putc(int port, char c) {
158     uintptr_t base = uart_to_ptr(port);
159 
160     /* spin while fifo is full */
161     while (read_uart_reg(base, UART_TFR) & UART_TFR_TXFF)
162         ;
163     write_uart_reg(base, UART_DR, c);
164 
165     return 1;
166 }
167 
uart_getc(int port,bool wait)168 int uart_getc(int port, bool wait) {
169     cbuf_t *rxbuf = &uart[port].uart_rx_buf;
170 
171     char c;
172     if (cbuf_read_char(rxbuf, &c, wait) == 1) {
173         set_uart_reg_bits(uart[port].config.base, UART_IMSC, UART_IMSC_RXIM); // rxim
174         return c;
175     }
176 
177     return -1;
178 }
179 
180 /* panic-time getc/putc */
uart_pputc(int port,char c)181 int uart_pputc(int port, char c) {
182     uintptr_t base = uart_to_ptr(port);
183 
184     /* spin while fifo is full */
185     while (read_uart_reg(base, UART_TFR) & UART_TFR_TXFF)
186         ;
187     write_uart_reg(base, UART_DR, c);
188 
189     return 1;
190 }
191 
uart_pgetc(int port)192 int uart_pgetc(int port) {
193     uintptr_t base = uart_to_ptr(port);
194 
195     if ((read_uart_reg(base, UART_TFR) & UART_TFR_RXFE) == 0) {
196         unsigned char c = read_uart_reg(base, UART_DR);
197         return c;
198     } else {
199         return -1;
200     }
201 }
202 
uart_flush_tx(int port)203 void uart_flush_tx(int port) {
204 }
205 
uart_flush_rx(int port)206 void uart_flush_rx(int port) {
207 }
208 
209 // TODO collapse this into the early/regular init routines
uart_init_port(int port,uint baud)210 void uart_init_port(int port, uint baud) {
211 }
212 
213