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