diff options
author | Maciej W. Rozycki <macro@linux-mips.org> | 2006-12-06 23:38:59 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-07 11:39:41 -0500 |
commit | 9399575dd30edcb84e821583daf81d4ba774a95b (patch) | |
tree | 0dbd1b3607277dfb67ecffaf5a35197d08a8b563 /drivers/serial/dz.c | |
parent | f7dff2b12654149c9cac8d8c79b6588759edd5a9 (diff) |
[PATCH] dz: Fixes to make it work
This a set of fixes mostly to make the driver actually work:
1. Actually select the line for setting parameters and receiver
disable/enable.
2. Select the line for receive and transmit interrupt handling correctly.
3. Report the transmitter empty state correctly.
4. Set the I/O type of ports correctly.
5. Perform polled transmission correctly.
6. Don't fix the console line at ttyS3.
7. Magic SysRq support.
8. Various small bits here and there.
Tested with a DECstation 2100 (thanks Flo for making this possible).
[akpm@osdl.org: fix typo]
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/serial/dz.c')
-rw-r--r-- | drivers/serial/dz.c | 371 |
1 files changed, 186 insertions, 185 deletions
diff --git a/drivers/serial/dz.c b/drivers/serial/dz.c index 53662b33b841..af1544f3356f 100644 --- a/drivers/serial/dz.c +++ b/drivers/serial/dz.c | |||
@@ -1,11 +1,13 @@ | |||
1 | /* | 1 | /* |
2 | * dz.c: Serial port driver for DECStations equiped | 2 | * dz.c: Serial port driver for DECstations equipped |
3 | * with the DZ chipset. | 3 | * with the DZ chipset. |
4 | * | 4 | * |
5 | * Copyright (C) 1998 Olivier A. D. Lebaillif | 5 | * Copyright (C) 1998 Olivier A. D. Lebaillif |
6 | * | 6 | * |
7 | * Email: olivier.lebaillif@ifrsys.com | 7 | * Email: olivier.lebaillif@ifrsys.com |
8 | * | 8 | * |
9 | * Copyright (C) 2004, 2006 Maciej W. Rozycki | ||
10 | * | ||
9 | * [31-AUG-98] triemer | 11 | * [31-AUG-98] triemer |
10 | * Changed IRQ to use Harald's dec internals interrupts.h | 12 | * Changed IRQ to use Harald's dec internals interrupts.h |
11 | * removed base_addr code - moving address assignment to setup.c | 13 | * removed base_addr code - moving address assignment to setup.c |
@@ -26,10 +28,16 @@ | |||
26 | 28 | ||
27 | #undef DEBUG_DZ | 29 | #undef DEBUG_DZ |
28 | 30 | ||
31 | #if defined(CONFIG_SERIAL_DZ_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) | ||
32 | #define SUPPORT_SYSRQ | ||
33 | #endif | ||
34 | |||
35 | #include <linux/delay.h> | ||
29 | #include <linux/module.h> | 36 | #include <linux/module.h> |
30 | #include <linux/interrupt.h> | 37 | #include <linux/interrupt.h> |
31 | #include <linux/init.h> | 38 | #include <linux/init.h> |
32 | #include <linux/console.h> | 39 | #include <linux/console.h> |
40 | #include <linux/sysrq.h> | ||
33 | #include <linux/tty.h> | 41 | #include <linux/tty.h> |
34 | #include <linux/tty_flip.h> | 42 | #include <linux/tty_flip.h> |
35 | #include <linux/serial_core.h> | 43 | #include <linux/serial_core.h> |
@@ -45,14 +53,10 @@ | |||
45 | #include <asm/system.h> | 53 | #include <asm/system.h> |
46 | #include <asm/uaccess.h> | 54 | #include <asm/uaccess.h> |
47 | 55 | ||
48 | #define CONSOLE_LINE (3) /* for definition of struct console */ | ||
49 | |||
50 | #include "dz.h" | 56 | #include "dz.h" |
51 | 57 | ||
52 | #define DZ_INTR_DEBUG 1 | ||
53 | |||
54 | static char *dz_name = "DECstation DZ serial driver version "; | 58 | static char *dz_name = "DECstation DZ serial driver version "; |
55 | static char *dz_version = "1.02"; | 59 | static char *dz_version = "1.03"; |
56 | 60 | ||
57 | struct dz_port { | 61 | struct dz_port { |
58 | struct uart_port port; | 62 | struct uart_port port; |
@@ -61,22 +65,6 @@ struct dz_port { | |||
61 | 65 | ||
62 | static struct dz_port dz_ports[DZ_NB_PORT]; | 66 | static struct dz_port dz_ports[DZ_NB_PORT]; |
63 | 67 | ||
64 | #ifdef DEBUG_DZ | ||
65 | /* | ||
66 | * debugging code to send out chars via prom | ||
67 | */ | ||
68 | static void debug_console(const char *s, int count) | ||
69 | { | ||
70 | unsigned i; | ||
71 | |||
72 | for (i = 0; i < count; i++) { | ||
73 | if (*s == 10) | ||
74 | prom_printf("%c", 13); | ||
75 | prom_printf("%c", *s++); | ||
76 | } | ||
77 | } | ||
78 | #endif | ||
79 | |||
80 | /* | 68 | /* |
81 | * ------------------------------------------------------------ | 69 | * ------------------------------------------------------------ |
82 | * dz_in () and dz_out () | 70 | * dz_in () and dz_out () |
@@ -90,6 +78,7 @@ static inline unsigned short dz_in(struct dz_port *dport, unsigned offset) | |||
90 | { | 78 | { |
91 | volatile unsigned short *addr = | 79 | volatile unsigned short *addr = |
92 | (volatile unsigned short *) (dport->port.membase + offset); | 80 | (volatile unsigned short *) (dport->port.membase + offset); |
81 | |||
93 | return *addr; | 82 | return *addr; |
94 | } | 83 | } |
95 | 84 | ||
@@ -98,6 +87,7 @@ static inline void dz_out(struct dz_port *dport, unsigned offset, | |||
98 | { | 87 | { |
99 | volatile unsigned short *addr = | 88 | volatile unsigned short *addr = |
100 | (volatile unsigned short *) (dport->port.membase + offset); | 89 | (volatile unsigned short *) (dport->port.membase + offset); |
90 | |||
101 | *addr = value; | 91 | *addr = value; |
102 | } | 92 | } |
103 | 93 | ||
@@ -144,7 +134,7 @@ static void dz_stop_rx(struct uart_port *uport) | |||
144 | 134 | ||
145 | spin_lock_irqsave(&dport->port.lock, flags); | 135 | spin_lock_irqsave(&dport->port.lock, flags); |
146 | dport->cflag &= ~DZ_CREAD; | 136 | dport->cflag &= ~DZ_CREAD; |
147 | dz_out(dport, DZ_LPR, dport->cflag); | 137 | dz_out(dport, DZ_LPR, dport->cflag | dport->port.line); |
148 | spin_unlock_irqrestore(&dport->port.lock, flags); | 138 | spin_unlock_irqrestore(&dport->port.lock, flags); |
149 | } | 139 | } |
150 | 140 | ||
@@ -155,14 +145,14 @@ static void dz_enable_ms(struct uart_port *port) | |||
155 | 145 | ||
156 | /* | 146 | /* |
157 | * ------------------------------------------------------------ | 147 | * ------------------------------------------------------------ |
158 | * Here starts the interrupt handling routines. All of the | ||
159 | * following subroutines are declared as inline and are folded | ||
160 | * into dz_interrupt. They were separated out for readability's | ||
161 | * sake. | ||
162 | * | 148 | * |
163 | * Note: rs_interrupt() is a "fast" interrupt, which means that it | 149 | * Here start the interrupt handling routines. All of the following |
150 | * subroutines are declared as inline and are folded into | ||
151 | * dz_interrupt. They were separated out for readability's sake. | ||
152 | * | ||
153 | * Note: dz_interrupt() is a "fast" interrupt, which means that it | ||
164 | * runs with interrupts turned off. People who may want to modify | 154 | * runs with interrupts turned off. People who may want to modify |
165 | * rs_interrupt() should try to keep the interrupt handler as fast as | 155 | * dz_interrupt() should try to keep the interrupt handler as fast as |
166 | * possible. After you are done making modifications, it is not a bad | 156 | * possible. After you are done making modifications, it is not a bad |
167 | * idea to do: | 157 | * idea to do: |
168 | * | 158 | * |
@@ -180,92 +170,74 @@ static void dz_enable_ms(struct uart_port *port) | |||
180 | * This routine deals with inputs from any lines. | 170 | * This routine deals with inputs from any lines. |
181 | * ------------------------------------------------------------ | 171 | * ------------------------------------------------------------ |
182 | */ | 172 | */ |
183 | static inline void dz_receive_chars(struct dz_port *dport) | 173 | static inline void dz_receive_chars(struct dz_port *dport_in, |
174 | struct pt_regs *regs) | ||
184 | { | 175 | { |
176 | struct dz_port *dport; | ||
185 | struct tty_struct *tty = NULL; | 177 | struct tty_struct *tty = NULL; |
186 | struct uart_icount *icount; | 178 | struct uart_icount *icount; |
187 | int ignore = 0; | 179 | int lines_rx[DZ_NB_PORT] = { [0 ... DZ_NB_PORT - 1] = 0 }; |
188 | unsigned short status, tmp; | 180 | unsigned short status; |
189 | unsigned char ch, flag; | 181 | unsigned char ch, flag; |
182 | int i; | ||
190 | 183 | ||
191 | /* this code is going to be a problem... | 184 | while ((status = dz_in(dport_in, DZ_RBUF)) & DZ_DVAL) { |
192 | the call to tty_flip_buffer is going to need | 185 | dport = &dz_ports[LINE(status)]; |
193 | to be rethought... | 186 | tty = dport->port.info->tty; /* point to the proper dev */ |
194 | */ | ||
195 | do { | ||
196 | status = dz_in(dport, DZ_RBUF); | ||
197 | |||
198 | /* punt so we don't get duplicate characters */ | ||
199 | if (!(status & DZ_DVAL)) | ||
200 | goto ignore_char; | ||
201 | |||
202 | |||
203 | ch = UCHAR(status); /* grab the char */ | ||
204 | flag = TTY_NORMAL; | ||
205 | 187 | ||
206 | #if 0 | 188 | ch = UCHAR(status); /* grab the char */ |
207 | if (info->is_console) { | ||
208 | if (ch == 0) | ||
209 | return; /* it's a break ... */ | ||
210 | } | ||
211 | #endif | ||
212 | 189 | ||
213 | tty = dport->port.info->tty;/* now tty points to the proper dev */ | ||
214 | icount = &dport->port.icount; | 190 | icount = &dport->port.icount; |
215 | |||
216 | if (!tty) | ||
217 | break; | ||
218 | |||
219 | icount->rx++; | 191 | icount->rx++; |
220 | 192 | ||
221 | /* keep track of the statistics */ | 193 | flag = TTY_NORMAL; |
222 | if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) { | 194 | if (status & DZ_FERR) { /* frame error */ |
223 | if (status & DZ_PERR) /* parity error */ | 195 | /* |
224 | icount->parity++; | 196 | * There is no separate BREAK status bit, so |
225 | else if (status & DZ_FERR) /* frame error */ | 197 | * treat framing errors as BREAKs for Magic SysRq |
226 | icount->frame++; | 198 | * and SAK; normally, otherwise. |
227 | if (status & DZ_OERR) /* overrun error */ | ||
228 | icount->overrun++; | ||
229 | |||
230 | /* check to see if we should ignore the character | ||
231 | and mask off conditions that should be ignored | ||
232 | */ | 199 | */ |
233 | 200 | if (uart_handle_break(&dport->port)) | |
234 | if (status & dport->port.ignore_status_mask) { | 201 | continue; |
235 | if (++ignore > 100) | 202 | if (dport->port.flags & UPF_SAK) |
236 | break; | 203 | flag = TTY_BREAK; |
237 | goto ignore_char; | 204 | else |
238 | } | ||
239 | /* mask off the error conditions we want to ignore */ | ||
240 | tmp = status & dport->port.read_status_mask; | ||
241 | |||
242 | if (tmp & DZ_PERR) { | ||
243 | flag = TTY_PARITY; | ||
244 | #ifdef DEBUG_DZ | ||
245 | debug_console("PERR\n", 5); | ||
246 | #endif | ||
247 | } else if (tmp & DZ_FERR) { | ||
248 | flag = TTY_FRAME; | 205 | flag = TTY_FRAME; |
249 | #ifdef DEBUG_DZ | 206 | } else if (status & DZ_OERR) /* overrun error */ |
250 | debug_console("FERR\n", 5); | 207 | flag = TTY_OVERRUN; |
251 | #endif | 208 | else if (status & DZ_PERR) /* parity error */ |
252 | } | 209 | flag = TTY_PARITY; |
253 | if (tmp & DZ_OERR) { | 210 | |
254 | #ifdef DEBUG_DZ | 211 | /* keep track of the statistics */ |
255 | debug_console("OERR\n", 5); | 212 | switch (flag) { |
256 | #endif | 213 | case TTY_FRAME: |
257 | tty_insert_flip_char(tty, ch, flag); | 214 | icount->frame++; |
258 | ch = 0; | 215 | break; |
259 | flag = TTY_OVERRUN; | 216 | case TTY_PARITY: |
260 | } | 217 | icount->parity++; |
218 | break; | ||
219 | case TTY_OVERRUN: | ||
220 | icount->overrun++; | ||
221 | break; | ||
222 | case TTY_BREAK: | ||
223 | icount->brk++; | ||
224 | break; | ||
225 | default: | ||
226 | break; | ||
261 | } | 227 | } |
262 | tty_insert_flip_char(tty, ch, flag); | ||
263 | ignore_char: | ||
264 | ; | ||
265 | } while (status & DZ_DVAL); | ||
266 | 228 | ||
267 | if (tty) | 229 | if (uart_handle_sysrq_char(&dport->port, ch, regs)) |
268 | tty_flip_buffer_push(tty); | 230 | continue; |
231 | |||
232 | if ((status & dport->port.ignore_status_mask) == 0) { | ||
233 | uart_insert_char(&dport->port, | ||
234 | status, DZ_OERR, ch, flag); | ||
235 | lines_rx[LINE(status)] = 1; | ||
236 | } | ||
237 | } | ||
238 | for (i = 0; i < DZ_NB_PORT; i++) | ||
239 | if (lines_rx[i]) | ||
240 | tty_flip_buffer_push(dz_ports[i].port.info->tty); | ||
269 | } | 241 | } |
270 | 242 | ||
271 | /* | 243 | /* |
@@ -275,26 +247,32 @@ static inline void dz_receive_chars(struct dz_port *dport) | |||
275 | * This routine deals with outputs to any lines. | 247 | * This routine deals with outputs to any lines. |
276 | * ------------------------------------------------------------ | 248 | * ------------------------------------------------------------ |
277 | */ | 249 | */ |
278 | static inline void dz_transmit_chars(struct dz_port *dport) | 250 | static inline void dz_transmit_chars(struct dz_port *dport_in) |
279 | { | 251 | { |
280 | struct circ_buf *xmit = &dport->port.info->xmit; | 252 | struct dz_port *dport; |
253 | struct circ_buf *xmit; | ||
254 | unsigned short status; | ||
281 | unsigned char tmp; | 255 | unsigned char tmp; |
282 | 256 | ||
283 | if (dport->port.x_char) { /* XON/XOFF chars */ | 257 | status = dz_in(dport_in, DZ_CSR); |
258 | dport = &dz_ports[LINE(status)]; | ||
259 | xmit = &dport->port.info->xmit; | ||
260 | |||
261 | if (dport->port.x_char) { /* XON/XOFF chars */ | ||
284 | dz_out(dport, DZ_TDR, dport->port.x_char); | 262 | dz_out(dport, DZ_TDR, dport->port.x_char); |
285 | dport->port.icount.tx++; | 263 | dport->port.icount.tx++; |
286 | dport->port.x_char = 0; | 264 | dport->port.x_char = 0; |
287 | return; | 265 | return; |
288 | } | 266 | } |
289 | /* if nothing to do or stopped or hardware stopped */ | 267 | /* If nothing to do or stopped or hardware stopped. */ |
290 | if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { | 268 | if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { |
291 | dz_stop_tx(&dport->port); | 269 | dz_stop_tx(&dport->port); |
292 | return; | 270 | return; |
293 | } | 271 | } |
294 | 272 | ||
295 | /* | 273 | /* |
296 | * if something to do ... (rember the dz has no output fifo so we go | 274 | * If something to do... (remember the dz has no output fifo, |
297 | * one char at a time :-< | 275 | * so we go one char at a time) :-< |
298 | */ | 276 | */ |
299 | tmp = xmit->buf[xmit->tail]; | 277 | tmp = xmit->buf[xmit->tail]; |
300 | xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1); | 278 | xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1); |
@@ -304,23 +282,29 @@ static inline void dz_transmit_chars(struct dz_port *dport) | |||
304 | if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS) | 282 | if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS) |
305 | uart_write_wakeup(&dport->port); | 283 | uart_write_wakeup(&dport->port); |
306 | 284 | ||
307 | /* Are we done */ | 285 | /* Are we are done. */ |
308 | if (uart_circ_empty(xmit)) | 286 | if (uart_circ_empty(xmit)) |
309 | dz_stop_tx(&dport->port); | 287 | dz_stop_tx(&dport->port); |
310 | } | 288 | } |
311 | 289 | ||
312 | /* | 290 | /* |
313 | * ------------------------------------------------------------ | 291 | * ------------------------------------------------------------ |
314 | * check_modem_status () | 292 | * check_modem_status() |
315 | * | 293 | * |
316 | * Only valid for the MODEM line duh ! | 294 | * DS 3100 & 5100: Only valid for the MODEM line, duh! |
295 | * DS 5000/200: Valid for the MODEM and PRINTER line. | ||
317 | * ------------------------------------------------------------ | 296 | * ------------------------------------------------------------ |
318 | */ | 297 | */ |
319 | static inline void check_modem_status(struct dz_port *dport) | 298 | static inline void check_modem_status(struct dz_port *dport) |
320 | { | 299 | { |
300 | /* | ||
301 | * FIXME: | ||
302 | * 1. No status change interrupt; use a timer. | ||
303 | * 2. Handle the 3100/5000 as appropriate. --macro | ||
304 | */ | ||
321 | unsigned short status; | 305 | unsigned short status; |
322 | 306 | ||
323 | /* if not ne modem line just return */ | 307 | /* If not the modem line just return. */ |
324 | if (dport->port.line != DZ_MODEM) | 308 | if (dport->port.line != DZ_MODEM) |
325 | return; | 309 | return; |
326 | 310 | ||
@@ -341,21 +325,18 @@ static inline void check_modem_status(struct dz_port *dport) | |||
341 | */ | 325 | */ |
342 | static irqreturn_t dz_interrupt(int irq, void *dev) | 326 | static irqreturn_t dz_interrupt(int irq, void *dev) |
343 | { | 327 | { |
344 | struct dz_port *dport; | 328 | struct dz_port *dport = (struct dz_port *)dev; |
345 | unsigned short status; | 329 | unsigned short status; |
346 | 330 | ||
347 | /* get the reason why we just got an irq */ | 331 | /* get the reason why we just got an irq */ |
348 | status = dz_in((struct dz_port *)dev, DZ_CSR); | 332 | status = dz_in(dport, DZ_CSR); |
349 | dport = &dz_ports[LINE(status)]; | ||
350 | 333 | ||
351 | if (status & DZ_RDONE) | 334 | if ((status & (DZ_RDONE | DZ_RIE)) == (DZ_RDONE | DZ_RIE)) |
352 | dz_receive_chars(dport); | 335 | dz_receive_chars(dport, regs); |
353 | 336 | ||
354 | if (status & DZ_TRDY) | 337 | if ((status & (DZ_TRDY | DZ_TIE)) == (DZ_TRDY | DZ_TIE)) |
355 | dz_transmit_chars(dport); | 338 | dz_transmit_chars(dport); |
356 | 339 | ||
357 | /* FIXME: what about check modem status??? --rmk */ | ||
358 | |||
359 | return IRQ_HANDLED; | 340 | return IRQ_HANDLED; |
360 | } | 341 | } |
361 | 342 | ||
@@ -367,13 +348,13 @@ static irqreturn_t dz_interrupt(int irq, void *dev) | |||
367 | 348 | ||
368 | static unsigned int dz_get_mctrl(struct uart_port *uport) | 349 | static unsigned int dz_get_mctrl(struct uart_port *uport) |
369 | { | 350 | { |
351 | /* | ||
352 | * FIXME: Handle the 3100/5000 as appropriate. --macro | ||
353 | */ | ||
370 | struct dz_port *dport = (struct dz_port *)uport; | 354 | struct dz_port *dport = (struct dz_port *)uport; |
371 | unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; | 355 | unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; |
372 | 356 | ||
373 | if (dport->port.line == DZ_MODEM) { | 357 | if (dport->port.line == DZ_MODEM) { |
374 | /* | ||
375 | * CHECKME: This is a guess from the other code... --rmk | ||
376 | */ | ||
377 | if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR) | 358 | if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR) |
378 | mctrl &= ~TIOCM_DSR; | 359 | mctrl &= ~TIOCM_DSR; |
379 | } | 360 | } |
@@ -383,6 +364,9 @@ static unsigned int dz_get_mctrl(struct uart_port *uport) | |||
383 | 364 | ||
384 | static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl) | 365 | static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl) |
385 | { | 366 | { |
367 | /* | ||
368 | * FIXME: Handle the 3100/5000 as appropriate. --macro | ||
369 | */ | ||
386 | struct dz_port *dport = (struct dz_port *)uport; | 370 | struct dz_port *dport = (struct dz_port *)uport; |
387 | unsigned short tmp; | 371 | unsigned short tmp; |
388 | 372 | ||
@@ -409,13 +393,6 @@ static int dz_startup(struct uart_port *uport) | |||
409 | unsigned long flags; | 393 | unsigned long flags; |
410 | unsigned short tmp; | 394 | unsigned short tmp; |
411 | 395 | ||
412 | /* The dz lines for the mouse/keyboard must be | ||
413 | * opened using their respective drivers. | ||
414 | */ | ||
415 | if ((dport->port.line == DZ_KEYBOARD) || | ||
416 | (dport->port.line == DZ_MOUSE)) | ||
417 | return -ENODEV; | ||
418 | |||
419 | spin_lock_irqsave(&dport->port.lock, flags); | 396 | spin_lock_irqsave(&dport->port.lock, flags); |
420 | 397 | ||
421 | /* enable the interrupt and the scanning */ | 398 | /* enable the interrupt and the scanning */ |
@@ -442,7 +419,8 @@ static void dz_shutdown(struct uart_port *uport) | |||
442 | } | 419 | } |
443 | 420 | ||
444 | /* | 421 | /* |
445 | * get_lsr_info - get line status register info | 422 | * ------------------------------------------------------------------- |
423 | * dz_tx_empty() -- get the transmitter empty status | ||
446 | * | 424 | * |
447 | * Purpose: Let user call ioctl() to get info when the UART physically | 425 | * Purpose: Let user call ioctl() to get info when the UART physically |
448 | * is emptied. On bus types like RS485, the transmitter must | 426 | * is emptied. On bus types like RS485, the transmitter must |
@@ -450,21 +428,28 @@ static void dz_shutdown(struct uart_port *uport) | |||
450 | * the transmit shift register is empty, not be done when the | 428 | * the transmit shift register is empty, not be done when the |
451 | * transmit holding register is empty. This functionality | 429 | * transmit holding register is empty. This functionality |
452 | * allows an RS485 driver to be written in user space. | 430 | * allows an RS485 driver to be written in user space. |
431 | * ------------------------------------------------------------------- | ||
453 | */ | 432 | */ |
454 | static unsigned int dz_tx_empty(struct uart_port *uport) | 433 | static unsigned int dz_tx_empty(struct uart_port *uport) |
455 | { | 434 | { |
456 | struct dz_port *dport = (struct dz_port *)uport; | 435 | struct dz_port *dport = (struct dz_port *)uport; |
457 | unsigned short status = dz_in(dport, DZ_LPR); | 436 | unsigned short tmp, mask = 1 << dport->port.line; |
458 | 437 | ||
459 | /* FIXME: this appears to be obviously broken --rmk. */ | 438 | tmp = dz_in(dport, DZ_TCR); |
460 | return status ? TIOCSER_TEMT : 0; | 439 | tmp &= mask; |
440 | |||
441 | return tmp ? 0 : TIOCSER_TEMT; | ||
461 | } | 442 | } |
462 | 443 | ||
463 | static void dz_break_ctl(struct uart_port *uport, int break_state) | 444 | static void dz_break_ctl(struct uart_port *uport, int break_state) |
464 | { | 445 | { |
446 | /* | ||
447 | * FIXME: Can't access BREAK bits in TDR easily; | ||
448 | * reuse the code for polled TX. --macro | ||
449 | */ | ||
465 | struct dz_port *dport = (struct dz_port *)uport; | 450 | struct dz_port *dport = (struct dz_port *)uport; |
466 | unsigned long flags; | 451 | unsigned long flags; |
467 | unsigned short tmp, mask = 1 << uport->line; | 452 | unsigned short tmp, mask = 1 << dport->port.line; |
468 | 453 | ||
469 | spin_lock_irqsave(&uport->lock, flags); | 454 | spin_lock_irqsave(&uport->lock, flags); |
470 | tmp = dz_in(dport, DZ_TCR); | 455 | tmp = dz_in(dport, DZ_TCR); |
@@ -561,7 +546,7 @@ static void dz_set_termios(struct uart_port *uport, struct termios *termios, | |||
561 | 546 | ||
562 | spin_lock_irqsave(&dport->port.lock, flags); | 547 | spin_lock_irqsave(&dport->port.lock, flags); |
563 | 548 | ||
564 | dz_out(dport, DZ_LPR, cflag); | 549 | dz_out(dport, DZ_LPR, cflag | dport->port.line); |
565 | dport->cflag = cflag; | 550 | dport->cflag = cflag; |
566 | 551 | ||
567 | /* setup accept flag */ | 552 | /* setup accept flag */ |
@@ -650,7 +635,7 @@ static void __init dz_init_ports(void) | |||
650 | for (i = 0, dport = dz_ports; i < DZ_NB_PORT; i++, dport++) { | 635 | for (i = 0, dport = dz_ports; i < DZ_NB_PORT; i++, dport++) { |
651 | spin_lock_init(&dport->port.lock); | 636 | spin_lock_init(&dport->port.lock); |
652 | dport->port.membase = (char *) base; | 637 | dport->port.membase = (char *) base; |
653 | dport->port.iotype = UPIO_PORT; | 638 | dport->port.iotype = UPIO_MEM; |
654 | dport->port.irq = dec_interrupt[DEC_IRQ_DZ11]; | 639 | dport->port.irq = dec_interrupt[DEC_IRQ_DZ11]; |
655 | dport->port.line = i; | 640 | dport->port.line = i; |
656 | dport->port.fifosize = 1; | 641 | dport->port.fifosize = 1; |
@@ -662,10 +647,7 @@ static void __init dz_init_ports(void) | |||
662 | static void dz_reset(struct dz_port *dport) | 647 | static void dz_reset(struct dz_port *dport) |
663 | { | 648 | { |
664 | dz_out(dport, DZ_CSR, DZ_CLR); | 649 | dz_out(dport, DZ_CSR, DZ_CLR); |
665 | |||
666 | while (dz_in(dport, DZ_CSR) & DZ_CLR); | 650 | while (dz_in(dport, DZ_CSR) & DZ_CLR); |
667 | /* FIXME: cpu_relax? */ | ||
668 | |||
669 | iob(); | 651 | iob(); |
670 | 652 | ||
671 | /* enable scanning */ | 653 | /* enable scanning */ |
@@ -673,26 +655,55 @@ static void dz_reset(struct dz_port *dport) | |||
673 | } | 655 | } |
674 | 656 | ||
675 | #ifdef CONFIG_SERIAL_DZ_CONSOLE | 657 | #ifdef CONFIG_SERIAL_DZ_CONSOLE |
658 | /* | ||
659 | * ------------------------------------------------------------------- | ||
660 | * dz_console_putchar() -- transmit a character | ||
661 | * | ||
662 | * Polled transmission. This is tricky. We need to mask transmit | ||
663 | * interrupts so that they do not interfere, enable the transmitter | ||
664 | * for the line requested and then wait till the transmit scanner | ||
665 | * requests data for this line. But it may request data for another | ||
666 | * line first, in which case we have to disable its transmitter and | ||
667 | * repeat waiting till our line pops up. Only then the character may | ||
668 | * be transmitted. Finally, the state of the transmitter mask is | ||
669 | * restored. Welcome to the world of PDP-11! | ||
670 | * ------------------------------------------------------------------- | ||
671 | */ | ||
676 | static void dz_console_putchar(struct uart_port *uport, int ch) | 672 | static void dz_console_putchar(struct uart_port *uport, int ch) |
677 | { | 673 | { |
678 | struct dz_port *dport = (struct dz_port *)uport; | 674 | struct dz_port *dport = (struct dz_port *)uport; |
679 | unsigned long flags; | 675 | unsigned long flags; |
680 | int loops = 2500; | 676 | unsigned short csr, tcr, trdy, mask; |
681 | unsigned short tmp = (unsigned char)ch; | 677 | int loops = 10000; |
682 | /* this code sends stuff out to serial device - spinning its | ||
683 | wheels and waiting. */ | ||
684 | 678 | ||
685 | spin_lock_irqsave(&dport->port.lock, flags); | 679 | spin_lock_irqsave(&dport->port.lock, flags); |
680 | csr = dz_in(dport, DZ_CSR); | ||
681 | dz_out(dport, DZ_CSR, csr & ~DZ_TIE); | ||
682 | tcr = dz_in(dport, DZ_TCR); | ||
683 | tcr |= 1 << dport->port.line; | ||
684 | mask = tcr; | ||
685 | dz_out(dport, DZ_TCR, mask); | ||
686 | iob(); | ||
687 | spin_unlock_irqrestore(&dport->port.lock, flags); | ||
686 | 688 | ||
687 | /* spin our wheels */ | 689 | while (loops--) { |
688 | while (((dz_in(dport, DZ_CSR) & DZ_TRDY) != DZ_TRDY) && loops--) | 690 | trdy = dz_in(dport, DZ_CSR); |
689 | /* FIXME: cpu_relax, udelay? --rmk */ | 691 | if (!(trdy & DZ_TRDY)) |
690 | ; | 692 | continue; |
693 | trdy = (trdy & DZ_TLINE) >> 8; | ||
694 | if (trdy == dport->port.line) | ||
695 | break; | ||
696 | mask &= ~(1 << trdy); | ||
697 | dz_out(dport, DZ_TCR, mask); | ||
698 | iob(); | ||
699 | udelay(2); | ||
700 | } | ||
691 | 701 | ||
692 | /* Actually transmit the character. */ | 702 | if (loops) /* Cannot send otherwise. */ |
693 | dz_out(dport, DZ_TDR, tmp); | 703 | dz_out(dport, DZ_TDR, ch); |
694 | 704 | ||
695 | spin_unlock_irqrestore(&dport->port.lock, flags); | 705 | dz_out(dport, DZ_TCR, tcr); |
706 | dz_out(dport, DZ_CSR, csr); | ||
696 | } | 707 | } |
697 | 708 | ||
698 | /* | 709 | /* |
@@ -703,11 +714,11 @@ static void dz_console_putchar(struct uart_port *uport, int ch) | |||
703 | * The console must be locked when we get here. | 714 | * The console must be locked when we get here. |
704 | * ------------------------------------------------------------------- | 715 | * ------------------------------------------------------------------- |
705 | */ | 716 | */ |
706 | static void dz_console_print(struct console *cons, | 717 | static void dz_console_print(struct console *co, |
707 | const char *str, | 718 | const char *str, |
708 | unsigned int count) | 719 | unsigned int count) |
709 | { | 720 | { |
710 | struct dz_port *dport = &dz_ports[CONSOLE_LINE]; | 721 | struct dz_port *dport = &dz_ports[co->index]; |
711 | #ifdef DEBUG_DZ | 722 | #ifdef DEBUG_DZ |
712 | prom_printf((char *) str); | 723 | prom_printf((char *) str); |
713 | #endif | 724 | #endif |
@@ -716,49 +727,43 @@ static void dz_console_print(struct console *cons, | |||
716 | 727 | ||
717 | static int __init dz_console_setup(struct console *co, char *options) | 728 | static int __init dz_console_setup(struct console *co, char *options) |
718 | { | 729 | { |
719 | struct dz_port *dport = &dz_ports[CONSOLE_LINE]; | 730 | struct dz_port *dport = &dz_ports[co->index]; |
720 | int baud = 9600; | 731 | int baud = 9600; |
721 | int bits = 8; | 732 | int bits = 8; |
722 | int parity = 'n'; | 733 | int parity = 'n'; |
723 | int flow = 'n'; | 734 | int flow = 'n'; |
724 | int ret; | ||
725 | unsigned short mask, tmp; | ||
726 | 735 | ||
727 | if (options) | 736 | if (options) |
728 | uart_parse_options(options, &baud, &parity, &bits, &flow); | 737 | uart_parse_options(options, &baud, &parity, &bits, &flow); |
729 | 738 | ||
730 | dz_reset(dport); | 739 | dz_reset(dport); |
731 | 740 | ||
732 | ret = uart_set_options(&dport->port, co, baud, parity, bits, flow); | 741 | return uart_set_options(&dport->port, co, baud, parity, bits, flow); |
733 | if (ret == 0) { | ||
734 | mask = 1 << dport->port.line; | ||
735 | tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ | ||
736 | if (!(tmp & mask)) { | ||
737 | tmp |= mask; /* set the TX flag */ | ||
738 | dz_out(dport, DZ_TCR, tmp); | ||
739 | } | ||
740 | } | ||
741 | |||
742 | return ret; | ||
743 | } | 742 | } |
744 | 743 | ||
745 | static struct console dz_sercons = | 744 | static struct uart_driver dz_reg; |
746 | { | 745 | static struct console dz_sercons = { |
747 | .name = "ttyS", | 746 | .name = "ttyS", |
748 | .write = dz_console_print, | 747 | .write = dz_console_print, |
749 | .device = uart_console_device, | 748 | .device = uart_console_device, |
750 | .setup = dz_console_setup, | 749 | .setup = dz_console_setup, |
751 | .flags = CON_CONSDEV | CON_PRINTBUFFER, | 750 | .flags = CON_PRINTBUFFER, |
752 | .index = CONSOLE_LINE, | 751 | .index = -1, |
752 | .data = &dz_reg, | ||
753 | }; | 753 | }; |
754 | 754 | ||
755 | void __init dz_serial_console_init(void) | 755 | static int __init dz_serial_console_init(void) |
756 | { | 756 | { |
757 | dz_init_ports(); | 757 | if (!IOASIC) { |
758 | 758 | dz_init_ports(); | |
759 | register_console(&dz_sercons); | 759 | register_console(&dz_sercons); |
760 | return 0; | ||
761 | } else | ||
762 | return -ENXIO; | ||
760 | } | 763 | } |
761 | 764 | ||
765 | console_initcall(dz_serial_console_init); | ||
766 | |||
762 | #define SERIAL_DZ_CONSOLE &dz_sercons | 767 | #define SERIAL_DZ_CONSOLE &dz_sercons |
763 | #else | 768 | #else |
764 | #define SERIAL_DZ_CONSOLE NULL | 769 | #define SERIAL_DZ_CONSOLE NULL |
@@ -767,35 +772,29 @@ void __init dz_serial_console_init(void) | |||
767 | static struct uart_driver dz_reg = { | 772 | static struct uart_driver dz_reg = { |
768 | .owner = THIS_MODULE, | 773 | .owner = THIS_MODULE, |
769 | .driver_name = "serial", | 774 | .driver_name = "serial", |
770 | .dev_name = "ttyS%d", | 775 | .dev_name = "ttyS", |
771 | .major = TTY_MAJOR, | 776 | .major = TTY_MAJOR, |
772 | .minor = 64, | 777 | .minor = 64, |
773 | .nr = DZ_NB_PORT, | 778 | .nr = DZ_NB_PORT, |
774 | .cons = SERIAL_DZ_CONSOLE, | 779 | .cons = SERIAL_DZ_CONSOLE, |
775 | }; | 780 | }; |
776 | 781 | ||
777 | int __init dz_init(void) | 782 | static int __init dz_init(void) |
778 | { | 783 | { |
779 | unsigned long flags; | ||
780 | int ret, i; | 784 | int ret, i; |
781 | 785 | ||
786 | if (IOASIC) | ||
787 | return -ENXIO; | ||
788 | |||
782 | printk("%s%s\n", dz_name, dz_version); | 789 | printk("%s%s\n", dz_name, dz_version); |
783 | 790 | ||
784 | dz_init_ports(); | 791 | dz_init_ports(); |
785 | 792 | ||
786 | save_flags(flags); | ||
787 | cli(); | ||
788 | |||
789 | #ifndef CONFIG_SERIAL_DZ_CONSOLE | 793 | #ifndef CONFIG_SERIAL_DZ_CONSOLE |
790 | /* reset the chip */ | 794 | /* reset the chip */ |
791 | dz_reset(&dz_ports[0]); | 795 | dz_reset(&dz_ports[0]); |
792 | #endif | 796 | #endif |
793 | 797 | ||
794 | /* order matters here... the trick is that flags | ||
795 | is updated... in request_irq - to immediatedly obliterate | ||
796 | it is unwise. */ | ||
797 | restore_flags(flags); | ||
798 | |||
799 | if (request_irq(dz_ports[0].port.irq, dz_interrupt, | 798 | if (request_irq(dz_ports[0].port.irq, dz_interrupt, |
800 | IRQF_DISABLED, "DZ", &dz_ports[0])) | 799 | IRQF_DISABLED, "DZ", &dz_ports[0])) |
801 | panic("Unable to register DZ interrupt"); | 800 | panic("Unable to register DZ interrupt"); |
@@ -810,5 +809,7 @@ int __init dz_init(void) | |||
810 | return ret; | 809 | return ret; |
811 | } | 810 | } |
812 | 811 | ||
812 | module_init(dz_init); | ||
813 | |||
813 | MODULE_DESCRIPTION("DECstation DZ serial driver"); | 814 | MODULE_DESCRIPTION("DECstation DZ serial driver"); |
814 | MODULE_LICENSE("GPL"); | 815 | MODULE_LICENSE("GPL"); |