1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include <common.h>
25#include <fdtdec.h>
26#include <linux/compiler.h>
27#include <asm/io.h>
28#include <asm/arch/uart.h>
29#include <asm/arch/clk.h>
30#include <serial.h>
31
32DECLARE_GLOBAL_DATA_PTR;
33
34#define RX_FIFO_COUNT_MASK 0xff
35#define RX_FIFO_FULL_MASK (1 << 8)
36#define TX_FIFO_FULL_MASK (1 << 24)
37
38
39struct fdt_serial {
40 u32 base_addr;
41 u8 port_id;
42 u8 enabled;
43} config __attribute__ ((section(".data")));
44
45static inline struct s5p_uart *s5p_get_base_uart(int dev_index)
46{
47#ifdef CONFIG_OF_CONTROL
48 return (struct s5p_uart *)(config.base_addr);
49#else
50 u32 offset = dev_index * sizeof(struct s5p_uart);
51 return (struct s5p_uart *)(samsung_get_base_uart() + offset);
52#endif
53}
54
55
56
57
58
59
60
61
62static const int udivslot[] = {
63 0,
64 0x0080,
65 0x0808,
66 0x0888,
67 0x2222,
68 0x4924,
69 0x4a52,
70 0x54aa,
71 0x5555,
72 0xd555,
73 0xd5d5,
74 0xddd5,
75 0xdddd,
76 0xdfdd,
77 0xdfdf,
78 0xffdf,
79};
80
81void serial_setbrg_dev(const int dev_index)
82{
83 struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
84 u32 uclk = get_uart_clk(dev_index);
85 u32 baudrate = gd->baudrate;
86 u32 val;
87
88#if defined(CONFIG_SILENT_CONSOLE) && \
89 defined(CONFIG_OF_CONTROL) && \
90 !defined(CONFIG_SPL_BUILD)
91 if (fdtdec_get_config_int(gd->fdt_blob, "silent_console", 0))
92 gd->flags |= GD_FLG_SILENT;
93#endif
94
95 if (!config.enabled)
96 return;
97
98 val = uclk / baudrate;
99
100 writel(val / 16 - 1, &uart->ubrdiv);
101
102 if (s5p_uart_divslot())
103 writew(udivslot[val % 16], &uart->rest.slot);
104 else
105 writeb(val % 16, &uart->rest.value);
106}
107
108
109
110
111
112int serial_init_dev(const int dev_index)
113{
114 struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
115
116
117 writel(0x1, &uart->ufcon);
118 writel(0, &uart->umcon);
119
120 writel(0x3, &uart->ulcon);
121
122 writel(0x245, &uart->ucon);
123
124 serial_setbrg_dev(dev_index);
125
126 return 0;
127}
128
129static int serial_err_check(const int dev_index, int op)
130{
131 struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
132 unsigned int mask;
133
134
135
136
137
138
139
140
141 if (op)
142 mask = 0x8;
143 else
144 mask = 0xf;
145
146 return readl(&uart->uerstat) & mask;
147}
148
149
150
151
152
153
154int serial_getc_dev(const int dev_index)
155{
156 struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
157
158 if (!config.enabled)
159 return 0;
160
161
162 while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK |
163 RX_FIFO_FULL_MASK))) {
164 if (serial_err_check(dev_index, 0))
165 return 0;
166 }
167
168 return (int)(readb(&uart->urxh) & 0xff);
169}
170
171
172
173
174void serial_putc_dev(const char c, const int dev_index)
175{
176 struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
177
178 if (!config.enabled)
179 return;
180
181
182 while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) {
183 if (serial_err_check(dev_index, 1))
184 return;
185 }
186
187 writeb(c, &uart->utxh);
188
189
190 if (c == '\n')
191 serial_putc('\r');
192}
193
194
195
196
197int serial_tstc_dev(const int dev_index)
198{
199 struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
200
201 if (!config.enabled)
202 return 0;
203
204 return (int)(readl(&uart->utrstat) & 0x1);
205}
206
207void serial_puts_dev(const char *s, const int dev_index)
208{
209 while (*s)
210 serial_putc_dev(*s++, dev_index);
211}
212
213
214#define DECLARE_S5P_SERIAL_FUNCTIONS(port) \
215int s5p_serial##port##_init(void) { return serial_init_dev(port); } \
216void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \
217int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \
218int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \
219void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \
220void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); }
221
222#define INIT_S5P_SERIAL_STRUCTURE(port, __name) { \
223 .name = __name, \
224 .start = s5p_serial##port##_init, \
225 .stop = NULL, \
226 .setbrg = s5p_serial##port##_setbrg, \
227 .getc = s5p_serial##port##_getc, \
228 .tstc = s5p_serial##port##_tstc, \
229 .putc = s5p_serial##port##_putc, \
230 .puts = s5p_serial##port##_puts, \
231}
232
233DECLARE_S5P_SERIAL_FUNCTIONS(0);
234struct serial_device s5p_serial0_device =
235 INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0");
236DECLARE_S5P_SERIAL_FUNCTIONS(1);
237struct serial_device s5p_serial1_device =
238 INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1");
239DECLARE_S5P_SERIAL_FUNCTIONS(2);
240struct serial_device s5p_serial2_device =
241 INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2");
242DECLARE_S5P_SERIAL_FUNCTIONS(3);
243struct serial_device s5p_serial3_device =
244 INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3");
245
246#ifdef CONFIG_OF_CONTROL
247int fdtdec_decode_console(int *index, struct fdt_serial *uart)
248{
249 const void *blob = gd->fdt_blob;
250 int node;
251
252 node = fdt_path_offset(blob, "console");
253 if (node < 0)
254 return node;
255
256 uart->base_addr = fdtdec_get_addr(blob, node, "reg");
257 if (uart->base_addr == FDT_ADDR_T_NONE)
258 return -FDT_ERR_NOTFOUND;
259
260 uart->port_id = fdtdec_get_int(blob, node, "id", -1);
261 uart->enabled = fdtdec_get_is_enabled(blob, node);
262
263 return 0;
264}
265#endif
266
267__weak struct serial_device *default_serial_console(void)
268{
269#ifdef CONFIG_OF_CONTROL
270 int index = 0;
271
272 if ((!config.base_addr) && (fdtdec_decode_console(&index, &config))) {
273 debug("Cannot decode default console node\n");
274 return NULL;
275 }
276
277 switch (config.port_id) {
278 case 0:
279 return &s5p_serial0_device;
280 case 1:
281 return &s5p_serial1_device;
282 case 2:
283 return &s5p_serial2_device;
284 case 3:
285 return &s5p_serial3_device;
286 default:
287 debug("Unknown config.port_id: %d", config.port_id);
288 break;
289 }
290
291 return NULL;
292#else
293 config.enabled = 1;
294#if defined(CONFIG_SERIAL0)
295 return &s5p_serial0_device;
296#elif defined(CONFIG_SERIAL1)
297 return &s5p_serial1_device;
298#elif defined(CONFIG_SERIAL2)
299 return &s5p_serial2_device;
300#elif defined(CONFIG_SERIAL3)
301 return &s5p_serial3_device;
302#else
303#error "CONFIG_SERIAL? missing."
304#endif
305#endif
306}
307
308void s5p_serial_initialize(void)
309{
310 serial_register(&s5p_serial0_device);
311 serial_register(&s5p_serial1_device);
312 serial_register(&s5p_serial2_device);
313 serial_register(&s5p_serial3_device);
314}
315