]>
Commit | Line | Data |
---|---|---|
0b135cfc NI |
1 | /* |
2 | * SuperH SCIF device driver. | |
48ca882c | 3 | * Copyright (C) 2013 Renesas Electronics Corporation |
59088e4a | 4 | * Copyright (C) 2007,2008,2010, 2014 Nobuhiro Iwamatsu |
3f6c8e36 | 5 | * Copyright (C) 2002 - 2008 Paul Mundt |
61fb15c5 | 6 | * |
1a459660 | 7 | * SPDX-License-Identifier: GPL-2.0+ |
0b135cfc NI |
8 | */ |
9 | ||
10 | #include <common.h> | |
59088e4a | 11 | #include <errno.h> |
8171499d | 12 | #include <clk.h> |
59088e4a | 13 | #include <dm.h> |
fc83c927 | 14 | #include <asm/io.h> |
0b135cfc | 15 | #include <asm/processor.h> |
8bdd7efa MV |
16 | #include <serial.h> |
17 | #include <linux/compiler.h> | |
59088e4a NI |
18 | #include <dm/platform_data/serial_sh.h> |
19 | #include "serial_sh.h" | |
0b135cfc | 20 | |
359787cf YS |
21 | DECLARE_GLOBAL_DATA_PTR; |
22 | ||
3f6c8e36 NI |
23 | #if defined(CONFIG_CPU_SH7760) || \ |
24 | defined(CONFIG_CPU_SH7780) || \ | |
25 | defined(CONFIG_CPU_SH7785) || \ | |
26 | defined(CONFIG_CPU_SH7786) | |
27 | static int scif_rxfill(struct uart_port *port) | |
28 | { | |
29 | return sci_in(port, SCRFDR) & 0xff; | |
30 | } | |
31 | #elif defined(CONFIG_CPU_SH7763) | |
32 | static int scif_rxfill(struct uart_port *port) | |
0b135cfc | 33 | { |
3f6c8e36 | 34 | if ((port->mapbase == 0xffe00000) || |
59088e4a | 35 | (port->mapbase == 0xffe08000)) { |
3f6c8e36 NI |
36 | /* SCIF0/1*/ |
37 | return sci_in(port, SCRFDR) & 0xff; | |
38 | } else { | |
39 | /* SCIF2 */ | |
40 | return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; | |
41 | } | |
42 | } | |
43 | #elif defined(CONFIG_ARCH_SH7372) | |
44 | static int scif_rxfill(struct uart_port *port) | |
45 | { | |
46 | if (port->type == PORT_SCIFA) | |
47 | return sci_in(port, SCFDR) & SCIF_RFDC_MASK; | |
48 | else | |
49 | return sci_in(port, SCRFDR); | |
50 | } | |
3ecff1d7 | 51 | #else |
3f6c8e36 NI |
52 | static int scif_rxfill(struct uart_port *port) |
53 | { | |
54 | return sci_in(port, SCFDR) & SCIF_RFDC_MASK; | |
55 | } | |
3ecff1d7 | 56 | #endif |
3f6c8e36 | 57 | |
59088e4a | 58 | static void sh_serial_init_generic(struct uart_port *port) |
3f6c8e36 | 59 | { |
59088e4a NI |
60 | sci_out(port, SCSCR , SCSCR_INIT(port)); |
61 | sci_out(port, SCSCR , SCSCR_INIT(port)); | |
62 | sci_out(port, SCSMR, 0); | |
63 | sci_out(port, SCSMR, 0); | |
64 | sci_out(port, SCFCR, SCFCR_RFRST|SCFCR_TFRST); | |
65 | sci_in(port, SCFCR); | |
66 | sci_out(port, SCFCR, 0); | |
0b135cfc NI |
67 | } |
68 | ||
59088e4a NI |
69 | static void |
70 | sh_serial_setbrg_generic(struct uart_port *port, int clk, int baudrate) | |
7c791b3f | 71 | { |
59088e4a NI |
72 | if (port->clk_mode == EXT_CLK) { |
73 | unsigned short dl = DL_VALUE(baudrate, clk); | |
74 | sci_out(port, DL, dl); | |
89f99a62 | 75 | /* Need wait: Clock * 1/dl * 1/16 */ |
59088e4a NI |
76 | udelay((1000000 * dl * 16 / clk) * 1000 + 1); |
77 | } else { | |
78 | sci_out(port, SCBRR, SCBRR_VALUE(baudrate, clk)); | |
79 | } | |
7c791b3f TK |
80 | } |
81 | ||
59088e4a | 82 | static void handle_error(struct uart_port *port) |
0b135cfc | 83 | { |
59088e4a NI |
84 | sci_in(port, SCxSR); |
85 | sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); | |
86 | sci_in(port, SCLSR); | |
87 | sci_out(port, SCLSR, 0x00); | |
88 | } | |
89 | ||
90 | static int serial_raw_putc(struct uart_port *port, const char c) | |
91 | { | |
92 | /* Tx fifo is empty */ | |
93 | if (!(sci_in(port, SCxSR) & SCxSR_TEND(port))) | |
94 | return -EAGAIN; | |
0b135cfc | 95 | |
59088e4a NI |
96 | sci_out(port, SCxTDR, c); |
97 | sci_out(port, SCxSR, sci_in(port, SCxSR) & ~SCxSR_TEND(port)); | |
98 | ||
99 | return 0; | |
0b135cfc NI |
100 | } |
101 | ||
59088e4a | 102 | static int serial_rx_fifo_level(struct uart_port *port) |
0b135cfc | 103 | { |
59088e4a | 104 | return scif_rxfill(port); |
0b135cfc NI |
105 | } |
106 | ||
59088e4a | 107 | static int sh_serial_tstc_generic(struct uart_port *port) |
0b135cfc | 108 | { |
59088e4a NI |
109 | if (sci_in(port, SCxSR) & SCIF_ERRORS) { |
110 | handle_error(port); | |
7c791b3f TK |
111 | return 0; |
112 | } | |
113 | ||
59088e4a | 114 | return serial_rx_fifo_level(port) ? 1 : 0; |
0b135cfc NI |
115 | } |
116 | ||
59088e4a | 117 | static int serial_getc_check(struct uart_port *port) |
08c5fabe | 118 | { |
0b135cfc NI |
119 | unsigned short status; |
120 | ||
59088e4a | 121 | status = sci_in(port, SCxSR); |
0b135cfc | 122 | |
3f6c8e36 | 123 | if (status & SCIF_ERRORS) |
59088e4a NI |
124 | handle_error(port); |
125 | if (sci_in(port, SCLSR) & SCxSR_ORER(port)) | |
126 | handle_error(port); | |
127 | return status & (SCIF_DR | SCxSR_RDxF(port)); | |
0b135cfc NI |
128 | } |
129 | ||
59088e4a | 130 | static int sh_serial_getc_generic(struct uart_port *port) |
0b135cfc | 131 | { |
08c5fabe | 132 | unsigned short status; |
0b135cfc | 133 | char ch; |
ab09f433 | 134 | |
59088e4a NI |
135 | if (!serial_getc_check(port)) |
136 | return -EAGAIN; | |
0b135cfc | 137 | |
59088e4a NI |
138 | ch = sci_in(port, SCxRDR); |
139 | status = sci_in(port, SCxSR); | |
0b135cfc | 140 | |
59088e4a | 141 | sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); |
0b135cfc | 142 | |
3f6c8e36 | 143 | if (status & SCIF_ERRORS) |
59088e4a NI |
144 | handle_error(port); |
145 | ||
146 | if (sci_in(port, SCLSR) & SCxSR_ORER(port)) | |
147 | handle_error(port); | |
148 | ||
149 | return ch; | |
150 | } | |
151 | ||
152 | #ifdef CONFIG_DM_SERIAL | |
153 | ||
154 | static int sh_serial_pending(struct udevice *dev, bool input) | |
155 | { | |
156 | struct uart_port *priv = dev_get_priv(dev); | |
157 | ||
158 | return sh_serial_tstc_generic(priv); | |
159 | } | |
160 | ||
161 | static int sh_serial_putc(struct udevice *dev, const char ch) | |
162 | { | |
163 | struct uart_port *priv = dev_get_priv(dev); | |
164 | ||
165 | return serial_raw_putc(priv, ch); | |
166 | } | |
167 | ||
168 | static int sh_serial_getc(struct udevice *dev) | |
169 | { | |
170 | struct uart_port *priv = dev_get_priv(dev); | |
171 | ||
172 | return sh_serial_getc_generic(priv); | |
173 | } | |
174 | ||
175 | static int sh_serial_setbrg(struct udevice *dev, int baudrate) | |
176 | { | |
177 | struct sh_serial_platdata *plat = dev_get_platdata(dev); | |
178 | struct uart_port *priv = dev_get_priv(dev); | |
179 | ||
180 | sh_serial_setbrg_generic(priv, plat->clk, baudrate); | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | static int sh_serial_probe(struct udevice *dev) | |
186 | { | |
187 | struct sh_serial_platdata *plat = dev_get_platdata(dev); | |
188 | struct uart_port *priv = dev_get_priv(dev); | |
189 | ||
190 | priv->membase = (unsigned char *)plat->base; | |
191 | priv->mapbase = plat->base; | |
192 | priv->type = plat->type; | |
193 | priv->clk_mode = plat->clk_mode; | |
194 | ||
195 | sh_serial_init_generic(priv); | |
196 | ||
197 | return 0; | |
198 | } | |
199 | ||
200 | static const struct dm_serial_ops sh_serial_ops = { | |
201 | .putc = sh_serial_putc, | |
202 | .pending = sh_serial_pending, | |
203 | .getc = sh_serial_getc, | |
204 | .setbrg = sh_serial_setbrg, | |
205 | }; | |
206 | ||
359787cf YS |
207 | #ifdef CONFIG_OF_CONTROL |
208 | static const struct udevice_id sh_serial_id[] ={ | |
747431b9 | 209 | {.compatible = "renesas,sci", .data = PORT_SCI}, |
359787cf YS |
210 | {.compatible = "renesas,scif", .data = PORT_SCIF}, |
211 | {.compatible = "renesas,scifa", .data = PORT_SCIFA}, | |
212 | {} | |
213 | }; | |
214 | ||
215 | static int sh_serial_ofdata_to_platdata(struct udevice *dev) | |
216 | { | |
217 | struct sh_serial_platdata *plat = dev_get_platdata(dev); | |
8171499d | 218 | struct clk sh_serial_clk; |
359787cf | 219 | fdt_addr_t addr; |
8171499d | 220 | int ret; |
359787cf | 221 | |
e160f7d4 | 222 | addr = fdtdec_get_addr(gd->fdt_blob, dev_of_offset(dev), "reg"); |
359787cf YS |
223 | if (addr == FDT_ADDR_T_NONE) |
224 | return -EINVAL; | |
225 | ||
226 | plat->base = addr; | |
8171499d MV |
227 | |
228 | ret = clk_get_by_name(dev, "fck", &sh_serial_clk); | |
791c174d MV |
229 | if (!ret) { |
230 | ret = clk_enable(&sh_serial_clk); | |
231 | if (!ret) | |
232 | plat->clk = clk_get_rate(&sh_serial_clk); | |
233 | } else { | |
8171499d MV |
234 | plat->clk = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), |
235 | "clock", 1); | |
791c174d | 236 | } |
8171499d | 237 | |
359787cf YS |
238 | plat->type = dev_get_driver_data(dev); |
239 | return 0; | |
240 | } | |
241 | #endif | |
242 | ||
59088e4a NI |
243 | U_BOOT_DRIVER(serial_sh) = { |
244 | .name = "serial_sh", | |
245 | .id = UCLASS_SERIAL, | |
359787cf YS |
246 | .of_match = of_match_ptr(sh_serial_id), |
247 | .ofdata_to_platdata = of_match_ptr(sh_serial_ofdata_to_platdata), | |
248 | .platdata_auto_alloc_size = sizeof(struct sh_serial_platdata), | |
59088e4a NI |
249 | .probe = sh_serial_probe, |
250 | .ops = &sh_serial_ops, | |
251 | .flags = DM_FLAG_PRE_RELOC, | |
252 | .priv_auto_alloc_size = sizeof(struct uart_port), | |
253 | }; | |
254 | ||
255 | #else /* CONFIG_DM_SERIAL */ | |
256 | ||
257 | #if defined(CONFIG_CONS_SCIF0) | |
258 | # define SCIF_BASE SCIF0_BASE | |
259 | #elif defined(CONFIG_CONS_SCIF1) | |
260 | # define SCIF_BASE SCIF1_BASE | |
261 | #elif defined(CONFIG_CONS_SCIF2) | |
262 | # define SCIF_BASE SCIF2_BASE | |
263 | #elif defined(CONFIG_CONS_SCIF3) | |
264 | # define SCIF_BASE SCIF3_BASE | |
265 | #elif defined(CONFIG_CONS_SCIF4) | |
266 | # define SCIF_BASE SCIF4_BASE | |
267 | #elif defined(CONFIG_CONS_SCIF5) | |
268 | # define SCIF_BASE SCIF5_BASE | |
269 | #elif defined(CONFIG_CONS_SCIF6) | |
270 | # define SCIF_BASE SCIF6_BASE | |
271 | #elif defined(CONFIG_CONS_SCIF7) | |
272 | # define SCIF_BASE SCIF7_BASE | |
273 | #else | |
274 | # error "Default SCIF doesn't set....." | |
275 | #endif | |
276 | ||
277 | #if defined(CONFIG_SCIF_A) | |
278 | #define SCIF_BASE_PORT PORT_SCIFA | |
747431b9 YS |
279 | #elif defined(CONFIG_SCI) |
280 | #define SCIF_BASE_PORT PORT_SCI | |
59088e4a NI |
281 | #else |
282 | #define SCIF_BASE_PORT PORT_SCIF | |
283 | #endif | |
284 | ||
285 | static struct uart_port sh_sci = { | |
286 | .membase = (unsigned char *)SCIF_BASE, | |
287 | .mapbase = SCIF_BASE, | |
288 | .type = SCIF_BASE_PORT, | |
289 | #ifdef CONFIG_SCIF_USE_EXT_CLK | |
290 | .clk_mode = EXT_CLK, | |
291 | #endif | |
292 | }; | |
293 | ||
294 | static void sh_serial_setbrg(void) | |
295 | { | |
296 | DECLARE_GLOBAL_DATA_PTR; | |
297 | struct uart_port *port = &sh_sci; | |
298 | ||
299 | sh_serial_setbrg_generic(port, CONFIG_SH_SCIF_CLK_FREQ, gd->baudrate); | |
300 | } | |
301 | ||
302 | static int sh_serial_init(void) | |
303 | { | |
304 | struct uart_port *port = &sh_sci; | |
305 | ||
306 | sh_serial_init_generic(port); | |
307 | serial_setbrg(); | |
308 | ||
309 | return 0; | |
310 | } | |
311 | ||
312 | static void sh_serial_putc(const char c) | |
313 | { | |
314 | struct uart_port *port = &sh_sci; | |
315 | ||
316 | if (c == '\n') { | |
317 | while (1) { | |
318 | if (serial_raw_putc(port, '\r') != -EAGAIN) | |
319 | break; | |
320 | } | |
321 | } | |
322 | while (1) { | |
323 | if (serial_raw_putc(port, c) != -EAGAIN) | |
324 | break; | |
325 | } | |
326 | } | |
327 | ||
328 | static int sh_serial_tstc(void) | |
329 | { | |
330 | struct uart_port *port = &sh_sci; | |
331 | ||
332 | return sh_serial_tstc_generic(port); | |
333 | } | |
334 | ||
335 | static int sh_serial_getc(void) | |
336 | { | |
337 | struct uart_port *port = &sh_sci; | |
338 | int ch; | |
339 | ||
340 | while (1) { | |
341 | ch = sh_serial_getc_generic(port); | |
342 | if (ch != -EAGAIN) | |
343 | break; | |
344 | } | |
0b135cfc | 345 | |
08c5fabe | 346 | return ch; |
0b135cfc | 347 | } |
8bdd7efa | 348 | |
8bdd7efa MV |
349 | static struct serial_device sh_serial_drv = { |
350 | .name = "sh_serial", | |
351 | .start = sh_serial_init, | |
352 | .stop = NULL, | |
353 | .setbrg = sh_serial_setbrg, | |
354 | .putc = sh_serial_putc, | |
ec3fd689 | 355 | .puts = default_serial_puts, |
8bdd7efa MV |
356 | .getc = sh_serial_getc, |
357 | .tstc = sh_serial_tstc, | |
358 | }; | |
359 | ||
360 | void sh_serial_initialize(void) | |
361 | { | |
362 | serial_register(&sh_serial_drv); | |
363 | } | |
364 | ||
365 | __weak struct serial_device *default_serial_console(void) | |
366 | { | |
367 | return &sh_serial_drv; | |
368 | } | |
59088e4a | 369 | #endif /* CONFIG_DM_SERIAL */ |