]>
Commit | Line | Data |
---|---|---|
dd2c9e6a MK |
1 | /* |
2 | * (C) Copyright 2009 SAMSUNG Electronics | |
3 | * Minkyu Kang <mk7.kang@samsung.com> | |
4 | * Heungjun Kim <riverful.kim@samsung.com> | |
5 | * | |
6 | * based on drivers/serial/s3c64xx.c | |
7 | * | |
1a459660 | 8 | * SPDX-License-Identifier: GPL-2.0+ |
dd2c9e6a MK |
9 | */ |
10 | ||
11 | #include <common.h> | |
d4ec8f08 | 12 | #include <fdtdec.h> |
6c768ca7 | 13 | #include <linux/compiler.h> |
dd2c9e6a MK |
14 | #include <asm/io.h> |
15 | #include <asm/arch/uart.h> | |
16 | #include <asm/arch/clk.h> | |
17 | #include <serial.h> | |
18 | ||
29565326 JR |
19 | DECLARE_GLOBAL_DATA_PTR; |
20 | ||
ffbff1dd AS |
21 | #define RX_FIFO_COUNT_MASK 0xff |
22 | #define RX_FIFO_FULL_MASK (1 << 8) | |
23 | #define TX_FIFO_FULL_MASK (1 << 24) | |
24 | ||
d4ec8f08 RS |
25 | /* Information about a serial port */ |
26 | struct fdt_serial { | |
27 | u32 base_addr; /* address of registers in physical memory */ | |
28 | u8 port_id; /* uart port number */ | |
29 | u8 enabled; /* 1 if enabled, 0 if disabled */ | |
30 | } config __attribute__ ((section(".data"))); | |
31 | ||
46a3b5c8 | 32 | static inline struct s5p_uart *s5p_get_base_uart(int dev_index) |
dd2c9e6a | 33 | { |
d4ec8f08 RS |
34 | #ifdef CONFIG_OF_CONTROL |
35 | return (struct s5p_uart *)(config.base_addr); | |
36 | #else | |
46a3b5c8 | 37 | u32 offset = dev_index * sizeof(struct s5p_uart); |
d93d0f0c | 38 | return (struct s5p_uart *)(samsung_get_base_uart() + offset); |
d4ec8f08 | 39 | #endif |
dd2c9e6a MK |
40 | } |
41 | ||
42 | /* | |
46a3b5c8 | 43 | * The coefficient, used to calculate the baudrate on S5P UARTs is |
dd2c9e6a MK |
44 | * calculated as |
45 | * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT | |
46 | * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1, | |
47 | * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants: | |
48 | */ | |
49 | static const int udivslot[] = { | |
50 | 0, | |
51 | 0x0080, | |
52 | 0x0808, | |
53 | 0x0888, | |
54 | 0x2222, | |
55 | 0x4924, | |
56 | 0x4a52, | |
57 | 0x54aa, | |
58 | 0x5555, | |
59 | 0xd555, | |
60 | 0xd5d5, | |
61 | 0xddd5, | |
62 | 0xdddd, | |
63 | 0xdfdd, | |
64 | 0xdfdf, | |
65 | 0xffdf, | |
66 | }; | |
67 | ||
68 | void serial_setbrg_dev(const int dev_index) | |
69 | { | |
46a3b5c8 | 70 | struct s5p_uart *const uart = s5p_get_base_uart(dev_index); |
f70409af | 71 | u32 uclk = get_uart_clk(dev_index); |
dd2c9e6a MK |
72 | u32 baudrate = gd->baudrate; |
73 | u32 val; | |
74 | ||
d4ec8f08 RS |
75 | #if defined(CONFIG_SILENT_CONSOLE) && \ |
76 | defined(CONFIG_OF_CONTROL) && \ | |
77 | !defined(CONFIG_SPL_BUILD) | |
78 | if (fdtdec_get_config_int(gd->fdt_blob, "silent_console", 0)) | |
79 | gd->flags |= GD_FLG_SILENT; | |
80 | #endif | |
81 | ||
82 | if (!config.enabled) | |
83 | return; | |
84 | ||
f70409af | 85 | val = uclk / baudrate; |
dd2c9e6a MK |
86 | |
87 | writel(val / 16 - 1, &uart->ubrdiv); | |
1628cfc4 | 88 | |
e0617c62 | 89 | if (s5p_uart_divslot()) |
1628cfc4 MK |
90 | writew(udivslot[val % 16], &uart->rest.slot); |
91 | else | |
92 | writeb(val % 16, &uart->rest.value); | |
dd2c9e6a MK |
93 | } |
94 | ||
95 | /* | |
96 | * Initialise the serial port with the given baudrate. The settings | |
97 | * are always 8 data bits, no parity, 1 stop bit, no start bits. | |
98 | */ | |
99 | int serial_init_dev(const int dev_index) | |
100 | { | |
46a3b5c8 | 101 | struct s5p_uart *const uart = s5p_get_base_uart(dev_index); |
dd2c9e6a | 102 | |
ffbff1dd AS |
103 | /* enable FIFOs */ |
104 | writel(0x1, &uart->ufcon); | |
dd2c9e6a MK |
105 | writel(0, &uart->umcon); |
106 | /* 8N1 */ | |
107 | writel(0x3, &uart->ulcon); | |
108 | /* No interrupts, no DMA, pure polling */ | |
109 | writel(0x245, &uart->ucon); | |
110 | ||
111 | serial_setbrg_dev(dev_index); | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
94003226 | 116 | static int serial_err_check(const int dev_index, int op) |
dd2c9e6a | 117 | { |
46a3b5c8 | 118 | struct s5p_uart *const uart = s5p_get_base_uart(dev_index); |
94003226 MK |
119 | unsigned int mask; |
120 | ||
121 | /* | |
122 | * UERSTAT | |
123 | * Break Detect [3] | |
124 | * Frame Err [2] : receive operation | |
125 | * Parity Err [1] : receive operation | |
126 | * Overrun Err [0] : receive operation | |
127 | */ | |
128 | if (op) | |
129 | mask = 0x8; | |
130 | else | |
131 | mask = 0xf; | |
dd2c9e6a | 132 | |
94003226 | 133 | return readl(&uart->uerstat) & mask; |
dd2c9e6a MK |
134 | } |
135 | ||
136 | /* | |
137 | * Read a single byte from the serial port. Returns 1 on success, 0 | |
138 | * otherwise. When the function is succesfull, the character read is | |
139 | * written into its argument c. | |
140 | */ | |
141 | int serial_getc_dev(const int dev_index) | |
142 | { | |
46a3b5c8 | 143 | struct s5p_uart *const uart = s5p_get_base_uart(dev_index); |
dd2c9e6a | 144 | |
d4ec8f08 RS |
145 | if (!config.enabled) |
146 | return 0; | |
147 | ||
dd2c9e6a | 148 | /* wait for character to arrive */ |
ffbff1dd AS |
149 | while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK | |
150 | RX_FIFO_FULL_MASK))) { | |
94003226 | 151 | if (serial_err_check(dev_index, 0)) |
dd2c9e6a MK |
152 | return 0; |
153 | } | |
154 | ||
1a4106dd | 155 | return (int)(readb(&uart->urxh) & 0xff); |
dd2c9e6a MK |
156 | } |
157 | ||
158 | /* | |
159 | * Output a single byte to the serial port. | |
160 | */ | |
161 | void serial_putc_dev(const char c, const int dev_index) | |
162 | { | |
46a3b5c8 | 163 | struct s5p_uart *const uart = s5p_get_base_uart(dev_index); |
dd2c9e6a | 164 | |
d4ec8f08 RS |
165 | if (!config.enabled) |
166 | return; | |
167 | ||
dd2c9e6a | 168 | /* wait for room in the tx FIFO */ |
ffbff1dd | 169 | while ((readl(&uart->ufstat) & TX_FIFO_FULL_MASK)) { |
94003226 | 170 | if (serial_err_check(dev_index, 1)) |
dd2c9e6a MK |
171 | return; |
172 | } | |
173 | ||
1a4106dd | 174 | writeb(c, &uart->utxh); |
dd2c9e6a MK |
175 | |
176 | /* If \n, also do \r */ | |
177 | if (c == '\n') | |
178 | serial_putc('\r'); | |
179 | } | |
180 | ||
181 | /* | |
182 | * Test whether a character is in the RX buffer | |
183 | */ | |
184 | int serial_tstc_dev(const int dev_index) | |
185 | { | |
46a3b5c8 | 186 | struct s5p_uart *const uart = s5p_get_base_uart(dev_index); |
dd2c9e6a | 187 | |
d4ec8f08 RS |
188 | if (!config.enabled) |
189 | return 0; | |
190 | ||
dd2c9e6a MK |
191 | return (int)(readl(&uart->utrstat) & 0x1); |
192 | } | |
193 | ||
194 | void serial_puts_dev(const char *s, const int dev_index) | |
195 | { | |
196 | while (*s) | |
197 | serial_putc_dev(*s++, dev_index); | |
198 | } | |
199 | ||
200 | /* Multi serial device functions */ | |
201 | #define DECLARE_S5P_SERIAL_FUNCTIONS(port) \ | |
202 | int s5p_serial##port##_init(void) { return serial_init_dev(port); } \ | |
203 | void s5p_serial##port##_setbrg(void) { serial_setbrg_dev(port); } \ | |
204 | int s5p_serial##port##_getc(void) { return serial_getc_dev(port); } \ | |
205 | int s5p_serial##port##_tstc(void) { return serial_tstc_dev(port); } \ | |
206 | void s5p_serial##port##_putc(const char c) { serial_putc_dev(c, port); } \ | |
207 | void s5p_serial##port##_puts(const char *s) { serial_puts_dev(s, port); } | |
208 | ||
90bad891 MV |
209 | #define INIT_S5P_SERIAL_STRUCTURE(port, __name) { \ |
210 | .name = __name, \ | |
211 | .start = s5p_serial##port##_init, \ | |
212 | .stop = NULL, \ | |
213 | .setbrg = s5p_serial##port##_setbrg, \ | |
214 | .getc = s5p_serial##port##_getc, \ | |
215 | .tstc = s5p_serial##port##_tstc, \ | |
216 | .putc = s5p_serial##port##_putc, \ | |
217 | .puts = s5p_serial##port##_puts, \ | |
218 | } | |
dd2c9e6a MK |
219 | |
220 | DECLARE_S5P_SERIAL_FUNCTIONS(0); | |
46a3b5c8 | 221 | struct serial_device s5p_serial0_device = |
1c9a5606 | 222 | INIT_S5P_SERIAL_STRUCTURE(0, "s5pser0"); |
dd2c9e6a | 223 | DECLARE_S5P_SERIAL_FUNCTIONS(1); |
46a3b5c8 | 224 | struct serial_device s5p_serial1_device = |
1c9a5606 | 225 | INIT_S5P_SERIAL_STRUCTURE(1, "s5pser1"); |
dd2c9e6a | 226 | DECLARE_S5P_SERIAL_FUNCTIONS(2); |
46a3b5c8 | 227 | struct serial_device s5p_serial2_device = |
1c9a5606 | 228 | INIT_S5P_SERIAL_STRUCTURE(2, "s5pser2"); |
dd2c9e6a | 229 | DECLARE_S5P_SERIAL_FUNCTIONS(3); |
46a3b5c8 | 230 | struct serial_device s5p_serial3_device = |
1c9a5606 | 231 | INIT_S5P_SERIAL_STRUCTURE(3, "s5pser3"); |
6c768ca7 | 232 | |
d4ec8f08 RS |
233 | #ifdef CONFIG_OF_CONTROL |
234 | int fdtdec_decode_console(int *index, struct fdt_serial *uart) | |
235 | { | |
236 | const void *blob = gd->fdt_blob; | |
237 | int node; | |
238 | ||
239 | node = fdt_path_offset(blob, "console"); | |
240 | if (node < 0) | |
241 | return node; | |
242 | ||
243 | uart->base_addr = fdtdec_get_addr(blob, node, "reg"); | |
244 | if (uart->base_addr == FDT_ADDR_T_NONE) | |
245 | return -FDT_ERR_NOTFOUND; | |
246 | ||
247 | uart->port_id = fdtdec_get_int(blob, node, "id", -1); | |
248 | uart->enabled = fdtdec_get_is_enabled(blob, node); | |
249 | ||
250 | return 0; | |
251 | } | |
252 | #endif | |
253 | ||
6c768ca7 MF |
254 | __weak struct serial_device *default_serial_console(void) |
255 | { | |
d4ec8f08 RS |
256 | #ifdef CONFIG_OF_CONTROL |
257 | int index = 0; | |
258 | ||
259 | if ((!config.base_addr) && (fdtdec_decode_console(&index, &config))) { | |
260 | debug("Cannot decode default console node\n"); | |
261 | return NULL; | |
262 | } | |
263 | ||
264 | switch (config.port_id) { | |
265 | case 0: | |
266 | return &s5p_serial0_device; | |
267 | case 1: | |
268 | return &s5p_serial1_device; | |
269 | case 2: | |
270 | return &s5p_serial2_device; | |
271 | case 3: | |
272 | return &s5p_serial3_device; | |
273 | default: | |
274 | debug("Unknown config.port_id: %d", config.port_id); | |
275 | break; | |
276 | } | |
277 | ||
278 | return NULL; | |
279 | #else | |
280 | config.enabled = 1; | |
6c768ca7 MF |
281 | #if defined(CONFIG_SERIAL0) |
282 | return &s5p_serial0_device; | |
283 | #elif defined(CONFIG_SERIAL1) | |
284 | return &s5p_serial1_device; | |
285 | #elif defined(CONFIG_SERIAL2) | |
286 | return &s5p_serial2_device; | |
287 | #elif defined(CONFIG_SERIAL3) | |
288 | return &s5p_serial3_device; | |
289 | #else | |
290 | #error "CONFIG_SERIAL? missing." | |
291 | #endif | |
d4ec8f08 | 292 | #endif |
6c768ca7 | 293 | } |
b4980515 MV |
294 | |
295 | void s5p_serial_initialize(void) | |
296 | { | |
297 | serial_register(&s5p_serial0_device); | |
298 | serial_register(&s5p_serial1_device); | |
299 | serial_register(&s5p_serial2_device); | |
300 | serial_register(&s5p_serial3_device); | |
301 | } |