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