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