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