1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * A udbg backend which logs messages and reads input from in memory |
4 | * buffers. |
5 | * |
6 | * The console output can be read from memcons_output which is a |
7 | * circular buffer whose next write position is stored in memcons.output_pos. |
8 | * |
9 | * Input may be passed by writing into the memcons_input buffer when it is |
10 | * empty. The input buffer is empty when both input_pos == input_start and |
11 | * *input_start == '\0'. |
12 | * |
13 | * Copyright (C) 2003-2005 Anton Blanchard and Milton Miller, IBM Corp |
14 | * Copyright (C) 2013 Alistair Popple, IBM Corp |
15 | */ |
16 | |
17 | #include <linux/kernel.h> |
18 | #include <asm/barrier.h> |
19 | #include <asm/page.h> |
20 | #include <asm/processor.h> |
21 | #include <asm/udbg.h> |
22 | |
23 | struct memcons { |
24 | char *output_start; |
25 | char *output_pos; |
26 | char *output_end; |
27 | char *input_start; |
28 | char *input_pos; |
29 | char *input_end; |
30 | }; |
31 | |
32 | static char memcons_output[CONFIG_PPC_MEMCONS_OUTPUT_SIZE]; |
33 | static char memcons_input[CONFIG_PPC_MEMCONS_INPUT_SIZE]; |
34 | |
35 | struct memcons memcons = { |
36 | .output_start = memcons_output, |
37 | .output_pos = memcons_output, |
38 | .output_end = &memcons_output[CONFIG_PPC_MEMCONS_OUTPUT_SIZE], |
39 | .input_start = memcons_input, |
40 | .input_pos = memcons_input, |
41 | .input_end = &memcons_input[CONFIG_PPC_MEMCONS_INPUT_SIZE], |
42 | }; |
43 | |
44 | static void memcons_putc(char c) |
45 | { |
46 | char *new_output_pos; |
47 | |
48 | *memcons.output_pos = c; |
49 | wmb(); |
50 | new_output_pos = memcons.output_pos + 1; |
51 | if (new_output_pos >= memcons.output_end) |
52 | new_output_pos = memcons.output_start; |
53 | |
54 | memcons.output_pos = new_output_pos; |
55 | } |
56 | |
57 | static int memcons_getc_poll(void) |
58 | { |
59 | char c; |
60 | char *new_input_pos; |
61 | |
62 | if (*memcons.input_pos) { |
63 | c = *memcons.input_pos; |
64 | |
65 | new_input_pos = memcons.input_pos + 1; |
66 | if (new_input_pos >= memcons.input_end) |
67 | new_input_pos = memcons.input_start; |
68 | else if (*new_input_pos == '\0') |
69 | new_input_pos = memcons.input_start; |
70 | |
71 | *memcons.input_pos = '\0'; |
72 | wmb(); |
73 | memcons.input_pos = new_input_pos; |
74 | return c; |
75 | } |
76 | |
77 | return -1; |
78 | } |
79 | |
80 | static int memcons_getc(void) |
81 | { |
82 | int c; |
83 | |
84 | while (1) { |
85 | c = memcons_getc_poll(); |
86 | if (c == -1) |
87 | cpu_relax(); |
88 | else |
89 | break; |
90 | } |
91 | |
92 | return c; |
93 | } |
94 | |
95 | void __init udbg_init_memcons(void) |
96 | { |
97 | udbg_putc = memcons_putc; |
98 | udbg_getc = memcons_getc; |
99 | udbg_getc_poll = memcons_getc_poll; |
100 | } |
101 | |