diff options
Diffstat (limited to 'drivers/tty/serial/vt8500_serial.c')
-rw-r--r-- | drivers/tty/serial/vt8500_serial.c | 648 |
1 files changed, 648 insertions, 0 deletions
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c new file mode 100644 index 000000000000..322bf56c0d89 --- /dev/null +++ b/drivers/tty/serial/vt8500_serial.c | |||
@@ -0,0 +1,648 @@ | |||
1 | /* | ||
2 | * drivers/serial/vt8500_serial.c | ||
3 | * | ||
4 | * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> | ||
5 | * | ||
6 | * Based on msm_serial.c, which is: | ||
7 | * Copyright (C) 2007 Google, Inc. | ||
8 | * Author: Robert Love <rlove@google.com> | ||
9 | * | ||
10 | * This software is licensed under the terms of the GNU General Public | ||
11 | * License version 2, as published by the Free Software Foundation, and | ||
12 | * may be copied, distributed, and modified under those terms. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | */ | ||
19 | |||
20 | #if defined(CONFIG_SERIAL_VT8500_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) | ||
21 | # define SUPPORT_SYSRQ | ||
22 | #endif | ||
23 | |||
24 | #include <linux/hrtimer.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/io.h> | ||
28 | #include <linux/ioport.h> | ||
29 | #include <linux/irq.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/console.h> | ||
32 | #include <linux/tty.h> | ||
33 | #include <linux/tty_flip.h> | ||
34 | #include <linux/serial_core.h> | ||
35 | #include <linux/serial.h> | ||
36 | #include <linux/slab.h> | ||
37 | #include <linux/clk.h> | ||
38 | #include <linux/platform_device.h> | ||
39 | |||
40 | /* | ||
41 | * UART Register offsets | ||
42 | */ | ||
43 | |||
44 | #define VT8500_URTDR 0x0000 /* Transmit data */ | ||
45 | #define VT8500_URRDR 0x0004 /* Receive data */ | ||
46 | #define VT8500_URDIV 0x0008 /* Clock/Baud rate divisor */ | ||
47 | #define VT8500_URLCR 0x000C /* Line control */ | ||
48 | #define VT8500_URICR 0x0010 /* IrDA control */ | ||
49 | #define VT8500_URIER 0x0014 /* Interrupt enable */ | ||
50 | #define VT8500_URISR 0x0018 /* Interrupt status */ | ||
51 | #define VT8500_URUSR 0x001c /* UART status */ | ||
52 | #define VT8500_URFCR 0x0020 /* FIFO control */ | ||
53 | #define VT8500_URFIDX 0x0024 /* FIFO index */ | ||
54 | #define VT8500_URBKR 0x0028 /* Break signal count */ | ||
55 | #define VT8500_URTOD 0x002c /* Time out divisor */ | ||
56 | #define VT8500_TXFIFO 0x1000 /* Transmit FIFO (16x8) */ | ||
57 | #define VT8500_RXFIFO 0x1020 /* Receive FIFO (16x10) */ | ||
58 | |||
59 | /* | ||
60 | * Interrupt enable and status bits | ||
61 | */ | ||
62 | |||
63 | #define TXDE (1 << 0) /* Tx Data empty */ | ||
64 | #define RXDF (1 << 1) /* Rx Data full */ | ||
65 | #define TXFAE (1 << 2) /* Tx FIFO almost empty */ | ||
66 | #define TXFE (1 << 3) /* Tx FIFO empty */ | ||
67 | #define RXFAF (1 << 4) /* Rx FIFO almost full */ | ||
68 | #define RXFF (1 << 5) /* Rx FIFO full */ | ||
69 | #define TXUDR (1 << 6) /* Tx underrun */ | ||
70 | #define RXOVER (1 << 7) /* Rx overrun */ | ||
71 | #define PER (1 << 8) /* Parity error */ | ||
72 | #define FER (1 << 9) /* Frame error */ | ||
73 | #define TCTS (1 << 10) /* Toggle of CTS */ | ||
74 | #define RXTOUT (1 << 11) /* Rx timeout */ | ||
75 | #define BKDONE (1 << 12) /* Break signal done */ | ||
76 | #define ERR (1 << 13) /* AHB error response */ | ||
77 | |||
78 | #define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT) | ||
79 | #define TX_FIFO_INTS (TXFAE | TXFE | TXUDR) | ||
80 | |||
81 | struct vt8500_port { | ||
82 | struct uart_port uart; | ||
83 | char name[16]; | ||
84 | struct clk *clk; | ||
85 | unsigned int ier; | ||
86 | }; | ||
87 | |||
88 | static inline void vt8500_write(struct uart_port *port, unsigned int val, | ||
89 | unsigned int off) | ||
90 | { | ||
91 | writel(val, port->membase + off); | ||
92 | } | ||
93 | |||
94 | static inline unsigned int vt8500_read(struct uart_port *port, unsigned int off) | ||
95 | { | ||
96 | return readl(port->membase + off); | ||
97 | } | ||
98 | |||
99 | static void vt8500_stop_tx(struct uart_port *port) | ||
100 | { | ||
101 | struct vt8500_port *vt8500_port = container_of(port, | ||
102 | struct vt8500_port, | ||
103 | uart); | ||
104 | |||
105 | vt8500_port->ier &= ~TX_FIFO_INTS; | ||
106 | vt8500_write(port, vt8500_port->ier, VT8500_URIER); | ||
107 | } | ||
108 | |||
109 | static void vt8500_stop_rx(struct uart_port *port) | ||
110 | { | ||
111 | struct vt8500_port *vt8500_port = container_of(port, | ||
112 | struct vt8500_port, | ||
113 | uart); | ||
114 | |||
115 | vt8500_port->ier &= ~RX_FIFO_INTS; | ||
116 | vt8500_write(port, vt8500_port->ier, VT8500_URIER); | ||
117 | } | ||
118 | |||
119 | static void vt8500_enable_ms(struct uart_port *port) | ||
120 | { | ||
121 | struct vt8500_port *vt8500_port = container_of(port, | ||
122 | struct vt8500_port, | ||
123 | uart); | ||
124 | |||
125 | vt8500_port->ier |= TCTS; | ||
126 | vt8500_write(port, vt8500_port->ier, VT8500_URIER); | ||
127 | } | ||
128 | |||
129 | static void handle_rx(struct uart_port *port) | ||
130 | { | ||
131 | struct tty_struct *tty = tty_port_tty_get(&port->state->port); | ||
132 | if (!tty) { | ||
133 | /* Discard data: no tty available */ | ||
134 | int count = (vt8500_read(port, VT8500_URFIDX) & 0x1f00) >> 8; | ||
135 | u16 ch; | ||
136 | while (count--) | ||
137 | ch = readw(port->membase + VT8500_RXFIFO); | ||
138 | return; | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * Handle overrun | ||
143 | */ | ||
144 | if ((vt8500_read(port, VT8500_URISR) & RXOVER)) { | ||
145 | port->icount.overrun++; | ||
146 | tty_insert_flip_char(tty, 0, TTY_OVERRUN); | ||
147 | } | ||
148 | |||
149 | /* and now the main RX loop */ | ||
150 | while (vt8500_read(port, VT8500_URFIDX) & 0x1f00) { | ||
151 | unsigned int c; | ||
152 | char flag = TTY_NORMAL; | ||
153 | |||
154 | c = readw(port->membase + VT8500_RXFIFO) & 0x3ff; | ||
155 | |||
156 | /* Mask conditions we're ignorning. */ | ||
157 | c &= ~port->read_status_mask; | ||
158 | |||
159 | if (c & FER) { | ||
160 | port->icount.frame++; | ||
161 | flag = TTY_FRAME; | ||
162 | } else if (c & PER) { | ||
163 | port->icount.parity++; | ||
164 | flag = TTY_PARITY; | ||
165 | } | ||
166 | port->icount.rx++; | ||
167 | |||
168 | if (!uart_handle_sysrq_char(port, c)) | ||
169 | tty_insert_flip_char(tty, c, flag); | ||
170 | } | ||
171 | |||
172 | tty_flip_buffer_push(tty); | ||
173 | tty_kref_put(tty); | ||
174 | } | ||
175 | |||
176 | static void handle_tx(struct uart_port *port) | ||
177 | { | ||
178 | struct circ_buf *xmit = &port->state->xmit; | ||
179 | |||
180 | if (port->x_char) { | ||
181 | writeb(port->x_char, port->membase + VT8500_TXFIFO); | ||
182 | port->icount.tx++; | ||
183 | port->x_char = 0; | ||
184 | } | ||
185 | if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { | ||
186 | vt8500_stop_tx(port); | ||
187 | return; | ||
188 | } | ||
189 | |||
190 | while ((vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16) { | ||
191 | if (uart_circ_empty(xmit)) | ||
192 | break; | ||
193 | |||
194 | writeb(xmit->buf[xmit->tail], port->membase + VT8500_TXFIFO); | ||
195 | |||
196 | xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); | ||
197 | port->icount.tx++; | ||
198 | } | ||
199 | |||
200 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | ||
201 | uart_write_wakeup(port); | ||
202 | |||
203 | if (uart_circ_empty(xmit)) | ||
204 | vt8500_stop_tx(port); | ||
205 | } | ||
206 | |||
207 | static void vt8500_start_tx(struct uart_port *port) | ||
208 | { | ||
209 | struct vt8500_port *vt8500_port = container_of(port, | ||
210 | struct vt8500_port, | ||
211 | uart); | ||
212 | |||
213 | vt8500_port->ier &= ~TX_FIFO_INTS; | ||
214 | vt8500_write(port, vt8500_port->ier, VT8500_URIER); | ||
215 | handle_tx(port); | ||
216 | vt8500_port->ier |= TX_FIFO_INTS; | ||
217 | vt8500_write(port, vt8500_port->ier, VT8500_URIER); | ||
218 | } | ||
219 | |||
220 | static void handle_delta_cts(struct uart_port *port) | ||
221 | { | ||
222 | port->icount.cts++; | ||
223 | wake_up_interruptible(&port->state->port.delta_msr_wait); | ||
224 | } | ||
225 | |||
226 | static irqreturn_t vt8500_irq(int irq, void *dev_id) | ||
227 | { | ||
228 | struct uart_port *port = dev_id; | ||
229 | unsigned long isr; | ||
230 | |||
231 | spin_lock(&port->lock); | ||
232 | isr = vt8500_read(port, VT8500_URISR); | ||
233 | |||
234 | /* Acknowledge active status bits */ | ||
235 | vt8500_write(port, isr, VT8500_URISR); | ||
236 | |||
237 | if (isr & RX_FIFO_INTS) | ||
238 | handle_rx(port); | ||
239 | if (isr & TX_FIFO_INTS) | ||
240 | handle_tx(port); | ||
241 | if (isr & TCTS) | ||
242 | handle_delta_cts(port); | ||
243 | |||
244 | spin_unlock(&port->lock); | ||
245 | |||
246 | return IRQ_HANDLED; | ||
247 | } | ||
248 | |||
249 | static unsigned int vt8500_tx_empty(struct uart_port *port) | ||
250 | { | ||
251 | return (vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16 ? | ||
252 | TIOCSER_TEMT : 0; | ||
253 | } | ||
254 | |||
255 | static unsigned int vt8500_get_mctrl(struct uart_port *port) | ||
256 | { | ||
257 | unsigned int usr; | ||
258 | |||
259 | usr = vt8500_read(port, VT8500_URUSR); | ||
260 | if (usr & (1 << 4)) | ||
261 | return TIOCM_CTS; | ||
262 | else | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl) | ||
267 | { | ||
268 | } | ||
269 | |||
270 | static void vt8500_break_ctl(struct uart_port *port, int break_ctl) | ||
271 | { | ||
272 | if (break_ctl) | ||
273 | vt8500_write(port, vt8500_read(port, VT8500_URLCR) | (1 << 9), | ||
274 | VT8500_URLCR); | ||
275 | } | ||
276 | |||
277 | static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud) | ||
278 | { | ||
279 | unsigned long div; | ||
280 | unsigned int loops = 1000; | ||
281 | |||
282 | div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff); | ||
283 | |||
284 | if (unlikely((baud < 900) || (baud > 921600))) | ||
285 | div |= 7; | ||
286 | else | ||
287 | div |= (921600 / baud) - 1; | ||
288 | |||
289 | while ((vt8500_read(port, VT8500_URUSR) & (1 << 5)) && --loops) | ||
290 | cpu_relax(); | ||
291 | vt8500_write(port, div, VT8500_URDIV); | ||
292 | |||
293 | return baud; | ||
294 | } | ||
295 | |||
296 | static int vt8500_startup(struct uart_port *port) | ||
297 | { | ||
298 | struct vt8500_port *vt8500_port = | ||
299 | container_of(port, struct vt8500_port, uart); | ||
300 | int ret; | ||
301 | |||
302 | snprintf(vt8500_port->name, sizeof(vt8500_port->name), | ||
303 | "vt8500_serial%d", port->line); | ||
304 | |||
305 | ret = request_irq(port->irq, vt8500_irq, IRQF_TRIGGER_HIGH, | ||
306 | vt8500_port->name, port); | ||
307 | if (unlikely(ret)) | ||
308 | return ret; | ||
309 | |||
310 | vt8500_write(port, 0x03, VT8500_URLCR); /* enable TX & RX */ | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | static void vt8500_shutdown(struct uart_port *port) | ||
316 | { | ||
317 | struct vt8500_port *vt8500_port = | ||
318 | container_of(port, struct vt8500_port, uart); | ||
319 | |||
320 | vt8500_port->ier = 0; | ||
321 | |||
322 | /* disable interrupts and FIFOs */ | ||
323 | vt8500_write(&vt8500_port->uart, 0, VT8500_URIER); | ||
324 | vt8500_write(&vt8500_port->uart, 0x880, VT8500_URFCR); | ||
325 | free_irq(port->irq, port); | ||
326 | } | ||
327 | |||
328 | static void vt8500_set_termios(struct uart_port *port, | ||
329 | struct ktermios *termios, | ||
330 | struct ktermios *old) | ||
331 | { | ||
332 | struct vt8500_port *vt8500_port = | ||
333 | container_of(port, struct vt8500_port, uart); | ||
334 | unsigned long flags; | ||
335 | unsigned int baud, lcr; | ||
336 | unsigned int loops = 1000; | ||
337 | |||
338 | spin_lock_irqsave(&port->lock, flags); | ||
339 | |||
340 | /* calculate and set baud rate */ | ||
341 | baud = uart_get_baud_rate(port, termios, old, 900, 921600); | ||
342 | baud = vt8500_set_baud_rate(port, baud); | ||
343 | if (tty_termios_baud_rate(termios)) | ||
344 | tty_termios_encode_baud_rate(termios, baud, baud); | ||
345 | |||
346 | /* calculate parity */ | ||
347 | lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR); | ||
348 | lcr &= ~((1 << 5) | (1 << 4)); | ||
349 | if (termios->c_cflag & PARENB) { | ||
350 | lcr |= (1 << 4); | ||
351 | termios->c_cflag &= ~CMSPAR; | ||
352 | if (termios->c_cflag & PARODD) | ||
353 | lcr |= (1 << 5); | ||
354 | } | ||
355 | |||
356 | /* calculate bits per char */ | ||
357 | lcr &= ~(1 << 2); | ||
358 | switch (termios->c_cflag & CSIZE) { | ||
359 | case CS7: | ||
360 | break; | ||
361 | case CS8: | ||
362 | default: | ||
363 | lcr |= (1 << 2); | ||
364 | termios->c_cflag &= ~CSIZE; | ||
365 | termios->c_cflag |= CS8; | ||
366 | break; | ||
367 | } | ||
368 | |||
369 | /* calculate stop bits */ | ||
370 | lcr &= ~(1 << 3); | ||
371 | if (termios->c_cflag & CSTOPB) | ||
372 | lcr |= (1 << 3); | ||
373 | |||
374 | /* set parity, bits per char, and stop bit */ | ||
375 | vt8500_write(&vt8500_port->uart, lcr, VT8500_URLCR); | ||
376 | |||
377 | /* Configure status bits to ignore based on termio flags. */ | ||
378 | port->read_status_mask = 0; | ||
379 | if (termios->c_iflag & IGNPAR) | ||
380 | port->read_status_mask = FER | PER; | ||
381 | |||
382 | uart_update_timeout(port, termios->c_cflag, baud); | ||
383 | |||
384 | /* Reset FIFOs */ | ||
385 | vt8500_write(&vt8500_port->uart, 0x88c, VT8500_URFCR); | ||
386 | while ((vt8500_read(&vt8500_port->uart, VT8500_URFCR) & 0xc) | ||
387 | && --loops) | ||
388 | cpu_relax(); | ||
389 | |||
390 | /* Every possible FIFO-related interrupt */ | ||
391 | vt8500_port->ier = RX_FIFO_INTS | TX_FIFO_INTS; | ||
392 | |||
393 | /* | ||
394 | * CTS flow control | ||
395 | */ | ||
396 | if (UART_ENABLE_MS(&vt8500_port->uart, termios->c_cflag)) | ||
397 | vt8500_port->ier |= TCTS; | ||
398 | |||
399 | vt8500_write(&vt8500_port->uart, 0x881, VT8500_URFCR); | ||
400 | vt8500_write(&vt8500_port->uart, vt8500_port->ier, VT8500_URIER); | ||
401 | |||
402 | spin_unlock_irqrestore(&port->lock, flags); | ||
403 | } | ||
404 | |||
405 | static const char *vt8500_type(struct uart_port *port) | ||
406 | { | ||
407 | struct vt8500_port *vt8500_port = | ||
408 | container_of(port, struct vt8500_port, uart); | ||
409 | return vt8500_port->name; | ||
410 | } | ||
411 | |||
412 | static void vt8500_release_port(struct uart_port *port) | ||
413 | { | ||
414 | } | ||
415 | |||
416 | static int vt8500_request_port(struct uart_port *port) | ||
417 | { | ||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static void vt8500_config_port(struct uart_port *port, int flags) | ||
422 | { | ||
423 | port->type = PORT_VT8500; | ||
424 | } | ||
425 | |||
426 | static int vt8500_verify_port(struct uart_port *port, | ||
427 | struct serial_struct *ser) | ||
428 | { | ||
429 | if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_VT8500)) | ||
430 | return -EINVAL; | ||
431 | if (unlikely(port->irq != ser->irq)) | ||
432 | return -EINVAL; | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static struct vt8500_port *vt8500_uart_ports[4]; | ||
437 | static struct uart_driver vt8500_uart_driver; | ||
438 | |||
439 | #ifdef CONFIG_SERIAL_VT8500_CONSOLE | ||
440 | |||
441 | static inline void wait_for_xmitr(struct uart_port *port) | ||
442 | { | ||
443 | unsigned int status, tmout = 10000; | ||
444 | |||
445 | /* Wait up to 10ms for the character(s) to be sent. */ | ||
446 | do { | ||
447 | status = vt8500_read(port, VT8500_URFIDX); | ||
448 | |||
449 | if (--tmout == 0) | ||
450 | break; | ||
451 | udelay(1); | ||
452 | } while (status & 0x10); | ||
453 | } | ||
454 | |||
455 | static void vt8500_console_putchar(struct uart_port *port, int c) | ||
456 | { | ||
457 | wait_for_xmitr(port); | ||
458 | writeb(c, port->membase + VT8500_TXFIFO); | ||
459 | } | ||
460 | |||
461 | static void vt8500_console_write(struct console *co, const char *s, | ||
462 | unsigned int count) | ||
463 | { | ||
464 | struct vt8500_port *vt8500_port = vt8500_uart_ports[co->index]; | ||
465 | unsigned long ier; | ||
466 | |||
467 | BUG_ON(co->index < 0 || co->index >= vt8500_uart_driver.nr); | ||
468 | |||
469 | ier = vt8500_read(&vt8500_port->uart, VT8500_URIER); | ||
470 | vt8500_write(&vt8500_port->uart, VT8500_URIER, 0); | ||
471 | |||
472 | uart_console_write(&vt8500_port->uart, s, count, | ||
473 | vt8500_console_putchar); | ||
474 | |||
475 | /* | ||
476 | * Finally, wait for transmitter to become empty | ||
477 | * and switch back to FIFO | ||
478 | */ | ||
479 | wait_for_xmitr(&vt8500_port->uart); | ||
480 | vt8500_write(&vt8500_port->uart, VT8500_URIER, ier); | ||
481 | } | ||
482 | |||
483 | static int __init vt8500_console_setup(struct console *co, char *options) | ||
484 | { | ||
485 | struct vt8500_port *vt8500_port; | ||
486 | int baud = 9600; | ||
487 | int bits = 8; | ||
488 | int parity = 'n'; | ||
489 | int flow = 'n'; | ||
490 | |||
491 | if (unlikely(co->index >= vt8500_uart_driver.nr || co->index < 0)) | ||
492 | return -ENXIO; | ||
493 | |||
494 | vt8500_port = vt8500_uart_ports[co->index]; | ||
495 | |||
496 | if (!vt8500_port) | ||
497 | return -ENODEV; | ||
498 | |||
499 | if (options) | ||
500 | uart_parse_options(options, &baud, &parity, &bits, &flow); | ||
501 | |||
502 | return uart_set_options(&vt8500_port->uart, | ||
503 | co, baud, parity, bits, flow); | ||
504 | } | ||
505 | |||
506 | static struct console vt8500_console = { | ||
507 | .name = "ttyWMT", | ||
508 | .write = vt8500_console_write, | ||
509 | .device = uart_console_device, | ||
510 | .setup = vt8500_console_setup, | ||
511 | .flags = CON_PRINTBUFFER, | ||
512 | .index = -1, | ||
513 | .data = &vt8500_uart_driver, | ||
514 | }; | ||
515 | |||
516 | #define VT8500_CONSOLE (&vt8500_console) | ||
517 | |||
518 | #else | ||
519 | #define VT8500_CONSOLE NULL | ||
520 | #endif | ||
521 | |||
522 | static struct uart_ops vt8500_uart_pops = { | ||
523 | .tx_empty = vt8500_tx_empty, | ||
524 | .set_mctrl = vt8500_set_mctrl, | ||
525 | .get_mctrl = vt8500_get_mctrl, | ||
526 | .stop_tx = vt8500_stop_tx, | ||
527 | .start_tx = vt8500_start_tx, | ||
528 | .stop_rx = vt8500_stop_rx, | ||
529 | .enable_ms = vt8500_enable_ms, | ||
530 | .break_ctl = vt8500_break_ctl, | ||
531 | .startup = vt8500_startup, | ||
532 | .shutdown = vt8500_shutdown, | ||
533 | .set_termios = vt8500_set_termios, | ||
534 | .type = vt8500_type, | ||
535 | .release_port = vt8500_release_port, | ||
536 | .request_port = vt8500_request_port, | ||
537 | .config_port = vt8500_config_port, | ||
538 | .verify_port = vt8500_verify_port, | ||
539 | }; | ||
540 | |||
541 | static struct uart_driver vt8500_uart_driver = { | ||
542 | .owner = THIS_MODULE, | ||
543 | .driver_name = "vt8500_serial", | ||
544 | .dev_name = "ttyWMT", | ||
545 | .nr = 6, | ||
546 | .cons = VT8500_CONSOLE, | ||
547 | }; | ||
548 | |||
549 | static int __init vt8500_serial_probe(struct platform_device *pdev) | ||
550 | { | ||
551 | struct vt8500_port *vt8500_port; | ||
552 | struct resource *mmres, *irqres; | ||
553 | int ret; | ||
554 | |||
555 | mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
556 | irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | ||
557 | if (!mmres || !irqres) | ||
558 | return -ENODEV; | ||
559 | |||
560 | vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL); | ||
561 | if (!vt8500_port) | ||
562 | return -ENOMEM; | ||
563 | |||
564 | vt8500_port->uart.type = PORT_VT8500; | ||
565 | vt8500_port->uart.iotype = UPIO_MEM; | ||
566 | vt8500_port->uart.mapbase = mmres->start; | ||
567 | vt8500_port->uart.irq = irqres->start; | ||
568 | vt8500_port->uart.fifosize = 16; | ||
569 | vt8500_port->uart.ops = &vt8500_uart_pops; | ||
570 | vt8500_port->uart.line = pdev->id; | ||
571 | vt8500_port->uart.dev = &pdev->dev; | ||
572 | vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; | ||
573 | vt8500_port->uart.uartclk = 24000000; | ||
574 | |||
575 | snprintf(vt8500_port->name, sizeof(vt8500_port->name), | ||
576 | "VT8500 UART%d", pdev->id); | ||
577 | |||
578 | vt8500_port->uart.membase = ioremap(mmres->start, | ||
579 | mmres->end - mmres->start + 1); | ||
580 | if (!vt8500_port->uart.membase) { | ||
581 | ret = -ENOMEM; | ||
582 | goto err; | ||
583 | } | ||
584 | |||
585 | vt8500_uart_ports[pdev->id] = vt8500_port; | ||
586 | |||
587 | uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart); | ||
588 | |||
589 | platform_set_drvdata(pdev, vt8500_port); | ||
590 | |||
591 | return 0; | ||
592 | |||
593 | err: | ||
594 | kfree(vt8500_port); | ||
595 | return ret; | ||
596 | } | ||
597 | |||
598 | static int __devexit vt8500_serial_remove(struct platform_device *pdev) | ||
599 | { | ||
600 | struct vt8500_port *vt8500_port = platform_get_drvdata(pdev); | ||
601 | |||
602 | platform_set_drvdata(pdev, NULL); | ||
603 | uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart); | ||
604 | kfree(vt8500_port); | ||
605 | |||
606 | return 0; | ||
607 | } | ||
608 | |||
609 | static struct platform_driver vt8500_platform_driver = { | ||
610 | .probe = vt8500_serial_probe, | ||
611 | .remove = vt8500_serial_remove, | ||
612 | .driver = { | ||
613 | .name = "vt8500_serial", | ||
614 | .owner = THIS_MODULE, | ||
615 | }, | ||
616 | }; | ||
617 | |||
618 | static int __init vt8500_serial_init(void) | ||
619 | { | ||
620 | int ret; | ||
621 | |||
622 | ret = uart_register_driver(&vt8500_uart_driver); | ||
623 | if (unlikely(ret)) | ||
624 | return ret; | ||
625 | |||
626 | ret = platform_driver_register(&vt8500_platform_driver); | ||
627 | |||
628 | if (unlikely(ret)) | ||
629 | uart_unregister_driver(&vt8500_uart_driver); | ||
630 | |||
631 | return ret; | ||
632 | } | ||
633 | |||
634 | static void __exit vt8500_serial_exit(void) | ||
635 | { | ||
636 | #ifdef CONFIG_SERIAL_VT8500_CONSOLE | ||
637 | unregister_console(&vt8500_console); | ||
638 | #endif | ||
639 | platform_driver_unregister(&vt8500_platform_driver); | ||
640 | uart_unregister_driver(&vt8500_uart_driver); | ||
641 | } | ||
642 | |||
643 | module_init(vt8500_serial_init); | ||
644 | module_exit(vt8500_serial_exit); | ||
645 | |||
646 | MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>"); | ||
647 | MODULE_DESCRIPTION("Driver for vt8500 serial device"); | ||
648 | MODULE_LICENSE("GPL"); | ||