]> git.saurik.com Git - apple/xnu.git/blob - pexpert/arm/pe_serial.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / pexpert / arm / pe_serial.c
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>
13 #include <libkern/section_keywords.h>
14 #include <mach/mach_time.h>
15 #include <machine/atomic.h>
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
32 struct 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);
39 struct pe_serial_functions *next;
40 };
41
42 SECURITY_READ_ONLY_LATE(static struct pe_serial_functions*) gPESF = NULL;
43
44 static int uart_initted = 0; /* 1 if init'ed */
45 static vm_offset_t uart_base = 0;
46
47 /*****************************************************************************/
48
49 #ifdef S3CUART
50
51 static int32_t dt_pclk = -1;
52 static int32_t dt_sampling = -1;
53 static int32_t dt_ubrdiv = -1;
54
55 static void ln2410_uart_set_baud_rate(__unused int unit, uint32_t baud_rate);
56
57 static void
58 ln2410_uart_init(void)
59 {
60 uint32_t ucon0 = 0x405; /* NCLK, No interrupts, No DMA - just polled */
61
62 rULCON0 = 0x03; /* 81N, not IR */
63
64 // Override with pclk dt entry
65 if (dt_pclk != -1) {
66 ucon0 = ucon0 & ~0x400;
67 }
68
69 rUCON0 = ucon0;
70 rUMCON0 = 0x00; /* Clear Flow Control */
71
72 ln2410_uart_set_baud_rate(0, 115200);
73
74 rUFCON0 = 0x03; /* Clear & Enable FIFOs */
75 rUMCON0 = 0x01; /* Assert RTS on UART0 */
76 }
77
78 static void
79 ln2410_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;
84
85 if (baud_rate < 300) {
86 baud_rate = 9600;
87 }
88
89 if (rUCON0 & 0x400) {
90 // NCLK
91 uart_clock = (uint32_t)gPEClockFrequencyInfo.fix_frequency_hz;
92 } else {
93 // PCLK
94 uart_clock = (uint32_t)gPEClockFrequencyInfo.prf_frequency_hz;
95 }
96
97 if (dt_sampling != -1) {
98 // Use the sampling rate specified in the Device Tree
99 sample_rate = dt_sampling & 0xf;
100 }
101
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);
108
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
113 if ((baud_rate - baud_low) > (actual_baud - baud_rate)) {
114 div--;
115 }
116 }
117
118 // Sample Rate [19:16], UBRDIV [15:0]
119 rUBRDIV0 = ((16 - sample_rate) << 16) | div;
120 }
121
122 static int
123 ln2410_tr0(void)
124 {
125 return rUTRSTAT0 & 0x04;
126 }
127 static void
128 ln2410_td0(int c)
129 {
130 rUTXH0 = (unsigned)(c & 0xff);
131 }
132 static int
133 ln2410_rr0(void)
134 {
135 return rUTRSTAT0 & 0x01;
136 }
137 static int
138 ln2410_rd0(void)
139 {
140 return (int)rURXH0;
141 }
142
143 SECURITY_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
151 };
152
153 #endif /* S3CUART */
154
155 /*****************************************************************************/
156
157 static void
158 dcc_uart_init(void)
159 {
160 }
161
162 static unsigned int
163 read_dtr(void)
164 {
165 #ifdef __arm__
166 unsigned int c;
167 __asm__ volatile (
168 "mrc p14, 0, %0, c0, c5\n"
169 : "=r"(c));
170 return c;
171 #else
172 /* ARM64_TODO */
173 panic_unimplemented();
174 return 0;
175 #endif
176 }
177 static void
178 write_dtr(unsigned int c)
179 {
180 #ifdef __arm__
181 __asm__ volatile (
182 "mcr p14, 0, %0, c0, c5\n"
183 :
184 :"r"(c));
185 #else
186 /* ARM64_TODO */
187 (void)c;
188 panic_unimplemented();
189 #endif
190 }
191
192 static int
193 dcc_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
204 static void
205 dcc_td0(int c)
206 {
207 write_dtr(c);
208 }
209
210 static int
211 dcc_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
222 static int
223 dcc_rd0(void)
224 {
225 return read_dtr();
226 }
227
228 SECURITY_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
236 };
237
238 /*****************************************************************************/
239
240 #ifdef SHMCON
241
242 #define CPU_CACHELINE_SIZE (1 << MMU_CLINE)
243
244 #ifndef SHMCON_NAME
245 #define SHMCON_NAME "AP-xnu"
246 #endif
247
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)
254
255 #define FLAG_CACHELINE_32 1
256 #define FLAG_CACHELINE_64 2
257
258 /* Defines to clarify the master/slave fields' use as circular buffer pointers */
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]
263
264 /* TODO: get from device tree/target */
265 #define NUM_CHILDREN 5
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
270 #define MAX(a, b) ((a) > (b) ? (a) : (b))
271 #define MIN(a, b) ((a) < (b) ? (a) : (b))
272
273 #define shmcon_barrier() do {__asm__ volatile("dmb ish" : : : "memory");} while(0)
274
275 struct shm_buffer_info {
276 uint64_t base;
277 uint32_t unused;
278 uint32_t magic;
279 };
280
281 struct shmcon_header {
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];
289
290 /* Slave-modified data - invalidate before read */
291 uint32_t sidx[2] __attribute__((aligned(FULL_ALIGNMENT))); /* In head, out tail */
292
293 /* Master-modified data - clean after write */
294 uint32_t midx[2] __attribute__((aligned(FULL_ALIGNMENT))); /* In tail, out head */
295
296 uint64_t child[0]; /* Physical address of child header pointers */
297 };
298
299 static volatile struct shmcon_header *shmcon = NULL;
300 static volatile uint8_t *shmbuf[2];
301 #ifdef SHMCON_THROTTLED
302 static uint64_t grace = 0;
303 static uint64_t full_timeout = 0;
304 #endif
305
306 static void
307 shmcon_set_baud_rate(__unused int unit, __unused uint32_t baud_rate)
308 {
309 return;
310 }
311
312 static int
313 shmcon_tr0(void)
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 }
331 if (full_timeout > mach_absolute_time()) {
332 return 0;
333 }
334
335 /* Timeout - slave not really there or not keeping up */
336 tail += (len / 4);
337 if (tail >= len) {
338 tail -= len;
339 }
340 shmcon_barrier();
341 shmcon->tail_out = tail;
342 full_timeout = 0;
343 #endif
344 return 1;
345 }
346
347 static void
348 shmcon_td0(int c)
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
359 static int
360 shmcon_rr0(void)
361 {
362 if (shmcon->tail_in == shmcon->head_in) {
363 return 0;
364 }
365 return 1;
366 }
367
368 static int
369 shmcon_rd0(void)
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
382 static void
383 shmcon_init(void)
384 {
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;
391
392 if (kSuccess != DTLookupEntry(0, "pram", &entry)) {
393 return;
394 }
395
396 if (kSuccess != DTGetProperty(entry, "reg", (void **)&reg_prop, &size)) {
397 return;
398 }
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) ||
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)) {
431 goto validation_failure;
432 }
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]) {
438 if ((uintptr_t)(shmbuf[0] + shmcon->buf_len[0]) > (uintptr_t)shmbuf[1]) {
439 goto validation_failure;
440 }
441 } else {
442 if ((uintptr_t)(shmbuf[1] + shmcon->buf_len[1]) > (uintptr_t)shmbuf[0]) {
443 goto validation_failure;
444 }
445 }
446 shmcon->tail_in = shmcon->head_in; /* Clear input buffer */
447 shmcon_barrier();
448 } else {
449 validation_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
467 for (i = 0; i < NUM_CHILDREN; i++) {
468 shmcon->child[0] = 0;
469 }
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
485 SECURITY_READ_ONLY_LATE(static struct pe_serial_functions) shmcon_serial_functions =
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
495 int
496 pe_shmcon_set_child(uint64_t paddr, uint32_t entry)
497 {
498 if (shmcon == NULL) {
499 return -1;
500 }
501
502 if (shmcon->children >= entry) {
503 return -1;
504 }
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
518 #define DOCKFIFO_WR_MAX_STALL_US (30*1000)
519
520 static uint64_t prev_dockfifo_drained_time; // Last time we've seen the DockFIFO drained by an external agent
521 static uint64_t prev_dockfifo_spaces; // Previous w_stat level of the DockFIFO.
522 static uint32_t dockfifo_capacity;
523 static uint64_t dockfifo_stall_grace;
524
525 static vm_offset_t dockfifo_uart_base = 0;
526
527 //=======================
528 // Local funtions
529 //=======================
530
531 static int
532 dockfifo_drain_on_stall()
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);
542 os_atomic_inc(&prev_dockfifo_spaces, relaxed);
543 return 1;
544 }
545 return 0;
546 }
547
548
549 static int
550 dockfifo_uart_tr0(void)
551 {
552 uint32_t spaces = rDOCKFIFO_W_STAT(DOCKFIFO_UART_WRITE) & 0xffff;
553 if (spaces >= dockfifo_capacity || spaces > prev_dockfifo_spaces) {
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.
557
558 prev_dockfifo_drained_time = mach_absolute_time();
559 }
560 prev_dockfifo_spaces = spaces;
561
562 return spaces || dockfifo_drain_on_stall();
563 }
564
565 static void
566 dockfifo_uart_td0(int c)
567 {
568 rDOCKFIFO_W_DATA(DOCKFIFO_UART_WRITE, 1) = (unsigned)(c & 0xff);
569 os_atomic_dec(&prev_dockfifo_spaces, relaxed); // After writing a byte we have one fewer space than previously expected.
570 }
571
572 static int
573 dockfifo_uart_rr0(void)
574 {
575 return rDOCKFIFO_R_DATA(DOCKFIFO_UART_READ, 0) & 0x7f;
576 }
577
578 static int
579 dockfifo_uart_rd0(void)
580 {
581 return (int)((rDOCKFIFO_R_DATA(DOCKFIFO_UART_READ, 1) >> 8) & 0xff);
582 }
583
584 static void
585 dockfifo_uart_init(void)
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
593 while (rDOCKFIFO_R_DATA(DOCKFIFO_UART_WRITE, 3) & 0x7F) {
594 ;
595 }
596 dockfifo_capacity = rDOCKFIFO_W_STAT(DOCKFIFO_UART_WRITE) & 0xffff;
597 }
598
599 SECURITY_READ_ONLY_LATE(static struct pe_serial_functions) dockfifo_uart_serial_functions =
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
614 #define DOCKCHANNEL_WR_MAX_STALL_US (30*1000)
615
616 static vm_offset_t dock_agent_base;
617 static uint32_t max_dockchannel_drain_period;
618 static bool use_sw_drain;
619 static uint64_t prev_dockchannel_drained_time; // Last time we've seen the DockChannel drained by an external agent
620 static uint64_t prev_dockchannel_spaces; // Previous w_stat level of the DockChannel.
621 static uint64_t dockchannel_stall_grace;
622 static vm_offset_t dockchannel_uart_base = 0;
623
624 //=======================
625 // Local funtions
626 //=======================
627
628 static int
629 dockchannel_drain_on_stall()
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);
639 os_atomic_inc(&prev_dockchannel_spaces, relaxed);
640 return 1;
641 }
642 return 0;
643 }
644
645 static int
646 dockchannel_uart_tr0(void)
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
661 return rDOCKCHANNELS_DEV_WSTAT(DOCKCHANNEL_UART_CHANNEL) & 0x1ff;
662 }
663 }
664
665 static void
666 dockchannel_uart_td0(int c)
667 {
668 rDOCKCHANNELS_DEV_WDATA1(DOCKCHANNEL_UART_CHANNEL) = (unsigned)(c & 0xff);
669 if (use_sw_drain) {
670 os_atomic_dec(&prev_dockchannel_spaces, relaxed); // After writing a byte we have one fewer space than previously expected.
671 }
672 }
673
674 static int
675 dockchannel_uart_rr0(void)
676 {
677 return rDOCKCHANNELS_DEV_RDATA0(DOCKCHANNEL_UART_CHANNEL) & 0x7f;
678 }
679
680 static int
681 dockchannel_uart_rd0(void)
682 {
683 return (int)((rDOCKCHANNELS_DEV_RDATA1(DOCKCHANNEL_UART_CHANNEL) >> 8) & 0xff);
684 }
685
686 static void
687 dockchannel_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
695 static void
696 dockchannel_uart_init(void)
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
703 dockchannel_uart_clear_intr();
704
705 // Setup DRAIN timer
706 rDOCKCHANNELS_DEV_DRAIN_CFG(DOCKCHANNEL_UART_CHANNEL) = max_dockchannel_drain_period;
707
708 // Drain timer doesn't get loaded with value from drain period register if fifo
709 // is already full. Drop a character from the fifo.
710 rDOCKCHANNELS_DOCK_RDATA1(DOCKCHANNEL_UART_CHANNEL);
711 }
712
713 SECURITY_READ_ONLY_LATE(static struct pe_serial_functions) dockchannel_uart_serial_functions =
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
725 /****************************************************************************/
726 #ifdef PI3_UART
727 vm_offset_t pi3_gpio_base_vaddr = 0;
728 vm_offset_t pi3_aux_base_vaddr = 0;
729 static int
730 pi3_uart_tr0(void)
731 {
732 return (int) BCM2837_GET32(BCM2837_AUX_MU_LSR_REG_V) & 0x20;
733 }
734
735 static void
736 pi3_uart_td0(int c)
737 {
738 BCM2837_PUT32(BCM2837_AUX_MU_IO_REG_V, (uint32_t) c);
739 }
740
741 static int
742 pi3_uart_rr0(void)
743 {
744 return (int) BCM2837_GET32(BCM2837_AUX_MU_LSR_REG_V) & 0x01;
745 }
746
747 static int
748 pi3_uart_rd0(void)
749 {
750 return (int) BCM2837_GET32(BCM2837_AUX_MU_IO_REG_V) & 0xff;
751 }
752
753 static void
754 pi3_uart_init(void)
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
768 i = BCM2837_FSEL_REG(14);
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
782 for (i = 0; i < 150; i++) {
783 asm volatile ("add x0, x0, xzr");
784 }
785
786 __builtin_arm_isb(ISB_SY);
787
788 BCM2837_PUT32(BCM2837_GPPUDCLK0_V, (1 << 14) | (1 << 15));
789
790 __builtin_arm_isb(ISB_SY);
791
792 for (i = 0; i < 150; i++) {
793 asm volatile ("add x0, x0, xzr");
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
803 SECURITY_READ_ONLY_LATE(static struct pe_serial_functions) pi3_uart_serial_functions =
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 /*****************************************************************************/
815
816 static void
817 register_serial_functions(struct pe_serial_functions *fns)
818 {
819 fns->next = gPESF;
820 gPESF = fns;
821 }
822
823 int
824 serial_init(void)
825 {
826 DTEntry entryP = NULL;
827 uint32_t prop_size;
828 vm_offset_t soc_base;
829 uintptr_t *reg_prop;
830 uint32_t *prop_value __unused = NULL;
831 char *serial_compat __unused = 0;
832 uint32_t dccmode;
833
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 }
841 kprintf("reinit serial\n");
842 return 1;
843 }
844
845 dccmode = 0;
846 if (PE_parse_boot_argn("dcc", &dccmode, sizeof(dccmode))) {
847 register_serial_functions(&dcc_serial_functions);
848 }
849 #ifdef SHMCON
850 uint32_t jconmode = 0;
851 if (PE_parse_boot_argn("jcon", &jconmode, sizeof jconmode)) {
852 register_serial_functions(&shmcon_serial_functions);
853 }
854 #endif /* SHMCON */
855
856 soc_base = pe_arm_get_soc_base_phys();
857
858 if (soc_base == 0) {
859 return 0;
860 }
861
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
876 #ifdef DOCKFIFO_UART
877 uint32_t no_dockfifo_uart = 0;
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);
882 dockfifo_uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
883 register_serial_functions(&dockfifo_uart_serial_functions);
884 }
885 }
886 #endif /* DOCKFIFO_UART */
887
888 #ifdef DOCKCHANNEL_UART
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);
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;
906 } else {
907 dockchannel_uart_clear_intr();
908 }
909 // If no dockchannel-uart is found in the device tree, fall back
910 // to looking for the traditional UART serial console.
911 }
912
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));
923 if (serial_compat == 0) {
924 DTGetProperty(entryP, "compatible", (void **)&serial_compat, &prop_size);
925 }
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));
929 if (serial_compat == 0) {
930 DTGetProperty(entryP, "compatible", (void **)&serial_compat, &prop_size);
931 }
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));
935 if (serial_compat == 0) {
936 DTGetProperty(entryP, "compatible", (void **)&serial_compat, &prop_size);
937 }
938 }
939 #ifdef S3CUART
940 if (NULL != entryP) {
941 DTGetProperty(entryP, "pclk", (void **)&prop_value, &prop_size);
942 if (prop_value) {
943 dt_pclk = *prop_value;
944 }
945
946 prop_value = NULL;
947 DTGetProperty(entryP, "sampling", (void **)&prop_value, &prop_size);
948 if (prop_value) {
949 dt_sampling = *prop_value;
950 }
951
952 prop_value = NULL;
953 DTGetProperty(entryP, "ubrdiv", (void **)&prop_value, &prop_size);
954 if (prop_value) {
955 dt_ubrdiv = *prop_value;
956 }
957 }
958 if (!strcmp(serial_compat, "uart,16550")) {
959 register_serial_functions(&ln2410_serial_functions);
960 } else if (!strcmp(serial_compat, "uart-16550")) {
961 register_serial_functions(&ln2410_serial_functions);
962 } else if (!strcmp(serial_compat, "uart,s5i3000")) {
963 register_serial_functions(&ln2410_serial_functions);
964 } else if (!strcmp(serial_compat, "uart-1,samsung")) {
965 register_serial_functions(&ln2410_serial_functions);
966 }
967 #endif /* S3CUART */
968
969 if (gPESF == NULL) {
970 return 0;
971 }
972
973 fns = gPESF;
974 while (fns != NULL) {
975 fns->uart_init();
976 fns = fns->next;
977 }
978
979 uart_initted = 1;
980
981 return 1;
982 }
983
984 void
985 uart_putc(char c)
986 {
987 struct pe_serial_functions *fns = gPESF;
988 while (fns != NULL) {
989 while (!fns->tr0()) {
990 ; /* Wait until THR is empty. */
991 }
992 fns->td0(c);
993 fns = fns->next;
994 }
995 }
996
997 int
998 uart_getc(void)
999 { /* returns -1 if no data available */
1000 struct pe_serial_functions *fns = gPESF;
1001 while (fns != NULL) {
1002 if (fns->rr0()) {
1003 return fns->rd0();
1004 }
1005 fns = fns->next;
1006 }
1007 return -1;
1008 }