1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * High Speed Serial Ports on NXP LPC32xx SoC |
4 | * |
5 | * Authors: Kevin Wells <kevin.wells@nxp.com> |
6 | * Roland Stigge <stigge@antcom.de> |
7 | * |
8 | * Copyright (C) 2010 NXP Semiconductors |
9 | * Copyright (C) 2012 Roland Stigge |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/ioport.h> |
14 | #include <linux/init.h> |
15 | #include <linux/console.h> |
16 | #include <linux/sysrq.h> |
17 | #include <linux/tty.h> |
18 | #include <linux/tty_flip.h> |
19 | #include <linux/serial_core.h> |
20 | #include <linux/serial.h> |
21 | #include <linux/platform_device.h> |
22 | #include <linux/delay.h> |
23 | #include <linux/nmi.h> |
24 | #include <linux/io.h> |
25 | #include <linux/irq.h> |
26 | #include <linux/of.h> |
27 | #include <linux/sizes.h> |
28 | #include <linux/soc/nxp/lpc32xx-misc.h> |
29 | |
30 | /* |
31 | * High Speed UART register offsets |
32 | */ |
33 | #define LPC32XX_HSUART_FIFO(x) ((x) + 0x00) |
34 | #define LPC32XX_HSUART_LEVEL(x) ((x) + 0x04) |
35 | #define LPC32XX_HSUART_IIR(x) ((x) + 0x08) |
36 | #define LPC32XX_HSUART_CTRL(x) ((x) + 0x0C) |
37 | #define LPC32XX_HSUART_RATE(x) ((x) + 0x10) |
38 | |
39 | #define LPC32XX_HSU_BREAK_DATA (1 << 10) |
40 | #define LPC32XX_HSU_ERROR_DATA (1 << 9) |
41 | #define LPC32XX_HSU_RX_EMPTY (1 << 8) |
42 | |
43 | #define LPC32XX_HSU_TX_LEV(n) (((n) >> 8) & 0xFF) |
44 | #define LPC32XX_HSU_RX_LEV(n) ((n) & 0xFF) |
45 | |
46 | #define LPC32XX_HSU_TX_INT_SET (1 << 6) |
47 | #define LPC32XX_HSU_RX_OE_INT (1 << 5) |
48 | #define LPC32XX_HSU_BRK_INT (1 << 4) |
49 | #define LPC32XX_HSU_FE_INT (1 << 3) |
50 | #define LPC32XX_HSU_RX_TIMEOUT_INT (1 << 2) |
51 | #define LPC32XX_HSU_RX_TRIG_INT (1 << 1) |
52 | #define LPC32XX_HSU_TX_INT (1 << 0) |
53 | |
54 | #define LPC32XX_HSU_HRTS_INV (1 << 21) |
55 | #define LPC32XX_HSU_HRTS_TRIG_8B (0x0 << 19) |
56 | #define LPC32XX_HSU_HRTS_TRIG_16B (0x1 << 19) |
57 | #define LPC32XX_HSU_HRTS_TRIG_32B (0x2 << 19) |
58 | #define LPC32XX_HSU_HRTS_TRIG_48B (0x3 << 19) |
59 | #define LPC32XX_HSU_HRTS_EN (1 << 18) |
60 | #define LPC32XX_HSU_TMO_DISABLED (0x0 << 16) |
61 | #define LPC32XX_HSU_TMO_INACT_4B (0x1 << 16) |
62 | #define LPC32XX_HSU_TMO_INACT_8B (0x2 << 16) |
63 | #define LPC32XX_HSU_TMO_INACT_16B (0x3 << 16) |
64 | #define LPC32XX_HSU_HCTS_INV (1 << 15) |
65 | #define LPC32XX_HSU_HCTS_EN (1 << 14) |
66 | #define LPC32XX_HSU_OFFSET(n) ((n) << 9) |
67 | #define LPC32XX_HSU_BREAK (1 << 8) |
68 | #define LPC32XX_HSU_ERR_INT_EN (1 << 7) |
69 | #define LPC32XX_HSU_RX_INT_EN (1 << 6) |
70 | #define LPC32XX_HSU_TX_INT_EN (1 << 5) |
71 | #define LPC32XX_HSU_RX_TL1B (0x0 << 2) |
72 | #define LPC32XX_HSU_RX_TL4B (0x1 << 2) |
73 | #define LPC32XX_HSU_RX_TL8B (0x2 << 2) |
74 | #define LPC32XX_HSU_RX_TL16B (0x3 << 2) |
75 | #define LPC32XX_HSU_RX_TL32B (0x4 << 2) |
76 | #define LPC32XX_HSU_RX_TL48B (0x5 << 2) |
77 | #define LPC32XX_HSU_TX_TLEMPTY (0x0 << 0) |
78 | #define LPC32XX_HSU_TX_TL0B (0x0 << 0) |
79 | #define LPC32XX_HSU_TX_TL4B (0x1 << 0) |
80 | #define LPC32XX_HSU_TX_TL8B (0x2 << 0) |
81 | #define LPC32XX_HSU_TX_TL16B (0x3 << 0) |
82 | |
83 | #define LPC32XX_MAIN_OSC_FREQ 13000000 |
84 | |
85 | #define MODNAME "lpc32xx_hsuart" |
86 | |
87 | struct lpc32xx_hsuart_port { |
88 | struct uart_port port; |
89 | }; |
90 | |
91 | #define FIFO_READ_LIMIT 128 |
92 | #define MAX_PORTS 3 |
93 | #define LPC32XX_TTY_NAME "ttyTX" |
94 | static struct lpc32xx_hsuart_port lpc32xx_hs_ports[MAX_PORTS]; |
95 | |
96 | #ifdef CONFIG_SERIAL_HS_LPC32XX_CONSOLE |
97 | static void wait_for_xmit_empty(struct uart_port *port) |
98 | { |
99 | unsigned int timeout = 10000; |
100 | |
101 | do { |
102 | if (LPC32XX_HSU_TX_LEV(readl(LPC32XX_HSUART_LEVEL( |
103 | port->membase))) == 0) |
104 | break; |
105 | if (--timeout == 0) |
106 | break; |
107 | udelay(1); |
108 | } while (1); |
109 | } |
110 | |
111 | static void wait_for_xmit_ready(struct uart_port *port) |
112 | { |
113 | unsigned int timeout = 10000; |
114 | |
115 | while (1) { |
116 | if (LPC32XX_HSU_TX_LEV(readl(LPC32XX_HSUART_LEVEL( |
117 | port->membase))) < 32) |
118 | break; |
119 | if (--timeout == 0) |
120 | break; |
121 | udelay(1); |
122 | } |
123 | } |
124 | |
125 | static void lpc32xx_hsuart_console_putchar(struct uart_port *port, unsigned char ch) |
126 | { |
127 | wait_for_xmit_ready(port); |
128 | writel(val: (u32)ch, LPC32XX_HSUART_FIFO(port->membase)); |
129 | } |
130 | |
131 | static void lpc32xx_hsuart_console_write(struct console *co, const char *s, |
132 | unsigned int count) |
133 | { |
134 | struct lpc32xx_hsuart_port *up = &lpc32xx_hs_ports[co->index]; |
135 | unsigned long flags; |
136 | int locked = 1; |
137 | |
138 | touch_nmi_watchdog(); |
139 | if (oops_in_progress) |
140 | locked = uart_port_trylock_irqsave(up: &up->port, flags: &flags); |
141 | else |
142 | uart_port_lock_irqsave(up: &up->port, flags: &flags); |
143 | |
144 | uart_console_write(port: &up->port, s, count, putchar: lpc32xx_hsuart_console_putchar); |
145 | wait_for_xmit_empty(port: &up->port); |
146 | |
147 | if (locked) |
148 | uart_port_unlock_irqrestore(up: &up->port, flags); |
149 | } |
150 | |
151 | static int __init lpc32xx_hsuart_console_setup(struct console *co, |
152 | char *options) |
153 | { |
154 | struct uart_port *port; |
155 | int baud = 115200; |
156 | int bits = 8; |
157 | int parity = 'n'; |
158 | int flow = 'n'; |
159 | |
160 | if (co->index >= MAX_PORTS) |
161 | co->index = 0; |
162 | |
163 | port = &lpc32xx_hs_ports[co->index].port; |
164 | if (!port->membase) |
165 | return -ENODEV; |
166 | |
167 | if (options) |
168 | uart_parse_options(options, baud: &baud, parity: &parity, bits: &bits, flow: &flow); |
169 | |
170 | lpc32xx_loopback_set(mapbase: port->mapbase, state: 0); /* get out of loopback mode */ |
171 | |
172 | return uart_set_options(port, co, baud, parity, bits, flow); |
173 | } |
174 | |
175 | static struct uart_driver lpc32xx_hsuart_reg; |
176 | static struct console lpc32xx_hsuart_console = { |
177 | .name = LPC32XX_TTY_NAME, |
178 | .write = lpc32xx_hsuart_console_write, |
179 | .device = uart_console_device, |
180 | .setup = lpc32xx_hsuart_console_setup, |
181 | .flags = CON_PRINTBUFFER, |
182 | .index = -1, |
183 | .data = &lpc32xx_hsuart_reg, |
184 | }; |
185 | |
186 | static int __init lpc32xx_hsuart_console_init(void) |
187 | { |
188 | register_console(&lpc32xx_hsuart_console); |
189 | return 0; |
190 | } |
191 | console_initcall(lpc32xx_hsuart_console_init); |
192 | |
193 | #define LPC32XX_HSUART_CONSOLE (&lpc32xx_hsuart_console) |
194 | #else |
195 | #define LPC32XX_HSUART_CONSOLE NULL |
196 | #endif |
197 | |
198 | static struct uart_driver lpc32xx_hs_reg = { |
199 | .owner = THIS_MODULE, |
200 | .driver_name = MODNAME, |
201 | .dev_name = LPC32XX_TTY_NAME, |
202 | .nr = MAX_PORTS, |
203 | .cons = LPC32XX_HSUART_CONSOLE, |
204 | }; |
205 | static int uarts_registered; |
206 | |
207 | static unsigned int __serial_get_clock_div(unsigned long uartclk, |
208 | unsigned long rate) |
209 | { |
210 | u32 div, goodrate, hsu_rate, l_hsu_rate, comprate; |
211 | u32 rate_diff; |
212 | |
213 | /* Find the closest divider to get the desired clock rate */ |
214 | div = uartclk / rate; |
215 | goodrate = hsu_rate = (div / 14) - 1; |
216 | if (hsu_rate != 0) |
217 | hsu_rate--; |
218 | |
219 | /* Tweak divider */ |
220 | l_hsu_rate = hsu_rate + 3; |
221 | rate_diff = 0xFFFFFFFF; |
222 | |
223 | while (hsu_rate < l_hsu_rate) { |
224 | comprate = uartclk / ((hsu_rate + 1) * 14); |
225 | if (abs(comprate - rate) < rate_diff) { |
226 | goodrate = hsu_rate; |
227 | rate_diff = abs(comprate - rate); |
228 | } |
229 | |
230 | hsu_rate++; |
231 | } |
232 | |
233 | return goodrate; |
234 | } |
235 | |
236 | static void __serial_uart_flush(struct uart_port *port) |
237 | { |
238 | int cnt = 0; |
239 | |
240 | while ((readl(LPC32XX_HSUART_LEVEL(port->membase)) > 0) && |
241 | (cnt++ < FIFO_READ_LIMIT)) |
242 | readl(LPC32XX_HSUART_FIFO(port->membase)); |
243 | } |
244 | |
245 | static void __serial_lpc32xx_rx(struct uart_port *port) |
246 | { |
247 | struct tty_port *tport = &port->state->port; |
248 | unsigned int tmp, flag; |
249 | |
250 | /* Read data from FIFO and push into terminal */ |
251 | tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); |
252 | while (!(tmp & LPC32XX_HSU_RX_EMPTY)) { |
253 | flag = TTY_NORMAL; |
254 | port->icount.rx++; |
255 | |
256 | if (tmp & LPC32XX_HSU_ERROR_DATA) { |
257 | /* Framing error */ |
258 | writel(LPC32XX_HSU_FE_INT, |
259 | LPC32XX_HSUART_IIR(port->membase)); |
260 | port->icount.frame++; |
261 | flag = TTY_FRAME; |
262 | tty_insert_flip_char(port: tport, ch: 0, TTY_FRAME); |
263 | } |
264 | |
265 | if (!uart_prepare_sysrq_char(port, ch: tmp & 0xff)) |
266 | tty_insert_flip_char(port: tport, ch: (tmp & 0xFF), flag); |
267 | |
268 | tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); |
269 | } |
270 | |
271 | tty_flip_buffer_push(port: tport); |
272 | } |
273 | |
274 | static bool serial_lpc32xx_tx_ready(struct uart_port *port) |
275 | { |
276 | u32 level = readl(LPC32XX_HSUART_LEVEL(port->membase)); |
277 | |
278 | return LPC32XX_HSU_TX_LEV(level) < 64; |
279 | } |
280 | |
281 | static void __serial_lpc32xx_tx(struct uart_port *port) |
282 | { |
283 | u8 ch; |
284 | |
285 | uart_port_tx(port, ch, |
286 | serial_lpc32xx_tx_ready(port), |
287 | writel(ch, LPC32XX_HSUART_FIFO(port->membase))); |
288 | } |
289 | |
290 | static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id) |
291 | { |
292 | struct uart_port *port = dev_id; |
293 | struct tty_port *tport = &port->state->port; |
294 | u32 status; |
295 | |
296 | uart_port_lock(up: port); |
297 | |
298 | /* Read UART status and clear latched interrupts */ |
299 | status = readl(LPC32XX_HSUART_IIR(port->membase)); |
300 | |
301 | if (status & LPC32XX_HSU_BRK_INT) { |
302 | /* Break received */ |
303 | writel(LPC32XX_HSU_BRK_INT, LPC32XX_HSUART_IIR(port->membase)); |
304 | port->icount.brk++; |
305 | uart_handle_break(port); |
306 | } |
307 | |
308 | /* Framing error */ |
309 | if (status & LPC32XX_HSU_FE_INT) |
310 | writel(LPC32XX_HSU_FE_INT, LPC32XX_HSUART_IIR(port->membase)); |
311 | |
312 | if (status & LPC32XX_HSU_RX_OE_INT) { |
313 | /* Receive FIFO overrun */ |
314 | writel(LPC32XX_HSU_RX_OE_INT, |
315 | LPC32XX_HSUART_IIR(port->membase)); |
316 | port->icount.overrun++; |
317 | tty_insert_flip_char(port: tport, ch: 0, TTY_OVERRUN); |
318 | tty_flip_buffer_push(port: tport); |
319 | } |
320 | |
321 | /* Data received? */ |
322 | if (status & (LPC32XX_HSU_RX_TIMEOUT_INT | LPC32XX_HSU_RX_TRIG_INT)) |
323 | __serial_lpc32xx_rx(port); |
324 | |
325 | /* Transmit data request? */ |
326 | if ((status & LPC32XX_HSU_TX_INT) && (!uart_tx_stopped(port))) { |
327 | writel(LPC32XX_HSU_TX_INT, LPC32XX_HSUART_IIR(port->membase)); |
328 | __serial_lpc32xx_tx(port); |
329 | } |
330 | |
331 | uart_unlock_and_check_sysrq(port); |
332 | |
333 | return IRQ_HANDLED; |
334 | } |
335 | |
336 | /* port->lock is not held. */ |
337 | static unsigned int serial_lpc32xx_tx_empty(struct uart_port *port) |
338 | { |
339 | unsigned int ret = 0; |
340 | |
341 | if (LPC32XX_HSU_TX_LEV(readl(LPC32XX_HSUART_LEVEL(port->membase))) == 0) |
342 | ret = TIOCSER_TEMT; |
343 | |
344 | return ret; |
345 | } |
346 | |
347 | /* port->lock held by caller. */ |
348 | static void serial_lpc32xx_set_mctrl(struct uart_port *port, |
349 | unsigned int mctrl) |
350 | { |
351 | /* No signals are supported on HS UARTs */ |
352 | } |
353 | |
354 | /* port->lock is held by caller and interrupts are disabled. */ |
355 | static unsigned int serial_lpc32xx_get_mctrl(struct uart_port *port) |
356 | { |
357 | /* No signals are supported on HS UARTs */ |
358 | return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; |
359 | } |
360 | |
361 | /* port->lock held by caller. */ |
362 | static void serial_lpc32xx_stop_tx(struct uart_port *port) |
363 | { |
364 | u32 tmp; |
365 | |
366 | tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); |
367 | tmp &= ~LPC32XX_HSU_TX_INT_EN; |
368 | writel(val: tmp, LPC32XX_HSUART_CTRL(port->membase)); |
369 | } |
370 | |
371 | /* port->lock held by caller. */ |
372 | static void serial_lpc32xx_start_tx(struct uart_port *port) |
373 | { |
374 | u32 tmp; |
375 | |
376 | __serial_lpc32xx_tx(port); |
377 | tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); |
378 | tmp |= LPC32XX_HSU_TX_INT_EN; |
379 | writel(val: tmp, LPC32XX_HSUART_CTRL(port->membase)); |
380 | } |
381 | |
382 | /* port->lock held by caller. */ |
383 | static void serial_lpc32xx_stop_rx(struct uart_port *port) |
384 | { |
385 | u32 tmp; |
386 | |
387 | tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); |
388 | tmp &= ~(LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN); |
389 | writel(val: tmp, LPC32XX_HSUART_CTRL(port->membase)); |
390 | |
391 | writel(val: (LPC32XX_HSU_BRK_INT | LPC32XX_HSU_RX_OE_INT | |
392 | LPC32XX_HSU_FE_INT), LPC32XX_HSUART_IIR(port->membase)); |
393 | } |
394 | |
395 | /* port->lock is not held. */ |
396 | static void serial_lpc32xx_break_ctl(struct uart_port *port, |
397 | int break_state) |
398 | { |
399 | unsigned long flags; |
400 | u32 tmp; |
401 | |
402 | uart_port_lock_irqsave(up: port, flags: &flags); |
403 | tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); |
404 | if (break_state != 0) |
405 | tmp |= LPC32XX_HSU_BREAK; |
406 | else |
407 | tmp &= ~LPC32XX_HSU_BREAK; |
408 | writel(val: tmp, LPC32XX_HSUART_CTRL(port->membase)); |
409 | uart_port_unlock_irqrestore(up: port, flags); |
410 | } |
411 | |
412 | /* port->lock is not held. */ |
413 | static int serial_lpc32xx_startup(struct uart_port *port) |
414 | { |
415 | int retval; |
416 | unsigned long flags; |
417 | u32 tmp; |
418 | |
419 | uart_port_lock_irqsave(up: port, flags: &flags); |
420 | |
421 | __serial_uart_flush(port); |
422 | |
423 | writel(val: (LPC32XX_HSU_TX_INT | LPC32XX_HSU_FE_INT | |
424 | LPC32XX_HSU_BRK_INT | LPC32XX_HSU_RX_OE_INT), |
425 | LPC32XX_HSUART_IIR(port->membase)); |
426 | |
427 | writel(val: 0xFF, LPC32XX_HSUART_RATE(port->membase)); |
428 | |
429 | /* |
430 | * Set receiver timeout, HSU offset of 20, no break, no interrupts, |
431 | * and default FIFO trigger levels |
432 | */ |
433 | tmp = LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B | |
434 | LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B; |
435 | writel(val: tmp, LPC32XX_HSUART_CTRL(port->membase)); |
436 | |
437 | lpc32xx_loopback_set(mapbase: port->mapbase, state: 0); /* get out of loopback mode */ |
438 | |
439 | uart_port_unlock_irqrestore(up: port, flags); |
440 | |
441 | retval = request_irq(irq: port->irq, handler: serial_lpc32xx_interrupt, |
442 | flags: 0, MODNAME, dev: port); |
443 | if (!retval) |
444 | writel(val: (tmp | LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN), |
445 | LPC32XX_HSUART_CTRL(port->membase)); |
446 | |
447 | return retval; |
448 | } |
449 | |
450 | /* port->lock is not held. */ |
451 | static void serial_lpc32xx_shutdown(struct uart_port *port) |
452 | { |
453 | u32 tmp; |
454 | unsigned long flags; |
455 | |
456 | uart_port_lock_irqsave(up: port, flags: &flags); |
457 | |
458 | tmp = LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B | |
459 | LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B; |
460 | writel(val: tmp, LPC32XX_HSUART_CTRL(port->membase)); |
461 | |
462 | lpc32xx_loopback_set(mapbase: port->mapbase, state: 1); /* go to loopback mode */ |
463 | |
464 | uart_port_unlock_irqrestore(up: port, flags); |
465 | |
466 | free_irq(port->irq, port); |
467 | } |
468 | |
469 | /* port->lock is not held. */ |
470 | static void serial_lpc32xx_set_termios(struct uart_port *port, |
471 | struct ktermios *termios, |
472 | const struct ktermios *old) |
473 | { |
474 | unsigned long flags; |
475 | unsigned int baud, quot; |
476 | u32 tmp; |
477 | |
478 | /* Always 8-bit, no parity, 1 stop bit */ |
479 | termios->c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD); |
480 | termios->c_cflag |= CS8; |
481 | |
482 | termios->c_cflag &= ~(HUPCL | CMSPAR | CLOCAL | CRTSCTS); |
483 | |
484 | baud = uart_get_baud_rate(port, termios, old, min: 0, |
485 | max: port->uartclk / 14); |
486 | |
487 | quot = __serial_get_clock_div(uartclk: port->uartclk, rate: baud); |
488 | |
489 | uart_port_lock_irqsave(up: port, flags: &flags); |
490 | |
491 | /* Ignore characters? */ |
492 | tmp = readl(LPC32XX_HSUART_CTRL(port->membase)); |
493 | if ((termios->c_cflag & CREAD) == 0) |
494 | tmp &= ~(LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN); |
495 | else |
496 | tmp |= LPC32XX_HSU_RX_INT_EN | LPC32XX_HSU_ERR_INT_EN; |
497 | writel(val: tmp, LPC32XX_HSUART_CTRL(port->membase)); |
498 | |
499 | writel(val: quot, LPC32XX_HSUART_RATE(port->membase)); |
500 | |
501 | uart_update_timeout(port, cflag: termios->c_cflag, baud); |
502 | |
503 | uart_port_unlock_irqrestore(up: port, flags); |
504 | |
505 | /* Don't rewrite B0 */ |
506 | if (tty_termios_baud_rate(termios)) |
507 | tty_termios_encode_baud_rate(termios, ibaud: baud, obaud: baud); |
508 | } |
509 | |
510 | static const char *serial_lpc32xx_type(struct uart_port *port) |
511 | { |
512 | return MODNAME; |
513 | } |
514 | |
515 | static void serial_lpc32xx_release_port(struct uart_port *port) |
516 | { |
517 | if ((port->iotype == UPIO_MEM32) && (port->mapbase)) { |
518 | if (port->flags & UPF_IOREMAP) { |
519 | iounmap(addr: port->membase); |
520 | port->membase = NULL; |
521 | } |
522 | |
523 | release_mem_region(port->mapbase, SZ_4K); |
524 | } |
525 | } |
526 | |
527 | static int serial_lpc32xx_request_port(struct uart_port *port) |
528 | { |
529 | int ret = -ENODEV; |
530 | |
531 | if ((port->iotype == UPIO_MEM32) && (port->mapbase)) { |
532 | ret = 0; |
533 | |
534 | if (!request_mem_region(port->mapbase, SZ_4K, MODNAME)) |
535 | ret = -EBUSY; |
536 | else if (port->flags & UPF_IOREMAP) { |
537 | port->membase = ioremap(offset: port->mapbase, SZ_4K); |
538 | if (!port->membase) { |
539 | release_mem_region(port->mapbase, SZ_4K); |
540 | ret = -ENOMEM; |
541 | } |
542 | } |
543 | } |
544 | |
545 | return ret; |
546 | } |
547 | |
548 | static void serial_lpc32xx_config_port(struct uart_port *port, int uflags) |
549 | { |
550 | int ret; |
551 | |
552 | ret = serial_lpc32xx_request_port(port); |
553 | if (ret < 0) |
554 | return; |
555 | port->type = PORT_UART00; |
556 | port->fifosize = 64; |
557 | |
558 | __serial_uart_flush(port); |
559 | |
560 | writel(val: (LPC32XX_HSU_TX_INT | LPC32XX_HSU_FE_INT | |
561 | LPC32XX_HSU_BRK_INT | LPC32XX_HSU_RX_OE_INT), |
562 | LPC32XX_HSUART_IIR(port->membase)); |
563 | |
564 | writel(val: 0xFF, LPC32XX_HSUART_RATE(port->membase)); |
565 | |
566 | /* Set receiver timeout, HSU offset of 20, no break, no interrupts, |
567 | and default FIFO trigger levels */ |
568 | writel(LPC32XX_HSU_TX_TL8B | LPC32XX_HSU_RX_TL32B | |
569 | LPC32XX_HSU_OFFSET(20) | LPC32XX_HSU_TMO_INACT_4B, |
570 | LPC32XX_HSUART_CTRL(port->membase)); |
571 | } |
572 | |
573 | static int serial_lpc32xx_verify_port(struct uart_port *port, |
574 | struct serial_struct *ser) |
575 | { |
576 | int ret = 0; |
577 | |
578 | if (ser->type != PORT_UART00) |
579 | ret = -EINVAL; |
580 | |
581 | return ret; |
582 | } |
583 | |
584 | static const struct uart_ops serial_lpc32xx_pops = { |
585 | .tx_empty = serial_lpc32xx_tx_empty, |
586 | .set_mctrl = serial_lpc32xx_set_mctrl, |
587 | .get_mctrl = serial_lpc32xx_get_mctrl, |
588 | .stop_tx = serial_lpc32xx_stop_tx, |
589 | .start_tx = serial_lpc32xx_start_tx, |
590 | .stop_rx = serial_lpc32xx_stop_rx, |
591 | .break_ctl = serial_lpc32xx_break_ctl, |
592 | .startup = serial_lpc32xx_startup, |
593 | .shutdown = serial_lpc32xx_shutdown, |
594 | .set_termios = serial_lpc32xx_set_termios, |
595 | .type = serial_lpc32xx_type, |
596 | .release_port = serial_lpc32xx_release_port, |
597 | .request_port = serial_lpc32xx_request_port, |
598 | .config_port = serial_lpc32xx_config_port, |
599 | .verify_port = serial_lpc32xx_verify_port, |
600 | }; |
601 | |
602 | /* |
603 | * Register a set of serial devices attached to a platform device |
604 | */ |
605 | static int serial_hs_lpc32xx_probe(struct platform_device *pdev) |
606 | { |
607 | struct lpc32xx_hsuart_port *p = &lpc32xx_hs_ports[uarts_registered]; |
608 | int ret = 0; |
609 | struct resource *res; |
610 | |
611 | if (uarts_registered >= MAX_PORTS) { |
612 | dev_err(&pdev->dev, |
613 | "Error: Number of possible ports exceeded (%d)!\n" , |
614 | uarts_registered + 1); |
615 | return -ENXIO; |
616 | } |
617 | |
618 | memset(p, 0, sizeof(*p)); |
619 | |
620 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
621 | if (!res) { |
622 | dev_err(&pdev->dev, |
623 | "Error getting mem resource for HS UART port %d\n" , |
624 | uarts_registered); |
625 | return -ENXIO; |
626 | } |
627 | p->port.mapbase = res->start; |
628 | p->port.membase = NULL; |
629 | |
630 | ret = platform_get_irq(pdev, 0); |
631 | if (ret < 0) |
632 | return ret; |
633 | p->port.irq = ret; |
634 | |
635 | p->port.iotype = UPIO_MEM32; |
636 | p->port.uartclk = LPC32XX_MAIN_OSC_FREQ; |
637 | p->port.regshift = 2; |
638 | p->port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_IOREMAP; |
639 | p->port.dev = &pdev->dev; |
640 | p->port.ops = &serial_lpc32xx_pops; |
641 | p->port.line = uarts_registered++; |
642 | spin_lock_init(&p->port.lock); |
643 | |
644 | /* send port to loopback mode by default */ |
645 | lpc32xx_loopback_set(mapbase: p->port.mapbase, state: 1); |
646 | |
647 | ret = uart_add_one_port(reg: &lpc32xx_hs_reg, port: &p->port); |
648 | |
649 | platform_set_drvdata(pdev, data: p); |
650 | |
651 | return ret; |
652 | } |
653 | |
654 | /* |
655 | * Remove serial ports registered against a platform device. |
656 | */ |
657 | static void serial_hs_lpc32xx_remove(struct platform_device *pdev) |
658 | { |
659 | struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev); |
660 | |
661 | uart_remove_one_port(reg: &lpc32xx_hs_reg, port: &p->port); |
662 | } |
663 | |
664 | |
665 | #ifdef CONFIG_PM |
666 | static int serial_hs_lpc32xx_suspend(struct platform_device *pdev, |
667 | pm_message_t state) |
668 | { |
669 | struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev); |
670 | |
671 | uart_suspend_port(reg: &lpc32xx_hs_reg, port: &p->port); |
672 | |
673 | return 0; |
674 | } |
675 | |
676 | static int serial_hs_lpc32xx_resume(struct platform_device *pdev) |
677 | { |
678 | struct lpc32xx_hsuart_port *p = platform_get_drvdata(pdev); |
679 | |
680 | uart_resume_port(reg: &lpc32xx_hs_reg, port: &p->port); |
681 | |
682 | return 0; |
683 | } |
684 | #else |
685 | #define serial_hs_lpc32xx_suspend NULL |
686 | #define serial_hs_lpc32xx_resume NULL |
687 | #endif |
688 | |
689 | static const struct of_device_id serial_hs_lpc32xx_dt_ids[] = { |
690 | { .compatible = "nxp,lpc3220-hsuart" }, |
691 | { /* sentinel */ } |
692 | }; |
693 | |
694 | MODULE_DEVICE_TABLE(of, serial_hs_lpc32xx_dt_ids); |
695 | |
696 | static struct platform_driver serial_hs_lpc32xx_driver = { |
697 | .probe = serial_hs_lpc32xx_probe, |
698 | .remove_new = serial_hs_lpc32xx_remove, |
699 | .suspend = serial_hs_lpc32xx_suspend, |
700 | .resume = serial_hs_lpc32xx_resume, |
701 | .driver = { |
702 | .name = MODNAME, |
703 | .of_match_table = serial_hs_lpc32xx_dt_ids, |
704 | }, |
705 | }; |
706 | |
707 | static int __init lpc32xx_hsuart_init(void) |
708 | { |
709 | int ret; |
710 | |
711 | ret = uart_register_driver(uart: &lpc32xx_hs_reg); |
712 | if (ret) |
713 | return ret; |
714 | |
715 | ret = platform_driver_register(&serial_hs_lpc32xx_driver); |
716 | if (ret) |
717 | uart_unregister_driver(uart: &lpc32xx_hs_reg); |
718 | |
719 | return ret; |
720 | } |
721 | |
722 | static void __exit lpc32xx_hsuart_exit(void) |
723 | { |
724 | platform_driver_unregister(&serial_hs_lpc32xx_driver); |
725 | uart_unregister_driver(uart: &lpc32xx_hs_reg); |
726 | } |
727 | |
728 | module_init(lpc32xx_hsuart_init); |
729 | module_exit(lpc32xx_hsuart_exit); |
730 | |
731 | MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>" ); |
732 | MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>" ); |
733 | MODULE_DESCRIPTION("NXP LPC32XX High Speed UART driver" ); |
734 | MODULE_LICENSE("GPL" ); |
735 | |