xref: /haiku/src/system/kernel/arch/arm64/arch_uart_linflex.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
1 /*
2  * Copyright 2022 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Oliver Ruiz Dorantes, oliver.ruiz.dorantes@gmail.com
7  */
8 
9 #include <debug.h>
10 #include <arch/arm/reg.h>
11 #include <arch/generic/debug_uart.h>
12 #include <arch/arm64/arch_uart_linflex.h>
13 #include <new>
14 
15 
16 using namespace LINFlexRegisters;
17 
18 
19 ArchUARTlinflex::ArchUARTlinflex(addr_t base, int64 clock)
20 	:
21 	DebugUART(base, clock)
22 {
23 	Barrier();
24 
25 	if (LinflexCell()->LINCR1.B.SLEEP == 0) {
26 		// This periperal is initialized
27 		if ((LinflexCell()->UARTCR.B.TXEN == 1)
28 			&& (LinflexCell()->UARTCR.B.RXEN == 1)
29 			&& (LinflexCell()->UARTCR.B.UART == 1)) {
30 			// LinFlex already configured as UART mode
31 			// TODO: good to go
32 		} else {
33 
34 		}
35 	}
36 }
37 
38 
39 ArchUARTlinflex::~ArchUARTlinflex()
40 {
41 }
42 
43 
44 void
45 ArchUARTlinflex::Barrier()
46 {
47 	asm volatile ("" : : : "memory");
48 }
49 
50 
51 void
52 ArchUARTlinflex::InitPort(uint32 baud)
53 {
54 	// Calculate baud divisor
55 	uint32 baudDivisor = Clock() / (16 * baud);
56 	uint32 remainder = Clock() % (16 * baud);
57 	uint32 baudFractional = ((8 * remainder) / baud >> 1)
58 		+ ((8 * remainder) / baud & 1);
59 
60 	// Disable UART
61 	Disable();
62 
63 	// Set baud divisor
64 
65 	// Set LCR 8n1, enable fifo
66 
67 	// Set FIFO levels
68 
69 	// Enable UART
70 	Enable();
71 }
72 
73 
74 void
75 ArchUARTlinflex::InitEarly()
76 {
77 	// Perform special hardware UART configuration
78 }
79 
80 
81 void
82 ArchUARTlinflex::Enable()
83 {
84 
85 	DebugUART::Enable();
86 }
87 
88 
89 void
90 ArchUARTlinflex::Disable()
91 {
92 
93 	DebugUART::Disable();
94 }
95 
96 
97 int
98 ArchUARTlinflex::PutChar(char c)
99 {
100 	if (Enabled() == true) {
101 		// Wait until there is room in fifo
102 		bool fifo = LinflexCell()->UARTCR.B.TFBM == 1;
103 
104 		if (fifo) {
105 			// TFF is set by hardware in UART FIFO mode (TFBM = 1) when TX FIFO is full.
106 			while (LinflexCell()->UARTSR.B.DTF == 1) {
107 				Barrier();
108 			}
109 		}
110 
111 		Out<uint8, vuint32>(&LinflexCell()->BDRL.R, c);
112 
113 		if (!fifo) {
114 			// DTF is set by hardware in UART buffer mode (TFBM = 0) and
115 			// indicates that data transmission is completed. DTF should be cleared by software.
116 			while (LinflexCell()->UARTSR.B.DTF == 0) {
117 				Barrier();
118 			}
119 
120 			auto uartsr = BitfieldRegister<UARTSR_register>();
121 			uartsr.B.DTF = 1;
122 			LinflexCell()->UARTSR.R = uartsr.R;
123 		}
124 
125 		return 0;
126 	}
127 
128 	return -1;
129 }
130 
131 
132 int
133 ArchUARTlinflex::GetChar(bool wait)
134 {
135 	int character;
136 
137 	if (Enabled() == true) {
138 		bool fifo = LinflexCell()->UARTCR.B.RFBM == 1;
139 
140 		if (fifo) {
141 			// RFE is set by hardware in UART FIFO mode (RFBM = 1) when the RX FIFO is empty.
142 			if (wait) {
143 				// Wait until a character is received
144 				while (LinflexCell()->UARTSR.B.DRF == 1) {
145 					Barrier();
146 				}
147 			} else {
148 				if (LinflexCell()->UARTSR.B.DRF == 1)
149 					return -1;
150 			}
151 		} else {
152 			// DRF is set by hardware in UART buffer mode (RFBM = 0) and
153 			// indicates that the number of bytes programmed in RDFL have been received.
154 			// DRF should be cleared by software.
155 			if (wait) {
156 				while (LinflexCell()->UARTSR.B.DRF == 0) {
157 					Barrier();
158 				}
159 			} else {
160 				if (LinflexCell()->UARTSR.B.DRF == 0)
161 					return -1;
162 			}
163 		}
164 
165 		character = In<uint8, vuint32>(&LinflexCell()->BDRM.R);
166 
167 		// Clear status
168 		auto uartsr = BitfieldRegister<UARTSR_register>();
169 		uartsr.B.RMB = 1;
170 		uartsr.B.DRF = 1;
171 		LinflexCell()->UARTSR.R = uartsr.R;
172 
173 		return character;
174 	}
175 
176 	return -1;
177 }
178 
179 
180 void
181 ArchUARTlinflex::FlushTx()
182 {
183 	// Wait until transmit fifo empty
184 }
185 
186 
187 void
188 ArchUARTlinflex::FlushRx()
189 {
190 	// Wait until receive fifo empty
191 }
192 
193 
194 ArchUARTlinflex*
195 arch_get_uart_linflex(addr_t base, int64 clock)
196 {
197 	static char buffer[sizeof(ArchUARTlinflex)];
198 	ArchUARTlinflex *uart = new(buffer) ArchUARTlinflex(base, clock);
199 	return uart;
200 }
201