]> git.saurik.com Git - apple/xnu.git/blame - pexpert/arm/pe_serial.c
xnu-6153.101.6.tar.gz
[apple/xnu.git] / pexpert / arm / pe_serial.c
CommitLineData
5ba3f43e
A
1/*
2 * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
3 */
4
5/*
6 * file: pe_serial.c Polled-mode UART0 driver for S3c2410 and PL011.
7 */
8
9
10#include <kern/clock.h>
11#include <kern/debug.h>
12#include <libkern/OSBase.h>
cb323159 13#include <libkern/section_keywords.h>
5ba3f43e 14#include <mach/mach_time.h>
d9a64523 15#include <machine/atomic.h>
5ba3f43e
A
16#include <machine/machine_routines.h>
17#include <pexpert/pexpert.h>
18#include <pexpert/protos.h>
19#include <pexpert/device_tree.h>
20#if defined __arm__
21#include <arm/caches_internal.h>
22#include <arm/machine_routines.h>
23#include <arm/proc_reg.h>
24#include <pexpert/arm/board_config.h>
25#include <vm/pmap.h>
26#elif defined __arm64__
27#include <pexpert/arm/consistent_debug.h>
28#include <pexpert/arm64/board_config.h>
29#include <arm64/proc_reg.h>
30#endif
31
32struct pe_serial_functions {
33 void (*uart_init) (void);
34 void (*uart_set_baud_rate) (int unit, uint32_t baud_rate);
35 int (*tr0) (void);
36 void (*td0) (int c);
37 int (*rr0) (void);
38 int (*rd0) (void);
cb323159 39 struct pe_serial_functions *next;
5ba3f43e
A
40};
41
cb323159 42SECURITY_READ_ONLY_LATE(static struct pe_serial_functions*) gPESF = NULL;
5ba3f43e 43
cb323159
A
44static int uart_initted = 0; /* 1 if init'ed */
45static vm_offset_t uart_base = 0;
5ba3f43e 46
5ba3f43e
A
47/*****************************************************************************/
48
0a7de745 49#ifdef S3CUART
5ba3f43e
A
50
51static int32_t dt_pclk = -1;
52static int32_t dt_sampling = -1;
53static int32_t dt_ubrdiv = -1;
54
cb323159
A
55static void ln2410_uart_set_baud_rate(__unused int unit, uint32_t baud_rate);
56
5ba3f43e
A
57static void
58ln2410_uart_init(void)
59{
0a7de745 60 uint32_t ucon0 = 0x405; /* NCLK, No interrupts, No DMA - just polled */
5ba3f43e 61
0a7de745 62 rULCON0 = 0x03; /* 81N, not IR */
5ba3f43e
A
63
64 // Override with pclk dt entry
0a7de745 65 if (dt_pclk != -1) {
5ba3f43e 66 ucon0 = ucon0 & ~0x400;
0a7de745 67 }
5ba3f43e
A
68
69 rUCON0 = ucon0;
0a7de745 70 rUMCON0 = 0x00; /* Clear Flow Control */
5ba3f43e 71
cb323159 72 ln2410_uart_set_baud_rate(0, 115200);
5ba3f43e 73
0a7de745
A
74 rUFCON0 = 0x03; /* Clear & Enable FIFOs */
75 rUMCON0 = 0x01; /* Assert RTS on UART0 */
5ba3f43e
A
76}
77
78static void
79ln2410_uart_set_baud_rate(__unused int unit, uint32_t baud_rate)
80{
81 uint32_t div = 0;
82 uint32_t uart_clock = 0;
83 uint32_t sample_rate = 16;
0a7de745
A
84
85 if (baud_rate < 300) {
5ba3f43e 86 baud_rate = 9600;
0a7de745 87 }
5ba3f43e 88
0a7de745 89 if (rUCON0 & 0x400) {
5ba3f43e
A
90 // NCLK
91 uart_clock = (uint32_t)gPEClockFrequencyInfo.fix_frequency_hz;
0a7de745
A
92 } else {
93 // PCLK
5ba3f43e 94 uart_clock = (uint32_t)gPEClockFrequencyInfo.prf_frequency_hz;
0a7de745 95 }
5ba3f43e
A
96
97 if (dt_sampling != -1) {
98 // Use the sampling rate specified in the Device Tree
99 sample_rate = dt_sampling & 0xf;
100 }
0a7de745 101
5ba3f43e
A
102 if (dt_ubrdiv != -1) {
103 // Use the ubrdiv specified in the Device Tree
104 div = dt_ubrdiv & 0xffff;
105 } else {
106 // Calculate ubrdiv. UBRDIV = (SourceClock / (BPS * Sample Rate)) - 1
107 div = uart_clock / (baud_rate * sample_rate);
0a7de745 108
5ba3f43e
A
109 uint32_t actual_baud = uart_clock / ((div + 0) * sample_rate);
110 uint32_t baud_low = uart_clock / ((div + 1) * sample_rate);
111
112 // Adjust div to get the closest target baudrate
0a7de745 113 if ((baud_rate - baud_low) > (actual_baud - baud_rate)) {
5ba3f43e 114 div--;
0a7de745 115 }
5ba3f43e
A
116 }
117
118 // Sample Rate [19:16], UBRDIV [15:0]
119 rUBRDIV0 = ((16 - sample_rate) << 16) | div;
120}
121
122static int
123ln2410_tr0(void)
124{
125 return rUTRSTAT0 & 0x04;
126}
127static void
128ln2410_td0(int c)
129{
130 rUTXH0 = (unsigned)(c & 0xff);
131}
132static int
133ln2410_rr0(void)
134{
135 return rUTRSTAT0 & 0x01;
136}
137static int
138ln2410_rd0(void)
139{
140 return (int)rURXH0;
141}
142
cb323159
A
143SECURITY_READ_ONLY_LATE(static struct pe_serial_functions) ln2410_serial_functions =
144{
145 .uart_init = ln2410_uart_init,
146 .uart_set_baud_rate = ln2410_uart_set_baud_rate,
147 .tr0 = ln2410_tr0,
148 .td0 = ln2410_td0,
149 .rr0 = ln2410_rr0,
150 .rd0 = ln2410_rd0
0a7de745 151};
5ba3f43e 152
0a7de745 153#endif /* S3CUART */
5ba3f43e
A
154
155/*****************************************************************************/
156
cb323159
A
157static void
158dcc_uart_init(void)
159{
160}
5ba3f43e
A
161
162static unsigned int
163read_dtr(void)
164{
165#ifdef __arm__
0a7de745
A
166 unsigned int c;
167 __asm__ volatile (
168 "mrc p14, 0, %0, c0, c5\n"
169 : "=r"(c));
5ba3f43e
A
170 return c;
171#else
172 /* ARM64_TODO */
173 panic_unimplemented();
174 return 0;
175#endif
176}
177static void
178write_dtr(unsigned int c)
179{
180#ifdef __arm__
0a7de745
A
181 __asm__ volatile (
182 "mcr p14, 0, %0, c0, c5\n"
183 :
184 :"r"(c));
5ba3f43e
A
185#else
186 /* ARM64_TODO */
187 (void)c;
188 panic_unimplemented();
189#endif
190}
191
192static int
193dcc_tr0(void)
194{
195#ifdef __arm__
196 return !(arm_debug_read_dscr() & ARM_DBGDSCR_TXFULL);
197#else
198 /* ARM64_TODO */
199 panic_unimplemented();
200 return 0;
201#endif
202}
203
204static void
205dcc_td0(int c)
206{
207 write_dtr(c);
208}
209
210static int
211dcc_rr0(void)
212{
213#ifdef __arm__
214 return arm_debug_read_dscr() & ARM_DBGDSCR_RXFULL;
215#else
216 /* ARM64_TODO */
217 panic_unimplemented();
218 return 0;
219#endif
220}
221
222static int
223dcc_rd0(void)
224{
225 return read_dtr();
226}
227
cb323159
A
228SECURITY_READ_ONLY_LATE(static struct pe_serial_functions) dcc_serial_functions =
229{
230 .uart_init = dcc_uart_init,
231 .uart_set_baud_rate = NULL,
232 .tr0 = dcc_tr0,
233 .td0 = dcc_td0,
234 .rr0 = dcc_rr0,
235 .rd0 = dcc_rd0
0a7de745 236};
5ba3f43e
A
237
238/*****************************************************************************/
239
240#ifdef SHMCON
241
0a7de745 242#define CPU_CACHELINE_SIZE (1 << MMU_CLINE)
5ba3f43e
A
243
244#ifndef SHMCON_NAME
0a7de745 245#define SHMCON_NAME "AP-xnu"
5ba3f43e
A
246#endif
247
0a7de745
A
248#define SHMCON_MAGIC 'SHMC'
249#define SHMCON_VERSION 2
250#define CBUF_IN 0
251#define CBUF_OUT 1
252#define INBUF_SIZE (panic_size / 16)
253#define FULL_ALIGNMENT (64)
5ba3f43e 254
0a7de745
A
255#define FLAG_CACHELINE_32 1
256#define FLAG_CACHELINE_64 2
5ba3f43e
A
257
258/* Defines to clarify the master/slave fields' use as circular buffer pointers */
0a7de745
A
259#define head_in sidx[CBUF_IN]
260#define tail_in midx[CBUF_IN]
261#define head_out midx[CBUF_OUT]
262#define tail_out sidx[CBUF_OUT]
5ba3f43e
A
263
264/* TODO: get from device tree/target */
0a7de745 265#define NUM_CHILDREN 5
5ba3f43e
A
266
267#define WRAP_INCR(len, x) do{ (x)++; if((x) >= (len)) (x) = 0; } while(0)
268#define ROUNDUP(a, b) (((a) + ((b) - 1)) & (~((b) - 1)))
269
0a7de745
A
270#define MAX(a, b) ((a) > (b) ? (a) : (b))
271#define MIN(a, b) ((a) < (b) ? (a) : (b))
5ba3f43e
A
272
273#define shmcon_barrier() do {__asm__ volatile("dmb ish" : : : "memory");} while(0)
274
275struct shm_buffer_info {
0a7de745
A
276 uint64_t base;
277 uint32_t unused;
278 uint32_t magic;
5ba3f43e
A
279};
280
281struct shmcon_header {
0a7de745
A
282 uint32_t magic;
283 uint8_t version;
284 uint8_t children; /* number of child entries in child_ent */
285 uint16_t flags;
286 uint64_t buf_paddr[2]; /* Physical address for buffers (in, out) */
287 uint32_t buf_len[2];
288 uint8_t name[8];
5ba3f43e
A
289
290 /* Slave-modified data - invalidate before read */
0a7de745 291 uint32_t sidx[2] __attribute__((aligned(FULL_ALIGNMENT))); /* In head, out tail */
5ba3f43e
A
292
293 /* Master-modified data - clean after write */
0a7de745 294 uint32_t midx[2] __attribute__((aligned(FULL_ALIGNMENT))); /* In tail, out head */
5ba3f43e 295
0a7de745 296 uint64_t child[0]; /* Physical address of child header pointers */
5ba3f43e
A
297};
298
299static volatile struct shmcon_header *shmcon = NULL;
300static volatile uint8_t *shmbuf[2];
301#ifdef SHMCON_THROTTLED
302static uint64_t grace = 0;
303static uint64_t full_timeout = 0;
304#endif
305
0a7de745
A
306static void
307shmcon_set_baud_rate(__unused int unit, __unused uint32_t baud_rate)
5ba3f43e
A
308{
309 return;
310}
311
0a7de745
A
312static int
313shmcon_tr0(void)
5ba3f43e
A
314{
315#ifdef SHMCON_THROTTLED
316 uint32_t head = shmcon->head_out;
317 uint32_t tail = shmcon->tail_out;
318 uint32_t len = shmcon->buf_len[CBUF_OUT];
319
320 WRAP_INCR(len, head);
321 if (head != tail) {
322 full_timeout = 0;
323 return 1;
324 }
325
326 /* Full. Is this buffer being serviced? */
327 if (full_timeout == 0) {
328 full_timeout = mach_absolute_time() + grace;
329 return 0;
330 }
0a7de745 331 if (full_timeout > mach_absolute_time()) {
5ba3f43e 332 return 0;
0a7de745 333 }
5ba3f43e
A
334
335 /* Timeout - slave not really there or not keeping up */
336 tail += (len / 4);
0a7de745 337 if (tail >= len) {
5ba3f43e 338 tail -= len;
0a7de745 339 }
5ba3f43e
A
340 shmcon_barrier();
341 shmcon->tail_out = tail;
342 full_timeout = 0;
343#endif
344 return 1;
345}
346
0a7de745
A
347static void
348shmcon_td0(int c)
5ba3f43e
A
349{
350 uint32_t head = shmcon->head_out;
351 uint32_t len = shmcon->buf_len[CBUF_OUT];
352
353 shmbuf[CBUF_OUT][head] = (uint8_t)c;
354 WRAP_INCR(len, head);
355 shmcon_barrier();
356 shmcon->head_out = head;
357}
358
0a7de745
A
359static int
360shmcon_rr0(void)
5ba3f43e 361{
0a7de745 362 if (shmcon->tail_in == shmcon->head_in) {
5ba3f43e 363 return 0;
0a7de745 364 }
5ba3f43e
A
365 return 1;
366}
367
0a7de745
A
368static int
369shmcon_rd0(void)
5ba3f43e
A
370{
371 int c;
372 uint32_t tail = shmcon->tail_in;
373 uint32_t len = shmcon->buf_len[CBUF_IN];
374
375 c = shmbuf[CBUF_IN][tail];
376 WRAP_INCR(len, tail);
377 shmcon_barrier();
378 shmcon->tail_in = tail;
379 return c;
380}
381
0a7de745
A
382static void
383shmcon_init(void)
5ba3f43e 384{
0a7de745
A
385 DTEntry entry;
386 uintptr_t *reg_prop;
387 volatile struct shm_buffer_info *end;
388 size_t i, header_size;
389 unsigned int size;
390 vm_offset_t pa_panic_base, panic_size, va_buffer_base, va_buffer_end;
5ba3f43e 391
0a7de745 392 if (kSuccess != DTLookupEntry(0, "pram", &entry)) {
5ba3f43e 393 return;
0a7de745 394 }
5ba3f43e 395
0a7de745 396 if (kSuccess != DTGetProperty(entry, "reg", (void **)&reg_prop, &size)) {
5ba3f43e 397 return;
0a7de745 398 }
5ba3f43e
A
399
400 pa_panic_base = reg_prop[0];
401 panic_size = reg_prop[1];
402
403 shmcon = (struct shmcon_header *)ml_map_high_window(pa_panic_base, panic_size);
404 header_size = sizeof(*shmcon) + (NUM_CHILDREN * sizeof(shmcon->child[0]));
405 va_buffer_base = ROUNDUP((uintptr_t)(shmcon) + header_size, CPU_CACHELINE_SIZE);
406 va_buffer_end = (uintptr_t)shmcon + panic_size - (sizeof(*end));
407
408 if ((shmcon->magic == SHMCON_MAGIC) && (shmcon->version == SHMCON_VERSION)) {
409 vm_offset_t pa_buffer_base, pa_buffer_end;
410
411 pa_buffer_base = ml_vtophys(va_buffer_base);
412 pa_buffer_end = ml_vtophys(va_buffer_end);
413
414 /* Resume previous console session */
415 for (i = 0; i < 2; i++) {
416 vm_offset_t pa_buf;
417 uint32_t len;
418
419 pa_buf = (uintptr_t)shmcon->buf_paddr[i];
420 len = shmcon->buf_len[i];
421 /* Validate buffers */
422 if ((pa_buf < pa_buffer_base) ||
0a7de745
A
423 (pa_buf >= pa_buffer_end) ||
424 ((pa_buf + len) > pa_buffer_end) ||
425 (shmcon->midx[i] >= len) || /* Index out of bounds */
426 (shmcon->sidx[i] >= len) ||
427 (pa_buf != ROUNDUP(pa_buf, CPU_CACHELINE_SIZE)) || /* Unaligned pa_buffer */
428 (len < 1024) ||
429 (len > (pa_buffer_end - pa_buffer_base)) ||
430 (shmcon->children != NUM_CHILDREN)) {
5ba3f43e 431 goto validation_failure;
0a7de745 432 }
5ba3f43e
A
433 /* Compute the VA offset of the buffer */
434 shmbuf[i] = (uint8_t *)(uintptr_t)shmcon + ((uintptr_t)pa_buf - (uintptr_t)pa_panic_base);
435 }
436 /* Check that buffers don't overlap */
437 if ((uintptr_t)shmbuf[0] < (uintptr_t)shmbuf[1]) {
0a7de745 438 if ((uintptr_t)(shmbuf[0] + shmcon->buf_len[0]) > (uintptr_t)shmbuf[1]) {
5ba3f43e 439 goto validation_failure;
0a7de745 440 }
5ba3f43e 441 } else {
0a7de745 442 if ((uintptr_t)(shmbuf[1] + shmcon->buf_len[1]) > (uintptr_t)shmbuf[0]) {
5ba3f43e 443 goto validation_failure;
0a7de745 444 }
5ba3f43e
A
445 }
446 shmcon->tail_in = shmcon->head_in; /* Clear input buffer */
447 shmcon_barrier();
448 } else {
449validation_failure:
450 shmcon->magic = 0;
451 shmcon_barrier();
452 shmcon->buf_len[CBUF_IN] = (uint32_t)INBUF_SIZE;
453 shmbuf[CBUF_IN] = (uint8_t *)va_buffer_base;
454 shmbuf[CBUF_OUT] = (uint8_t *)ROUNDUP(va_buffer_base + INBUF_SIZE, CPU_CACHELINE_SIZE);
455 for (i = 0; i < 2; i++) {
456 shmcon->midx[i] = 0;
457 shmcon->sidx[i] = 0;
458 shmcon->buf_paddr[i] = (uintptr_t)ml_vtophys((vm_offset_t)shmbuf[i]);
459 }
460 shmcon->buf_len[CBUF_OUT] = (uint32_t)(va_buffer_end - (uintptr_t)shmbuf[CBUF_OUT]);
461 shmcon->version = SHMCON_VERSION;
462#pragma clang diagnostic push
463#pragma clang diagnostic ignored "-Wcast-qual"
464 memset((void *)shmcon->name, ' ', sizeof(shmcon->name));
465 memcpy((void *)shmcon->name, SHMCON_NAME, MIN(sizeof(shmcon->name), strlen(SHMCON_NAME)));
466#pragma clang diagnostic pop
0a7de745 467 for (i = 0; i < NUM_CHILDREN; i++) {
5ba3f43e 468 shmcon->child[0] = 0;
0a7de745 469 }
5ba3f43e
A
470 shmcon_barrier();
471 shmcon->magic = SHMCON_MAGIC;
472 }
473 end = (volatile struct shm_buffer_info *)va_buffer_end;
474 end->base = pa_panic_base;
475 end->unused = 0;
476 shmcon_barrier();
477 end->magic = SHMCON_MAGIC;
478#ifdef SHMCON_THROTTLED
479 grace = gPEClockFrequencyInfo.timebase_frequency_hz;
480#endif
481
482 PE_consistent_debug_register(kDbgIdConsoleHeaderAP, pa_panic_base, panic_size);
483}
484
cb323159 485SECURITY_READ_ONLY_LATE(static struct pe_serial_functions) shmcon_serial_functions =
5ba3f43e
A
486{
487 .uart_init = shmcon_init,
488 .uart_set_baud_rate = shmcon_set_baud_rate,
489 .tr0 = shmcon_tr0,
490 .td0 = shmcon_td0,
491 .rr0 = shmcon_rr0,
492 .rd0 = shmcon_rd0
493};
494
0a7de745
A
495int
496pe_shmcon_set_child(uint64_t paddr, uint32_t entry)
5ba3f43e 497{
0a7de745 498 if (shmcon == NULL) {
5ba3f43e 499 return -1;
0a7de745 500 }
5ba3f43e 501
0a7de745 502 if (shmcon->children >= entry) {
5ba3f43e 503 return -1;
0a7de745 504 }
5ba3f43e
A
505
506 shmcon->child[entry] = paddr;
507 return 0;
508}
509
510#endif /* SHMCON */
511
512/*****************************************************************************/
513
514#ifdef DOCKFIFO_UART
515
516
517// Allow a 30ms stall of wall clock time before DockFIFO starts dropping characters
0a7de745 518#define DOCKFIFO_WR_MAX_STALL_US (30*1000)
5ba3f43e
A
519
520static uint64_t prev_dockfifo_drained_time; // Last time we've seen the DockFIFO drained by an external agent
0a7de745 521static uint64_t prev_dockfifo_spaces; // Previous w_stat level of the DockFIFO.
5ba3f43e
A
522static uint32_t dockfifo_capacity;
523static uint64_t dockfifo_stall_grace;
524
cb323159 525static vm_offset_t dockfifo_uart_base = 0;
5ba3f43e
A
526
527//=======================
528// Local funtions
529//=======================
530
0a7de745
A
531static int
532dockfifo_drain_on_stall()
5ba3f43e
A
533{
534 // Called when DockFIFO runs out of spaces.
535 // Check if the DockFIFO reader has stalled. If so, empty the DockFIFO ourselves.
536 // Return number of bytes drained.
537
538 if (mach_absolute_time() - prev_dockfifo_drained_time >= dockfifo_stall_grace) {
539 // It's been more than DOCKFIFO_WR_MAX_STALL_US and nobody read from the FIFO
540 // Drop a character.
541 (void)rDOCKFIFO_R_DATA(DOCKFIFO_UART_READ, 1);
cb323159 542 os_atomic_inc(&prev_dockfifo_spaces, relaxed);
5ba3f43e
A
543 return 1;
544 }
545 return 0;
546}
547
548
0a7de745
A
549static int
550dockfifo_uart_tr0(void)
5ba3f43e
A
551{
552 uint32_t spaces = rDOCKFIFO_W_STAT(DOCKFIFO_UART_WRITE) & 0xffff;
553 if (spaces >= dockfifo_capacity || spaces > prev_dockfifo_spaces) {
0a7de745
A
554 // More spaces showed up. That can only mean someone read the FIFO.
555 // Note that if the DockFIFO is empty we cannot tell if someone is listening,
556 // we can only give them the benefit of the doubt.
5ba3f43e 557
0a7de745 558 prev_dockfifo_drained_time = mach_absolute_time();
5ba3f43e
A
559 }
560 prev_dockfifo_spaces = spaces;
561
562 return spaces || dockfifo_drain_on_stall();
5ba3f43e
A
563}
564
0a7de745
A
565static void
566dockfifo_uart_td0(int c)
5ba3f43e
A
567{
568 rDOCKFIFO_W_DATA(DOCKFIFO_UART_WRITE, 1) = (unsigned)(c & 0xff);
cb323159 569 os_atomic_dec(&prev_dockfifo_spaces, relaxed); // After writing a byte we have one fewer space than previously expected.
5ba3f43e
A
570}
571
0a7de745
A
572static int
573dockfifo_uart_rr0(void)
5ba3f43e
A
574{
575 return rDOCKFIFO_R_DATA(DOCKFIFO_UART_READ, 0) & 0x7f;
576}
577
0a7de745
A
578static int
579dockfifo_uart_rd0(void)
5ba3f43e
A
580{
581 return (int)((rDOCKFIFO_R_DATA(DOCKFIFO_UART_READ, 1) >> 8) & 0xff);
582}
583
0a7de745
A
584static void
585dockfifo_uart_init(void)
5ba3f43e
A
586{
587 nanoseconds_to_absolutetime(DOCKFIFO_WR_MAX_STALL_US * 1000, &dockfifo_stall_grace);
588
589 // Disable autodraining of the FIFO. We now purely manage it in software.
590 rDOCKFIFO_DRAIN(DOCKFIFO_UART_WRITE) = 0;
591
592 // Empty the DockFIFO by draining it until OCCUPANCY is 0, then measure its capacity
0a7de745
A
593 while (rDOCKFIFO_R_DATA(DOCKFIFO_UART_WRITE, 3) & 0x7F) {
594 ;
595 }
5ba3f43e
A
596 dockfifo_capacity = rDOCKFIFO_W_STAT(DOCKFIFO_UART_WRITE) & 0xffff;
597}
598
cb323159 599SECURITY_READ_ONLY_LATE(static struct pe_serial_functions) dockfifo_uart_serial_functions =
5ba3f43e
A
600{
601 .uart_init = dockfifo_uart_init,
602 .uart_set_baud_rate = NULL,
603 .tr0 = dockfifo_uart_tr0,
604 .td0 = dockfifo_uart_td0,
605 .rr0 = dockfifo_uart_rr0,
606 .rd0 = dockfifo_uart_rd0
607};
608
609#endif /* DOCKFIFO_UART */
610
611/*****************************************************************************/
612
613#ifdef DOCKCHANNEL_UART
0a7de745 614#define DOCKCHANNEL_WR_MAX_STALL_US (30*1000)
5ba3f43e 615
0a7de745
A
616static vm_offset_t dock_agent_base;
617static uint32_t max_dockchannel_drain_period;
618static bool use_sw_drain;
619static uint64_t prev_dockchannel_drained_time; // Last time we've seen the DockChannel drained by an external agent
620static uint64_t prev_dockchannel_spaces; // Previous w_stat level of the DockChannel.
621static uint64_t dockchannel_stall_grace;
cb323159 622static vm_offset_t dockchannel_uart_base = 0;
5ba3f43e
A
623
624//=======================
625// Local funtions
626//=======================
627
0a7de745
A
628static int
629dockchannel_drain_on_stall()
5ba3f43e
A
630{
631 // Called when DockChannel runs out of spaces.
632 // Check if the DockChannel reader has stalled. If so, empty the DockChannel ourselves.
633 // Return number of bytes drained.
634
635 if ((mach_absolute_time() - prev_dockchannel_drained_time) >= dockchannel_stall_grace) {
636 // It's been more than DOCKCHANEL_WR_MAX_STALL_US and nobody read from the FIFO
637 // Drop a character.
638 (void)rDOCKCHANNELS_DEV_RDATA1(DOCKCHANNEL_UART_CHANNEL);
cb323159 639 os_atomic_inc(&prev_dockchannel_spaces, relaxed);
5ba3f43e
A
640 return 1;
641 }
642 return 0;
643}
644
0a7de745
A
645static int
646dockchannel_uart_tr0(void)
5ba3f43e
A
647{
648 if (use_sw_drain) {
649 uint32_t spaces = rDOCKCHANNELS_DEV_WSTAT(DOCKCHANNEL_UART_CHANNEL) & 0x1ff;
650 if (spaces > prev_dockchannel_spaces) {
651 // More spaces showed up. That can only mean someone read the FIFO.
652 // Note that if the DockFIFO is empty we cannot tell if someone is listening,
653 // we can only give them the benefit of the doubt.
654 prev_dockchannel_drained_time = mach_absolute_time();
655 }
656 prev_dockchannel_spaces = spaces;
657
658 return spaces || dockchannel_drain_on_stall();
659 } else {
660 // Returns spaces in dockchannel fifo
0a7de745 661 return rDOCKCHANNELS_DEV_WSTAT(DOCKCHANNEL_UART_CHANNEL) & 0x1ff;
5ba3f43e
A
662 }
663}
664
0a7de745
A
665static void
666dockchannel_uart_td0(int c)
5ba3f43e
A
667{
668 rDOCKCHANNELS_DEV_WDATA1(DOCKCHANNEL_UART_CHANNEL) = (unsigned)(c & 0xff);
669 if (use_sw_drain) {
cb323159 670 os_atomic_dec(&prev_dockchannel_spaces, relaxed); // After writing a byte we have one fewer space than previously expected.
5ba3f43e
A
671 }
672}
673
0a7de745
A
674static int
675dockchannel_uart_rr0(void)
5ba3f43e
A
676{
677 return rDOCKCHANNELS_DEV_RDATA0(DOCKCHANNEL_UART_CHANNEL) & 0x7f;
678}
679
0a7de745
A
680static int
681dockchannel_uart_rd0(void)
5ba3f43e 682{
0a7de745 683 return (int)((rDOCKCHANNELS_DEV_RDATA1(DOCKCHANNEL_UART_CHANNEL) >> 8) & 0xff);
5ba3f43e
A
684}
685
cb323159
A
686static void
687dockchannel_uart_clear_intr(void)
688{
689 rDOCKCHANNELS_AGENT_AP_INTR_CTRL &= ~(0x3);
690 rDOCKCHANNELS_AGENT_AP_INTR_STATUS |= 0x3;
691 rDOCKCHANNELS_AGENT_AP_ERR_INTR_CTRL &= ~(0x3);
692 rDOCKCHANNELS_AGENT_AP_ERR_INTR_STATUS |= 0x3;
693}
694
0a7de745
A
695static void
696dockchannel_uart_init(void)
5ba3f43e
A
697{
698 if (use_sw_drain) {
699 nanoseconds_to_absolutetime(DOCKCHANNEL_WR_MAX_STALL_US * NSEC_PER_USEC, &dockchannel_stall_grace);
700 }
701
702 // Clear all interrupt enable and status bits
cb323159 703 dockchannel_uart_clear_intr();
5ba3f43e
A
704
705 // Setup DRAIN timer
706 rDOCKCHANNELS_DEV_DRAIN_CFG(DOCKCHANNEL_UART_CHANNEL) = max_dockchannel_drain_period;
707
a39ff7e2 708 // Drain timer doesn't get loaded with value from drain period register if fifo
0a7de745 709 // is already full. Drop a character from the fifo.
5ba3f43e
A
710 rDOCKCHANNELS_DOCK_RDATA1(DOCKCHANNEL_UART_CHANNEL);
711}
712
cb323159 713SECURITY_READ_ONLY_LATE(static struct pe_serial_functions) dockchannel_uart_serial_functions =
5ba3f43e
A
714{
715 .uart_init = dockchannel_uart_init,
716 .uart_set_baud_rate = NULL,
717 .tr0 = dockchannel_uart_tr0,
718 .td0 = dockchannel_uart_td0,
719 .rr0 = dockchannel_uart_rr0,
720 .rd0 = dockchannel_uart_rd0
721};
722
723#endif /* DOCKCHANNEL_UART */
724
d9a64523 725/****************************************************************************/
0a7de745 726#ifdef PI3_UART
cb323159
A
727vm_offset_t pi3_gpio_base_vaddr = 0;
728vm_offset_t pi3_aux_base_vaddr = 0;
0a7de745
A
729static int
730pi3_uart_tr0(void)
d9a64523 731{
0a7de745 732 return (int) BCM2837_GET32(BCM2837_AUX_MU_LSR_REG_V) & 0x20;
d9a64523
A
733}
734
0a7de745
A
735static void
736pi3_uart_td0(int c)
d9a64523 737{
0a7de745 738 BCM2837_PUT32(BCM2837_AUX_MU_IO_REG_V, (uint32_t) c);
d9a64523
A
739}
740
0a7de745
A
741static int
742pi3_uart_rr0(void)
743{
744 return (int) BCM2837_GET32(BCM2837_AUX_MU_LSR_REG_V) & 0x01;
d9a64523
A
745}
746
0a7de745
A
747static int
748pi3_uart_rd0(void)
d9a64523 749{
0a7de745 750 return (int) BCM2837_GET32(BCM2837_AUX_MU_IO_REG_V) & 0xff;
d9a64523
A
751}
752
0a7de745
A
753static void
754pi3_uart_init(void)
d9a64523
A
755{
756 // Scratch variable
757 uint32_t i;
758
759 // Reset mini uart registers
760 BCM2837_PUT32(BCM2837_AUX_ENABLES_V, 1);
761 BCM2837_PUT32(BCM2837_AUX_MU_CNTL_REG_V, 0);
762 BCM2837_PUT32(BCM2837_AUX_MU_LCR_REG_V, 3);
763 BCM2837_PUT32(BCM2837_AUX_MU_MCR_REG_V, 0);
764 BCM2837_PUT32(BCM2837_AUX_MU_IER_REG_V, 0);
765 BCM2837_PUT32(BCM2837_AUX_MU_IIR_REG_V, 0xC6);
766 BCM2837_PUT32(BCM2837_AUX_MU_BAUD_REG_V, 270);
767
0a7de745 768 i = BCM2837_FSEL_REG(14);
d9a64523
A
769 // Configure GPIOs 14 & 15 for alternate function 5
770 i &= ~(BCM2837_FSEL_MASK(14));
771 i |= (BCM2837_FSEL_ALT5 << BCM2837_FSEL_OFFS(14));
772 i &= ~(BCM2837_FSEL_MASK(15));
773 i |= (BCM2837_FSEL_ALT5 << BCM2837_FSEL_OFFS(15));
774
775 BCM2837_PUT32(BCM2837_FSEL_REG(14), i);
776
777 BCM2837_PUT32(BCM2837_GPPUD_V, 0);
778
779 // Barrier before AP spinning for 150 cycles
780 __builtin_arm_isb(ISB_SY);
781
0a7de745
A
782 for (i = 0; i < 150; i++) {
783 asm volatile ("add x0, x0, xzr");
d9a64523 784 }
5ba3f43e 785
d9a64523
A
786 __builtin_arm_isb(ISB_SY);
787
0a7de745 788 BCM2837_PUT32(BCM2837_GPPUDCLK0_V, (1 << 14) | (1 << 15));
d9a64523
A
789
790 __builtin_arm_isb(ISB_SY);
791
0a7de745
A
792 for (i = 0; i < 150; i++) {
793 asm volatile ("add x0, x0, xzr");
d9a64523
A
794 }
795
796 __builtin_arm_isb(ISB_SY);
797
798 BCM2837_PUT32(BCM2837_GPPUDCLK0_V, 0);
799
800 BCM2837_PUT32(BCM2837_AUX_MU_CNTL_REG_V, 3);
801}
802
cb323159 803SECURITY_READ_ONLY_LATE(static struct pe_serial_functions) pi3_uart_serial_functions =
d9a64523
A
804{
805 .uart_init = pi3_uart_init,
806 .uart_set_baud_rate = NULL,
807 .tr0 = pi3_uart_tr0,
808 .td0 = pi3_uart_td0,
809 .rr0 = pi3_uart_rr0,
810 .rd0 = pi3_uart_rd0
811};
812
813#endif /* PI3_UART */
814/*****************************************************************************/
cb323159
A
815
816static void
817register_serial_functions(struct pe_serial_functions *fns)
818{
819 fns->next = gPESF;
820 gPESF = fns;
821}
822
5ba3f43e
A
823int
824serial_init(void)
825{
0a7de745 826 DTEntry entryP = NULL;
cb323159 827 uint32_t prop_size;
0a7de745
A
828 vm_offset_t soc_base;
829 uintptr_t *reg_prop;
cb323159
A
830 uint32_t *prop_value __unused = NULL;
831 char *serial_compat __unused = 0;
832 uint32_t dccmode;
5ba3f43e 833
cb323159
A
834 struct pe_serial_functions *fns = gPESF;
835
836 if (uart_initted) {
837 while (fns != NULL) {
838 fns->uart_init();
839 fns = fns->next;
840 }
5ba3f43e
A
841 kprintf("reinit serial\n");
842 return 1;
843 }
d9a64523 844
5ba3f43e 845 dccmode = 0;
0a7de745 846 if (PE_parse_boot_argn("dcc", &dccmode, sizeof(dccmode))) {
cb323159 847 register_serial_functions(&dcc_serial_functions);
5ba3f43e
A
848 }
849#ifdef SHMCON
cb323159 850 uint32_t jconmode = 0;
5ba3f43e 851 if (PE_parse_boot_argn("jcon", &jconmode, sizeof jconmode)) {
cb323159 852 register_serial_functions(&shmcon_serial_functions);
5ba3f43e
A
853 }
854#endif /* SHMCON */
855
856 soc_base = pe_arm_get_soc_base_phys();
857
0a7de745 858 if (soc_base == 0) {
5ba3f43e 859 return 0;
0a7de745 860 }
5ba3f43e 861
cb323159
A
862#ifdef PI3_UART
863 if (DTFindEntry("name", "gpio", &entryP) == kSuccess) {
864 DTGetProperty(entryP, "reg", (void **)&reg_prop, &prop_size);
865 pi3_gpio_base_vaddr = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
866 }
867 if (DTFindEntry("name", "aux", &entryP) == kSuccess) {
868 DTGetProperty(entryP, "reg", (void **)&reg_prop, &prop_size);
869 pi3_aux_base_vaddr = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
870 }
871 if ((pi3_gpio_base_vaddr != 0) && (pi3_aux_base_vaddr != 0)) {
872 register_serial_functions(&pi3_uart_serial_functions);
873 }
874#endif /* PI3_UART */
875
5ba3f43e 876#ifdef DOCKFIFO_UART
cb323159 877 uint32_t no_dockfifo_uart = 0;
5ba3f43e
A
878 PE_parse_boot_argn("no-dockfifo-uart", &no_dockfifo_uart, sizeof(no_dockfifo_uart));
879 if (no_dockfifo_uart == 0) {
880 if (DTFindEntry("name", "dockfifo-uart", &entryP) == kSuccess) {
881 DTGetProperty(entryP, "reg", (void **)&reg_prop, &prop_size);
cb323159
A
882 dockfifo_uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
883 register_serial_functions(&dockfifo_uart_serial_functions);
5ba3f43e 884 }
5ba3f43e
A
885 }
886#endif /* DOCKFIFO_UART */
887
888#ifdef DOCKCHANNEL_UART
cb323159
A
889 uint32_t no_dockchannel_uart = 0;
890 if (DTFindEntry("name", "dockchannel-uart", &entryP) == kSuccess) {
891 DTGetProperty(entryP, "reg", (void **)&reg_prop, &prop_size);
892 // Should be two reg entries
893 if (prop_size / sizeof(uintptr_t) != 4) {
894 panic("Malformed dockchannel-uart property");
895 }
896 dockchannel_uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
897 dock_agent_base = ml_io_map(soc_base + *(reg_prop + 2), *(reg_prop + 3));
898 PE_parse_boot_argn("no-dockfifo-uart", &no_dockchannel_uart, sizeof(no_dockchannel_uart));
899 // Keep the old name for boot-arg
900 if (no_dockchannel_uart == 0) {
901 register_serial_functions(&dockchannel_uart_serial_functions);
5ba3f43e
A
902 DTGetProperty(entryP, "max-aop-clk", (void **)&prop_value, &prop_size);
903 max_dockchannel_drain_period = (uint32_t)((prop_value)? (*prop_value * 0.03) : DOCKCHANNEL_DRAIN_PERIOD);
904 DTGetProperty(entryP, "enable-sw-drain", (void **)&prop_value, &prop_size);
905 use_sw_drain = (prop_value)? *prop_value : 0;
cb323159
A
906 } else {
907 dockchannel_uart_clear_intr();
5ba3f43e
A
908 }
909 // If no dockchannel-uart is found in the device tree, fall back
910 // to looking for the traditional UART serial console.
911 }
cb323159 912
5ba3f43e
A
913#endif /* DOCKCHANNEL_UART */
914
915 /*
916 * The boot serial port should have a property named "boot-console".
917 * If we don't find it there, look for "uart0" and "uart1".
918 */
919
920 if (DTFindEntry("boot-console", NULL, &entryP) == kSuccess) {
921 DTGetProperty(entryP, "reg", (void **)&reg_prop, &prop_size);
922 uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
0a7de745 923 if (serial_compat == 0) {
5ba3f43e 924 DTGetProperty(entryP, "compatible", (void **)&serial_compat, &prop_size);
0a7de745 925 }
5ba3f43e
A
926 } else if (DTFindEntry("name", "uart0", &entryP) == kSuccess) {
927 DTGetProperty(entryP, "reg", (void **)&reg_prop, &prop_size);
928 uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
0a7de745 929 if (serial_compat == 0) {
5ba3f43e 930 DTGetProperty(entryP, "compatible", (void **)&serial_compat, &prop_size);
0a7de745 931 }
5ba3f43e
A
932 } else if (DTFindEntry("name", "uart1", &entryP) == kSuccess) {
933 DTGetProperty(entryP, "reg", (void **)&reg_prop, &prop_size);
934 uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
0a7de745 935 if (serial_compat == 0) {
5ba3f43e 936 DTGetProperty(entryP, "compatible", (void **)&serial_compat, &prop_size);
0a7de745 937 }
5ba3f43e 938 }
0a7de745 939#ifdef S3CUART
5ba3f43e
A
940 if (NULL != entryP) {
941 DTGetProperty(entryP, "pclk", (void **)&prop_value, &prop_size);
0a7de745
A
942 if (prop_value) {
943 dt_pclk = *prop_value;
944 }
5ba3f43e
A
945
946 prop_value = NULL;
947 DTGetProperty(entryP, "sampling", (void **)&prop_value, &prop_size);
0a7de745
A
948 if (prop_value) {
949 dt_sampling = *prop_value;
950 }
5ba3f43e
A
951
952 prop_value = NULL;
953 DTGetProperty(entryP, "ubrdiv", (void **)&prop_value, &prop_size);
0a7de745
A
954 if (prop_value) {
955 dt_ubrdiv = *prop_value;
956 }
5ba3f43e 957 }
0a7de745 958 if (!strcmp(serial_compat, "uart,16550")) {
cb323159 959 register_serial_functions(&ln2410_serial_functions);
0a7de745 960 } else if (!strcmp(serial_compat, "uart-16550")) {
cb323159 961 register_serial_functions(&ln2410_serial_functions);
0a7de745 962 } else if (!strcmp(serial_compat, "uart,s5i3000")) {
cb323159 963 register_serial_functions(&ln2410_serial_functions);
0a7de745 964 } else if (!strcmp(serial_compat, "uart-1,samsung")) {
cb323159 965 register_serial_functions(&ln2410_serial_functions);
0a7de745 966 }
cb323159
A
967#endif /* S3CUART */
968
969 if (gPESF == NULL) {
5ba3f43e 970 return 0;
0a7de745 971 }
5ba3f43e 972
cb323159
A
973 fns = gPESF;
974 while (fns != NULL) {
975 fns->uart_init();
976 fns = fns->next;
977 }
5ba3f43e
A
978
979 uart_initted = 1;
980
981 return 1;
982}
983
984void
985uart_putc(char c)
986{
cb323159
A
987 struct pe_serial_functions *fns = gPESF;
988 while (fns != NULL) {
989 while (!fns->tr0()) {
0a7de745
A
990 ; /* Wait until THR is empty. */
991 }
cb323159
A
992 fns->td0(c);
993 fns = fns->next;
5ba3f43e
A
994 }
995}
996
997int
998uart_getc(void)
0a7de745 999{ /* returns -1 if no data available */
cb323159
A
1000 struct pe_serial_functions *fns = gPESF;
1001 while (fns != NULL) {
1002 if (fns->rr0()) {
1003 return fns->rd0();
0a7de745 1004 }
cb323159 1005 fns = fns->next;
5ba3f43e
A
1006 }
1007 return -1;
1008}