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