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 | 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 { |
259 | uint32_t ; |
260 | uint8_t ; |
261 | uint8_t ; /* number of child entries in child_ent */ |
262 | uint16_t ; |
263 | uint64_t [2]; /* Physical address for buffers (in, out) */ |
264 | uint32_t [2]; |
265 | uint8_t [8]; |
266 | |
267 | /* Slave-modified data - invalidate before read */ |
268 | uint32_t [2] __attribute__((aligned (FULL_ALIGNMENT))); /* In head, out tail */ |
269 | |
270 | /* Master-modified data - clean after write */ |
271 | uint32_t [2] __attribute__((aligned (FULL_ALIGNMENT))); /* In tail, out head */ |
272 | |
273 | uint64_t [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, ; |
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 **)®_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. |
649 | rDOCKCHANNELS_DOCK_RDATA1(DOCKCHANNEL_UART_CHANNEL); |
650 | } |
651 | |
652 | static 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 |
666 | vm_offset_t pi3_gpio_base_vaddr; |
667 | vm_offset_t pi3_aux_base_vaddr; |
668 | static int pi3_uart_tr0(void) |
669 | { |
670 | return (int) BCM2837_GET32(BCM2837_AUX_MU_LSR_REG_V) & 0x20; |
671 | } |
672 | |
673 | static void pi3_uart_td0(int c) |
674 | { |
675 | BCM2837_PUT32(BCM2837_AUX_MU_IO_REG_V, (uint32_t) c); |
676 | } |
677 | |
678 | static int pi3_uart_rr0(void) |
679 | { |
680 | return (int) BCM2837_GET32(BCM2837_AUX_MU_LSR_REG_V) & 0x01; |
681 | } |
682 | |
683 | static int pi3_uart_rd0(void) |
684 | { |
685 | return (int) BCM2837_GET32(BCM2837_AUX_MU_IO_REG_V) & 0xff; |
686 | } |
687 | |
688 | static 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 | |
737 | static 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 | /*****************************************************************************/ |
749 | int |
750 | serial_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 **)®_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 **)®_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 **)®_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 **)®_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 **)®_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 | |
911 | void |
912 | uart_putc(char c) |
913 | { |
914 | if (uart_initted) { |
915 | while (!gPESF->tr0()); /* Wait until THR is empty. */ |
916 | gPESF->td0(c); |
917 | } |
918 | } |
919 | |
920 | int |
921 | uart_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 | |