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