1 /*
2 * Copyright (c) 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 <lk/reg.h>
9 #include <stdio.h>
10 #include <lk/trace.h>
11 #include <lib/cbuf.h>
12 #include <kernel/thread.h>
13 #include <platform/interrupts.h>
14 #include <platform/debug.h>
15 #include <platform/bcm28xx.h>
16
17 /* TODO: extract this into a generic PL011 driver */
18
19 /* PL011 implementation */
20 #define UART_DR (0x00)
21 #define UART_RSR (0x04)
22 #define UART_TFR (0x18)
23 #define UART_ILPR (0x20)
24 #define UART_IBRD (0x24)
25 #define UART_FBRD (0x28)
26 #define UART_LCRH (0x2c)
27 #define UART_CR (0x30)
28 #define UART_IFLS (0x34)
29 #define UART_IMSC (0x38)
30 #define UART_TRIS (0x3c)
31 #define UART_TMIS (0x40)
32 #define UART_ICR (0x44)
33 #define UART_DMACR (0x48)
34
35 #define UARTREG(base, reg) (*REG32((base) + (reg)))
36
37 #define RXBUF_SIZE 16
38 #define NUM_UART 1
39
40 static cbuf_t uart_rx_buf[NUM_UART];
41
uart_to_ptr(unsigned int n)42 static inline uintptr_t uart_to_ptr(unsigned int n) {
43 switch (n) {
44 default:
45 case 0:
46 return UART0_BASE;
47 }
48 }
49
uart_irq(void * arg)50 static enum handler_return uart_irq(void *arg) {
51 bool resched = false;
52 uint port = (uint)arg;
53 uintptr_t base = uart_to_ptr(port);
54
55 /* read interrupt status and mask */
56 uint32_t isr = UARTREG(base, UART_TMIS);
57
58 if (isr & ((1<<6) | (1<<4))) { // rtmis, rxmis
59 UARTREG(base, UART_ICR) = (1<<4);
60 cbuf_t *rxbuf = &uart_rx_buf[port];
61
62 /* while fifo is not empty, read chars out of it */
63 while ((UARTREG(base, UART_TFR) & (1<<4)) == 0) {
64 char c = UARTREG(base, UART_DR);
65 cbuf_write_char(rxbuf, c, false);
66
67 resched = true;
68 }
69 }
70
71 return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
72 }
73
uart_init(void)74 void uart_init(void) {
75 for (size_t i = 0; i < NUM_UART; i++) {
76 // create circular buffer to hold received data
77 cbuf_initialize(&uart_rx_buf[i], RXBUF_SIZE);
78
79 // assumes interrupts are contiguous
80 register_int_handler(INTERRUPT_VC_UART + i, &uart_irq, (void *)i);
81
82 // clear all irqs
83 UARTREG(uart_to_ptr(i), UART_ICR) = 0x3ff;
84
85 // set fifo trigger level
86 UARTREG(uart_to_ptr(i), UART_IFLS) = 0; // 1/8 rxfifo, 1/8 txfifo
87
88 // enable rx interrupt
89 UARTREG(uart_to_ptr(i), UART_IMSC) = (1<<6)|(1<<4); // rtim, rxim
90
91 // enable receive
92 UARTREG(uart_to_ptr(i), UART_CR) |= (1<<9); // rxen
93
94 // enable interrupt
95 unmask_interrupt(INTERRUPT_VC_UART + i);
96 }
97 }
98
uart_init_early(void)99 void uart_init_early(void) {
100 for (size_t i = 0; i < NUM_UART; i++) {
101 UARTREG(uart_to_ptr(i), UART_CR) = (1<<8)|(1<<0); // tx_enable, uarten
102 }
103 }
104
uart_putc(int port,char c)105 int uart_putc(int port, char c) {
106 uintptr_t base = uart_to_ptr(port);
107
108 /* spin while fifo is full */
109 while (UARTREG(base, UART_TFR) & (1<<5))
110 ;
111 UARTREG(base, UART_DR) = c;
112
113 return 1;
114 }
115
uart_getc(int port,bool wait)116 int uart_getc(int port, bool wait) {
117 cbuf_t *rxbuf = &uart_rx_buf[port];
118
119 char c;
120 if (cbuf_read_char(rxbuf, &c, wait) == 1)
121 return c;
122
123 return -1;
124 }
125
uart_flush_tx(int port)126 void uart_flush_tx(int port) {
127 }
128
uart_flush_rx(int port)129 void uart_flush_rx(int port) {
130 }
131
uart_init_port(int port,uint baud)132 void uart_init_port(int port, uint baud) {
133 }
134
135
136