blob: 2ae115ea83638c4f32f622d97f88940eb32c52da [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2015 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <reg.h>
24#include <stdio.h>
25#include <trace.h>
26#include <lib/cbuf.h>
27#include <kernel/thread.h>
28#include <platform/interrupts.h>
29#include <platform/debug.h>
30#include <platform/bcm2837.h>
31
32/* TODO: extract this into a generic PL011 driver */
33
34/* PL011 implementation */
35#define UART_DR (0x00)
36#define UART_RSR (0x04)
37#define UART_TFR (0x18)
38#define UART_ILPR (0x20)
39#define UART_IBRD (0x24)
40#define UART_FBRD (0x28)
41#define UART_LCRH (0x2c)
42#define UART_CR (0x30)
43#define UART_IFLS (0x34)
44#define UART_IMSC (0x38)
45#define UART_TRIS (0x3c)
46#define UART_TMIS (0x40)
47#define UART_ICR (0x44)
48#define UART_DMACR (0x48)
49
50#define UARTREG(base, reg) (*REG32((base) + (reg)))
51
52#define RXBUF_SIZE 16
53#define NUM_UART 1
54
55static cbuf_t uart_rx_buf[NUM_UART];
56
57static inline uintptr_t uart_to_ptr(unsigned int n)
58{
59 switch (n) {
60 case 0:
61 return UART0_BASE;
62 default:
63 case 1:
64 return UART1_BASE;
65 }
66}
67
68
69
70#define BIT(x) (1 << (x))
71
72#define BCM2835_MU_BASE 0x3f215040
73#define BCM2835_MU_BASE2 0xffffffffc0215040ULL
74
75struct bcm283x_mu_regs {
76 uint32_t io;
77 uint32_t iir;
78 uint32_t ier;
79 uint32_t lcr;
80 uint32_t mcr;
81 uint32_t lsr;
82 uint32_t msr;
83 uint32_t scratch;
84 uint32_t cntl;
85 uint32_t stat;
86 uint32_t baud;
87};
88
89/* This actually means not full, but is named not empty in the docs */
90#define BCM283X_MU_LSR_TX_EMPTY BIT(5)
91#define BCM283X_MU_LSR_RX_READY BIT(0)
92
93#define __arch_getl(a) (*(volatile unsigned int *)(a))
94#define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v))
95#define __arch_get32(a) (*(volatile uint32_t *)(a))
96
97#define dmb() __asm__ __volatile__ ("" : : : "memory")
98#define __iormb() dmb()
99#define __iowmb() dmb()
100
101#define readl(c) ({ uint32_t __v = __arch_getl(c); __iormb(); __v; })
102#define writel(v,c) ({ uint32_t __v = v; __iowmb(); __arch_putl(__v,c); __v; })
103
104static void bcm283x_mu_serial_putc(const char data)
105{
106 struct bcm283x_mu_regs *regs = (struct bcm283x_mu_regs *)BCM2835_MU_BASE;
107
108 /* Wait until there is space in the FIFO */
109 while (!(readl(&regs->lsr) & BCM283X_MU_LSR_TX_EMPTY))
110 ;
111
112 /* Send the character */
113 writel(data, &regs->io);
114}
115
116
117static enum handler_return uart_irq(void *arg)
118{
119 bool resched = false;
120 uint port = (uint)arg;
121 uintptr_t base = uart_to_ptr(port);
122
123 /* read interrupt status and mask */
124 uint32_t isr = UARTREG(base, UART_TMIS);
125
126 if (isr & ((1<<6) | (1<<4))) { // rtmis, rxmis
127 UARTREG(base, UART_ICR) = (1<<4);
128 cbuf_t *rxbuf = &uart_rx_buf[port];
129
130 /* while fifo is not empty, read chars out of it */
131 while ((UARTREG(base, UART_TFR) & (1<<4)) == 0) {
132 char c = UARTREG(base, UART_DR);
133 cbuf_write_char(rxbuf, c, false);
134
135 resched = true;
136 }
137 }
138
139 return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
140}
141
142void uart_init(void)
143{
144 //for (size_t i = 0; i < NUM_UART; i++) {
145 size_t i =1;
146 // create circular buffer to hold received data
147 cbuf_initialize(&uart_rx_buf[i], RXBUF_SIZE);
148
149 // assumes interrupts are contiguous
150 register_int_handler(INTERRUPT_VC_UART + i, &uart_irq, (void *)i);
151
152 // clear all irqs
153 UARTREG(uart_to_ptr(i), UART_ICR) = 0x3ff;
154
155 // set fifo trigger level
156 UARTREG(uart_to_ptr(i), UART_IFLS) = 0; // 1/8 rxfifo, 1/8 txfifo
157
158 // enable rx interrupt
159 UARTREG(uart_to_ptr(i), UART_IMSC) = (1<<6)|(1<<4); // rtim, rxim
160
161 // enable receive
162 UARTREG(uart_to_ptr(i), UART_CR) |= (1<<9); // rxen
163
164 // enable interrupt
165 unmask_interrupt(INTERRUPT_VC_UART + i);
166 //}
167}
168
169void uart_init_early(void)
170{
171 //for (size_t i = 0; i < NUM_UART; i++) {
172 UARTREG(uart_to_ptr(1), UART_CR) = (1<<8)|(1<<0); // tx_enable, uarten
173 //}
174}
175
176int uart_putc(int port, char c)
177{
178
179 struct bcm283x_mu_regs *regs = (struct bcm283x_mu_regs *)BCM2835_MU_BASE2;
180
181 /* Wait until there is space in the FIFO */
182 while (!(readl(&regs->lsr) & BCM283X_MU_LSR_TX_EMPTY))
183 ;
184
185 /* Send the character */
186 writel(c, &regs->io);
187
188 return 1;
189}
190
191
192
193int uart_getc(int port, bool wait)
194{
195 cbuf_t *rxbuf = &uart_rx_buf[port];
196
197 char c;
198 if (cbuf_read_char(rxbuf, &c, wait) == 1)
199 return c;
200
201 return -1;
202}
203
204void uart_flush_tx(int port)
205{
206}
207
208void uart_flush_rx(int port)
209{
210}
211
212void uart_init_port(int port, uint baud)
213{
214}
215
216
217