/
serial.c
757 lines (649 loc) · 22.7 KB
/
serial.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <stdlib.h>
#include <platsupport/serial.h>
#include <platsupport/plat/serial.h>
#include <string.h>
#include <utils/fence.h>
#include "../../chardev.h"
#define UART_BYTE_MASK 0xff
#define NV_UART_INPUT_CLOCK_FREQ_HZ (408000000)
#define LCR_DLAB BIT(7)
#define LCR_SET_BREAK BIT(6)
#define LCR_SET_PARITY BIT(5) /* Force parity to value of BIT(4) */
#define LCR_EVEN BIT(4) /* even parity? */
#define LCR_PAR BIT(3) /* send parity? */
#define LCR_STOP BIT(2) /* transmit 1 (0) or 2 (1) stop bits */
#define LCR_WD_SIZE_5 0
#define LCR_WD_SIZE_6 1
#define LCR_WD_SIZE_7 2
#define LCR_WD_SIZE_8 3
#define LSR_THRE_EMPTY (BIT(5))
#define LSR_RDR_READY BIT(0)
#define IER_RHR_ENABLE BIT(0)
#define IER_THR_EMPTY_ENABLE BIT(1)
#define IER_RX_FIFO_TIMEDOUT BIT(4)
#define IER_EO_RECEIVE_DATA BIT(5)
/* IIR is read-only, FCR is write-only. Both share the same address mapping.
* IIR is automatically selected when you do a read-access.
* FCR is automatically selected when you do a write-access
*/
#define FCR_FIFO_ENABLE BIT(0)
#define FCR_DMA_MODE0 (0)
#define FCR_DMA_MODE1 BIT(3)
#define FCR_RX_TRIG_SHIFT (6)
#define FCR_RX_TRIG_MASK (0x3)
#define FCR_RX_TRIG_FIFO_GT_1 (0)
#define FCR_RX_TRIG_FIFO_GT_4 (1)
#define FCR_RX_TRIG_FIFO_GT_8 (2)
#define FCR_RX_TRIG_FIFO_GT_16 (3)
#define FCR_TX_TRIG_SHIFT (4)
#define FCR_TX_TRIG_MASK (0x3)
#define FCR_TX_TRIG_FIFO_GT_1 (3)
#define FCR_TX_TRIG_FIFO_GT_4 (2)
#define FCR_TX_TRIG_FIFO_GT_8 (1)
#define FCR_TX_TRIG_FIFO_GT_16 (0)
#define FCR_TX_FIFO_CLEAR_SHIFT (2)
#define FCR_RX_FIFO_CLEAR_SHIFT (1)
#define IIR_FIFO_MODE_STATUS_SHIFT (6)
#define IIR_FIFO_MODE_STATUS_MASK (0x3)
#define IIR_INT_PENDING BIT(0)
#define IIR_INT_SOURCE_SHIFT (0)
#define IIR_INT_SOURCE_MASK (0xF)
enum iir_int_source {
IIR_INT_SOURCE_NO_INTERRUPT = 0x1,
IIR_INT_SOURCE_DATA_ERROR = 0x6,
IIR_INT_SOURCE_RECEIVED_DATA_AVAILABLE = 0x4,
IIR_INT_SOURCE_RECEIVED_DATA_TIMEOUT = 0xC,
IIR_INT_SOURCE_EO_RECEIVED_DATA = 0x8,
IIR_INT_SOURCE_THR_TXRDY = 0x2,
IIR_INT_SOURCE_MODEM_STATUS = 0x0
};
#define EXTRACT_BITS(bf,shift,mask) (((bf) >> (shift)) & (mask))
#define ENCODE_BITS(bf,shift,mask,v) (((bf) & ~(((mask) << (shift)))) \
| (((v) & (mask)) << (shift)))
struct tk1_uart_regs {
uint32_t thr_dlab; /* 0x0: tx holding register */
uint32_t ier_dlab; /* 0x4: IER and DLH registers */
uint32_t iir_fcr; /* 0x8: FIFO control; interrupt identification */
uint32_t lcr; /* 0xc: line control */
uint32_t mcr; /* 0x10: modem control */
uint32_t lsr; /* 0x14: line status */
uint32_t msr; /* 0x18: modem status */
uint32_t spr; /* 0x1c: scratchpad */
uint32_t csr; /* 0x20: IrDA pulse coding */
uint32_t rx_fifo_cfg;/* 0x24: */
uint32_t mie; /* 0x28: modem interrupt enable */
uint32_t asr; /* 0x3c: auto sense baud */
};
typedef volatile struct tk1_uart_regs tk1_uart_regs_t;
static inline tk1_uart_regs_t*
tk1_uart_get_priv(ps_chardevice_t *d)
{
return (tk1_uart_regs_t*)d->vaddr;
}
static inline bool
tk1_uart_is_async(ps_chardevice_t *d)
{
/* NV_UART[ABCD] are polling, while NV_UART[ABCD]_ASYNC are asynchronous
* versions of the first 4 devices.
*/
return (d->id >= NV_UARTA_ASYNC);
}
static inline void
tk1_uart_set_thr_irq(tk1_uart_regs_t *regs, bool enable)
{
uint32_t ier;
ier = regs->ier_dlab;
if (enable) {
ier |= IER_THR_EMPTY_ENABLE;
} else {
ier &= ~IER_THR_EMPTY_ENABLE;
}
regs->ier_dlab = ier;
}
static inline void
tk1_uart_set_rbr_irq(tk1_uart_regs_t *regs, bool enable)
{
uint32_t ier;
ier = regs->ier_dlab;
if (enable) {
ier |= IER_RHR_ENABLE;
ier |= IER_RX_FIFO_TIMEDOUT;
} else {
ier &= ~IER_RHR_ENABLE;
ier &= ~IER_RX_FIFO_TIMEDOUT;
}
regs->ier_dlab = ier;
}
int uart_getchar(ps_chardevice_t *d)
{
tk1_uart_regs_t* regs = tk1_uart_get_priv(d);
uint32_t reg = 0;
int c = -1;
if (regs->lsr & LSR_RDR_READY) {
reg = regs->thr_dlab;
c = reg & UART_BYTE_MASK;
}
return c;
}
int uart_putchar(ps_chardevice_t* d, int c)
{
tk1_uart_regs_t* regs = tk1_uart_get_priv(d);
uint32_t lsr = regs->lsr;
if (((lsr & LSR_THRE_EMPTY) == LSR_THRE_EMPTY)) {
if (c == '\n' && (d->flags & SERIAL_AUTO_CR)) {
uart_putchar(d, '\r');
}
regs->thr_dlab = (uint8_t) c;
return c;
} else {
return -1;
}
}
static void
tk1_uart_invoke_callback(ps_chardevice_t *d, enum chardev_status stat,
size_t n_bytes_xferred,
bool invoke_read, bool invoke_write)
{
tk1_uart_regs_t *regs = tk1_uart_get_priv(d);
if (!invoke_read && !invoke_write) {
ZF_LOGW("invoke_cb called with no indication of what should be done.");
return;
}
if (invoke_read) {
if (d->read_descriptor.callback != NULL
&& d->read_descriptor.bytes_requested > 0) {
d->read_descriptor.callback(d, stat,
n_bytes_xferred,
d->read_descriptor.token);
/* If the client set bytes_requested to 0 inside of the callback,
* that is a signal to us that we should close any asynchronous
* reading from the line.
*/
if (d->read_descriptor.bytes_requested == 0) {
tk1_uart_set_rbr_irq(regs, false);
}
}
}
if (invoke_write) {
if (d->write_descriptor.callback != NULL
&& d->write_descriptor.bytes_requested > 0) {
d->write_descriptor.callback(d, stat,
n_bytes_xferred,
d->write_descriptor.token);
/* If the client set bytes_requested to 0 inside of the callback,
* that is a signal to us that we should consider the async write
* to be closed.
*/
if (d->write_descriptor.bytes_requested == 0) {
tk1_uart_set_thr_irq(regs, false);
}
}
}
}
static void
uart_handle_irq(ps_chardevice_t* d)
{
tk1_uart_regs_t *regs = tk1_uart_get_priv(d);
uintptr_t irq_ident_val;
/* Determine the cause of the IRQ. */
irq_ident_val = regs->iir_fcr;
irq_ident_val &= IIR_INT_SOURCE_MASK;
switch (irq_ident_val) {
case IIR_INT_SOURCE_NO_INTERRUPT:
break;
case IIR_INT_SOURCE_DATA_ERROR:
ZF_LOGE("Parity, overrun, or framing error.");
if (d->read_descriptor.data != NULL) {
tk1_uart_invoke_callback(d, CHARDEV_STAT_ERROR, 0, true, false);
}
break;
case IIR_INT_SOURCE_RECEIVED_DATA_TIMEOUT:
case IIR_INT_SOURCE_EO_RECEIVED_DATA:
case IIR_INT_SOURCE_RECEIVED_DATA_AVAILABLE: {
uint8_t *client_buff;
int c;
struct chardev_xmit_descriptor *rd = &d->read_descriptor;
if (irq_ident_val == IIR_INT_SOURCE_EO_RECEIVED_DATA) {
/* Tegra K1 Mobile Processor TRM, section 23.4.2:
*
* "EORD (End of Receive Data) Interrupt occurs when the receiver
* detects that data stops coming in for more than 4 character
* times. This interrupt is useful for determining that the sending
* device has completed sending all its data. EORD timeout will not
* occur if the receiving data stream is stopped because of hardware
* handshaking."
*
* "To clear the EORD timeout interrupt you must DISABLE the EORD
* interrupt enable (IE_EORD)."
*
* But I assume I have to re-enable it too, because otherwise I
* won't get them anymore.
*/
ZF_LOGV("Int reason EO received data.");
regs->ier_dlab &= ~IER_EO_RECEIVE_DATA;
regs->ier_dlab |= IER_EO_RECEIVE_DATA;
}
if (rd->data == NULL || rd->bytes_requested == 0) {
/* Even if there is no rx buffer, or no bytes have been requested,
* or some other unusual case has been triggered, we should read the
* RBR register and consume the bytes in it.
*/
ZF_LOGW("Draining.");
while (uart_getchar(d) != -1) {
/* Just read the bytes out to clear the FIFO */
}
break;
}
/* So everytime a new RX data IRQ comes in, the buffer cursor gets
* reset to the beginning of the client-supplied buffer.
*
* Therefore the client should try to service IRQs as quickly as possible.
*/
client_buff = rd->data;
c = uart_getchar(d);
while (c != -1) {
/* Don't overrun the client-supplied buffer */
if (rd->bytes_transfered >= rd->bytes_requested) {
ZF_LOGV("Buffer of %dB will be overrun.", rd->bytes_requested);
tk1_uart_invoke_callback(d, CHARDEV_STAT_INCOMPLETE,
rd->bytes_transfered,
true, false);
/* We use bytes_requested as a flag to indicate to the this IRQ
* handler that it shouldn't call the callback again.
* If bytes_requested is 0, we won't get here, so this callback
* won't be called repeatedly if there's more data than the
* caller's buffer can hold.
*/
rd->bytes_requested = 0;
break;
}
client_buff[rd->bytes_transfered] = (char)c;
rd->bytes_transfered++;
c = uart_getchar(d);
}
/* If the loop exits early because the buffer was overrun, "c" will
* not be -1, because the UART would have returned a character. We
* just didn't have any buffer memory remaining.
*
* I.e, c will be -1 if the loop exited normally.
*/
if (c == -1 && rd->bytes_transfered > 0) {
tk1_uart_invoke_callback(d, CHARDEV_STAT_COMPLETE,
rd->bytes_transfered,
true, false);
}
break;
}
case IIR_INT_SOURCE_THR_TXRDY:
ZF_LOGV("Int reason THR ready: %d of %d bytes transferred.",
d->write_descriptor.bytes_transfered,
d->write_descriptor.bytes_requested);
if (d->write_descriptor.data != NULL) {
struct chardev_xmit_descriptor *wd = &d->write_descriptor;
uint8_t *client_data;
int status;
client_data = wd->data;
while (wd->bytes_transfered < wd->bytes_requested) {
status = uart_putchar(d, client_data[wd->bytes_transfered]);
if (status == -1) {
ZF_LOGV("One DMA pass finished: written %d of %dB!",
wd->bytes_transfered, wd->bytes_requested);
break;
}
wd->bytes_transfered++;
}
if (wd->bytes_transfered >= wd->bytes_requested) {
/* Disable the THR_EMPTY IRQ until a new write request is made. */
tk1_uart_set_thr_irq(regs, false);
tk1_uart_invoke_callback(d, CHARDEV_STAT_COMPLETE,
wd->bytes_transfered,
false, true);
}
} else {
/* If there's no input data buffer to read from, disable the
* THR_EMPTY IRQ, because it was triggered, for one reason or
* another.
*/
tk1_uart_set_thr_irq(regs, false);
}
break;
case IIR_INT_SOURCE_MODEM_STATUS:
ZF_LOGV("Modem status changed.");
break;
default:
ZF_LOGW("Unknown interrupt reason %d.", irq_ident_val);
};
}
static ssize_t
tk1_uart_write(ps_chardevice_t* d, const void* vdata,
size_t count, chardev_callback_t rcb,
void* token)
{
struct chardev_xmit_descriptor wd = {
.callback = rcb,
.token = token,
.bytes_transfered = 0,
.bytes_requested = count,
.data = (void *)vdata
};
d->write_descriptor = wd;
if (count == 0) {
/* Call the callback immediately */
if (rcb != NULL) {
rcb(d, CHARDEV_STAT_COMPLETE, count, token);
}
return 0;
}
if (!tk1_uart_is_async(d)) {
/* Write the data out over the line synchronously. */
for (int i = 0; i < count; i++) {
while (uart_putchar(d, ((uint8_t *)vdata)[i]) == -1) {
}
d->write_descriptor.bytes_transfered++;
}
if (rcb != NULL) {
rcb(d, CHARDEV_STAT_COMPLETE, d->write_descriptor.bytes_transfered,
token);
}
} else {
/* Else enable the THRE IRQ and return. */
tk1_uart_set_thr_irq(tk1_uart_get_priv(d), true);
THREAD_MEMORY_RELEASE();
}
return d->write_descriptor.bytes_transfered;
}
static ssize_t
tk1_uart_read(ps_chardevice_t* d, void* vdata,
size_t count, chardev_callback_t rcb,
void* token)
{
if (count < 1) {
count = 0;
}
struct chardev_xmit_descriptor rd = {
.callback = rcb,
.token = token,
.bytes_transfered = 0,
.bytes_requested = count,
.data = vdata
};
d->read_descriptor = rd;
if (count == 0 && rcb != NULL) {
ZF_LOGV("read call with 0 count.");
rcb(d, CHARDEV_STAT_COMPLETE, count, token);
}
if (!tk1_uart_is_async(d)) {
int n_chars_read = 0;
int c;
while ((c = uart_getchar(d)) == -1) {
/* Ideally we should use a cpu_relax() type of opcode here. */
}
/* Read the data synchronously. */
while (c != -1) {
((uint8_t *)vdata)[n_chars_read] = c;
c = uart_getchar(d);
n_chars_read++;
}
d->read_descriptor.bytes_transfered = n_chars_read;
if (rcb != NULL) {
rcb(d, CHARDEV_STAT_COMPLETE, d->read_descriptor.bytes_transfered,
token);
}
}
return d->read_descriptor.bytes_transfered;
}
/** Used for debugging. Concats the dlab hi and lo bytes into a 16-bit int.
* @param d Pointer to the device whose divisor you want to get.
* @return 16-bit divisor.
*/
UNUSED static uint16_t
tk1_uart_get_dlab_divisor(ps_chardevice_t *d)
{
uint16_t ret=0;
tk1_uart_regs_t* regs = tk1_uart_get_priv(d);
regs->lcr |= LCR_DLAB;
THREAD_MEMORY_RELEASE();
ret = regs->thr_dlab & 0xFF;
ret |= (regs->ier_dlab & 0xFF) << 8;
THREAD_MEMORY_RELEASE();
regs->lcr &= ~LCR_DLAB;
return ret;
}
static void
tk1_uart_set_dlab_divisor(ps_chardevice_t *d, uint16_t divisor)
{
tk1_uart_regs_t* regs = tk1_uart_get_priv(d);
regs->lcr |= LCR_DLAB;
THREAD_MEMORY_RELEASE();
regs->thr_dlab = divisor & 0xFF;
regs->ier_dlab = (divisor >> 8) & 0xFF;
THREAD_MEMORY_RELEASE();
regs->lcr &= ~LCR_DLAB;
}
static int
tk1_uart_get_divisor_for(int baud)
{
int ret;
switch (baud) {
case 115200:
case 57600:
case 38400:
case 19200:
case 9600:
case 4800:
case 2400:
case 1200:
case 300:
/* Do nothing; This switch is for input validation. */
break;
default:
ZF_LOGE("TK1-uart: Unsupported baud rate %d.",
baud);
return -1;
}
/* Both we an u-boot program the UARTs to use PllP_out as their input clock,
* which is fixed at 408MHz:
*
* TegraK1 TRM, Section 5.22, Table 14:
* "pllP_out: This is the PLLP’s output clock which is set to 408 MHz."
*
* This 408MHz output, is then channeled into the UART-controllers after
* being passed through a divider. The divider's default divisor is 17
* on hardware #RESET, but u-boot sets the divider's divisor to 0, and so
* the UART controller gets the full 408 MHz as its input.
*
* From there, we just calculate a divisor to put into the UART controller's
* DLAB (divisor latch) registers, based on the caller's requested baud
* rate, according to the formula:
* Divisor = input_clock_freq / (16 * desired_baud)
*
* The number "16" comes from the fact that the UART controller takes 16
* clock phases to generate one bit of output on the line (TK1 TRM, section
* 34.1.1.)
*/
ret = (NV_UART_INPUT_CLOCK_FREQ_HZ / 16) / baud;
return ret;
}
int
serial_configure(ps_chardevice_t* d, long bps, int char_size, enum serial_parity parity, int stop_bits)
{
tk1_uart_regs_t* regs = tk1_uart_get_priv(d);
int divisor;
/* line control register */
uint32_t lcr = 0;
/* Disable the receive IRQ while changing line configuration. */
tk1_uart_set_rbr_irq(regs, false);
THREAD_MEMORY_RELEASE();
switch (char_size) {
case 5:
lcr |= LCR_WD_SIZE_5;
break;
case 6:
lcr |= LCR_WD_SIZE_6;
break;
case 7:
lcr |= LCR_WD_SIZE_7;
break;
case 8:
lcr |= LCR_WD_SIZE_8;
break;
default:
return -1;
}
switch (parity) {
case PARITY_NONE:
break;
case PARITY_EVEN:
lcr |= LCR_EVEN | LCR_PAR;
break;
case PARITY_ODD:
lcr |= LCR_PAR;
break;
/*
* Uncomment if we ever need fixed values for parity bits
*
case PARITY_ONE:
lcr |= LCR_SET_PARITY | LCR_EVEN;
break;
case PARITY_ZERO:
lcr |= LCR_SET_PARITY;
break
*/
default:
return -1;
}
/* one stop bit */
regs->lcr = lcr;
divisor = tk1_uart_get_divisor_for(bps);
if (divisor < 1) {
/* Unsupported baud rate. */
return -1;
}
tk1_uart_set_dlab_divisor(d, divisor);
/* Disable hardware flow control for all UARTs other than UARTD,
* because UARTD actually has an RS232 pinout port.
*/
if (d->id != NV_UARTD && d->id != NV_UARTD_ASYNC) {
uint32_t mcr = regs->mcr;
/* Clear RTS_EN and CTS_EN. */
mcr &= ~(BIT(6) | BIT(5));
/* Force RTS and DTR to low (active) */
mcr |= (BIT(1) | BIT(0));
regs->mcr = mcr;
}
/* Drain the RX buffer when configuring a new set of line options.
* By implication, the caller should wait for previous transmissions to be
* completed before reconfiguring.
*/
while (uart_getchar(d) != -1) {
}
if (tk1_uart_is_async(d)) {
/* Re-enable receive IRQ. */
tk1_uart_set_rbr_irq(regs, true);
}
return 0;
}
/** Initialize a ps_chardevice_t instance.
*
* Expects an already valid mapping to the TK1 UART MMIO vaddr range.
*/
int
tk1_uart_init_common(const struct dev_defn *defn, void *const uart_mmio_vaddr,
ps_chardevice_t *dev)
{
volatile void *uart_vaddr = 0;
tk1_uart_regs_t* regs;
uint32_t iir_fcr;
struct chardev_xmit_descriptor cxd_zero = {0};
ps_io_ops_t ioops_zero = {{0}};
/* add offsets properly */
switch (defn->id) {
case NV_UARTA:
case NV_UARTA_ASYNC:
uart_vaddr = uart_mmio_vaddr;
break;
case NV_UARTB:
case NV_UARTB_ASYNC:
uart_vaddr = uart_mmio_vaddr + UARTB_OFFSET;
break;
case NV_UARTC:
case NV_UARTC_ASYNC:
uart_vaddr = uart_mmio_vaddr + UARTC_OFFSET;
break;
case NV_UARTD:
case NV_UARTD_ASYNC:
uart_vaddr = uart_mmio_vaddr + UARTD_OFFSET;
/* The kernel uses UART-D. Recommend not conflicting with it. */
break;
default:
return -1;
}
memset(dev, 0, sizeof(*dev));
/* Set up all the device properties. */
dev->id = defn->id;
dev->vaddr = (void*)uart_vaddr;
dev->read = &tk1_uart_read;
dev->write = &tk1_uart_write;
dev->handle_irq = &uart_handle_irq;
dev->irqs = defn->irqs;
dev->ioops = ioops_zero;
dev->flags = SERIAL_AUTO_CR;
/* Zero out the client state. */
dev->write_descriptor = cxd_zero;
dev->read_descriptor = cxd_zero;
regs = tk1_uart_get_priv(dev);
/* Disable IRQs. */
tk1_uart_set_rbr_irq(regs, false);
tk1_uart_set_thr_irq(regs, false);
/* Line configuration */
serial_configure(dev, 115200, 8, PARITY_NONE, 1);
/* Set FCR[0] to 1 to enable FIFO mode, and enable DMA mode 1 which will
* generate an interrupt only when the buffer has.
*
* There's no point in doing an R-M-W sequence because reading the FCR
* actually returns the values from the IIR, so you can't actually read FCR.
*/
iir_fcr = 0
| FCR_FIFO_ENABLE | FCR_DMA_MODE0
| ENCODE_BITS(0, FCR_RX_TRIG_SHIFT, FCR_RX_TRIG_MASK, FCR_RX_TRIG_FIFO_GT_16)
| ENCODE_BITS(0, FCR_TX_TRIG_SHIFT, FCR_TX_TRIG_MASK, FCR_TX_TRIG_FIFO_GT_16)
| BIT(FCR_TX_FIFO_CLEAR_SHIFT)
| BIT(FCR_RX_FIFO_CLEAR_SHIFT);
regs->iir_fcr = iir_fcr;
/* Read the status bit to ensure the FIFO was enabled. */
iir_fcr = regs->iir_fcr;
if (EXTRACT_BITS(iir_fcr,
IIR_FIFO_MODE_STATUS_SHIFT,
IIR_FIFO_MODE_STATUS_MASK) != 3) {
ZF_LOGE("FIFO mode wasn't enabled.\n");
return -1;
}
return 0;
}
/** Initializes a ps_chardevice_t.
*
* Expects a viable ps_io_ops_t for mapping the registers.
*/
int
uart_init(const struct dev_defn* defn,
const ps_io_ops_t* ops,
ps_chardevice_t* dev)
{
static void *vaddr = 0;
int ret;
/* Attempt to map the virtual address, assure this works */
if (vaddr == 0) {
vaddr = chardev_map(defn, ops);
if (vaddr == NULL) {
return -1;
}
}
ret = tk1_uart_init_common(defn, vaddr, dev);
dev->ioops = *ops;
return ret;
}