quic/qbox
Loading...
Searching...
No Matches
uart-pl011.h
1/*
2 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
3 * Author: GreenSocs 2022
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8#pragma once
9
10#include <mutex>
11#include <queue>
12
13#include "tlm.h"
14#include "tlm_utils/simple_target_socket.h"
15#include <systemc>
16
17#include <ports/initiator-signal-socket.h>
18#include <async_event.h>
19#include <ports/biflow-socket.h>
20#include <module_factory_registery.h>
21#include <scp/report.h>
22#include <tlm_sockets_buswidth.h>
23
24#define PL011_INT_TX 0x20
25#define PL011_INT_RX 0x10
26
27#define PL011_FLAG_TXFE 0x80
28#define PL011_FLAG_RXFF 0x40
29#define PL011_FLAG_TXFF 0x20
30#define PL011_FLAG_RXFE 0x10
31
32/* Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC */
33#define INT_OE (1 << 10)
34#define INT_BE (1 << 9)
35#define INT_PE (1 << 8)
36#define INT_FE (1 << 7)
37#define INT_RT (1 << 6)
38#define INT_TX (1 << 5)
39#define INT_RX (1 << 4)
40#define INT_DSR (1 << 3)
41#define INT_DCD (1 << 2)
42#define INT_CTS (1 << 1)
43#define INT_RI (1 << 0)
44#define INT_E (INT_OE | INT_BE | INT_PE | INT_FE)
45#define INT_MS (INT_RI | INT_DSR | INT_DCD | INT_CTS)
46
47typedef struct PL011State {
48 uint32_t readbuff;
49 uint32_t flags;
50 uint32_t lcr;
51 uint32_t rsr;
52 uint32_t cr;
53 uint32_t dmacr;
54 uint32_t int_enabled;
55 uint32_t int_level;
56 uint32_t read_fifo[16];
57 uint32_t ilpr;
58 uint32_t ibrd;
59 uint32_t fbrd;
60 uint32_t ifl;
61 int read_pos;
62 int read_count;
63 int read_trigger;
64 const unsigned char* id;
66
67static const uint32_t irqmask[] = {
68 INT_E | INT_MS | INT_RT | INT_TX | INT_RX, /* combined IRQ */
69 INT_RX,
70 INT_TX,
71 INT_RT,
72 INT_MS,
73 INT_E,
74};
75
76class Pl011;
77
78/* for legacy */
79typedef Pl011 Uart;
80
81class Pl011 : public sc_core::sc_module
82{
83 SCP_LOGGER();
84
85public:
86 PL011State* s;
87
88 tlm_utils::simple_target_socket<Pl011, DEFAULT_TLM_BUSWIDTH> socket;
89
90 gs::biflow_socket<Pl011> backend_socket;
91
93
94 sc_core::sc_event update_event;
95
96 Pl011(sc_core::sc_module_name name)
97 : irq("irq"), s(nullptr), socket("target_socket"), backend_socket("backend_socket")
98 {
99 SCP_TRACE(()) << "Pl011 constructor";
100
101 socket.register_b_transport(this, &Pl011::b_transport);
102 backend_socket.register_b_transport(this, &Pl011::pl011_receive);
103
104 s = new PL011State();
105
106 s->read_trigger = 1;
107 s->ifl = 0x12;
108 s->cr = 0x300;
109 s->flags = 0x90;
110
111 static const unsigned char pl011_id_arm[8] = { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
112
113 s->id = pl011_id_arm;
114
115 SC_METHOD(pl011_update_sysc);
116 sensitive << update_event;
117 }
118
119#if 0
120 void write(uint64_t offset, uint32_t value) {
121 switch (offset >> 2) {
122 case 0:
123 putchar(value);
124 /* FIXME: flush on '\n' or a timeout */
125 if (value == '\r' || value == '\n') {
126 fflush(stdout);
127 }
128 break;
129 default:
130 break;
131 }
132 }
133#endif
134 void b_transport(tlm::tlm_generic_payload& trans, sc_core::sc_time& delay)
135 {
136 unsigned char* ptr = trans.get_data_ptr();
137 uint64_t addr = trans.get_address();
138 unsigned int len = trans.get_data_length();
139
140 trans.set_dmi_allowed(false);
141 trans.set_response_status(tlm::TLM_OK_RESPONSE);
142
143 switch (trans.get_command()) {
144 case tlm::TLM_WRITE_COMMAND:
145 switch (len) {
146 case 1:
147 pl011_write(addr, *ptr);
148 break;
149 case 2:
150 pl011_write(addr, *(uint16_t*)ptr);
151 break;
152 case 4:
153 pl011_write(addr, *(uint32_t*)ptr);
154 break;
155 default:
156 SCP_FATAL(()) << "Incorrect transaction size";
157 break;
158 }
159 break;
160 case tlm::TLM_READ_COMMAND:
161 switch (len) {
162 case 1:
163 *ptr = (uint8_t)pl011_read(addr);
164 break;
165 case 2:
166 *ptr = (uint16_t)pl011_read(addr);
167 break;
168 case 4:
169 *(uint32_t*)ptr = pl011_read(addr);
170 break;
171 default:
172 SCP_FATAL(()) << "Incorrect transaction size";
173 break;
174 }
175 break;
176 default:
177 break;
178 }
179 }
180
181 void pl011_update() { update_event.notify(); }
182
183 void pl011_update_sysc()
184 {
185 uint32_t flags;
186 size_t i;
187
188 flags = s->int_level & s->int_enabled;
189 if (irqmask[0] & s->int_enabled) {
190 irq->write((flags & irqmask[0]) != 0);
191 }
192 }
193
194 uint32_t pl011_read(uint64_t offset)
195 {
196 uint32_t c;
197 uint64_t r;
198
199 switch (offset >> 2) {
200 case 0: /* UARTDR */
201 s->flags &= ~PL011_FLAG_RXFF;
202 c = s->read_fifo[s->read_pos];
203 if (s->read_count > 0) {
204 backend_socket.can_receive_more(1);
205 s->read_count--;
206 if (++s->read_pos == 16) s->read_pos = 0;
207 }
208 if (s->read_count == 0) {
209 s->flags |= PL011_FLAG_RXFE;
210 }
211 if (s->read_count == s->read_trigger - 1) s->int_level &= ~PL011_INT_RX;
212 s->rsr = c >> 8;
213 pl011_update();
214 r = c;
215 break;
216 case 1: /* UARTRSR */
217 r = s->rsr;
218 break;
219 case 6: /* UARTFR */
220 r = s->flags;
221 break;
222 case 8: /* UARTILPR */
223 r = s->ilpr;
224 break;
225 case 9: /* UARTIBRD */
226 r = s->ibrd;
227 break;
228 case 10: /* UARTFBRD */
229 r = s->fbrd;
230 break;
231 case 11: /* UARTLCR_H */
232 r = s->lcr;
233 break;
234 case 12: /* UARTCR */
235 r = s->cr;
236 break;
237 case 13: /* UARTIFLS */
238 r = s->ifl;
239 break;
240 case 14: /* UARTIMSC */
241 r = s->int_enabled;
242 break;
243 case 15: /* UARTRIS */
244 r = s->int_level;
245 break;
246 case 16: /* UARTMIS */
247 r = s->int_level & s->int_enabled;
248 break;
249 case 18: /* UARTDMACR */
250 r = s->dmacr;
251 break;
252 default:
253 if ((offset >> 2) >= 0x3f8 && (offset >> 2) <= 0x400) {
254 r = s->id[(offset - 0xfe0) >> 2];
255 break;
256 }
257 r = 0;
258 break;
259 }
260
261 return r;
262 }
263
264 void pl011_set_read_trigger()
265 {
266#if 0
267 /* The docs say the RX interrupt is triggered when the FIFO exceeds
268 the threshold. However linux only reads the FIFO in response to an
269 interrupt. Triggering the interrupt when the FIFO is non-empty seems
270 to make things work. */
271 if (s->lcr & 0x10)
272 s->read_trigger = (s->ifl >> 1) & 0x1c;
273 else
274#endif
275 s->read_trigger = 1;
276
277 /* fixes an issue in QBox when characters are typed before linux boots */
278 s->read_count = 0;
279 backend_socket.can_receive_set(16);
280 }
281
282 void pl011_write(uint64_t offset, uint32_t value)
283 {
284 unsigned char ch;
285
286 switch (offset >> 2) {
287 case 0: /* UARTDR */
288 /* ??? Check if transmitter is enabled. */
289 ch = (unsigned char)(value & 0xff);
290 backend_socket.enqueue(ch);
291 s->int_level |= PL011_INT_TX;
292 pl011_update();
293 break;
294 case 1: /* UARTRSR/UARTECR */
295 s->rsr = 0;
296 break;
297 case 6: /* UARTFR */
298 /* Writes to Flag register are ignored. */
299 break;
300 case 8: /* UARTUARTILPR */
301 s->ilpr = value;
302 break;
303 case 9: /* UARTIBRD */
304 s->ibrd = value;
305 break;
306 case 10: /* UARTFBRD */
307 s->fbrd = value;
308 break;
309 case 11: /* UARTLCR_H */
310 /* Reset the FIFO state on FIFO enable or disable */
311 if ((s->lcr ^ value) & 0x10) {
312 s->read_count = 0;
313 s->read_pos = 0;
314 }
315 s->lcr = value;
316 pl011_set_read_trigger();
317 break;
318 case 12: /* UARTCR */
319 /* ??? Need to implement the enable and loopback bits. */
320 s->cr = value;
321 break;
322 case 13: /* UARTIFS */
323 s->ifl = value;
324 pl011_set_read_trigger();
325 break;
326 case 14: /* UARTIMSC */
327 s->int_enabled = value;
328 pl011_update();
329 break;
330 case 17: /* UARTICR */
331 s->int_level &= ~value;
332 pl011_update();
333 break;
334 case 18: /* UARTDMACR */
335 s->dmacr = value;
336 if (value & 3) {
337 }
338 break;
339 default:
340 break;
341 }
342 }
343
344 void pl011_put_fifo(uint32_t value)
345 {
346 int slot;
347
348 slot = s->read_pos + s->read_count;
349 if (slot >= 16) slot -= 16;
350 s->read_fifo[slot] = value;
351 s->read_count++;
352 s->flags &= ~PL011_FLAG_RXFE;
353 if (!(s->lcr & 0x10) || s->read_count == 16) {
354 s->flags |= PL011_FLAG_RXFF;
355 }
356 if (s->read_count == s->read_trigger) {
357 s->int_level |= PL011_INT_RX;
358 pl011_update();
359 }
360 }
361 void pl011_receive(tlm::tlm_generic_payload& txn, sc_core::sc_time& t)
362 {
363 uint8_t* data = txn.get_data_ptr();
364 for (int i = 0; i < txn.get_streaming_width(); i++) {
365 pl011_put_fifo(data[i]);
366 }
367 }
368
369 ~Pl011() { delete s; }
370};
371
372extern "C" void module_register();
Definition uart-pl011.h:82
Definition target.h:160
Definition biflow-socket.h:73
void enqueue(T data)
enqueue Enqueue data to be sent (unlimited queue size) NOTE: Thread safe.
Definition biflow-socket.h:277
void can_receive_set(int i)
can_receive_set
Definition biflow-socket.h:253
void register_b_transport(MODULE *mod, void(MODULE::*cb)(tlm::tlm_generic_payload &, sc_core::sc_time &))
Register b_transport to be called whenever data is received from the socket.
Definition biflow-socket.h:227
void can_receive_more(int i)
can_receive_more
Definition biflow-socket.h:237
Definition uart-pl011.h:47