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
31struct 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
40static struct pe_serial_functions *gPESF;
41
42static int uart_initted = 0; /* 1 if init'ed */
43
44static vm_offset_t uart_base;
45
46/*****************************************************************************/
47
48#ifdef S3CUART
49
50static int32_t dt_pclk = -1;
51static int32_t dt_sampling = -1;
52static int32_t dt_ubrdiv = -1;
53
54static void
55ln2410_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
74static void
75ln2410_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
115static int
116ln2410_tr0(void)
117{
118 return rUTRSTAT0 & 0x04;
119}
120static void
121ln2410_td0(int c)
122{
123 rUTXH0 = (unsigned)(c & 0xff);
124}
125static int
126ln2410_rr0(void)
127{
128 return rUTRSTAT0 & 0x01;
129}
130static int
131ln2410_rd0(void)
132{
133 return (int)rURXH0;
134}
135
136static struct pe_serial_functions ln2410_serial_functions = {
137 ln2410_uart_init, ln2410_uart_set_baud_rate,
138ln2410_tr0, ln2410_td0, ln2410_rr0, ln2410_rd0};
139
140#endif /* S3CUART */
141
142/*****************************************************************************/
143
144
145static unsigned int
146read_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}
160static void
161write_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
175static int
176dcc_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
187static void
188dcc_td0(int c)
189{
190 write_dtr(c);
191}
192
193static int
194dcc_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
205static int
206dcc_rd0(void)
207{
208 return read_dtr();
209}
210
211static struct pe_serial_functions dcc_serial_functions = {
212 NULL, NULL,
213dcc_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
252struct shm_buffer_info {
253 uint64_t base;
254 uint32_t unused;
255 uint32_t magic;
256};
257
258struct 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
276static volatile struct shmcon_header *shmcon = NULL;
277static volatile uint8_t *shmbuf[2];
278#ifdef SHMCON_THROTTLED
279static uint64_t grace = 0;
280static uint64_t full_timeout = 0;
281#endif
282
283static void shmcon_set_baud_rate(__unused int unit, __unused uint32_t baud_rate)
284{
285 return;
286}
287
288static 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
320static 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
331static int shmcon_rr0(void)
332{
333 if (shmcon->tail_in == shmcon->head_in)
334 return 0;
335 return 1;
336}
337
338static 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
351static 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 {
412validation_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
447static 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
457int 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
479static uint64_t prev_dockfifo_drained_time; // Last time we've seen the DockFIFO drained by an external agent
480static uint64_t prev_dockfifo_spaces; // Previous w_stat level of the DockFIFO.
481static uint32_t dockfifo_capacity;
482static uint64_t dockfifo_stall_grace;
483
484
485//=======================
486// Local funtions
487//=======================
488
489static 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
506static 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
522static 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
529static int dockfifo_uart_rr0(void)
530{
531 return rDOCKFIFO_R_DATA(DOCKFIFO_UART_READ, 0) & 0x7f;
532}
533
534static int dockfifo_uart_rd0(void)
535{
536 return (int)((rDOCKFIFO_R_DATA(DOCKFIFO_UART_READ, 1) >> 8) & 0xff);
537}
538
539static 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
551static 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
568static vm_offset_t dock_agent_base;
569static uint32_t max_dockchannel_drain_period;
570static bool use_sw_drain;
571static uint64_t prev_dockchannel_drained_time; // Last time we've seen the DockChannel drained by an external agent
572static uint64_t prev_dockchannel_spaces; // Previous w_stat level of the DockChannel.
573static uint64_t dockchannel_stall_grace;
574
575//=======================
576// Local funtions
577//=======================
578
579static 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
595static 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
614static 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
622static int dockchannel_uart_rr0(void)
623{
624 return rDOCKCHANNELS_DEV_RDATA0(DOCKCHANNEL_UART_CHANNEL) & 0x7f;
625}
626
627static int dockchannel_uart_rd0(void)
628{
629 return (int)((rDOCKCHANNELS_DEV_RDATA1(DOCKCHANNEL_UART_CHANNEL)>> 8) & 0xff);
630}
631
632static 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.
649 rDOCKCHANNELS_DOCK_RDATA1(DOCKCHANNEL_UART_CHANNEL);
650}
651
652static struct pe_serial_functions dockchannel_uart_serial_functions =
653{
654 .uart_init = dockchannel_uart_init,
655 .uart_set_baud_rate = NULL,
656 .tr0 = dockchannel_uart_tr0,
657 .td0 = dockchannel_uart_td0,
658 .rr0 = dockchannel_uart_rr0,
659 .rd0 = dockchannel_uart_rd0
660};
661
662#endif /* DOCKCHANNEL_UART */
663
664/****************************************************************************/
665#ifdef PI3_UART
666vm_offset_t pi3_gpio_base_vaddr;
667vm_offset_t pi3_aux_base_vaddr;
668static int pi3_uart_tr0(void)
669{
670 return (int) BCM2837_GET32(BCM2837_AUX_MU_LSR_REG_V) & 0x20;
671}
672
673static void pi3_uart_td0(int c)
674{
675 BCM2837_PUT32(BCM2837_AUX_MU_IO_REG_V, (uint32_t) c);
676}
677
678static int pi3_uart_rr0(void)
679{
680 return (int) BCM2837_GET32(BCM2837_AUX_MU_LSR_REG_V) & 0x01;
681}
682
683static int pi3_uart_rd0(void)
684{
685 return (int) BCM2837_GET32(BCM2837_AUX_MU_IO_REG_V) & 0xff;
686}
687
688static void pi3_uart_init(void)
689{
690 // Scratch variable
691 uint32_t i;
692
693 // Reset mini uart registers
694 BCM2837_PUT32(BCM2837_AUX_ENABLES_V, 1);
695 BCM2837_PUT32(BCM2837_AUX_MU_CNTL_REG_V, 0);
696 BCM2837_PUT32(BCM2837_AUX_MU_LCR_REG_V, 3);
697 BCM2837_PUT32(BCM2837_AUX_MU_MCR_REG_V, 0);
698 BCM2837_PUT32(BCM2837_AUX_MU_IER_REG_V, 0);
699 BCM2837_PUT32(BCM2837_AUX_MU_IIR_REG_V, 0xC6);
700 BCM2837_PUT32(BCM2837_AUX_MU_BAUD_REG_V, 270);
701
702 i = BCM2837_FSEL_REG(14);
703 // Configure GPIOs 14 & 15 for alternate function 5
704 i &= ~(BCM2837_FSEL_MASK(14));
705 i |= (BCM2837_FSEL_ALT5 << BCM2837_FSEL_OFFS(14));
706 i &= ~(BCM2837_FSEL_MASK(15));
707 i |= (BCM2837_FSEL_ALT5 << BCM2837_FSEL_OFFS(15));
708
709 BCM2837_PUT32(BCM2837_FSEL_REG(14), i);
710
711 BCM2837_PUT32(BCM2837_GPPUD_V, 0);
712
713 // Barrier before AP spinning for 150 cycles
714 __builtin_arm_isb(ISB_SY);
715
716 for(i = 0; i < 150; i++) {
717 asm volatile("add x0, x0, xzr");
718 }
719
720 __builtin_arm_isb(ISB_SY);
721
722 BCM2837_PUT32(BCM2837_GPPUDCLK0_V,(1 << 14) | (1 << 15));
723
724 __builtin_arm_isb(ISB_SY);
725
726 for(i = 0; i < 150; i++) {
727 asm volatile("add x0, x0, xzr");
728 }
729
730 __builtin_arm_isb(ISB_SY);
731
732 BCM2837_PUT32(BCM2837_GPPUDCLK0_V, 0);
733
734 BCM2837_PUT32(BCM2837_AUX_MU_CNTL_REG_V, 3);
735}
736
737static struct pe_serial_functions pi3_uart_serial_functions =
738{
739 .uart_init = pi3_uart_init,
740 .uart_set_baud_rate = NULL,
741 .tr0 = pi3_uart_tr0,
742 .td0 = pi3_uart_td0,
743 .rr0 = pi3_uart_rr0,
744 .rd0 = pi3_uart_rd0
745};
746
747#endif /* PI3_UART */
748/*****************************************************************************/
749int
750serial_init(void)
751{
752 DTEntry entryP = NULL;
753 uint32_t prop_size, dccmode;
754 vm_offset_t soc_base;
755 uintptr_t *reg_prop;
756 uint32_t *prop_value = NULL;
757 char *serial_compat = 0;
758#ifdef SHMCON
759 uint32_t jconmode;
760#endif
761#ifdef DOCKFIFO_UART
762 uint32_t no_dockfifo_uart;
763#endif
764#ifdef DOCKCHANNEL_UART
765 uint32_t no_dockchannel_uart;
766#endif
767#ifdef PI3_UART
768 uint32_t is_pi3;
769#endif
770
771 if (uart_initted && gPESF) {
772 gPESF->uart_init();
773 kprintf("reinit serial\n");
774 return 1;
775 }
776
777 dccmode = 0;
778 if (PE_parse_boot_argn("dcc", &dccmode, sizeof (dccmode))) {
779 gPESF = &dcc_serial_functions;
780 uart_initted = 1;
781 return 1;
782 }
783#ifdef SHMCON
784 jconmode = 0;
785 if (PE_parse_boot_argn("jcon", &jconmode, sizeof jconmode)) {
786 gPESF = &shmcon_serial_functions;
787 gPESF->uart_init();
788 uart_initted = 1;
789 return 1;
790 }
791#endif /* SHMCON */
792
793#ifdef PI3_UART
794#pragma unused(prop_value)
795 is_pi3 = 0;
796 if (PE_parse_boot_argn("-pi3", &is_pi3, sizeof(is_pi3))) { // FIXME: remove the not operator after boot args are set up.
797 pi3_gpio_base_vaddr = ml_io_map((vm_offset_t)BCM2837_GPIO_BASE, BCM2837_GPIO_SIZE);
798 pi3_aux_base_vaddr = ml_io_map((vm_offset_t)BCM2837_AUX_BASE, BCM2837_AUX_SIZE);
799 gPESF = &pi3_uart_serial_functions;
800 gPESF->uart_init();
801 uart_initted = 1;
802 return 1;
803 }
804#endif /* PI3_UART */
805
806 soc_base = pe_arm_get_soc_base_phys();
807
808 if (soc_base == 0)
809 return 0;
810
811#ifdef DOCKFIFO_UART
812 no_dockfifo_uart = 0;
813 PE_parse_boot_argn("no-dockfifo-uart", &no_dockfifo_uart, sizeof(no_dockfifo_uart));
814 if (no_dockfifo_uart == 0) {
815 if (DTFindEntry("name", "dockfifo-uart", &entryP) == kSuccess) {
816 DTGetProperty(entryP, "reg", (void **)&reg_prop, &prop_size);
817 uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
818 }
819 else {
820 return 0;
821 }
822 gPESF = &dockfifo_uart_serial_functions;
823 gPESF->uart_init();
824 uart_initted = 1;
825 return 1;
826 }
827#endif /* DOCKFIFO_UART */
828
829#ifdef DOCKCHANNEL_UART
830 no_dockchannel_uart = 0;
831 // Keep the old name for boot-arg
832 PE_parse_boot_argn("no-dockfifo-uart", &no_dockchannel_uart, sizeof(no_dockchannel_uart));
833 if (no_dockchannel_uart == 0) {
834 if (DTFindEntry("name", "dockchannel-uart", &entryP) == kSuccess) {
835 DTGetProperty(entryP, "reg", (void **)&reg_prop, &prop_size);
836 // Should be two reg entries
837 if (prop_size/sizeof(uintptr_t) != 4)
838 panic("Malformed dockchannel-uart property");
839 uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
840 dock_agent_base = ml_io_map(soc_base + *(reg_prop + 2), *(reg_prop + 3));
841 gPESF = &dockchannel_uart_serial_functions;
842 DTGetProperty(entryP, "max-aop-clk", (void **)&prop_value, &prop_size);
843 max_dockchannel_drain_period = (uint32_t)((prop_value)? (*prop_value * 0.03) : DOCKCHANNEL_DRAIN_PERIOD);
844 DTGetProperty(entryP, "enable-sw-drain", (void **)&prop_value, &prop_size);
845 use_sw_drain = (prop_value)? *prop_value : 0;
846 gPESF->uart_init();
847 uart_initted = 1;
848 return 1;
849 }
850 // If no dockchannel-uart is found in the device tree, fall back
851 // to looking for the traditional UART serial console.
852 }
853#endif /* DOCKCHANNEL_UART */
854
855 /*
856 * The boot serial port should have a property named "boot-console".
857 * If we don't find it there, look for "uart0" and "uart1".
858 */
859
860 if (DTFindEntry("boot-console", NULL, &entryP) == kSuccess) {
861 DTGetProperty(entryP, "reg", (void **)&reg_prop, &prop_size);
862 uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
863 if (serial_compat == 0)
864 DTGetProperty(entryP, "compatible", (void **)&serial_compat, &prop_size);
865 } else if (DTFindEntry("name", "uart0", &entryP) == kSuccess) {
866 DTGetProperty(entryP, "reg", (void **)&reg_prop, &prop_size);
867 uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
868 if (serial_compat == 0)
869 DTGetProperty(entryP, "compatible", (void **)&serial_compat, &prop_size);
870 } else if (DTFindEntry("name", "uart1", &entryP) == kSuccess) {
871 DTGetProperty(entryP, "reg", (void **)&reg_prop, &prop_size);
872 uart_base = ml_io_map(soc_base + *reg_prop, *(reg_prop + 1));
873 if (serial_compat == 0)
874 DTGetProperty(entryP, "compatible", (void **)&serial_compat, &prop_size);
875 }
876#ifdef S3CUART
877 if (NULL != entryP) {
878 DTGetProperty(entryP, "pclk", (void **)&prop_value, &prop_size);
879 if (prop_value) dt_pclk = *prop_value;
880
881 prop_value = NULL;
882 DTGetProperty(entryP, "sampling", (void **)&prop_value, &prop_size);
883 if (prop_value) dt_sampling = *prop_value;
884
885 prop_value = NULL;
886 DTGetProperty(entryP, "ubrdiv", (void **)&prop_value, &prop_size);
887 if (prop_value) dt_ubrdiv = *prop_value;
888 }
889 if (!strcmp(serial_compat, "uart,16550"))
890 gPESF = &ln2410_serial_functions;
891 else if (!strcmp(serial_compat, "uart-16550"))
892 gPESF = &ln2410_serial_functions;
893 else if (!strcmp(serial_compat, "uart,s5i3000"))
894 gPESF = &ln2410_serial_functions;
895 else if (!strcmp(serial_compat, "uart-1,samsung"))
896 gPESF = &ln2410_serial_functions;
897#elif defined (ARM_BOARD_CONFIG_MV88F6710)
898 if (!strcmp(serial_compat, "uart16x50,mmio"))
899 gPESF = &uart16x50_serial_functions;
900#endif
901 else
902 return 0;
903
904 gPESF->uart_init();
905
906 uart_initted = 1;
907
908 return 1;
909}
910
911void
912uart_putc(char c)
913{
914 if (uart_initted) {
915 while (!gPESF->tr0()); /* Wait until THR is empty. */
916 gPESF->td0(c);
917 }
918}
919
920int
921uart_getc(void)
922{ /* returns -1 if no data available */
923 if (uart_initted) {
924 if (!gPESF->rr0())
925 return -1; /* Receive data read */
926 return gPESF->rd0();
927 }
928 return -1;
929}
930