1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Freescale LINFlexD UART serial port driver |
4 | * |
5 | * Copyright 2012-2016 Freescale Semiconductor, Inc. |
6 | * Copyright 2017-2019 NXP |
7 | */ |
8 | |
9 | #include <linux/console.h> |
10 | #include <linux/io.h> |
11 | #include <linux/irq.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/serial_core.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/tty_flip.h> |
18 | #include <linux/delay.h> |
19 | |
20 | /* All registers are 32-bit width */ |
21 | |
22 | #define LINCR1 0x0000 /* LIN control register */ |
23 | #define LINIER 0x0004 /* LIN interrupt enable register */ |
24 | #define LINSR 0x0008 /* LIN status register */ |
25 | #define LINESR 0x000C /* LIN error status register */ |
26 | #define UARTCR 0x0010 /* UART mode control register */ |
27 | #define UARTSR 0x0014 /* UART mode status register */ |
28 | #define LINTCSR 0x0018 /* LIN timeout control status register */ |
29 | #define LINOCR 0x001C /* LIN output compare register */ |
30 | #define LINTOCR 0x0020 /* LIN timeout control register */ |
31 | #define LINFBRR 0x0024 /* LIN fractional baud rate register */ |
32 | #define LINIBRR 0x0028 /* LIN integer baud rate register */ |
33 | #define LINCFR 0x002C /* LIN checksum field register */ |
34 | #define LINCR2 0x0030 /* LIN control register 2 */ |
35 | #define BIDR 0x0034 /* Buffer identifier register */ |
36 | #define BDRL 0x0038 /* Buffer data register least significant */ |
37 | #define BDRM 0x003C /* Buffer data register most significant */ |
38 | #define IFER 0x0040 /* Identifier filter enable register */ |
39 | #define IFMI 0x0044 /* Identifier filter match index */ |
40 | #define IFMR 0x0048 /* Identifier filter mode register */ |
41 | #define GCR 0x004C /* Global control register */ |
42 | #define UARTPTO 0x0050 /* UART preset timeout register */ |
43 | #define UARTCTO 0x0054 /* UART current timeout register */ |
44 | |
45 | /* |
46 | * Register field definitions |
47 | */ |
48 | |
49 | #define LINFLEXD_LINCR1_INIT BIT(0) |
50 | #define LINFLEXD_LINCR1_MME BIT(4) |
51 | #define LINFLEXD_LINCR1_BF BIT(7) |
52 | |
53 | #define LINFLEXD_LINSR_LINS_INITMODE BIT(12) |
54 | #define LINFLEXD_LINSR_LINS_MASK (0xF << 12) |
55 | |
56 | #define LINFLEXD_LINIER_SZIE BIT(15) |
57 | #define LINFLEXD_LINIER_OCIE BIT(14) |
58 | #define LINFLEXD_LINIER_BEIE BIT(13) |
59 | #define LINFLEXD_LINIER_CEIE BIT(12) |
60 | #define LINFLEXD_LINIER_HEIE BIT(11) |
61 | #define LINFLEXD_LINIER_FEIE BIT(8) |
62 | #define LINFLEXD_LINIER_BOIE BIT(7) |
63 | #define LINFLEXD_LINIER_LSIE BIT(6) |
64 | #define LINFLEXD_LINIER_WUIE BIT(5) |
65 | #define LINFLEXD_LINIER_DBFIE BIT(4) |
66 | #define LINFLEXD_LINIER_DBEIETOIE BIT(3) |
67 | #define LINFLEXD_LINIER_DRIE BIT(2) |
68 | #define LINFLEXD_LINIER_DTIE BIT(1) |
69 | #define LINFLEXD_LINIER_HRIE BIT(0) |
70 | |
71 | #define LINFLEXD_UARTCR_OSR_MASK (0xF << 24) |
72 | #define LINFLEXD_UARTCR_OSR(uartcr) (((uartcr) \ |
73 | & LINFLEXD_UARTCR_OSR_MASK) >> 24) |
74 | |
75 | #define LINFLEXD_UARTCR_ROSE BIT(23) |
76 | |
77 | #define LINFLEXD_UARTCR_RFBM BIT(9) |
78 | #define LINFLEXD_UARTCR_TFBM BIT(8) |
79 | #define LINFLEXD_UARTCR_WL1 BIT(7) |
80 | #define LINFLEXD_UARTCR_PC1 BIT(6) |
81 | |
82 | #define LINFLEXD_UARTCR_RXEN BIT(5) |
83 | #define LINFLEXD_UARTCR_TXEN BIT(4) |
84 | #define LINFLEXD_UARTCR_PC0 BIT(3) |
85 | |
86 | #define LINFLEXD_UARTCR_PCE BIT(2) |
87 | #define LINFLEXD_UARTCR_WL0 BIT(1) |
88 | #define LINFLEXD_UARTCR_UART BIT(0) |
89 | |
90 | #define LINFLEXD_UARTSR_SZF BIT(15) |
91 | #define LINFLEXD_UARTSR_OCF BIT(14) |
92 | #define LINFLEXD_UARTSR_PE3 BIT(13) |
93 | #define LINFLEXD_UARTSR_PE2 BIT(12) |
94 | #define LINFLEXD_UARTSR_PE1 BIT(11) |
95 | #define LINFLEXD_UARTSR_PE0 BIT(10) |
96 | #define LINFLEXD_UARTSR_RMB BIT(9) |
97 | #define LINFLEXD_UARTSR_FEF BIT(8) |
98 | #define LINFLEXD_UARTSR_BOF BIT(7) |
99 | #define LINFLEXD_UARTSR_RPS BIT(6) |
100 | #define LINFLEXD_UARTSR_WUF BIT(5) |
101 | #define LINFLEXD_UARTSR_4 BIT(4) |
102 | |
103 | #define LINFLEXD_UARTSR_TO BIT(3) |
104 | |
105 | #define LINFLEXD_UARTSR_DRFRFE BIT(2) |
106 | #define LINFLEXD_UARTSR_DTFTFF BIT(1) |
107 | #define LINFLEXD_UARTSR_NF BIT(0) |
108 | #define LINFLEXD_UARTSR_PE (LINFLEXD_UARTSR_PE0 |\ |
109 | LINFLEXD_UARTSR_PE1 |\ |
110 | LINFLEXD_UARTSR_PE2 |\ |
111 | LINFLEXD_UARTSR_PE3) |
112 | |
113 | #define LINFLEX_LDIV_MULTIPLIER (16) |
114 | |
115 | #define DRIVER_NAME "fsl-linflexuart" |
116 | #define DEV_NAME "ttyLF" |
117 | #define UART_NR 4 |
118 | |
119 | #define EARLYCON_BUFFER_INITIAL_CAP 8 |
120 | |
121 | #define PREINIT_DELAY 2000 /* us */ |
122 | |
123 | static const struct of_device_id linflex_dt_ids[] = { |
124 | { |
125 | .compatible = "fsl,s32v234-linflexuart" , |
126 | }, |
127 | { /* sentinel */ } |
128 | }; |
129 | MODULE_DEVICE_TABLE(of, linflex_dt_ids); |
130 | |
131 | #ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE |
132 | static struct uart_port *earlycon_port; |
133 | static bool linflex_earlycon_same_instance; |
134 | static DEFINE_SPINLOCK(init_lock); |
135 | static bool during_init; |
136 | |
137 | static struct { |
138 | char *content; |
139 | unsigned int len, cap; |
140 | } earlycon_buf; |
141 | #endif |
142 | |
143 | static void linflex_stop_tx(struct uart_port *port) |
144 | { |
145 | unsigned long ier; |
146 | |
147 | ier = readl(addr: port->membase + LINIER); |
148 | ier &= ~(LINFLEXD_LINIER_DTIE); |
149 | writel(val: ier, addr: port->membase + LINIER); |
150 | } |
151 | |
152 | static void linflex_stop_rx(struct uart_port *port) |
153 | { |
154 | unsigned long ier; |
155 | |
156 | ier = readl(addr: port->membase + LINIER); |
157 | writel(val: ier & ~LINFLEXD_LINIER_DRIE, addr: port->membase + LINIER); |
158 | } |
159 | |
160 | static void linflex_put_char(struct uart_port *sport, unsigned char c) |
161 | { |
162 | unsigned long status; |
163 | |
164 | writeb(val: c, addr: sport->membase + BDRL); |
165 | |
166 | /* Waiting for data transmission completed. */ |
167 | while (((status = readl(addr: sport->membase + UARTSR)) & |
168 | LINFLEXD_UARTSR_DTFTFF) != |
169 | LINFLEXD_UARTSR_DTFTFF) |
170 | ; |
171 | |
172 | writel(val: status | LINFLEXD_UARTSR_DTFTFF, addr: sport->membase + UARTSR); |
173 | } |
174 | |
175 | static inline void linflex_transmit_buffer(struct uart_port *sport) |
176 | { |
177 | struct circ_buf *xmit = &sport->state->xmit; |
178 | |
179 | while (!uart_circ_empty(xmit)) { |
180 | linflex_put_char(sport, c: xmit->buf[xmit->tail]); |
181 | uart_xmit_advance(up: sport, chars: 1); |
182 | } |
183 | |
184 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) |
185 | uart_write_wakeup(port: sport); |
186 | |
187 | if (uart_circ_empty(xmit)) |
188 | linflex_stop_tx(port: sport); |
189 | } |
190 | |
191 | static void linflex_start_tx(struct uart_port *port) |
192 | { |
193 | unsigned long ier; |
194 | |
195 | linflex_transmit_buffer(sport: port); |
196 | ier = readl(addr: port->membase + LINIER); |
197 | writel(val: ier | LINFLEXD_LINIER_DTIE, addr: port->membase + LINIER); |
198 | } |
199 | |
200 | static irqreturn_t linflex_txint(int irq, void *dev_id) |
201 | { |
202 | struct uart_port *sport = dev_id; |
203 | struct circ_buf *xmit = &sport->state->xmit; |
204 | unsigned long flags; |
205 | |
206 | uart_port_lock_irqsave(up: sport, flags: &flags); |
207 | |
208 | if (sport->x_char) { |
209 | linflex_put_char(sport, c: sport->x_char); |
210 | goto out; |
211 | } |
212 | |
213 | if (uart_circ_empty(xmit) || uart_tx_stopped(port: sport)) { |
214 | linflex_stop_tx(port: sport); |
215 | goto out; |
216 | } |
217 | |
218 | linflex_transmit_buffer(sport); |
219 | out: |
220 | uart_port_unlock_irqrestore(up: sport, flags); |
221 | return IRQ_HANDLED; |
222 | } |
223 | |
224 | static irqreturn_t linflex_rxint(int irq, void *dev_id) |
225 | { |
226 | struct uart_port *sport = dev_id; |
227 | unsigned int flg; |
228 | struct tty_port *port = &sport->state->port; |
229 | unsigned long flags, status; |
230 | unsigned char rx; |
231 | bool brk; |
232 | |
233 | uart_port_lock_irqsave(up: sport, flags: &flags); |
234 | |
235 | status = readl(addr: sport->membase + UARTSR); |
236 | while (status & LINFLEXD_UARTSR_RMB) { |
237 | rx = readb(addr: sport->membase + BDRM); |
238 | brk = false; |
239 | flg = TTY_NORMAL; |
240 | sport->icount.rx++; |
241 | |
242 | if (status & (LINFLEXD_UARTSR_BOF | LINFLEXD_UARTSR_FEF | |
243 | LINFLEXD_UARTSR_PE)) { |
244 | if (status & LINFLEXD_UARTSR_BOF) |
245 | sport->icount.overrun++; |
246 | if (status & LINFLEXD_UARTSR_FEF) { |
247 | if (!rx) { |
248 | brk = true; |
249 | sport->icount.brk++; |
250 | } else |
251 | sport->icount.frame++; |
252 | } |
253 | if (status & LINFLEXD_UARTSR_PE) |
254 | sport->icount.parity++; |
255 | } |
256 | |
257 | writel(val: status, addr: sport->membase + UARTSR); |
258 | status = readl(addr: sport->membase + UARTSR); |
259 | |
260 | if (brk) { |
261 | uart_handle_break(port: sport); |
262 | } else { |
263 | if (uart_handle_sysrq_char(port: sport, ch: (unsigned char)rx)) |
264 | continue; |
265 | tty_insert_flip_char(port, ch: rx, flag: flg); |
266 | } |
267 | } |
268 | |
269 | uart_port_unlock_irqrestore(up: sport, flags); |
270 | |
271 | tty_flip_buffer_push(port); |
272 | |
273 | return IRQ_HANDLED; |
274 | } |
275 | |
276 | static irqreturn_t linflex_int(int irq, void *dev_id) |
277 | { |
278 | struct uart_port *sport = dev_id; |
279 | unsigned long status; |
280 | |
281 | status = readl(addr: sport->membase + UARTSR); |
282 | |
283 | if (status & LINFLEXD_UARTSR_DRFRFE) |
284 | linflex_rxint(irq, dev_id); |
285 | if (status & LINFLEXD_UARTSR_DTFTFF) |
286 | linflex_txint(irq, dev_id); |
287 | |
288 | return IRQ_HANDLED; |
289 | } |
290 | |
291 | /* return TIOCSER_TEMT when transmitter is not busy */ |
292 | static unsigned int linflex_tx_empty(struct uart_port *port) |
293 | { |
294 | unsigned long status; |
295 | |
296 | status = readl(addr: port->membase + UARTSR) & LINFLEXD_UARTSR_DTFTFF; |
297 | |
298 | return status ? TIOCSER_TEMT : 0; |
299 | } |
300 | |
301 | static unsigned int linflex_get_mctrl(struct uart_port *port) |
302 | { |
303 | return 0; |
304 | } |
305 | |
306 | static void linflex_set_mctrl(struct uart_port *port, unsigned int mctrl) |
307 | { |
308 | } |
309 | |
310 | static void linflex_break_ctl(struct uart_port *port, int break_state) |
311 | { |
312 | } |
313 | |
314 | static void linflex_setup_watermark(struct uart_port *sport) |
315 | { |
316 | unsigned long cr, ier, cr1; |
317 | |
318 | /* Disable transmission/reception */ |
319 | ier = readl(addr: sport->membase + LINIER); |
320 | ier &= ~(LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE); |
321 | writel(val: ier, addr: sport->membase + LINIER); |
322 | |
323 | cr = readl(addr: sport->membase + UARTCR); |
324 | cr &= ~(LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN); |
325 | writel(val: cr, addr: sport->membase + UARTCR); |
326 | |
327 | /* Enter initialization mode by setting INIT bit */ |
328 | |
329 | /* set the Linflex in master mode and activate by-pass filter */ |
330 | cr1 = LINFLEXD_LINCR1_BF | LINFLEXD_LINCR1_MME |
331 | | LINFLEXD_LINCR1_INIT; |
332 | writel(val: cr1, addr: sport->membase + LINCR1); |
333 | |
334 | /* wait for init mode entry */ |
335 | while ((readl(addr: sport->membase + LINSR) |
336 | & LINFLEXD_LINSR_LINS_MASK) |
337 | != LINFLEXD_LINSR_LINS_INITMODE) |
338 | ; |
339 | |
340 | /* |
341 | * UART = 0x1; - Linflex working in UART mode |
342 | * TXEN = 0x1; - Enable transmission of data now |
343 | * RXEn = 0x1; - Receiver enabled |
344 | * WL0 = 0x1; - 8 bit data |
345 | * PCE = 0x0; - No parity |
346 | */ |
347 | |
348 | /* set UART bit to allow writing other bits */ |
349 | writel(LINFLEXD_UARTCR_UART, addr: sport->membase + UARTCR); |
350 | |
351 | cr = (LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN | |
352 | LINFLEXD_UARTCR_WL0 | LINFLEXD_UARTCR_UART); |
353 | |
354 | writel(val: cr, addr: sport->membase + UARTCR); |
355 | |
356 | cr1 &= ~(LINFLEXD_LINCR1_INIT); |
357 | |
358 | writel(val: cr1, addr: sport->membase + LINCR1); |
359 | |
360 | ier = readl(addr: sport->membase + LINIER); |
361 | ier |= LINFLEXD_LINIER_DRIE; |
362 | ier |= LINFLEXD_LINIER_DTIE; |
363 | |
364 | writel(val: ier, addr: sport->membase + LINIER); |
365 | } |
366 | |
367 | static int linflex_startup(struct uart_port *port) |
368 | { |
369 | int ret = 0; |
370 | unsigned long flags; |
371 | |
372 | uart_port_lock_irqsave(up: port, flags: &flags); |
373 | |
374 | linflex_setup_watermark(sport: port); |
375 | |
376 | uart_port_unlock_irqrestore(up: port, flags); |
377 | |
378 | ret = devm_request_irq(dev: port->dev, irq: port->irq, handler: linflex_int, irqflags: 0, |
379 | DRIVER_NAME, dev_id: port); |
380 | |
381 | return ret; |
382 | } |
383 | |
384 | static void linflex_shutdown(struct uart_port *port) |
385 | { |
386 | unsigned long ier; |
387 | unsigned long flags; |
388 | |
389 | uart_port_lock_irqsave(up: port, flags: &flags); |
390 | |
391 | /* disable interrupts */ |
392 | ier = readl(addr: port->membase + LINIER); |
393 | ier &= ~(LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE); |
394 | writel(val: ier, addr: port->membase + LINIER); |
395 | |
396 | uart_port_unlock_irqrestore(up: port, flags); |
397 | |
398 | devm_free_irq(dev: port->dev, irq: port->irq, dev_id: port); |
399 | } |
400 | |
401 | static void |
402 | linflex_set_termios(struct uart_port *port, struct ktermios *termios, |
403 | const struct ktermios *old) |
404 | { |
405 | unsigned long flags; |
406 | unsigned long cr, old_cr, cr1; |
407 | unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; |
408 | |
409 | cr = readl(addr: port->membase + UARTCR); |
410 | old_cr = cr; |
411 | |
412 | /* Enter initialization mode by setting INIT bit */ |
413 | cr1 = readl(addr: port->membase + LINCR1); |
414 | cr1 |= LINFLEXD_LINCR1_INIT; |
415 | writel(val: cr1, addr: port->membase + LINCR1); |
416 | |
417 | /* wait for init mode entry */ |
418 | while ((readl(addr: port->membase + LINSR) |
419 | & LINFLEXD_LINSR_LINS_MASK) |
420 | != LINFLEXD_LINSR_LINS_INITMODE) |
421 | ; |
422 | |
423 | /* |
424 | * only support CS8 and CS7, and for CS7 must enable PE. |
425 | * supported mode: |
426 | * - (7,e/o,1) |
427 | * - (8,n,1) |
428 | * - (8,e/o,1) |
429 | */ |
430 | /* enter the UART into configuration mode */ |
431 | |
432 | while ((termios->c_cflag & CSIZE) != CS8 && |
433 | (termios->c_cflag & CSIZE) != CS7) { |
434 | termios->c_cflag &= ~CSIZE; |
435 | termios->c_cflag |= old_csize; |
436 | old_csize = CS8; |
437 | } |
438 | |
439 | if ((termios->c_cflag & CSIZE) == CS7) { |
440 | /* Word length: WL1WL0:00 */ |
441 | cr = old_cr & ~LINFLEXD_UARTCR_WL1 & ~LINFLEXD_UARTCR_WL0; |
442 | } |
443 | |
444 | if ((termios->c_cflag & CSIZE) == CS8) { |
445 | /* Word length: WL1WL0:01 */ |
446 | cr = (old_cr | LINFLEXD_UARTCR_WL0) & ~LINFLEXD_UARTCR_WL1; |
447 | } |
448 | |
449 | if (termios->c_cflag & CMSPAR) { |
450 | if ((termios->c_cflag & CSIZE) != CS8) { |
451 | termios->c_cflag &= ~CSIZE; |
452 | termios->c_cflag |= CS8; |
453 | } |
454 | /* has a space/sticky bit */ |
455 | cr |= LINFLEXD_UARTCR_WL0; |
456 | } |
457 | |
458 | if (termios->c_cflag & CSTOPB) |
459 | termios->c_cflag &= ~CSTOPB; |
460 | |
461 | /* parity must be enabled when CS7 to match 8-bits format */ |
462 | if ((termios->c_cflag & CSIZE) == CS7) |
463 | termios->c_cflag |= PARENB; |
464 | |
465 | if ((termios->c_cflag & PARENB)) { |
466 | cr |= LINFLEXD_UARTCR_PCE; |
467 | if (termios->c_cflag & PARODD) |
468 | cr = (cr | LINFLEXD_UARTCR_PC0) & |
469 | (~LINFLEXD_UARTCR_PC1); |
470 | else |
471 | cr = cr & (~LINFLEXD_UARTCR_PC1 & |
472 | ~LINFLEXD_UARTCR_PC0); |
473 | } else { |
474 | cr &= ~LINFLEXD_UARTCR_PCE; |
475 | } |
476 | |
477 | uart_port_lock_irqsave(up: port, flags: &flags); |
478 | |
479 | port->read_status_mask = 0; |
480 | |
481 | if (termios->c_iflag & INPCK) |
482 | port->read_status_mask |= (LINFLEXD_UARTSR_FEF | |
483 | LINFLEXD_UARTSR_PE0 | |
484 | LINFLEXD_UARTSR_PE1 | |
485 | LINFLEXD_UARTSR_PE2 | |
486 | LINFLEXD_UARTSR_PE3); |
487 | if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) |
488 | port->read_status_mask |= LINFLEXD_UARTSR_FEF; |
489 | |
490 | /* characters to ignore */ |
491 | port->ignore_status_mask = 0; |
492 | if (termios->c_iflag & IGNPAR) |
493 | port->ignore_status_mask |= LINFLEXD_UARTSR_PE; |
494 | if (termios->c_iflag & IGNBRK) { |
495 | port->ignore_status_mask |= LINFLEXD_UARTSR_PE; |
496 | /* |
497 | * if we're ignoring parity and break indicators, |
498 | * ignore overruns too (for real raw support). |
499 | */ |
500 | if (termios->c_iflag & IGNPAR) |
501 | port->ignore_status_mask |= LINFLEXD_UARTSR_BOF; |
502 | } |
503 | |
504 | writel(val: cr, addr: port->membase + UARTCR); |
505 | |
506 | cr1 &= ~(LINFLEXD_LINCR1_INIT); |
507 | |
508 | writel(val: cr1, addr: port->membase + LINCR1); |
509 | |
510 | uart_port_unlock_irqrestore(up: port, flags); |
511 | } |
512 | |
513 | static const char *linflex_type(struct uart_port *port) |
514 | { |
515 | return "FSL_LINFLEX" ; |
516 | } |
517 | |
518 | static void linflex_release_port(struct uart_port *port) |
519 | { |
520 | /* nothing to do */ |
521 | } |
522 | |
523 | static int linflex_request_port(struct uart_port *port) |
524 | { |
525 | return 0; |
526 | } |
527 | |
528 | /* configure/auto-configure the port */ |
529 | static void linflex_config_port(struct uart_port *port, int flags) |
530 | { |
531 | if (flags & UART_CONFIG_TYPE) |
532 | port->type = PORT_LINFLEXUART; |
533 | } |
534 | |
535 | static const struct uart_ops linflex_pops = { |
536 | .tx_empty = linflex_tx_empty, |
537 | .set_mctrl = linflex_set_mctrl, |
538 | .get_mctrl = linflex_get_mctrl, |
539 | .stop_tx = linflex_stop_tx, |
540 | .start_tx = linflex_start_tx, |
541 | .stop_rx = linflex_stop_rx, |
542 | .break_ctl = linflex_break_ctl, |
543 | .startup = linflex_startup, |
544 | .shutdown = linflex_shutdown, |
545 | .set_termios = linflex_set_termios, |
546 | .type = linflex_type, |
547 | .request_port = linflex_request_port, |
548 | .release_port = linflex_release_port, |
549 | .config_port = linflex_config_port, |
550 | }; |
551 | |
552 | static struct uart_port *linflex_ports[UART_NR]; |
553 | |
554 | #ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE |
555 | static void linflex_console_putchar(struct uart_port *port, unsigned char ch) |
556 | { |
557 | unsigned long cr; |
558 | |
559 | cr = readl(addr: port->membase + UARTCR); |
560 | |
561 | writeb(val: ch, addr: port->membase + BDRL); |
562 | |
563 | if (!(cr & LINFLEXD_UARTCR_TFBM)) |
564 | while ((readl(addr: port->membase + UARTSR) & |
565 | LINFLEXD_UARTSR_DTFTFF) |
566 | != LINFLEXD_UARTSR_DTFTFF) |
567 | ; |
568 | else |
569 | while (readl(addr: port->membase + UARTSR) & |
570 | LINFLEXD_UARTSR_DTFTFF) |
571 | ; |
572 | |
573 | if (!(cr & LINFLEXD_UARTCR_TFBM)) { |
574 | writel(val: (readl(addr: port->membase + UARTSR) | |
575 | LINFLEXD_UARTSR_DTFTFF), |
576 | addr: port->membase + UARTSR); |
577 | } |
578 | } |
579 | |
580 | static void linflex_earlycon_putchar(struct uart_port *port, unsigned char ch) |
581 | { |
582 | unsigned long flags; |
583 | char *ret; |
584 | |
585 | if (!linflex_earlycon_same_instance) { |
586 | linflex_console_putchar(port, ch); |
587 | return; |
588 | } |
589 | |
590 | spin_lock_irqsave(&init_lock, flags); |
591 | if (!during_init) |
592 | goto outside_init; |
593 | |
594 | if (earlycon_buf.len >= 1 << CONFIG_LOG_BUF_SHIFT) |
595 | goto init_release; |
596 | |
597 | if (!earlycon_buf.cap) { |
598 | earlycon_buf.content = kmalloc(EARLYCON_BUFFER_INITIAL_CAP, |
599 | GFP_ATOMIC); |
600 | earlycon_buf.cap = earlycon_buf.content ? |
601 | EARLYCON_BUFFER_INITIAL_CAP : 0; |
602 | } else if (earlycon_buf.len == earlycon_buf.cap) { |
603 | ret = krealloc(objp: earlycon_buf.content, new_size: earlycon_buf.cap << 1, |
604 | GFP_ATOMIC); |
605 | if (ret) { |
606 | earlycon_buf.content = ret; |
607 | earlycon_buf.cap <<= 1; |
608 | } |
609 | } |
610 | |
611 | if (earlycon_buf.len < earlycon_buf.cap) |
612 | earlycon_buf.content[earlycon_buf.len++] = ch; |
613 | |
614 | goto init_release; |
615 | |
616 | outside_init: |
617 | linflex_console_putchar(port, ch); |
618 | init_release: |
619 | spin_unlock_irqrestore(lock: &init_lock, flags); |
620 | } |
621 | |
622 | static void linflex_string_write(struct uart_port *sport, const char *s, |
623 | unsigned int count) |
624 | { |
625 | unsigned long cr, ier = 0; |
626 | |
627 | ier = readl(addr: sport->membase + LINIER); |
628 | linflex_stop_tx(port: sport); |
629 | |
630 | cr = readl(addr: sport->membase + UARTCR); |
631 | cr |= (LINFLEXD_UARTCR_TXEN); |
632 | writel(val: cr, addr: sport->membase + UARTCR); |
633 | |
634 | uart_console_write(port: sport, s, count, putchar: linflex_console_putchar); |
635 | |
636 | writel(val: ier, addr: sport->membase + LINIER); |
637 | } |
638 | |
639 | static void |
640 | linflex_console_write(struct console *co, const char *s, unsigned int count) |
641 | { |
642 | struct uart_port *sport = linflex_ports[co->index]; |
643 | unsigned long flags; |
644 | int locked = 1; |
645 | |
646 | if (sport->sysrq) |
647 | locked = 0; |
648 | else if (oops_in_progress) |
649 | locked = uart_port_trylock_irqsave(up: sport, flags: &flags); |
650 | else |
651 | uart_port_lock_irqsave(up: sport, flags: &flags); |
652 | |
653 | linflex_string_write(sport, s, count); |
654 | |
655 | if (locked) |
656 | uart_port_unlock_irqrestore(up: sport, flags); |
657 | } |
658 | |
659 | /* |
660 | * if the port was already initialised (eg, by a boot loader), |
661 | * try to determine the current setup. |
662 | */ |
663 | static void __init |
664 | linflex_console_get_options(struct uart_port *sport, int *parity, int *bits) |
665 | { |
666 | unsigned long cr; |
667 | |
668 | cr = readl(addr: sport->membase + UARTCR); |
669 | cr &= LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN; |
670 | |
671 | if (!cr) |
672 | return; |
673 | |
674 | /* ok, the port was enabled */ |
675 | |
676 | *parity = 'n'; |
677 | if (cr & LINFLEXD_UARTCR_PCE) { |
678 | if (cr & LINFLEXD_UARTCR_PC0) |
679 | *parity = 'o'; |
680 | else |
681 | *parity = 'e'; |
682 | } |
683 | |
684 | if ((cr & LINFLEXD_UARTCR_WL0) && ((cr & LINFLEXD_UARTCR_WL1) == 0)) { |
685 | if (cr & LINFLEXD_UARTCR_PCE) |
686 | *bits = 9; |
687 | else |
688 | *bits = 8; |
689 | } |
690 | } |
691 | |
692 | static int __init linflex_console_setup(struct console *co, char *options) |
693 | { |
694 | struct uart_port *sport; |
695 | int baud = 115200; |
696 | int bits = 8; |
697 | int parity = 'n'; |
698 | int flow = 'n'; |
699 | int ret; |
700 | int i; |
701 | unsigned long flags; |
702 | /* |
703 | * check whether an invalid uart number has been specified, and |
704 | * if so, search for the first available port that does have |
705 | * console support. |
706 | */ |
707 | if (co->index == -1 || co->index >= ARRAY_SIZE(linflex_ports)) |
708 | co->index = 0; |
709 | |
710 | sport = linflex_ports[co->index]; |
711 | if (!sport) |
712 | return -ENODEV; |
713 | |
714 | if (options) |
715 | uart_parse_options(options, baud: &baud, parity: &parity, bits: &bits, flow: &flow); |
716 | else |
717 | linflex_console_get_options(sport, parity: &parity, bits: &bits); |
718 | |
719 | if (earlycon_port && sport->mapbase == earlycon_port->mapbase) { |
720 | linflex_earlycon_same_instance = true; |
721 | |
722 | spin_lock_irqsave(&init_lock, flags); |
723 | during_init = true; |
724 | spin_unlock_irqrestore(lock: &init_lock, flags); |
725 | |
726 | /* Workaround for character loss or output of many invalid |
727 | * characters, when INIT mode is entered shortly after a |
728 | * character has just been printed. |
729 | */ |
730 | udelay(PREINIT_DELAY); |
731 | } |
732 | |
733 | linflex_setup_watermark(sport); |
734 | |
735 | ret = uart_set_options(port: sport, co, baud, parity, bits, flow); |
736 | |
737 | if (!linflex_earlycon_same_instance) |
738 | goto done; |
739 | |
740 | spin_lock_irqsave(&init_lock, flags); |
741 | |
742 | /* Emptying buffer */ |
743 | if (earlycon_buf.len) { |
744 | for (i = 0; i < earlycon_buf.len; i++) |
745 | linflex_console_putchar(port: earlycon_port, |
746 | ch: earlycon_buf.content[i]); |
747 | |
748 | kfree(objp: earlycon_buf.content); |
749 | earlycon_buf.len = 0; |
750 | } |
751 | |
752 | during_init = false; |
753 | spin_unlock_irqrestore(lock: &init_lock, flags); |
754 | |
755 | done: |
756 | return ret; |
757 | } |
758 | |
759 | static struct uart_driver linflex_reg; |
760 | static struct console linflex_console = { |
761 | .name = DEV_NAME, |
762 | .write = linflex_console_write, |
763 | .device = uart_console_device, |
764 | .setup = linflex_console_setup, |
765 | .flags = CON_PRINTBUFFER, |
766 | .index = -1, |
767 | .data = &linflex_reg, |
768 | }; |
769 | |
770 | static void linflex_earlycon_write(struct console *con, const char *s, |
771 | unsigned int n) |
772 | { |
773 | struct earlycon_device *dev = con->data; |
774 | |
775 | uart_console_write(port: &dev->port, s, count: n, putchar: linflex_earlycon_putchar); |
776 | } |
777 | |
778 | static int __init linflex_early_console_setup(struct earlycon_device *device, |
779 | const char *options) |
780 | { |
781 | if (!device->port.membase) |
782 | return -ENODEV; |
783 | |
784 | device->con->write = linflex_earlycon_write; |
785 | earlycon_port = &device->port; |
786 | |
787 | return 0; |
788 | } |
789 | |
790 | OF_EARLYCON_DECLARE(linflex, "fsl,s32v234-linflexuart" , |
791 | linflex_early_console_setup); |
792 | |
793 | #define LINFLEX_CONSOLE (&linflex_console) |
794 | #else |
795 | #define LINFLEX_CONSOLE NULL |
796 | #endif |
797 | |
798 | static struct uart_driver linflex_reg = { |
799 | .owner = THIS_MODULE, |
800 | .driver_name = DRIVER_NAME, |
801 | .dev_name = DEV_NAME, |
802 | .nr = ARRAY_SIZE(linflex_ports), |
803 | .cons = LINFLEX_CONSOLE, |
804 | }; |
805 | |
806 | static int linflex_probe(struct platform_device *pdev) |
807 | { |
808 | struct device_node *np = pdev->dev.of_node; |
809 | struct uart_port *sport; |
810 | struct resource *res; |
811 | int ret; |
812 | |
813 | sport = devm_kzalloc(dev: &pdev->dev, size: sizeof(*sport), GFP_KERNEL); |
814 | if (!sport) |
815 | return -ENOMEM; |
816 | |
817 | ret = of_alias_get_id(np, stem: "serial" ); |
818 | if (ret < 0) { |
819 | dev_err(&pdev->dev, "failed to get alias id, errno %d\n" , ret); |
820 | return ret; |
821 | } |
822 | if (ret >= UART_NR) { |
823 | dev_err(&pdev->dev, "driver limited to %d serial ports\n" , |
824 | UART_NR); |
825 | return -ENOMEM; |
826 | } |
827 | |
828 | sport->line = ret; |
829 | |
830 | sport->membase = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
831 | if (IS_ERR(ptr: sport->membase)) |
832 | return PTR_ERR(ptr: sport->membase); |
833 | sport->mapbase = res->start; |
834 | |
835 | ret = platform_get_irq(pdev, 0); |
836 | if (ret < 0) |
837 | return ret; |
838 | |
839 | sport->dev = &pdev->dev; |
840 | sport->iotype = UPIO_MEM; |
841 | sport->irq = ret; |
842 | sport->ops = &linflex_pops; |
843 | sport->flags = UPF_BOOT_AUTOCONF; |
844 | sport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE); |
845 | |
846 | linflex_ports[sport->line] = sport; |
847 | |
848 | platform_set_drvdata(pdev, data: sport); |
849 | |
850 | return uart_add_one_port(reg: &linflex_reg, port: sport); |
851 | } |
852 | |
853 | static void linflex_remove(struct platform_device *pdev) |
854 | { |
855 | struct uart_port *sport = platform_get_drvdata(pdev); |
856 | |
857 | uart_remove_one_port(reg: &linflex_reg, port: sport); |
858 | } |
859 | |
860 | #ifdef CONFIG_PM_SLEEP |
861 | static int linflex_suspend(struct device *dev) |
862 | { |
863 | struct uart_port *sport = dev_get_drvdata(dev); |
864 | |
865 | uart_suspend_port(reg: &linflex_reg, port: sport); |
866 | |
867 | return 0; |
868 | } |
869 | |
870 | static int linflex_resume(struct device *dev) |
871 | { |
872 | struct uart_port *sport = dev_get_drvdata(dev); |
873 | |
874 | uart_resume_port(reg: &linflex_reg, port: sport); |
875 | |
876 | return 0; |
877 | } |
878 | #endif |
879 | |
880 | static SIMPLE_DEV_PM_OPS(linflex_pm_ops, linflex_suspend, linflex_resume); |
881 | |
882 | static struct platform_driver linflex_driver = { |
883 | .probe = linflex_probe, |
884 | .remove_new = linflex_remove, |
885 | .driver = { |
886 | .name = DRIVER_NAME, |
887 | .of_match_table = linflex_dt_ids, |
888 | .pm = &linflex_pm_ops, |
889 | }, |
890 | }; |
891 | |
892 | static int __init linflex_serial_init(void) |
893 | { |
894 | int ret; |
895 | |
896 | ret = uart_register_driver(uart: &linflex_reg); |
897 | if (ret) |
898 | return ret; |
899 | |
900 | ret = platform_driver_register(&linflex_driver); |
901 | if (ret) |
902 | uart_unregister_driver(uart: &linflex_reg); |
903 | |
904 | return ret; |
905 | } |
906 | |
907 | static void __exit linflex_serial_exit(void) |
908 | { |
909 | platform_driver_unregister(&linflex_driver); |
910 | uart_unregister_driver(uart: &linflex_reg); |
911 | } |
912 | |
913 | module_init(linflex_serial_init); |
914 | module_exit(linflex_serial_exit); |
915 | |
916 | MODULE_DESCRIPTION("Freescale LINFlexD serial port driver" ); |
917 | MODULE_LICENSE("GPL v2" ); |
918 | |