diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/serial/dz.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/serial/dz.c')
-rw-r--r-- | drivers/serial/dz.c | 822 |
1 files changed, 822 insertions, 0 deletions
diff --git a/drivers/serial/dz.c b/drivers/serial/dz.c new file mode 100644 index 000000000000..97824eeeafae --- /dev/null +++ b/drivers/serial/dz.c | |||
@@ -0,0 +1,822 @@ | |||
1 | /* | ||
2 | * dz.c: Serial port driver for DECStations equiped | ||
3 | * with the DZ chipset. | ||
4 | * | ||
5 | * Copyright (C) 1998 Olivier A. D. Lebaillif | ||
6 | * | ||
7 | * Email: olivier.lebaillif@ifrsys.com | ||
8 | * | ||
9 | * [31-AUG-98] triemer | ||
10 | * Changed IRQ to use Harald's dec internals interrupts.h | ||
11 | * removed base_addr code - moving address assignment to setup.c | ||
12 | * Changed name of dz_init to rs_init to be consistent with tc code | ||
13 | * [13-NOV-98] triemer fixed code to receive characters | ||
14 | * after patches by harald to irq code. | ||
15 | * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout | ||
16 | * field from "current" - somewhere between 2.1.121 and 2.1.131 | ||
17 | Qua Jun 27 15:02:26 BRT 2001 | ||
18 | * [27-JUN-2001] Arnaldo Carvalho de Melo <acme@conectiva.com.br> - cleanups | ||
19 | * | ||
20 | * Parts (C) 1999 David Airlie, airlied@linux.ie | ||
21 | * [07-SEP-99] Bugfixes | ||
22 | * | ||
23 | * [06-Jan-2002] Russell King <rmk@arm.linux.org.uk> | ||
24 | * Converted to new serial core | ||
25 | */ | ||
26 | |||
27 | #undef DEBUG_DZ | ||
28 | |||
29 | #include <linux/config.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/init.h> | ||
33 | #include <linux/console.h> | ||
34 | #include <linux/tty.h> | ||
35 | #include <linux/tty_flip.h> | ||
36 | #include <linux/serial_core.h> | ||
37 | #include <linux/serial.h> | ||
38 | |||
39 | #include <asm/bootinfo.h> | ||
40 | #include <asm/dec/interrupts.h> | ||
41 | #include <asm/dec/kn01.h> | ||
42 | #include <asm/dec/kn02.h> | ||
43 | #include <asm/dec/machtype.h> | ||
44 | #include <asm/dec/prom.h> | ||
45 | #include <asm/irq.h> | ||
46 | #include <asm/system.h> | ||
47 | #include <asm/uaccess.h> | ||
48 | |||
49 | #define CONSOLE_LINE (3) /* for definition of struct console */ | ||
50 | |||
51 | #include "dz.h" | ||
52 | |||
53 | #define DZ_INTR_DEBUG 1 | ||
54 | |||
55 | static char *dz_name = "DECstation DZ serial driver version "; | ||
56 | static char *dz_version = "1.02"; | ||
57 | |||
58 | struct dz_port { | ||
59 | struct uart_port port; | ||
60 | unsigned int cflag; | ||
61 | }; | ||
62 | |||
63 | static struct dz_port dz_ports[DZ_NB_PORT]; | ||
64 | |||
65 | #ifdef DEBUG_DZ | ||
66 | /* | ||
67 | * debugging code to send out chars via prom | ||
68 | */ | ||
69 | static void debug_console(const char *s, int count) | ||
70 | { | ||
71 | unsigned i; | ||
72 | |||
73 | for (i = 0; i < count; i++) { | ||
74 | if (*s == 10) | ||
75 | prom_printf("%c", 13); | ||
76 | prom_printf("%c", *s++); | ||
77 | } | ||
78 | } | ||
79 | #endif | ||
80 | |||
81 | /* | ||
82 | * ------------------------------------------------------------ | ||
83 | * dz_in () and dz_out () | ||
84 | * | ||
85 | * These routines are used to access the registers of the DZ | ||
86 | * chip, hiding relocation differences between implementation. | ||
87 | * ------------------------------------------------------------ | ||
88 | */ | ||
89 | |||
90 | static inline unsigned short dz_in(struct dz_port *dport, unsigned offset) | ||
91 | { | ||
92 | volatile unsigned short *addr = | ||
93 | (volatile unsigned short *) (dport->port.membase + offset); | ||
94 | return *addr; | ||
95 | } | ||
96 | |||
97 | static inline void dz_out(struct dz_port *dport, unsigned offset, | ||
98 | unsigned short value) | ||
99 | { | ||
100 | volatile unsigned short *addr = | ||
101 | (volatile unsigned short *) (dport->port.membase + offset); | ||
102 | *addr = value; | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * ------------------------------------------------------------ | ||
107 | * rs_stop () and rs_start () | ||
108 | * | ||
109 | * These routines are called before setting or resetting | ||
110 | * tty->stopped. They enable or disable transmitter interrupts, | ||
111 | * as necessary. | ||
112 | * ------------------------------------------------------------ | ||
113 | */ | ||
114 | |||
115 | static void dz_stop_tx(struct uart_port *uport, unsigned int tty_stop) | ||
116 | { | ||
117 | struct dz_port *dport = (struct dz_port *)uport; | ||
118 | unsigned short tmp, mask = 1 << dport->port.line; | ||
119 | unsigned long flags; | ||
120 | |||
121 | spin_lock_irqsave(&dport->port.lock, flags); | ||
122 | tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ | ||
123 | tmp &= ~mask; /* clear the TX flag */ | ||
124 | dz_out(dport, DZ_TCR, tmp); | ||
125 | spin_unlock_irqrestore(&dport->port.lock, flags); | ||
126 | } | ||
127 | |||
128 | static void dz_start_tx(struct uart_port *uport, unsigned int tty_start) | ||
129 | { | ||
130 | struct dz_port *dport = (struct dz_port *)uport; | ||
131 | unsigned short tmp, mask = 1 << dport->port.line; | ||
132 | unsigned long flags; | ||
133 | |||
134 | spin_lock_irqsave(&dport->port.lock, flags); | ||
135 | tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ | ||
136 | tmp |= mask; /* set the TX flag */ | ||
137 | dz_out(dport, DZ_TCR, tmp); | ||
138 | spin_unlock_irqrestore(&dport->port.lock, flags); | ||
139 | } | ||
140 | |||
141 | static void dz_stop_rx(struct uart_port *uport) | ||
142 | { | ||
143 | struct dz_port *dport = (struct dz_port *)uport; | ||
144 | unsigned long flags; | ||
145 | |||
146 | spin_lock_irqsave(&dport->port.lock, flags); | ||
147 | dport->cflag &= ~DZ_CREAD; | ||
148 | dz_out(dport, DZ_LPR, dport->cflag); | ||
149 | spin_unlock_irqrestore(&dport->port.lock, flags); | ||
150 | } | ||
151 | |||
152 | static void dz_enable_ms(struct uart_port *port) | ||
153 | { | ||
154 | /* nothing to do */ | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * ------------------------------------------------------------ | ||
159 | * Here starts the interrupt handling routines. All of the | ||
160 | * following subroutines are declared as inline and are folded | ||
161 | * into dz_interrupt. They were separated out for readability's | ||
162 | * sake. | ||
163 | * | ||
164 | * Note: rs_interrupt() is a "fast" interrupt, which means that it | ||
165 | * runs with interrupts turned off. People who may want to modify | ||
166 | * rs_interrupt() should try to keep the interrupt handler as fast as | ||
167 | * possible. After you are done making modifications, it is not a bad | ||
168 | * idea to do: | ||
169 | * | ||
170 | * make drivers/serial/dz.s | ||
171 | * | ||
172 | * and look at the resulting assemble code in dz.s. | ||
173 | * | ||
174 | * ------------------------------------------------------------ | ||
175 | */ | ||
176 | |||
177 | /* | ||
178 | * ------------------------------------------------------------ | ||
179 | * receive_char () | ||
180 | * | ||
181 | * This routine deals with inputs from any lines. | ||
182 | * ------------------------------------------------------------ | ||
183 | */ | ||
184 | static inline void dz_receive_chars(struct dz_port *dport) | ||
185 | { | ||
186 | struct tty_struct *tty = NULL; | ||
187 | struct uart_icount *icount; | ||
188 | int ignore = 0; | ||
189 | unsigned short status, tmp; | ||
190 | unsigned char ch, flag; | ||
191 | |||
192 | /* this code is going to be a problem... | ||
193 | the call to tty_flip_buffer is going to need | ||
194 | to be rethought... | ||
195 | */ | ||
196 | do { | ||
197 | status = dz_in(dport, DZ_RBUF); | ||
198 | |||
199 | /* punt so we don't get duplicate characters */ | ||
200 | if (!(status & DZ_DVAL)) | ||
201 | goto ignore_char; | ||
202 | |||
203 | |||
204 | ch = UCHAR(status); /* grab the char */ | ||
205 | flag = TTY_NORMAL; | ||
206 | |||
207 | #if 0 | ||
208 | if (info->is_console) { | ||
209 | if (ch == 0) | ||
210 | return; /* it's a break ... */ | ||
211 | } | ||
212 | #endif | ||
213 | |||
214 | tty = dport->port.info->tty;/* now tty points to the proper dev */ | ||
215 | icount = &dport->port.icount; | ||
216 | |||
217 | if (!tty) | ||
218 | break; | ||
219 | if (tty->flip.count >= TTY_FLIPBUF_SIZE) | ||
220 | break; | ||
221 | |||
222 | icount->rx++; | ||
223 | |||
224 | /* keep track of the statistics */ | ||
225 | if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) { | ||
226 | if (status & DZ_PERR) /* parity error */ | ||
227 | icount->parity++; | ||
228 | else if (status & DZ_FERR) /* frame error */ | ||
229 | icount->frame++; | ||
230 | if (status & DZ_OERR) /* overrun error */ | ||
231 | icount->overrun++; | ||
232 | |||
233 | /* check to see if we should ignore the character | ||
234 | and mask off conditions that should be ignored | ||
235 | */ | ||
236 | |||
237 | if (status & dport->port.ignore_status_mask) { | ||
238 | if (++ignore > 100) | ||
239 | break; | ||
240 | goto ignore_char; | ||
241 | } | ||
242 | /* mask off the error conditions we want to ignore */ | ||
243 | tmp = status & dport->port.read_status_mask; | ||
244 | |||
245 | if (tmp & DZ_PERR) { | ||
246 | flag = TTY_PARITY; | ||
247 | #ifdef DEBUG_DZ | ||
248 | debug_console("PERR\n", 5); | ||
249 | #endif | ||
250 | } else if (tmp & DZ_FERR) { | ||
251 | flag = TTY_FRAME; | ||
252 | #ifdef DEBUG_DZ | ||
253 | debug_console("FERR\n", 5); | ||
254 | #endif | ||
255 | } | ||
256 | if (tmp & DZ_OERR) { | ||
257 | #ifdef DEBUG_DZ | ||
258 | debug_console("OERR\n", 5); | ||
259 | #endif | ||
260 | tty_insert_flip_char(tty, ch, flag); | ||
261 | ch = 0; | ||
262 | flag = TTY_OVERRUN; | ||
263 | } | ||
264 | } | ||
265 | tty_insert_flip_char(tty, ch, flag); | ||
266 | ignore_char: | ||
267 | } while (status & DZ_DVAL); | ||
268 | |||
269 | if (tty) | ||
270 | tty_flip_buffer_push(tty); | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * ------------------------------------------------------------ | ||
275 | * transmit_char () | ||
276 | * | ||
277 | * This routine deals with outputs to any lines. | ||
278 | * ------------------------------------------------------------ | ||
279 | */ | ||
280 | static inline void dz_transmit_chars(struct dz_port *dport) | ||
281 | { | ||
282 | struct circ_buf *xmit = &dport->port.info->xmit; | ||
283 | unsigned char tmp; | ||
284 | |||
285 | if (dport->port.x_char) { /* XON/XOFF chars */ | ||
286 | dz_out(dport, DZ_TDR, dport->port.x_char); | ||
287 | dport->port.icount.tx++; | ||
288 | dport->port.x_char = 0; | ||
289 | return; | ||
290 | } | ||
291 | /* if nothing to do or stopped or hardware stopped */ | ||
292 | if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { | ||
293 | dz_stop_tx(&dport->port, 0); | ||
294 | return; | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * if something to do ... (rember the dz has no output fifo so we go | ||
299 | * one char at a time :-< | ||
300 | */ | ||
301 | tmp = xmit->buf[xmit->tail]; | ||
302 | xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1); | ||
303 | dz_out(dport, DZ_TDR, tmp); | ||
304 | dport->port.icount.tx++; | ||
305 | |||
306 | if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS) | ||
307 | uart_write_wakeup(&dport->port); | ||
308 | |||
309 | /* Are we done */ | ||
310 | if (uart_circ_empty(xmit)) | ||
311 | dz_stop_tx(&dport->port, 0); | ||
312 | } | ||
313 | |||
314 | /* | ||
315 | * ------------------------------------------------------------ | ||
316 | * check_modem_status () | ||
317 | * | ||
318 | * Only valid for the MODEM line duh ! | ||
319 | * ------------------------------------------------------------ | ||
320 | */ | ||
321 | static inline void check_modem_status(struct dz_port *dport) | ||
322 | { | ||
323 | unsigned short status; | ||
324 | |||
325 | /* if not ne modem line just return */ | ||
326 | if (dport->port.line != DZ_MODEM) | ||
327 | return; | ||
328 | |||
329 | status = dz_in(dport, DZ_MSR); | ||
330 | |||
331 | /* it's easy, since DSR2 is the only bit in the register */ | ||
332 | if (status) | ||
333 | dport->port.icount.dsr++; | ||
334 | } | ||
335 | |||
336 | /* | ||
337 | * ------------------------------------------------------------ | ||
338 | * dz_interrupt () | ||
339 | * | ||
340 | * this is the main interrupt routine for the DZ chip. | ||
341 | * It deals with the multiple ports. | ||
342 | * ------------------------------------------------------------ | ||
343 | */ | ||
344 | static irqreturn_t dz_interrupt(int irq, void *dev, struct pt_regs *regs) | ||
345 | { | ||
346 | struct dz_port *dport; | ||
347 | unsigned short status; | ||
348 | |||
349 | /* get the reason why we just got an irq */ | ||
350 | status = dz_in((struct dz_port *)dev, DZ_CSR); | ||
351 | dport = &dz_ports[LINE(status)]; | ||
352 | |||
353 | if (status & DZ_RDONE) | ||
354 | dz_receive_chars(dport); | ||
355 | |||
356 | if (status & DZ_TRDY) | ||
357 | dz_transmit_chars(dport); | ||
358 | |||
359 | /* FIXME: what about check modem status??? --rmk */ | ||
360 | |||
361 | return IRQ_HANDLED; | ||
362 | } | ||
363 | |||
364 | /* | ||
365 | * ------------------------------------------------------------------- | ||
366 | * Here ends the DZ interrupt routines. | ||
367 | * ------------------------------------------------------------------- | ||
368 | */ | ||
369 | |||
370 | static unsigned int dz_get_mctrl(struct uart_port *uport) | ||
371 | { | ||
372 | struct dz_port *dport = (struct dz_port *)uport; | ||
373 | unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; | ||
374 | |||
375 | if (dport->port.line == DZ_MODEM) { | ||
376 | /* | ||
377 | * CHECKME: This is a guess from the other code... --rmk | ||
378 | */ | ||
379 | if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR) | ||
380 | mctrl &= ~TIOCM_DSR; | ||
381 | } | ||
382 | |||
383 | return mctrl; | ||
384 | } | ||
385 | |||
386 | static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl) | ||
387 | { | ||
388 | struct dz_port *dport = (struct dz_port *)uport; | ||
389 | unsigned short 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 = (struct dz_port *)uport; | ||
411 | unsigned long flags; | ||
412 | unsigned short tmp; | ||
413 | |||
414 | /* The dz lines for the mouse/keyboard must be | ||
415 | * opened using their respective drivers. | ||
416 | */ | ||
417 | if ((dport->port.line == DZ_KEYBOARD) || | ||
418 | (dport->port.line == DZ_MOUSE)) | ||
419 | return -ENODEV; | ||
420 | |||
421 | spin_lock_irqsave(&dport->port.lock, flags); | ||
422 | |||
423 | /* enable the interrupt and the scanning */ | ||
424 | tmp = dz_in(dport, DZ_CSR); | ||
425 | tmp |= DZ_RIE | DZ_TIE | DZ_MSE; | ||
426 | dz_out(dport, DZ_CSR, tmp); | ||
427 | |||
428 | spin_unlock_irqrestore(&dport->port.lock, flags); | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | /* | ||
434 | * ------------------------------------------------------------------- | ||
435 | * shutdown () | ||
436 | * | ||
437 | * This routine will shutdown a serial port; interrupts are disabled, and | ||
438 | * DTR is dropped if the hangup on close termio flag is on. | ||
439 | * ------------------------------------------------------------------- | ||
440 | */ | ||
441 | static void dz_shutdown(struct uart_port *uport) | ||
442 | { | ||
443 | dz_stop_tx(uport, 0); | ||
444 | } | ||
445 | |||
446 | /* | ||
447 | * get_lsr_info - get line status register info | ||
448 | * | ||
449 | * Purpose: Let user call ioctl() to get info when the UART physically | ||
450 | * is emptied. On bus types like RS485, the transmitter must | ||
451 | * release the bus after transmitting. This must be done when | ||
452 | * the transmit shift register is empty, not be done when the | ||
453 | * transmit holding register is empty. This functionality | ||
454 | * allows an RS485 driver to be written in user space. | ||
455 | */ | ||
456 | static unsigned int dz_tx_empty(struct uart_port *uport) | ||
457 | { | ||
458 | struct dz_port *dport = (struct dz_port *)uport; | ||
459 | unsigned short status = dz_in(dport, DZ_LPR); | ||
460 | |||
461 | /* FIXME: this appears to be obviously broken --rmk. */ | ||
462 | return status ? TIOCSER_TEMT : 0; | ||
463 | } | ||
464 | |||
465 | static void dz_break_ctl(struct uart_port *uport, int break_state) | ||
466 | { | ||
467 | struct dz_port *dport = (struct dz_port *)uport; | ||
468 | unsigned long flags; | ||
469 | unsigned short tmp, mask = 1 << uport->line; | ||
470 | |||
471 | spin_lock_irqsave(&uport->lock, flags); | ||
472 | tmp = dz_in(dport, DZ_TCR); | ||
473 | if (break_state) | ||
474 | tmp |= mask; | ||
475 | else | ||
476 | tmp &= ~mask; | ||
477 | dz_out(dport, DZ_TCR, tmp); | ||
478 | spin_unlock_irqrestore(&uport->lock, flags); | ||
479 | } | ||
480 | |||
481 | static void dz_set_termios(struct uart_port *uport, struct termios *termios, | ||
482 | struct termios *old_termios) | ||
483 | { | ||
484 | struct dz_port *dport = (struct dz_port *)uport; | ||
485 | unsigned long flags; | ||
486 | unsigned int cflag, baud; | ||
487 | |||
488 | cflag = dport->port.line; | ||
489 | |||
490 | switch (termios->c_cflag & CSIZE) { | ||
491 | case CS5: | ||
492 | cflag |= DZ_CS5; | ||
493 | break; | ||
494 | case CS6: | ||
495 | cflag |= DZ_CS6; | ||
496 | break; | ||
497 | case CS7: | ||
498 | cflag |= DZ_CS7; | ||
499 | break; | ||
500 | case CS8: | ||
501 | default: | ||
502 | cflag |= DZ_CS8; | ||
503 | } | ||
504 | |||
505 | if (termios->c_cflag & CSTOPB) | ||
506 | cflag |= DZ_CSTOPB; | ||
507 | if (termios->c_cflag & PARENB) | ||
508 | cflag |= DZ_PARENB; | ||
509 | if (termios->c_cflag & PARODD) | ||
510 | cflag |= DZ_PARODD; | ||
511 | |||
512 | baud = uart_get_baud_rate(uport, termios, old_termios, 50, 9600); | ||
513 | switch (baud) { | ||
514 | case 50: | ||
515 | cflag |= DZ_B50; | ||
516 | break; | ||
517 | case 75: | ||
518 | cflag |= DZ_B75; | ||
519 | break; | ||
520 | case 110: | ||
521 | cflag |= DZ_B110; | ||
522 | break; | ||
523 | case 134: | ||
524 | cflag |= DZ_B134; | ||
525 | break; | ||
526 | case 150: | ||
527 | cflag |= DZ_B150; | ||
528 | break; | ||
529 | case 300: | ||
530 | cflag |= DZ_B300; | ||
531 | break; | ||
532 | case 600: | ||
533 | cflag |= DZ_B600; | ||
534 | break; | ||
535 | case 1200: | ||
536 | cflag |= DZ_B1200; | ||
537 | break; | ||
538 | case 1800: | ||
539 | cflag |= DZ_B1800; | ||
540 | break; | ||
541 | case 2000: | ||
542 | cflag |= DZ_B2000; | ||
543 | break; | ||
544 | case 2400: | ||
545 | cflag |= DZ_B2400; | ||
546 | break; | ||
547 | case 3600: | ||
548 | cflag |= DZ_B3600; | ||
549 | break; | ||
550 | case 4800: | ||
551 | cflag |= DZ_B4800; | ||
552 | break; | ||
553 | case 7200: | ||
554 | cflag |= DZ_B7200; | ||
555 | break; | ||
556 | case 9600: | ||
557 | default: | ||
558 | cflag |= DZ_B9600; | ||
559 | } | ||
560 | |||
561 | if (termios->c_cflag & CREAD) | ||
562 | cflag |= DZ_RXENAB; | ||
563 | |||
564 | spin_lock_irqsave(&dport->port.lock, flags); | ||
565 | |||
566 | dz_out(dport, DZ_LPR, cflag); | ||
567 | dport->cflag = cflag; | ||
568 | |||
569 | /* setup accept flag */ | ||
570 | dport->port.read_status_mask = DZ_OERR; | ||
571 | if (termios->c_iflag & INPCK) | ||
572 | dport->port.read_status_mask |= DZ_FERR | DZ_PERR; | ||
573 | |||
574 | /* characters to ignore */ | ||
575 | uport->ignore_status_mask = 0; | ||
576 | if (termios->c_iflag & IGNPAR) | ||
577 | dport->port.ignore_status_mask |= DZ_FERR | DZ_PERR; | ||
578 | |||
579 | spin_unlock_irqrestore(&dport->port.lock, flags); | ||
580 | } | ||
581 | |||
582 | static const char *dz_type(struct uart_port *port) | ||
583 | { | ||
584 | return "DZ"; | ||
585 | } | ||
586 | |||
587 | static void dz_release_port(struct uart_port *port) | ||
588 | { | ||
589 | /* nothing to do */ | ||
590 | } | ||
591 | |||
592 | static int dz_request_port(struct uart_port *port) | ||
593 | { | ||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | static void dz_config_port(struct uart_port *port, int flags) | ||
598 | { | ||
599 | if (flags & UART_CONFIG_TYPE) | ||
600 | port->type = PORT_DZ; | ||
601 | } | ||
602 | |||
603 | /* | ||
604 | * verify the new serial_struct (for TIOCSSERIAL). | ||
605 | */ | ||
606 | static int dz_verify_port(struct uart_port *port, struct serial_struct *ser) | ||
607 | { | ||
608 | int ret = 0; | ||
609 | if (ser->type != PORT_UNKNOWN && ser->type != PORT_DZ) | ||
610 | ret = -EINVAL; | ||
611 | if (ser->irq != port->irq) | ||
612 | ret = -EINVAL; | ||
613 | return ret; | ||
614 | } | ||
615 | |||
616 | static struct uart_ops dz_ops = { | ||
617 | .tx_empty = dz_tx_empty, | ||
618 | .get_mctrl = dz_get_mctrl, | ||
619 | .set_mctrl = dz_set_mctrl, | ||
620 | .stop_tx = dz_stop_tx, | ||
621 | .start_tx = dz_start_tx, | ||
622 | .stop_rx = dz_stop_rx, | ||
623 | .enable_ms = dz_enable_ms, | ||
624 | .break_ctl = dz_break_ctl, | ||
625 | .startup = dz_startup, | ||
626 | .shutdown = dz_shutdown, | ||
627 | .set_termios = dz_set_termios, | ||
628 | .type = dz_type, | ||
629 | .release_port = dz_release_port, | ||
630 | .request_port = dz_request_port, | ||
631 | .config_port = dz_config_port, | ||
632 | .verify_port = dz_verify_port, | ||
633 | }; | ||
634 | |||
635 | static void __init dz_init_ports(void) | ||
636 | { | ||
637 | static int first = 1; | ||
638 | struct dz_port *dport; | ||
639 | unsigned long base; | ||
640 | int i; | ||
641 | |||
642 | if (!first) | ||
643 | return; | ||
644 | first = 0; | ||
645 | |||
646 | if (mips_machtype == MACH_DS23100 || | ||
647 | mips_machtype == MACH_DS5100) | ||
648 | base = (unsigned long) KN01_DZ11_BASE; | ||
649 | else | ||
650 | base = (unsigned long) KN02_DZ11_BASE; | ||
651 | |||
652 | for (i = 0, dport = dz_ports; i < DZ_NB_PORT; i++, dport++) { | ||
653 | spin_lock_init(&dport->port.lock); | ||
654 | dport->port.membase = (char *) base; | ||
655 | dport->port.iotype = SERIAL_IO_PORT; | ||
656 | dport->port.irq = dec_interrupt[DEC_IRQ_DZ11]; | ||
657 | dport->port.line = i; | ||
658 | dport->port.fifosize = 1; | ||
659 | dport->port.ops = &dz_ops; | ||
660 | dport->port.flags = UPF_BOOT_AUTOCONF; | ||
661 | } | ||
662 | } | ||
663 | |||
664 | static void dz_reset(struct dz_port *dport) | ||
665 | { | ||
666 | dz_out(dport, DZ_CSR, DZ_CLR); | ||
667 | |||
668 | while (dz_in(dport, DZ_CSR) & DZ_CLR); | ||
669 | /* FIXME: cpu_relax? */ | ||
670 | |||
671 | iob(); | ||
672 | |||
673 | /* enable scanning */ | ||
674 | dz_out(dport, DZ_CSR, DZ_MSE); | ||
675 | } | ||
676 | |||
677 | #ifdef CONFIG_SERIAL_DZ_CONSOLE | ||
678 | static void dz_console_put_char(struct dz_port *dport, unsigned char ch) | ||
679 | { | ||
680 | unsigned long flags; | ||
681 | int loops = 2500; | ||
682 | unsigned short tmp = ch; | ||
683 | /* this code sends stuff out to serial device - spinning its | ||
684 | wheels and waiting. */ | ||
685 | |||
686 | spin_lock_irqsave(&dport->port.lock, flags); | ||
687 | |||
688 | /* spin our wheels */ | ||
689 | while (((dz_in(dport, DZ_CSR) & DZ_TRDY) != DZ_TRDY) && loops--) | ||
690 | /* FIXME: cpu_relax, udelay? --rmk */ | ||
691 | ; | ||
692 | |||
693 | /* Actually transmit the character. */ | ||
694 | dz_out(dport, DZ_TDR, tmp); | ||
695 | |||
696 | spin_unlock_irqrestore(&dport->port.lock, flags); | ||
697 | } | ||
698 | /* | ||
699 | * ------------------------------------------------------------------- | ||
700 | * dz_console_print () | ||
701 | * | ||
702 | * dz_console_print is registered for printk. | ||
703 | * The console must be locked when we get here. | ||
704 | * ------------------------------------------------------------------- | ||
705 | */ | ||
706 | static void dz_console_print(struct console *cons, | ||
707 | const char *str, | ||
708 | unsigned int count) | ||
709 | { | ||
710 | struct dz_port *dport = &dz_ports[CONSOLE_LINE]; | ||
711 | #ifdef DEBUG_DZ | ||
712 | prom_printf((char *) str); | ||
713 | #endif | ||
714 | while (count--) { | ||
715 | if (*str == '\n') | ||
716 | dz_console_put_char(dport, '\r'); | ||
717 | dz_console_put_char(dport, *str++); | ||
718 | } | ||
719 | } | ||
720 | |||
721 | static int __init dz_console_setup(struct console *co, char *options) | ||
722 | { | ||
723 | struct dz_port *dport = &dz_ports[CONSOLE_LINE]; | ||
724 | int baud = 9600; | ||
725 | int bits = 8; | ||
726 | int parity = 'n'; | ||
727 | int flow = 'n'; | ||
728 | int ret; | ||
729 | unsigned short mask, tmp; | ||
730 | |||
731 | if (options) | ||
732 | uart_parse_options(options, &baud, &parity, &bits, &flow); | ||
733 | |||
734 | dz_reset(dport); | ||
735 | |||
736 | ret = uart_set_options(&dport->port, co, baud, parity, bits, flow); | ||
737 | if (ret == 0) { | ||
738 | mask = 1 << dport->port.line; | ||
739 | tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ | ||
740 | if (!(tmp & mask)) { | ||
741 | tmp |= mask; /* set the TX flag */ | ||
742 | dz_out(dport, DZ_TCR, tmp); | ||
743 | } | ||
744 | } | ||
745 | |||
746 | return ret; | ||
747 | } | ||
748 | |||
749 | static struct console dz_sercons = | ||
750 | { | ||
751 | .name = "ttyS", | ||
752 | .write = dz_console_print, | ||
753 | .device = uart_console_device, | ||
754 | .setup = dz_console_setup, | ||
755 | .flags = CON_CONSDEV | CON_PRINTBUFFER, | ||
756 | .index = CONSOLE_LINE, | ||
757 | }; | ||
758 | |||
759 | void __init dz_serial_console_init(void) | ||
760 | { | ||
761 | dz_init_ports(); | ||
762 | |||
763 | register_console(&dz_sercons); | ||
764 | } | ||
765 | |||
766 | #define SERIAL_DZ_CONSOLE &dz_sercons | ||
767 | #else | ||
768 | #define SERIAL_DZ_CONSOLE NULL | ||
769 | #endif /* CONFIG_SERIAL_DZ_CONSOLE */ | ||
770 | |||
771 | static struct uart_driver dz_reg = { | ||
772 | .owner = THIS_MODULE, | ||
773 | .driver_name = "serial", | ||
774 | #ifdef CONFIG_DEVFS | ||
775 | .dev_name = "tts/%d", | ||
776 | #else | ||
777 | .dev_name = "ttyS%d", | ||
778 | #endif | ||
779 | .major = TTY_MAJOR, | ||
780 | .minor = 64, | ||
781 | .nr = DZ_NB_PORT, | ||
782 | .cons = SERIAL_DZ_CONSOLE, | ||
783 | }; | ||
784 | |||
785 | int __init dz_init(void) | ||
786 | { | ||
787 | unsigned long flags; | ||
788 | int ret, i; | ||
789 | |||
790 | printk("%s%s\n", dz_name, dz_version); | ||
791 | |||
792 | dz_init_ports(); | ||
793 | |||
794 | save_flags(flags); | ||
795 | cli(); | ||
796 | |||
797 | #ifndef CONFIG_SERIAL_DZ_CONSOLE | ||
798 | /* reset the chip */ | ||
799 | dz_reset(&dz_ports[0]); | ||
800 | #endif | ||
801 | |||
802 | /* order matters here... the trick is that flags | ||
803 | is updated... in request_irq - to immediatedly obliterate | ||
804 | it is unwise. */ | ||
805 | restore_flags(flags); | ||
806 | |||
807 | if (request_irq(dz_ports[0].port.irq, dz_interrupt, | ||
808 | SA_INTERRUPT, "DZ", &dz_ports[0])) | ||
809 | panic("Unable to register DZ interrupt"); | ||
810 | |||
811 | ret = uart_register_driver(&dz_reg); | ||
812 | if (ret != 0) | ||
813 | return ret; | ||
814 | |||
815 | for (i = 0; i < DZ_NB_PORT; i++) | ||
816 | uart_add_one_port(&dz_reg, &dz_ports[i].port); | ||
817 | |||
818 | return ret; | ||
819 | } | ||
820 | |||
821 | MODULE_DESCRIPTION("DECstation DZ serial driver"); | ||
822 | MODULE_LICENSE("GPL"); | ||