diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2011-01-13 15:10:18 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-01-13 15:10:18 -0500 |
commit | ab4382d27412e7e3e7c936e8d50d8888dfac3df8 (patch) | |
tree | 51d96dea2431140358784b6b426715f37f74fd53 /drivers/tty/serial/dz.c | |
parent | 728674a7e466628df2aeec6d11a2ae1ef968fb67 (diff) |
tty: move drivers/serial/ to drivers/tty/serial/
The serial drivers are really just tty drivers, so move them to
drivers/tty/ to make things a bit neater overall.
This is part of the tty/serial driver movement proceedure as proposed by
Arnd Bergmann and approved by everyone involved a number of months ago.
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Rogier Wolff <R.E.Wolff@bitwizard.nl>
Cc: Michael H. Warfield <mhw@wittsend.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/tty/serial/dz.c')
-rw-r--r-- | drivers/tty/serial/dz.c | 955 |
1 files changed, 955 insertions, 0 deletions
diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c new file mode 100644 index 000000000000..57421d776329 --- /dev/null +++ b/drivers/tty/serial/dz.c | |||
@@ -0,0 +1,955 @@ | |||
1 | /* | ||
2 | * dz.c: Serial port driver for DECstations equipped | ||
3 | * with the DZ chipset. | ||
4 | * | ||
5 | * Copyright (C) 1998 Olivier A. D. Lebaillif | ||
6 | * | ||
7 | * Email: olivier.lebaillif@ifrsys.com | ||
8 | * | ||
9 | * Copyright (C) 2004, 2006, 2007 Maciej W. Rozycki | ||
10 | * | ||
11 | * [31-AUG-98] triemer | ||
12 | * Changed IRQ to use Harald's dec internals interrupts.h | ||
13 | * removed base_addr code - moving address assignment to setup.c | ||
14 | * Changed name of dz_init to rs_init to be consistent with tc code | ||
15 | * [13-NOV-98] triemer fixed code to receive characters | ||
16 | * after patches by harald to irq code. | ||
17 | * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout | ||
18 | * field from "current" - somewhere between 2.1.121 and 2.1.131 | ||
19 | Qua Jun 27 15:02:26 BRT 2001 | ||
20 | * [27-JUN-2001] Arnaldo Carvalho de Melo <acme@conectiva.com.br> - cleanups | ||
21 | * | ||
22 | * Parts (C) 1999 David Airlie, airlied@linux.ie | ||
23 | * [07-SEP-99] Bugfixes | ||
24 | * | ||
25 | * [06-Jan-2002] Russell King <rmk@arm.linux.org.uk> | ||
26 | * Converted to new serial core | ||
27 | */ | ||
28 | |||
29 | #undef DEBUG_DZ | ||
30 | |||
31 | #if defined(CONFIG_SERIAL_DZ_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) | ||
32 | #define SUPPORT_SYSRQ | ||
33 | #endif | ||
34 | |||
35 | #include <linux/bitops.h> | ||
36 | #include <linux/compiler.h> | ||
37 | #include <linux/console.h> | ||
38 | #include <linux/delay.h> | ||
39 | #include <linux/errno.h> | ||
40 | #include <linux/init.h> | ||
41 | #include <linux/interrupt.h> | ||
42 | #include <linux/ioport.h> | ||
43 | #include <linux/kernel.h> | ||
44 | #include <linux/major.h> | ||
45 | #include <linux/module.h> | ||
46 | #include <linux/serial.h> | ||
47 | #include <linux/serial_core.h> | ||
48 | #include <linux/sysrq.h> | ||
49 | #include <linux/tty.h> | ||
50 | |||
51 | #include <asm/atomic.h> | ||
52 | #include <asm/bootinfo.h> | ||
53 | #include <asm/io.h> | ||
54 | #include <asm/system.h> | ||
55 | |||
56 | #include <asm/dec/interrupts.h> | ||
57 | #include <asm/dec/kn01.h> | ||
58 | #include <asm/dec/kn02.h> | ||
59 | #include <asm/dec/machtype.h> | ||
60 | #include <asm/dec/prom.h> | ||
61 | #include <asm/dec/system.h> | ||
62 | |||
63 | #include "dz.h" | ||
64 | |||
65 | |||
66 | MODULE_DESCRIPTION("DECstation DZ serial driver"); | ||
67 | MODULE_LICENSE("GPL"); | ||
68 | |||
69 | |||
70 | static char dz_name[] __initdata = "DECstation DZ serial driver version "; | ||
71 | static char dz_version[] __initdata = "1.04"; | ||
72 | |||
73 | struct dz_port { | ||
74 | struct dz_mux *mux; | ||
75 | struct uart_port port; | ||
76 | unsigned int cflag; | ||
77 | }; | ||
78 | |||
79 | struct dz_mux { | ||
80 | struct dz_port dport[DZ_NB_PORT]; | ||
81 | atomic_t map_guard; | ||
82 | atomic_t irq_guard; | ||
83 | int initialised; | ||
84 | }; | ||
85 | |||
86 | static struct dz_mux dz_mux; | ||
87 | |||
88 | static inline struct dz_port *to_dport(struct uart_port *uport) | ||
89 | { | ||
90 | return container_of(uport, struct dz_port, port); | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * ------------------------------------------------------------ | ||
95 | * dz_in () and dz_out () | ||
96 | * | ||
97 | * These routines are used to access the registers of the DZ | ||
98 | * chip, hiding relocation differences between implementation. | ||
99 | * ------------------------------------------------------------ | ||
100 | */ | ||
101 | |||
102 | static u16 dz_in(struct dz_port *dport, unsigned offset) | ||
103 | { | ||
104 | void __iomem *addr = dport->port.membase + offset; | ||
105 | |||
106 | return readw(addr); | ||
107 | } | ||
108 | |||
109 | static void dz_out(struct dz_port *dport, unsigned offset, u16 value) | ||
110 | { | ||
111 | void __iomem *addr = dport->port.membase + offset; | ||
112 | |||
113 | writew(value, addr); | ||
114 | } | ||
115 | |||
116 | /* | ||
117 | * ------------------------------------------------------------ | ||
118 | * rs_stop () and rs_start () | ||
119 | * | ||
120 | * These routines are called before setting or resetting | ||
121 | * tty->stopped. They enable or disable transmitter interrupts, | ||
122 | * as necessary. | ||
123 | * ------------------------------------------------------------ | ||
124 | */ | ||
125 | |||
126 | static void dz_stop_tx(struct uart_port *uport) | ||
127 | { | ||
128 | struct dz_port *dport = to_dport(uport); | ||
129 | u16 tmp, mask = 1 << dport->port.line; | ||
130 | |||
131 | tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ | ||
132 | tmp &= ~mask; /* clear the TX flag */ | ||
133 | dz_out(dport, DZ_TCR, tmp); | ||
134 | } | ||
135 | |||
136 | static void dz_start_tx(struct uart_port *uport) | ||
137 | { | ||
138 | struct dz_port *dport = to_dport(uport); | ||
139 | u16 tmp, mask = 1 << dport->port.line; | ||
140 | |||
141 | tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ | ||
142 | tmp |= mask; /* set the TX flag */ | ||
143 | dz_out(dport, DZ_TCR, tmp); | ||
144 | } | ||
145 | |||
146 | static void dz_stop_rx(struct uart_port *uport) | ||
147 | { | ||
148 | struct dz_port *dport = to_dport(uport); | ||
149 | |||
150 | dport->cflag &= ~DZ_RXENAB; | ||
151 | dz_out(dport, DZ_LPR, dport->cflag); | ||
152 | } | ||
153 | |||
154 | static void dz_enable_ms(struct uart_port *uport) | ||
155 | { | ||
156 | /* nothing to do */ | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * ------------------------------------------------------------ | ||
161 | * | ||
162 | * Here start the interrupt handling routines. All of the following | ||
163 | * subroutines are declared as inline and are folded into | ||
164 | * dz_interrupt. They were separated out for readability's sake. | ||
165 | * | ||
166 | * Note: dz_interrupt() is a "fast" interrupt, which means that it | ||
167 | * runs with interrupts turned off. People who may want to modify | ||
168 | * dz_interrupt() should try to keep the interrupt handler as fast as | ||
169 | * possible. After you are done making modifications, it is not a bad | ||
170 | * idea to do: | ||
171 | * | ||
172 | * make drivers/serial/dz.s | ||
173 | * | ||
174 | * and look at the resulting assemble code in dz.s. | ||
175 | * | ||
176 | * ------------------------------------------------------------ | ||
177 | */ | ||
178 | |||
179 | /* | ||
180 | * ------------------------------------------------------------ | ||
181 | * receive_char () | ||
182 | * | ||
183 | * This routine deals with inputs from any lines. | ||
184 | * ------------------------------------------------------------ | ||
185 | */ | ||
186 | static inline void dz_receive_chars(struct dz_mux *mux) | ||
187 | { | ||
188 | struct uart_port *uport; | ||
189 | struct dz_port *dport = &mux->dport[0]; | ||
190 | struct tty_struct *tty = NULL; | ||
191 | struct uart_icount *icount; | ||
192 | int lines_rx[DZ_NB_PORT] = { [0 ... DZ_NB_PORT - 1] = 0 }; | ||
193 | unsigned char ch, flag; | ||
194 | u16 status; | ||
195 | int i; | ||
196 | |||
197 | while ((status = dz_in(dport, DZ_RBUF)) & DZ_DVAL) { | ||
198 | dport = &mux->dport[LINE(status)]; | ||
199 | uport = &dport->port; | ||
200 | tty = uport->state->port.tty; /* point to the proper dev */ | ||
201 | |||
202 | ch = UCHAR(status); /* grab the char */ | ||
203 | flag = TTY_NORMAL; | ||
204 | |||
205 | icount = &uport->icount; | ||
206 | icount->rx++; | ||
207 | |||
208 | if (unlikely(status & (DZ_OERR | DZ_FERR | DZ_PERR))) { | ||
209 | |||
210 | /* | ||
211 | * There is no separate BREAK status bit, so treat | ||
212 | * null characters with framing errors as BREAKs; | ||
213 | * normally, otherwise. For this move the Framing | ||
214 | * Error bit to a simulated BREAK bit. | ||
215 | */ | ||
216 | if (!ch) { | ||
217 | status |= (status & DZ_FERR) >> | ||
218 | (ffs(DZ_FERR) - ffs(DZ_BREAK)); | ||
219 | status &= ~DZ_FERR; | ||
220 | } | ||
221 | |||
222 | /* Handle SysRq/SAK & keep track of the statistics. */ | ||
223 | if (status & DZ_BREAK) { | ||
224 | icount->brk++; | ||
225 | if (uart_handle_break(uport)) | ||
226 | continue; | ||
227 | } else if (status & DZ_FERR) | ||
228 | icount->frame++; | ||
229 | else if (status & DZ_PERR) | ||
230 | icount->parity++; | ||
231 | if (status & DZ_OERR) | ||
232 | icount->overrun++; | ||
233 | |||
234 | status &= uport->read_status_mask; | ||
235 | if (status & DZ_BREAK) | ||
236 | flag = TTY_BREAK; | ||
237 | else if (status & DZ_FERR) | ||
238 | flag = TTY_FRAME; | ||
239 | else if (status & DZ_PERR) | ||
240 | flag = TTY_PARITY; | ||
241 | |||
242 | } | ||
243 | |||
244 | if (uart_handle_sysrq_char(uport, ch)) | ||
245 | continue; | ||
246 | |||
247 | uart_insert_char(uport, status, DZ_OERR, ch, flag); | ||
248 | lines_rx[LINE(status)] = 1; | ||
249 | } | ||
250 | for (i = 0; i < DZ_NB_PORT; i++) | ||
251 | if (lines_rx[i]) | ||
252 | tty_flip_buffer_push(mux->dport[i].port.state->port.tty); | ||
253 | } | ||
254 | |||
255 | /* | ||
256 | * ------------------------------------------------------------ | ||
257 | * transmit_char () | ||
258 | * | ||
259 | * This routine deals with outputs to any lines. | ||
260 | * ------------------------------------------------------------ | ||
261 | */ | ||
262 | static inline void dz_transmit_chars(struct dz_mux *mux) | ||
263 | { | ||
264 | struct dz_port *dport = &mux->dport[0]; | ||
265 | struct circ_buf *xmit; | ||
266 | unsigned char tmp; | ||
267 | u16 status; | ||
268 | |||
269 | status = dz_in(dport, DZ_CSR); | ||
270 | dport = &mux->dport[LINE(status)]; | ||
271 | xmit = &dport->port.state->xmit; | ||
272 | |||
273 | if (dport->port.x_char) { /* XON/XOFF chars */ | ||
274 | dz_out(dport, DZ_TDR, dport->port.x_char); | ||
275 | dport->port.icount.tx++; | ||
276 | dport->port.x_char = 0; | ||
277 | return; | ||
278 | } | ||
279 | /* If nothing to do or stopped or hardware stopped. */ | ||
280 | if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { | ||
281 | spin_lock(&dport->port.lock); | ||
282 | dz_stop_tx(&dport->port); | ||
283 | spin_unlock(&dport->port.lock); | ||
284 | return; | ||
285 | } | ||
286 | |||
287 | /* | ||
288 | * If something to do... (remember the dz has no output fifo, | ||
289 | * so we go one char at a time) :-< | ||
290 | */ | ||
291 | tmp = xmit->buf[xmit->tail]; | ||
292 | xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1); | ||
293 | dz_out(dport, DZ_TDR, tmp); | ||
294 | dport->port.icount.tx++; | ||
295 | |||
296 | if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS) | ||
297 | uart_write_wakeup(&dport->port); | ||
298 | |||
299 | /* Are we are done. */ | ||
300 | if (uart_circ_empty(xmit)) { | ||
301 | spin_lock(&dport->port.lock); | ||
302 | dz_stop_tx(&dport->port); | ||
303 | spin_unlock(&dport->port.lock); | ||
304 | } | ||
305 | } | ||
306 | |||
307 | /* | ||
308 | * ------------------------------------------------------------ | ||
309 | * check_modem_status() | ||
310 | * | ||
311 | * DS 3100 & 5100: Only valid for the MODEM line, duh! | ||
312 | * DS 5000/200: Valid for the MODEM and PRINTER line. | ||
313 | * ------------------------------------------------------------ | ||
314 | */ | ||
315 | static inline void check_modem_status(struct dz_port *dport) | ||
316 | { | ||
317 | /* | ||
318 | * FIXME: | ||
319 | * 1. No status change interrupt; use a timer. | ||
320 | * 2. Handle the 3100/5000 as appropriate. --macro | ||
321 | */ | ||
322 | u16 status; | ||
323 | |||
324 | /* If not the modem line just return. */ | ||
325 | if (dport->port.line != DZ_MODEM) | ||
326 | return; | ||
327 | |||
328 | status = dz_in(dport, DZ_MSR); | ||
329 | |||
330 | /* it's easy, since DSR2 is the only bit in the register */ | ||
331 | if (status) | ||
332 | dport->port.icount.dsr++; | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * ------------------------------------------------------------ | ||
337 | * dz_interrupt () | ||
338 | * | ||
339 | * this is the main interrupt routine for the DZ chip. | ||
340 | * It deals with the multiple ports. | ||
341 | * ------------------------------------------------------------ | ||
342 | */ | ||
343 | static irqreturn_t dz_interrupt(int irq, void *dev_id) | ||
344 | { | ||
345 | struct dz_mux *mux = dev_id; | ||
346 | struct dz_port *dport = &mux->dport[0]; | ||
347 | u16 status; | ||
348 | |||
349 | /* get the reason why we just got an irq */ | ||
350 | status = dz_in(dport, DZ_CSR); | ||
351 | |||
352 | if ((status & (DZ_RDONE | DZ_RIE)) == (DZ_RDONE | DZ_RIE)) | ||
353 | dz_receive_chars(mux); | ||
354 | |||
355 | if ((status & (DZ_TRDY | DZ_TIE)) == (DZ_TRDY | DZ_TIE)) | ||
356 | dz_transmit_chars(mux); | ||
357 | |||
358 | return IRQ_HANDLED; | ||
359 | } | ||
360 | |||
361 | /* | ||
362 | * ------------------------------------------------------------------- | ||
363 | * Here ends the DZ interrupt routines. | ||
364 | * ------------------------------------------------------------------- | ||
365 | */ | ||
366 | |||
367 | static unsigned int dz_get_mctrl(struct uart_port *uport) | ||
368 | { | ||
369 | /* | ||
370 | * FIXME: Handle the 3100/5000 as appropriate. --macro | ||
371 | */ | ||
372 | struct dz_port *dport = to_dport(uport); | ||
373 | unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; | ||
374 | |||
375 | if (dport->port.line == DZ_MODEM) { | ||
376 | if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR) | ||
377 | mctrl &= ~TIOCM_DSR; | ||
378 | } | ||
379 | |||
380 | return mctrl; | ||
381 | } | ||
382 | |||
383 | static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl) | ||
384 | { | ||
385 | /* | ||
386 | * FIXME: Handle the 3100/5000 as appropriate. --macro | ||
387 | */ | ||
388 | struct dz_port *dport = to_dport(uport); | ||
389 | u16 tmp; | ||
390 | |||
391 | if (dport->port.line == DZ_MODEM) { | ||
392 | tmp = dz_in(dport, DZ_TCR); | ||
393 | if (mctrl & TIOCM_DTR) | ||
394 | tmp &= ~DZ_MODEM_DTR; | ||
395 | else | ||
396 | tmp |= DZ_MODEM_DTR; | ||
397 | dz_out(dport, DZ_TCR, tmp); | ||
398 | } | ||
399 | } | ||
400 | |||
401 | /* | ||
402 | * ------------------------------------------------------------------- | ||
403 | * startup () | ||
404 | * | ||
405 | * various initialization tasks | ||
406 | * ------------------------------------------------------------------- | ||
407 | */ | ||
408 | static int dz_startup(struct uart_port *uport) | ||
409 | { | ||
410 | struct dz_port *dport = to_dport(uport); | ||
411 | struct dz_mux *mux = dport->mux; | ||
412 | unsigned long flags; | ||
413 | int irq_guard; | ||
414 | int ret; | ||
415 | u16 tmp; | ||
416 | |||
417 | irq_guard = atomic_add_return(1, &mux->irq_guard); | ||
418 | if (irq_guard != 1) | ||
419 | return 0; | ||
420 | |||
421 | ret = request_irq(dport->port.irq, dz_interrupt, | ||
422 | IRQF_SHARED, "dz", mux); | ||
423 | if (ret) { | ||
424 | atomic_add(-1, &mux->irq_guard); | ||
425 | printk(KERN_ERR "dz: Cannot get IRQ %d!\n", dport->port.irq); | ||
426 | return ret; | ||
427 | } | ||
428 | |||
429 | spin_lock_irqsave(&dport->port.lock, flags); | ||
430 | |||
431 | /* Enable interrupts. */ | ||
432 | tmp = dz_in(dport, DZ_CSR); | ||
433 | tmp |= DZ_RIE | DZ_TIE; | ||
434 | dz_out(dport, DZ_CSR, tmp); | ||
435 | |||
436 | spin_unlock_irqrestore(&dport->port.lock, flags); | ||
437 | |||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | /* | ||
442 | * ------------------------------------------------------------------- | ||
443 | * shutdown () | ||
444 | * | ||
445 | * This routine will shutdown a serial port; interrupts are disabled, and | ||
446 | * DTR is dropped if the hangup on close termio flag is on. | ||
447 | * ------------------------------------------------------------------- | ||
448 | */ | ||
449 | static void dz_shutdown(struct uart_port *uport) | ||
450 | { | ||
451 | struct dz_port *dport = to_dport(uport); | ||
452 | struct dz_mux *mux = dport->mux; | ||
453 | unsigned long flags; | ||
454 | int irq_guard; | ||
455 | u16 tmp; | ||
456 | |||
457 | spin_lock_irqsave(&dport->port.lock, flags); | ||
458 | dz_stop_tx(&dport->port); | ||
459 | spin_unlock_irqrestore(&dport->port.lock, flags); | ||
460 | |||
461 | irq_guard = atomic_add_return(-1, &mux->irq_guard); | ||
462 | if (!irq_guard) { | ||
463 | /* Disable interrupts. */ | ||
464 | tmp = dz_in(dport, DZ_CSR); | ||
465 | tmp &= ~(DZ_RIE | DZ_TIE); | ||
466 | dz_out(dport, DZ_CSR, tmp); | ||
467 | |||
468 | free_irq(dport->port.irq, mux); | ||
469 | } | ||
470 | } | ||
471 | |||
472 | /* | ||
473 | * ------------------------------------------------------------------- | ||
474 | * dz_tx_empty() -- get the transmitter empty status | ||
475 | * | ||
476 | * Purpose: Let user call ioctl() to get info when the UART physically | ||
477 | * is emptied. On bus types like RS485, the transmitter must | ||
478 | * release the bus after transmitting. This must be done when | ||
479 | * the transmit shift register is empty, not be done when the | ||
480 | * transmit holding register is empty. This functionality | ||
481 | * allows an RS485 driver to be written in user space. | ||
482 | * ------------------------------------------------------------------- | ||
483 | */ | ||
484 | static unsigned int dz_tx_empty(struct uart_port *uport) | ||
485 | { | ||
486 | struct dz_port *dport = to_dport(uport); | ||
487 | unsigned short tmp, mask = 1 << dport->port.line; | ||
488 | |||
489 | tmp = dz_in(dport, DZ_TCR); | ||
490 | tmp &= mask; | ||
491 | |||
492 | return tmp ? 0 : TIOCSER_TEMT; | ||
493 | } | ||
494 | |||
495 | static void dz_break_ctl(struct uart_port *uport, int break_state) | ||
496 | { | ||
497 | /* | ||
498 | * FIXME: Can't access BREAK bits in TDR easily; | ||
499 | * reuse the code for polled TX. --macro | ||
500 | */ | ||
501 | struct dz_port *dport = to_dport(uport); | ||
502 | unsigned long flags; | ||
503 | unsigned short tmp, mask = 1 << dport->port.line; | ||
504 | |||
505 | spin_lock_irqsave(&uport->lock, flags); | ||
506 | tmp = dz_in(dport, DZ_TCR); | ||
507 | if (break_state) | ||
508 | tmp |= mask; | ||
509 | else | ||
510 | tmp &= ~mask; | ||
511 | dz_out(dport, DZ_TCR, tmp); | ||
512 | spin_unlock_irqrestore(&uport->lock, flags); | ||
513 | } | ||
514 | |||
515 | static int dz_encode_baud_rate(unsigned int baud) | ||
516 | { | ||
517 | switch (baud) { | ||
518 | case 50: | ||
519 | return DZ_B50; | ||
520 | case 75: | ||
521 | return DZ_B75; | ||
522 | case 110: | ||
523 | return DZ_B110; | ||
524 | case 134: | ||
525 | return DZ_B134; | ||
526 | case 150: | ||
527 | return DZ_B150; | ||
528 | case 300: | ||
529 | return DZ_B300; | ||
530 | case 600: | ||
531 | return DZ_B600; | ||
532 | case 1200: | ||
533 | return DZ_B1200; | ||
534 | case 1800: | ||
535 | return DZ_B1800; | ||
536 | case 2000: | ||
537 | return DZ_B2000; | ||
538 | case 2400: | ||
539 | return DZ_B2400; | ||
540 | case 3600: | ||
541 | return DZ_B3600; | ||
542 | case 4800: | ||
543 | return DZ_B4800; | ||
544 | case 7200: | ||
545 | return DZ_B7200; | ||
546 | case 9600: | ||
547 | return DZ_B9600; | ||
548 | default: | ||
549 | return -1; | ||
550 | } | ||
551 | } | ||
552 | |||
553 | |||
554 | static void dz_reset(struct dz_port *dport) | ||
555 | { | ||
556 | struct dz_mux *mux = dport->mux; | ||
557 | |||
558 | if (mux->initialised) | ||
559 | return; | ||
560 | |||
561 | dz_out(dport, DZ_CSR, DZ_CLR); | ||
562 | while (dz_in(dport, DZ_CSR) & DZ_CLR); | ||
563 | iob(); | ||
564 | |||
565 | /* Enable scanning. */ | ||
566 | dz_out(dport, DZ_CSR, DZ_MSE); | ||
567 | |||
568 | mux->initialised = 1; | ||
569 | } | ||
570 | |||
571 | static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, | ||
572 | struct ktermios *old_termios) | ||
573 | { | ||
574 | struct dz_port *dport = to_dport(uport); | ||
575 | unsigned long flags; | ||
576 | unsigned int cflag, baud; | ||
577 | int bflag; | ||
578 | |||
579 | cflag = dport->port.line; | ||
580 | |||
581 | switch (termios->c_cflag & CSIZE) { | ||
582 | case CS5: | ||
583 | cflag |= DZ_CS5; | ||
584 | break; | ||
585 | case CS6: | ||
586 | cflag |= DZ_CS6; | ||
587 | break; | ||
588 | case CS7: | ||
589 | cflag |= DZ_CS7; | ||
590 | break; | ||
591 | case CS8: | ||
592 | default: | ||
593 | cflag |= DZ_CS8; | ||
594 | } | ||
595 | |||
596 | if (termios->c_cflag & CSTOPB) | ||
597 | cflag |= DZ_CSTOPB; | ||
598 | if (termios->c_cflag & PARENB) | ||
599 | cflag |= DZ_PARENB; | ||
600 | if (termios->c_cflag & PARODD) | ||
601 | cflag |= DZ_PARODD; | ||
602 | |||
603 | baud = uart_get_baud_rate(uport, termios, old_termios, 50, 9600); | ||
604 | bflag = dz_encode_baud_rate(baud); | ||
605 | if (bflag < 0) { /* Try to keep unchanged. */ | ||
606 | baud = uart_get_baud_rate(uport, old_termios, NULL, 50, 9600); | ||
607 | bflag = dz_encode_baud_rate(baud); | ||
608 | if (bflag < 0) { /* Resort to 9600. */ | ||
609 | baud = 9600; | ||
610 | bflag = DZ_B9600; | ||
611 | } | ||
612 | tty_termios_encode_baud_rate(termios, baud, baud); | ||
613 | } | ||
614 | cflag |= bflag; | ||
615 | |||
616 | if (termios->c_cflag & CREAD) | ||
617 | cflag |= DZ_RXENAB; | ||
618 | |||
619 | spin_lock_irqsave(&dport->port.lock, flags); | ||
620 | |||
621 | uart_update_timeout(uport, termios->c_cflag, baud); | ||
622 | |||
623 | dz_out(dport, DZ_LPR, cflag); | ||
624 | dport->cflag = cflag; | ||
625 | |||
626 | /* setup accept flag */ | ||
627 | dport->port.read_status_mask = DZ_OERR; | ||
628 | if (termios->c_iflag & INPCK) | ||
629 | dport->port.read_status_mask |= DZ_FERR | DZ_PERR; | ||
630 | if (termios->c_iflag & (BRKINT | PARMRK)) | ||
631 | dport->port.read_status_mask |= DZ_BREAK; | ||
632 | |||
633 | /* characters to ignore */ | ||
634 | uport->ignore_status_mask = 0; | ||
635 | if ((termios->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) | ||
636 | dport->port.ignore_status_mask |= DZ_OERR; | ||
637 | if (termios->c_iflag & IGNPAR) | ||
638 | dport->port.ignore_status_mask |= DZ_FERR | DZ_PERR; | ||
639 | if (termios->c_iflag & IGNBRK) | ||
640 | dport->port.ignore_status_mask |= DZ_BREAK; | ||
641 | |||
642 | spin_unlock_irqrestore(&dport->port.lock, flags); | ||
643 | } | ||
644 | |||
645 | /* | ||
646 | * Hack alert! | ||
647 | * Required solely so that the initial PROM-based console | ||
648 | * works undisturbed in parallel with this one. | ||
649 | */ | ||
650 | static void dz_pm(struct uart_port *uport, unsigned int state, | ||
651 | unsigned int oldstate) | ||
652 | { | ||
653 | struct dz_port *dport = to_dport(uport); | ||
654 | unsigned long flags; | ||
655 | |||
656 | spin_lock_irqsave(&dport->port.lock, flags); | ||
657 | if (state < 3) | ||
658 | dz_start_tx(&dport->port); | ||
659 | else | ||
660 | dz_stop_tx(&dport->port); | ||
661 | spin_unlock_irqrestore(&dport->port.lock, flags); | ||
662 | } | ||
663 | |||
664 | |||
665 | static const char *dz_type(struct uart_port *uport) | ||
666 | { | ||
667 | return "DZ"; | ||
668 | } | ||
669 | |||
670 | static void dz_release_port(struct uart_port *uport) | ||
671 | { | ||
672 | struct dz_mux *mux = to_dport(uport)->mux; | ||
673 | int map_guard; | ||
674 | |||
675 | iounmap(uport->membase); | ||
676 | uport->membase = NULL; | ||
677 | |||
678 | map_guard = atomic_add_return(-1, &mux->map_guard); | ||
679 | if (!map_guard) | ||
680 | release_mem_region(uport->mapbase, dec_kn_slot_size); | ||
681 | } | ||
682 | |||
683 | static int dz_map_port(struct uart_port *uport) | ||
684 | { | ||
685 | if (!uport->membase) | ||
686 | uport->membase = ioremap_nocache(uport->mapbase, | ||
687 | dec_kn_slot_size); | ||
688 | if (!uport->membase) { | ||
689 | printk(KERN_ERR "dz: Cannot map MMIO\n"); | ||
690 | return -ENOMEM; | ||
691 | } | ||
692 | return 0; | ||
693 | } | ||
694 | |||
695 | static int dz_request_port(struct uart_port *uport) | ||
696 | { | ||
697 | struct dz_mux *mux = to_dport(uport)->mux; | ||
698 | int map_guard; | ||
699 | int ret; | ||
700 | |||
701 | map_guard = atomic_add_return(1, &mux->map_guard); | ||
702 | if (map_guard == 1) { | ||
703 | if (!request_mem_region(uport->mapbase, dec_kn_slot_size, | ||
704 | "dz")) { | ||
705 | atomic_add(-1, &mux->map_guard); | ||
706 | printk(KERN_ERR | ||
707 | "dz: Unable to reserve MMIO resource\n"); | ||
708 | return -EBUSY; | ||
709 | } | ||
710 | } | ||
711 | ret = dz_map_port(uport); | ||
712 | if (ret) { | ||
713 | map_guard = atomic_add_return(-1, &mux->map_guard); | ||
714 | if (!map_guard) | ||
715 | release_mem_region(uport->mapbase, dec_kn_slot_size); | ||
716 | return ret; | ||
717 | } | ||
718 | return 0; | ||
719 | } | ||
720 | |||
721 | static void dz_config_port(struct uart_port *uport, int flags) | ||
722 | { | ||
723 | struct dz_port *dport = to_dport(uport); | ||
724 | |||
725 | if (flags & UART_CONFIG_TYPE) { | ||
726 | if (dz_request_port(uport)) | ||
727 | return; | ||
728 | |||
729 | uport->type = PORT_DZ; | ||
730 | |||
731 | dz_reset(dport); | ||
732 | } | ||
733 | } | ||
734 | |||
735 | /* | ||
736 | * Verify the new serial_struct (for TIOCSSERIAL). | ||
737 | */ | ||
738 | static int dz_verify_port(struct uart_port *uport, struct serial_struct *ser) | ||
739 | { | ||
740 | int ret = 0; | ||
741 | |||
742 | if (ser->type != PORT_UNKNOWN && ser->type != PORT_DZ) | ||
743 | ret = -EINVAL; | ||
744 | if (ser->irq != uport->irq) | ||
745 | ret = -EINVAL; | ||
746 | return ret; | ||
747 | } | ||
748 | |||
749 | static struct uart_ops dz_ops = { | ||
750 | .tx_empty = dz_tx_empty, | ||
751 | .get_mctrl = dz_get_mctrl, | ||
752 | .set_mctrl = dz_set_mctrl, | ||
753 | .stop_tx = dz_stop_tx, | ||
754 | .start_tx = dz_start_tx, | ||
755 | .stop_rx = dz_stop_rx, | ||
756 | .enable_ms = dz_enable_ms, | ||
757 | .break_ctl = dz_break_ctl, | ||
758 | .startup = dz_startup, | ||
759 | .shutdown = dz_shutdown, | ||
760 | .set_termios = dz_set_termios, | ||
761 | .pm = dz_pm, | ||
762 | .type = dz_type, | ||
763 | .release_port = dz_release_port, | ||
764 | .request_port = dz_request_port, | ||
765 | .config_port = dz_config_port, | ||
766 | .verify_port = dz_verify_port, | ||
767 | }; | ||
768 | |||
769 | static void __init dz_init_ports(void) | ||
770 | { | ||
771 | static int first = 1; | ||
772 | unsigned long base; | ||
773 | int line; | ||
774 | |||
775 | if (!first) | ||
776 | return; | ||
777 | first = 0; | ||
778 | |||
779 | if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100) | ||
780 | base = dec_kn_slot_base + KN01_DZ11; | ||
781 | else | ||
782 | base = dec_kn_slot_base + KN02_DZ11; | ||
783 | |||
784 | for (line = 0; line < DZ_NB_PORT; line++) { | ||
785 | struct dz_port *dport = &dz_mux.dport[line]; | ||
786 | struct uart_port *uport = &dport->port; | ||
787 | |||
788 | dport->mux = &dz_mux; | ||
789 | |||
790 | uport->irq = dec_interrupt[DEC_IRQ_DZ11]; | ||
791 | uport->fifosize = 1; | ||
792 | uport->iotype = UPIO_MEM; | ||
793 | uport->flags = UPF_BOOT_AUTOCONF; | ||
794 | uport->ops = &dz_ops; | ||
795 | uport->line = line; | ||
796 | uport->mapbase = base; | ||
797 | } | ||
798 | } | ||
799 | |||
800 | #ifdef CONFIG_SERIAL_DZ_CONSOLE | ||
801 | /* | ||
802 | * ------------------------------------------------------------------- | ||
803 | * dz_console_putchar() -- transmit a character | ||
804 | * | ||
805 | * Polled transmission. This is tricky. We need to mask transmit | ||
806 | * interrupts so that they do not interfere, enable the transmitter | ||
807 | * for the line requested and then wait till the transmit scanner | ||
808 | * requests data for this line. But it may request data for another | ||
809 | * line first, in which case we have to disable its transmitter and | ||
810 | * repeat waiting till our line pops up. Only then the character may | ||
811 | * be transmitted. Finally, the state of the transmitter mask is | ||
812 | * restored. Welcome to the world of PDP-11! | ||
813 | * ------------------------------------------------------------------- | ||
814 | */ | ||
815 | static void dz_console_putchar(struct uart_port *uport, int ch) | ||
816 | { | ||
817 | struct dz_port *dport = to_dport(uport); | ||
818 | unsigned long flags; | ||
819 | unsigned short csr, tcr, trdy, mask; | ||
820 | int loops = 10000; | ||
821 | |||
822 | spin_lock_irqsave(&dport->port.lock, flags); | ||
823 | csr = dz_in(dport, DZ_CSR); | ||
824 | dz_out(dport, DZ_CSR, csr & ~DZ_TIE); | ||
825 | tcr = dz_in(dport, DZ_TCR); | ||
826 | tcr |= 1 << dport->port.line; | ||
827 | mask = tcr; | ||
828 | dz_out(dport, DZ_TCR, mask); | ||
829 | iob(); | ||
830 | spin_unlock_irqrestore(&dport->port.lock, flags); | ||
831 | |||
832 | do { | ||
833 | trdy = dz_in(dport, DZ_CSR); | ||
834 | if (!(trdy & DZ_TRDY)) | ||
835 | continue; | ||
836 | trdy = (trdy & DZ_TLINE) >> 8; | ||
837 | if (trdy == dport->port.line) | ||
838 | break; | ||
839 | mask &= ~(1 << trdy); | ||
840 | dz_out(dport, DZ_TCR, mask); | ||
841 | iob(); | ||
842 | udelay(2); | ||
843 | } while (--loops); | ||
844 | |||
845 | if (loops) /* Cannot send otherwise. */ | ||
846 | dz_out(dport, DZ_TDR, ch); | ||
847 | |||
848 | dz_out(dport, DZ_TCR, tcr); | ||
849 | dz_out(dport, DZ_CSR, csr); | ||
850 | } | ||
851 | |||
852 | /* | ||
853 | * ------------------------------------------------------------------- | ||
854 | * dz_console_print () | ||
855 | * | ||
856 | * dz_console_print is registered for printk. | ||
857 | * The console must be locked when we get here. | ||
858 | * ------------------------------------------------------------------- | ||
859 | */ | ||
860 | static void dz_console_print(struct console *co, | ||
861 | const char *str, | ||
862 | unsigned int count) | ||
863 | { | ||
864 | struct dz_port *dport = &dz_mux.dport[co->index]; | ||
865 | #ifdef DEBUG_DZ | ||
866 | prom_printf((char *) str); | ||
867 | #endif | ||
868 | uart_console_write(&dport->port, str, count, dz_console_putchar); | ||
869 | } | ||
870 | |||
871 | static int __init dz_console_setup(struct console *co, char *options) | ||
872 | { | ||
873 | struct dz_port *dport = &dz_mux.dport[co->index]; | ||
874 | struct uart_port *uport = &dport->port; | ||
875 | int baud = 9600; | ||
876 | int bits = 8; | ||
877 | int parity = 'n'; | ||
878 | int flow = 'n'; | ||
879 | int ret; | ||
880 | |||
881 | ret = dz_map_port(uport); | ||
882 | if (ret) | ||
883 | return ret; | ||
884 | |||
885 | spin_lock_init(&dport->port.lock); /* For dz_pm(). */ | ||
886 | |||
887 | dz_reset(dport); | ||
888 | dz_pm(uport, 0, -1); | ||
889 | |||
890 | if (options) | ||
891 | uart_parse_options(options, &baud, &parity, &bits, &flow); | ||
892 | |||
893 | return uart_set_options(&dport->port, co, baud, parity, bits, flow); | ||
894 | } | ||
895 | |||
896 | static struct uart_driver dz_reg; | ||
897 | static struct console dz_console = { | ||
898 | .name = "ttyS", | ||
899 | .write = dz_console_print, | ||
900 | .device = uart_console_device, | ||
901 | .setup = dz_console_setup, | ||
902 | .flags = CON_PRINTBUFFER, | ||
903 | .index = -1, | ||
904 | .data = &dz_reg, | ||
905 | }; | ||
906 | |||
907 | static int __init dz_serial_console_init(void) | ||
908 | { | ||
909 | if (!IOASIC) { | ||
910 | dz_init_ports(); | ||
911 | register_console(&dz_console); | ||
912 | return 0; | ||
913 | } else | ||
914 | return -ENXIO; | ||
915 | } | ||
916 | |||
917 | console_initcall(dz_serial_console_init); | ||
918 | |||
919 | #define SERIAL_DZ_CONSOLE &dz_console | ||
920 | #else | ||
921 | #define SERIAL_DZ_CONSOLE NULL | ||
922 | #endif /* CONFIG_SERIAL_DZ_CONSOLE */ | ||
923 | |||
924 | static struct uart_driver dz_reg = { | ||
925 | .owner = THIS_MODULE, | ||
926 | .driver_name = "serial", | ||
927 | .dev_name = "ttyS", | ||
928 | .major = TTY_MAJOR, | ||
929 | .minor = 64, | ||
930 | .nr = DZ_NB_PORT, | ||
931 | .cons = SERIAL_DZ_CONSOLE, | ||
932 | }; | ||
933 | |||
934 | static int __init dz_init(void) | ||
935 | { | ||
936 | int ret, i; | ||
937 | |||
938 | if (IOASIC) | ||
939 | return -ENXIO; | ||
940 | |||
941 | printk("%s%s\n", dz_name, dz_version); | ||
942 | |||
943 | dz_init_ports(); | ||
944 | |||
945 | ret = uart_register_driver(&dz_reg); | ||
946 | if (ret) | ||
947 | return ret; | ||
948 | |||
949 | for (i = 0; i < DZ_NB_PORT; i++) | ||
950 | uart_add_one_port(&dz_reg, &dz_mux.dport[i].port); | ||
951 | |||
952 | return 0; | ||
953 | } | ||
954 | |||
955 | module_init(dz_init); | ||