1 /*
2 * Copyright (c) 2016 Gurjant Kalsi
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
9 // TODO(gkalsi): Unify the two UART codepaths and use the port parameter to
10 // select between the real uart and the miniuart.
11
12 #include <assert.h>
13 #include <kernel/thread.h>
14 #include <lib/cbuf.h>
15 #include <platform/bcm28xx.h>
16 #include <platform/debug.h>
17 #include <platform/interrupts.h>
18 #include <lk/reg.h>
19 #include <stdio.h>
20 #include <lk/trace.h>
21
22 #define RXBUF_SIZE 16
23
24 static cbuf_t uart_rx_buf;
25
26 struct bcm283x_mu_regs {
27 uint32_t io;
28 uint32_t ier;
29 uint32_t iir;
30 uint32_t lcr;
31 uint32_t mcr;
32 uint32_t lsr;
33 uint32_t msr;
34 uint32_t scratch;
35 uint32_t cntl;
36 uint32_t stat;
37 uint32_t baud;
38 };
39
40 struct bcm283x_aux_regs {
41 uint32_t auxirq;
42 uint32_t auxenb;
43 };
44
45 #define AUX_IRQ_MINIUART (1 << 0)
46 #define AUX_ENB_MINIUART (1 << 0)
47
48 #define MU_IIR_BYTE_AVAIL (1 << 2) // For reading
49 #define MU_IIR_CLR_XMIT_FIFO (1 << 2) // For writing.
50 #define MU_IIR_CLR_RECV_FIFO (1 << 1)
51
52 #define MU_IIR_EN_RX_IRQ (1 << 0) // Enable the recv interrupt.
53
54 #define MU_LSR_TX_EMPTY (1 << 5)
55
aux_irq(void * arg)56 static enum handler_return aux_irq(void *arg) {
57 volatile struct bcm283x_mu_regs *mu_regs =
58 (struct bcm283x_mu_regs *)MINIUART_BASE;
59 volatile struct bcm283x_aux_regs *aux_regs =
60 (struct bcm283x_aux_regs *)AUX_BASE;
61
62 // Make sure this interrupt is intended for the miniuart.
63 uint32_t auxirq = readl(&aux_regs->auxirq);
64 if ((auxirq & AUX_IRQ_MINIUART) == 0) {
65 return INT_NO_RESCHEDULE;
66 }
67
68 bool resched = false;
69
70 while (true) {
71 uint32_t iir = readl(&mu_regs->iir);
72 if ((iir & MU_IIR_BYTE_AVAIL) == 0) break;
73
74 resched = true;
75 char ch = readl(&mu_regs->io);
76 cbuf_write_char(&uart_rx_buf, ch, false);
77 }
78
79 return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
80 }
81
uart_putc(int port,char c)82 int uart_putc(int port, char c) {
83 // There's only one UART for now.
84 // TODO(gkalsi): Unify the two UART code paths using the port.
85 struct bcm283x_mu_regs *regs = (struct bcm283x_mu_regs *)MINIUART_BASE;
86
87 /* Wait until there is space in the FIFO */
88 while (!(readl(®s->lsr) & MU_LSR_TX_EMPTY))
89 ;
90
91 /* Send the character */
92 writel(c, ®s->io);
93
94 return 1;
95 }
96
uart_init(void)97 void uart_init(void) {
98 volatile struct bcm283x_mu_regs *mu_regs =
99 (struct bcm283x_mu_regs *)MINIUART_BASE;
100 volatile struct bcm283x_aux_regs *aux_regs =
101 (struct bcm283x_aux_regs *)AUX_BASE;
102
103 // Create circular buffer to hold received data.
104 cbuf_initialize(&uart_rx_buf, RXBUF_SIZE);
105
106 // AUX Interrupt handler handles interrupts for SPI1, SPI2, and miniuart
107 // Interrupt handler must decode IRQ.
108 register_int_handler(INTERRUPT_AUX, &aux_irq, NULL);
109
110 // Enable the Interrupt.
111 unmask_interrupt(INTERRUPT_AUX);
112
113 writel(MU_IIR_CLR_RECV_FIFO | MU_IIR_CLR_XMIT_FIFO, &mu_regs->iir);
114
115 // Enable the miniuart peripheral. This also enables Miniuart register
116 // access. It's likely that the VideoCore chip already enables this
117 // peripheral for us, but we hit the enable bit just to be sure.
118 writel(AUX_ENB_MINIUART, &aux_regs->auxenb);
119
120 // Enable the receive interrupt on the UART peripheral.
121 writel(MU_IIR_EN_RX_IRQ, &mu_regs->ier);
122 }
123
uart_init_early(void)124 void uart_init_early(void) {
125 }
126
uart_getc(int port,bool wait)127 int uart_getc(int port, bool wait) {
128 cbuf_t *rxbuf = &uart_rx_buf;
129
130 char c;
131 if (cbuf_read_char(rxbuf, &c, wait) == 1)
132 return c;
133
134 return -1;
135 }
136
uart_flush_tx(int port)137 void uart_flush_tx(int port) {
138 volatile struct bcm283x_mu_regs *mu_regs =
139 (struct bcm283x_mu_regs *)MINIUART_BASE;
140 writel(MU_IIR_CLR_XMIT_FIFO, &mu_regs->iir);
141 }
142
uart_flush_rx(int port)143 void uart_flush_rx(int port) {
144 volatile struct bcm283x_mu_regs *mu_regs =
145 (struct bcm283x_mu_regs *)MINIUART_BASE;
146 writel(MU_IIR_CLR_RECV_FIFO, &mu_regs->iir);
147 }
148
uart_init_port(int port,uint baud)149 void uart_init_port(int port, uint baud) {
150 }
151
152
153
154