blob: d2d6b01dcd05a1168dbd7795ebd13d5c5be32dc9 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * specialix.c -- specialix IO8+ multiport serial driver.
3 *
4 * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
5 * Copyright (C) 1994-1996 Dmitry Gorodchanin (pgmdsg@ibi.com)
6 *
7 * Specialix pays for the development and support of this driver.
8 * Please DO contact io8-linux@specialix.co.uk if you require
9 * support. But please read the documentation (specialix.txt)
10 * first.
11 *
12 * This driver was developped in the BitWizard linux device
13 * driver service. If you require a linux device driver for your
14 * product, please contact devices@BitWizard.nl for a quote.
15 *
16 * This code is firmly based on the riscom/8 serial driver,
17 * written by Dmitry Gorodchanin. The specialix IO8+ card
18 * programming information was obtained from the CL-CD1865 Data
19 * Book, and Specialix document number 6200059: IO8+ Hardware
20 * Functional Specification.
21 *
22 * This program is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU General Public License as
24 * published by the Free Software Foundation; either version 2 of
25 * the License, or (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be
28 * useful, but WITHOUT ANY WARRANTY; without even the implied
29 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
30 * PURPOSE. See the GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public
33 * License along with this program; if not, write to the Free
34 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
35 * USA.
36 *
37 * Revision history:
38 *
39 * Revision 1.0: April 1st 1997.
40 * Initial release for alpha testing.
Jeff Garzikd61780c2005-10-30 15:01:51 -080041 * Revision 1.1: April 14th 1997.
42 * Incorporated Richard Hudsons suggestions,
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 * removed some debugging printk's.
44 * Revision 1.2: April 15th 1997.
45 * Ported to 2.1.x kernels.
Jeff Garzikd61780c2005-10-30 15:01:51 -080046 * Revision 1.3: April 17th 1997
47 * Backported to 2.0. (Compatibility macros).
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 * Revision 1.4: April 18th 1997
Jeff Garzikd61780c2005-10-30 15:01:51 -080049 * Fixed DTR/RTS bug that caused the card to indicate
50 * "don't send data" to a modem after the password prompt.
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 * Fixed bug for premature (fake) interrupts.
52 * Revision 1.5: April 19th 1997
Jeff Garzikd61780c2005-10-30 15:01:51 -080053 * fixed a minor typo in the header file, cleanup a little.
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 * performance warnings are now MAXed at once per minute.
55 * Revision 1.6: May 23 1997
56 * Changed the specialix=... format to include interrupt.
57 * Revision 1.7: May 27 1997
58 * Made many more debug printk's a compile time option.
59 * Revision 1.8: Jul 1 1997
60 * port to linux-2.1.43 kernel.
61 * Revision 1.9: Oct 9 1998
62 * Added stuff for the IO8+/PCI version.
Jeff Garzikd61780c2005-10-30 15:01:51 -080063 * Revision 1.10: Oct 22 1999 / Jan 21 2000.
64 * Added stuff for setserial.
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 * Nicolas Mailhot (Nicolas.Mailhot@email.enst.fr)
Jeff Garzikd61780c2005-10-30 15:01:51 -080066 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 */
68
69#define VERSION "1.11"
70
71
72/*
73 * There is a bunch of documentation about the card, jumpers, config
74 * settings, restrictions, cables, device names and numbers in
75 * Documentation/specialix.txt
76 */
77
78#include <linux/config.h>
79#include <linux/module.h>
80
81#include <asm/io.h>
82#include <linux/kernel.h>
83#include <linux/sched.h>
84#include <linux/ioport.h>
85#include <linux/interrupt.h>
86#include <linux/errno.h>
87#include <linux/tty.h>
Alan Cox33f0f882006-01-09 20:54:13 -080088#include <linux/tty_flip.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070089#include <linux/mm.h>
90#include <linux/serial.h>
91#include <linux/fcntl.h>
92#include <linux/major.h>
93#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070094#include <linux/pci.h>
95#include <linux/init.h>
96#include <asm/uaccess.h>
97
98#include "specialix_io8.h"
99#include "cd1865.h"
100
101
102/*
103 This driver can spew a whole lot of debugging output at you. If you
104 need maximum performance, you should disable the DEBUG define. To
105 aid in debugging in the field, I'm leaving the compile-time debug
106 features enabled, and disable them "runtime". That allows me to
107 instruct people with problems to enable debugging without requiring
108 them to recompile...
109*/
110#define DEBUG
111
112static int sx_debug;
113static int sx_rxfifo = SPECIALIX_RXFIFO;
114
115#ifdef DEBUG
116#define dprintk(f, str...) if (sx_debug & f) printk (str)
117#else
118#define dprintk(f, str...) /* nothing */
119#endif
120
121#define SX_DEBUG_FLOW 0x0001
122#define SX_DEBUG_DATA 0x0002
123#define SX_DEBUG_PROBE 0x0004
124#define SX_DEBUG_CHAN 0x0008
125#define SX_DEBUG_INIT 0x0010
126#define SX_DEBUG_RX 0x0020
127#define SX_DEBUG_TX 0x0040
128#define SX_DEBUG_IRQ 0x0080
129#define SX_DEBUG_OPEN 0x0100
130#define SX_DEBUG_TERMIOS 0x0200
131#define SX_DEBUG_SIGNALS 0x0400
132#define SX_DEBUG_FIFO 0x0800
133
134
135#define func_enter() dprintk (SX_DEBUG_FLOW, "io8: enter %s\n",__FUNCTION__)
136#define func_exit() dprintk (SX_DEBUG_FLOW, "io8: exit %s\n", __FUNCTION__)
137
138#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
139
140
141/* Configurable options: */
142
143/* Am I paranoid or not ? ;-) */
144#define SPECIALIX_PARANOIA_CHECK
145
146/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help)
147 When the IRQ routine leaves the chip in a state that is keeps on
148 requiring attention, the timer doesn't help either. */
149#undef SPECIALIX_TIMER
150
151#ifdef SPECIALIX_TIMER
152static int sx_poll = HZ;
153#endif
154
155
156
Jeff Garzikd61780c2005-10-30 15:01:51 -0800157/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 * The following defines are mostly for testing purposes. But if you need
159 * some nice reporting in your syslog, you can define them also.
160 */
161#undef SX_REPORT_FIFO
162#undef SX_REPORT_OVERRUN
163
164
165
166#ifdef CONFIG_SPECIALIX_RTSCTS
167#define SX_CRTSCTS(bla) 1
168#else
169#define SX_CRTSCTS(tty) C_CRTSCTS(tty)
170#endif
171
172
173/* Used to be outb (0xff, 0x80); */
174#define short_pause() udelay (1)
175
176
177#define SPECIALIX_LEGAL_FLAGS \
178 (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
179 ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
180 ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
181
182#undef RS_EVENT_WRITE_WAKEUP
183#define RS_EVENT_WRITE_WAKEUP 0
184
185static struct tty_driver *specialix_driver;
186static unsigned char * tmp_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
188static unsigned long baud_table[] = {
189 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
Jeff Garzikd61780c2005-10-30 15:01:51 -0800190 9600, 19200, 38400, 57600, 115200, 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191};
192
193static struct specialix_board sx_board[SX_NBOARD] = {
194 { 0, SX_IOBASE1, 9, },
195 { 0, SX_IOBASE2, 11, },
196 { 0, SX_IOBASE3, 12, },
197 { 0, SX_IOBASE4, 15, },
198};
199
200static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
201
202
203#ifdef SPECIALIX_TIMER
204static struct timer_list missed_irq_timer;
205static irqreturn_t sx_interrupt(int irq, void * dev_id, struct pt_regs * regs);
206#endif
207
208
209
210static inline int sx_paranoia_check(struct specialix_port const * port,
211 char *name, const char *routine)
212{
213#ifdef SPECIALIX_PARANOIA_CHECK
214 static const char *badmagic =
215 KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n";
216 static const char *badinfo =
217 KERN_ERR "sx: Warning: null specialix port for device %s in %s\n";
Jeff Garzikd61780c2005-10-30 15:01:51 -0800218
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 if (!port) {
220 printk(badinfo, name, routine);
221 return 1;
222 }
223 if (port->magic != SPECIALIX_MAGIC) {
224 printk(badmagic, name, routine);
225 return 1;
226 }
227#endif
228 return 0;
229}
230
231
232/*
Jeff Garzikd61780c2005-10-30 15:01:51 -0800233 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 * Service functions for specialix IO8+ driver.
Jeff Garzikd61780c2005-10-30 15:01:51 -0800235 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 */
237
238/* Get board number from pointer */
239static inline int board_No (struct specialix_board * bp)
240{
241 return bp - sx_board;
242}
243
244
245/* Get port number from pointer */
246static inline int port_No (struct specialix_port const * port)
247{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800248 return SX_PORT(port - sx_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249}
250
251
252/* Get pointer to board from pointer to port */
253static inline struct specialix_board * port_Board(struct specialix_port const * port)
254{
255 return &sx_board[SX_BOARD(port - sx_port)];
256}
257
258
259/* Input Byte from CL CD186x register */
260static inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg)
261{
262 bp->reg = reg | 0x80;
263 outb (reg | 0x80, bp->base + SX_ADDR_REG);
264 return inb (bp->base + SX_DATA_REG);
265}
266
267
268/* Output Byte to CL CD186x register */
269static inline void sx_out(struct specialix_board * bp, unsigned short reg,
270 unsigned char val)
271{
272 bp->reg = reg | 0x80;
273 outb (reg | 0x80, bp->base + SX_ADDR_REG);
274 outb (val, bp->base + SX_DATA_REG);
275}
276
277
278/* Input Byte from CL CD186x register */
279static inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg)
280{
281 bp->reg = reg;
282 outb (reg, bp->base + SX_ADDR_REG);
283 return inb (bp->base + SX_DATA_REG);
284}
285
286
287/* Output Byte to CL CD186x register */
288static inline void sx_out_off(struct specialix_board * bp, unsigned short reg,
289 unsigned char val)
290{
291 bp->reg = reg;
292 outb (reg, bp->base + SX_ADDR_REG);
293 outb (val, bp->base + SX_DATA_REG);
294}
295
296
297/* Wait for Channel Command Register ready */
298static inline void sx_wait_CCR(struct specialix_board * bp)
299{
300 unsigned long delay, flags;
301 unsigned char ccr;
302
303 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
304 spin_lock_irqsave(&bp->lock, flags);
305 ccr = sx_in(bp, CD186x_CCR);
306 spin_unlock_irqrestore(&bp->lock, flags);
307 if (!ccr)
308 return;
309 udelay (1);
310 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800311
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
313}
314
315
316/* Wait for Channel Command Register ready */
317static inline void sx_wait_CCR_off(struct specialix_board * bp)
318{
319 unsigned long delay;
320 unsigned char crr;
321 unsigned long flags;
322
323 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
324 spin_lock_irqsave(&bp->lock, flags);
325 crr = sx_in_off(bp, CD186x_CCR);
326 spin_unlock_irqrestore(&bp->lock, flags);
327 if (!crr)
328 return;
329 udelay (1);
330 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800331
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
333}
334
335
336/*
337 * specialix IO8+ IO range functions.
338 */
339
Jeff Garzikd61780c2005-10-30 15:01:51 -0800340static inline int sx_request_io_range(struct specialix_board * bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800342 return request_region(bp->base,
343 bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
344 "specialix IO8+") == NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345}
346
347
348static inline void sx_release_io_range(struct specialix_board * bp)
349{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800350 release_region(bp->base,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE);
352}
353
Jeff Garzikd61780c2005-10-30 15:01:51 -0800354
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355/* Must be called with enabled interrupts */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800356/* Ugly. Very ugly. Don't use this for anything else than initialization
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 code */
358static inline void sx_long_delay(unsigned long delay)
359{
360 unsigned long i;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800361
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 for (i = jiffies + delay; time_after(i, jiffies); ) ;
363}
364
365
366
367/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
368static int sx_set_irq ( struct specialix_board *bp)
369{
370 int virq;
371 int i;
372 unsigned long flags;
373
Jeff Garzikd61780c2005-10-30 15:01:51 -0800374 if (bp->flags & SX_BOARD_IS_PCI)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 return 1;
376 switch (bp->irq) {
377 /* In the same order as in the docs... */
378 case 15: virq = 0;break;
379 case 12: virq = 1;break;
380 case 11: virq = 2;break;
381 case 9: virq = 3;break;
382 default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq);
383 return 0;
384 }
385 spin_lock_irqsave(&bp->lock, flags);
386 for (i=0;i<2;i++) {
387 sx_out(bp, CD186x_CAR, i);
388 sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
389 }
390 spin_unlock_irqrestore(&bp->lock, flags);
391 return 1;
392}
393
394
395/* Reset and setup CD186x chip */
396static int sx_init_CD186x(struct specialix_board * bp)
397{
398 unsigned long flags;
399 int scaler;
400 int rv = 1;
401
402 func_enter();
403 sx_wait_CCR_off(bp); /* Wait for CCR ready */
404 spin_lock_irqsave(&bp->lock, flags);
405 sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
406 spin_unlock_irqrestore(&bp->lock, flags);
407 sx_long_delay(HZ/20); /* Delay 0.05 sec */
408 spin_lock_irqsave(&bp->lock, flags);
409 sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
410 sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
411 sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
412 sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
413 sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
414 /* Set RegAckEn */
415 sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800416
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 /* Setting up prescaler. We need 4 ticks per 1 ms */
418 scaler = SX_OSCFREQ/SPECIALIX_TPS;
419
420 sx_out_off(bp, CD186x_PPRH, scaler >> 8);
421 sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
422 spin_unlock_irqrestore(&bp->lock, flags);
423
424 if (!sx_set_irq (bp)) {
425 /* Figure out how to pass this along... */
426 printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq);
427 rv = 0;
428 }
429
430 func_exit();
431 return rv;
432}
433
434
435static int read_cross_byte (struct specialix_board *bp, int reg, int bit)
436{
437 int i;
438 int t;
439 unsigned long flags;
440
441 spin_lock_irqsave(&bp->lock, flags);
442 for (i=0, t=0;i<8;i++) {
443 sx_out_off (bp, CD186x_CAR, i);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800444 if (sx_in_off (bp, reg) & bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445 t |= 1 << i;
446 }
447 spin_unlock_irqrestore(&bp->lock, flags);
448
449 return t;
450}
451
452
453#ifdef SPECIALIX_TIMER
454void missed_irq (unsigned long data)
455{
456 unsigned char irq;
457 unsigned long flags;
458 struct specialix_board *bp = (struct specialix_board *)data;
459
460 spin_lock_irqsave(&bp->lock, flags);
461 irq = sx_in ((struct specialix_board *)data, CD186x_SRSR) &
462 (SRSR_RREQint |
463 SRSR_TREQint |
464 SRSR_MREQint);
465 spin_unlock_irqrestore(&bp->lock, flags);
466 if (irq) {
467 printk (KERN_INFO "Missed interrupt... Calling int from timer. \n");
Jeff Garzikd61780c2005-10-30 15:01:51 -0800468 sx_interrupt (((struct specialix_board *)data)->irq,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 (void*)data, NULL);
470 }
471 missed_irq_timer.expires = jiffies + sx_poll;
472 add_timer (&missed_irq_timer);
473}
474#endif
475
476
477
478/* Main probing routine, also sets irq. */
479static int sx_probe(struct specialix_board *bp)
480{
481 unsigned char val1, val2;
482#if 0
483 int irqs = 0;
484 int retries;
485#endif
486 int rev;
487 int chip;
488
489 func_enter();
490
Jeff Garzikd61780c2005-10-30 15:01:51 -0800491 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 func_exit();
493 return 1;
494 }
495
496 /* Are the I/O ports here ? */
497 sx_out_off(bp, CD186x_PPRL, 0x5a);
498 short_pause ();
499 val1 = sx_in_off(bp, CD186x_PPRL);
500
501 sx_out_off(bp, CD186x_PPRL, 0xa5);
502 short_pause ();
503 val2 = sx_in_off(bp, CD186x_PPRL);
504
Jeff Garzikd61780c2005-10-30 15:01:51 -0800505
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 if ((val1 != 0x5a) || (val2 != 0xa5)) {
507 printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
508 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800509 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 func_exit();
511 return 1;
512 }
513
Jeff Garzikd61780c2005-10-30 15:01:51 -0800514 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 identification */
516 val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
517 val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
518 dprintk (SX_DEBUG_INIT, "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
519 board_No(bp), val1, val2);
520
521 /* They managed to switch the bit order between the docs and
522 the IO8+ card. The new PCI card now conforms to old docs.
523 They changed the PCI docs to reflect the situation on the
524 old card. */
525 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
526 if (val1 != val2) {
527 printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
528 board_No(bp), val2, bp->base, val1);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800529 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 func_exit();
531 return 1;
532 }
533
534
535#if 0
536 /* It's time to find IRQ for this board */
537 for (retries = 0; retries < 5 && irqs <= 0; retries++) {
538 irqs = probe_irq_on();
539 sx_init_CD186x(bp); /* Reset CD186x chip */
540 sx_out(bp, CD186x_CAR, 2); /* Select port 2 */
541 sx_wait_CCR(bp);
542 sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */
543 sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800544 sx_long_delay(HZ/20);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 irqs = probe_irq_off(irqs);
546
547 dprintk (SX_DEBUG_INIT, "SRSR = %02x, ", sx_in(bp, CD186x_SRSR));
548 dprintk (SX_DEBUG_INIT, "TRAR = %02x, ", sx_in(bp, CD186x_TRAR));
549 dprintk (SX_DEBUG_INIT, "GIVR = %02x, ", sx_in(bp, CD186x_GIVR));
550 dprintk (SX_DEBUG_INIT, "GICR = %02x, ", sx_in(bp, CD186x_GICR));
551 dprintk (SX_DEBUG_INIT, "\n");
552
553 /* Reset CD186x again */
554 if (!sx_init_CD186x(bp)) {
555 /* Hmmm. This is dead code anyway. */
556 }
557
558 dprintk (SX_DEBUG_INIT "val1 = %02x, val2 = %02x, val3 = %02x.\n",
Jeff Garzikd61780c2005-10-30 15:01:51 -0800559 val1, val2, val3);
560
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800562
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563#if 0
564 if (irqs <= 0) {
565 printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
566 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800567 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 func_exit();
569 return 1;
570 }
571#endif
572 printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
573 if (irqs > 0)
574 bp->irq = irqs;
575#endif
576 /* Reset CD186x again */
577 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800578 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 func_exit();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800580 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 }
582
583 sx_request_io_range(bp);
584 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800585
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 /* Chip revcode pkgtype
587 GFRCR SRCR bit 7
588 CD180 rev B 0x81 0
589 CD180 rev C 0x82 0
590 CD1864 rev A 0x82 1
Jeff Garzikd61780c2005-10-30 15:01:51 -0800591 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 CD1865 rev B 0x84 1
593 -- Thanks to Gwen Wang, Cirrus Logic.
594 */
595
596 switch (sx_in_off(bp, CD186x_GFRCR)) {
597 case 0x82:chip = 1864;rev='A';break;
598 case 0x83:chip = 1865;rev='A';break;
599 case 0x84:chip = 1865;rev='B';break;
600 case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
601 default:chip=-1;rev='x';
602 }
603
604 dprintk (SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
605
606#ifdef SPECIALIX_TIMER
607 init_timer (&missed_irq_timer);
608 missed_irq_timer.function = missed_irq;
609 missed_irq_timer.data = (unsigned long) bp;
610 missed_irq_timer.expires = jiffies + sx_poll;
611 add_timer (&missed_irq_timer);
612#endif
613
614 printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
615 board_No(bp),
616 bp->base, bp->irq,
617 chip, rev);
618
619 func_exit();
620 return 0;
621}
622
Jeff Garzikd61780c2005-10-30 15:01:51 -0800623/*
624 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 * Interrupt processing routines.
626 * */
627
628static inline void sx_mark_event(struct specialix_port * port, int event)
629{
630 func_enter();
631
632 set_bit(event, &port->event);
633 schedule_work(&port->tqueue);
634
635 func_exit();
636}
637
638
639static inline struct specialix_port * sx_get_port(struct specialix_board * bp,
640 unsigned char const * what)
641{
642 unsigned char channel;
643 struct specialix_port * port = NULL;
644
645 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
646 dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel);
647 if (channel < CD186x_NCH) {
648 port = &sx_port[board_No(bp) * SX_NPORT + channel];
649 dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%x\n",board_No(bp) * SX_NPORT + channel, port, port->flags & ASYNC_INITIALIZED);
650
651 if (port->flags & ASYNC_INITIALIZED) {
652 dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
653 func_exit();
654 return port;
655 }
656 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800657 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 board_No(bp), what, channel);
659 return NULL;
660}
661
662
663static inline void sx_receive_exc(struct specialix_board * bp)
664{
665 struct specialix_port *port;
666 struct tty_struct *tty;
667 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800668 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
670 func_enter();
671
672 port = sx_get_port(bp, "Receive");
673 if (!port) {
674 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
675 func_exit();
676 return;
677 }
678 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800679
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 status = sx_in(bp, CD186x_RCSR);
681
682 dprintk (SX_DEBUG_RX, "status: 0x%x\n", status);
683 if (status & RCSR_OE) {
684 port->overrun++;
685 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Overrun. Total %ld overruns.\n",
686 board_No(bp), port_No(port), port->overrun);
687 }
688 status &= port->mark_mask;
689
690 /* This flip buffer check needs to be below the reading of the
691 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800692 if (tty_buffer_request_room(tty, 1) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Working around flip buffer overflow.\n",
694 board_No(bp), port_No(port));
695 func_exit();
696 return;
697 }
698
699 ch = sx_in(bp, CD186x_RDR);
700 if (!status) {
701 func_exit();
702 return;
703 }
704 if (status & RCSR_TOUT) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800705 printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 board_No(bp), port_No(port));
707 func_exit();
708 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800709
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 } else if (status & RCSR_BREAK) {
711 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
712 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800713 flag = TTY_BREAK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 if (port->flags & ASYNC_SAK)
715 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800716
717 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800718 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800719
720 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800721 flag = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800722
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800724 flag = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800725
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 else
Alan Cox33f0f882006-01-09 20:54:13 -0800727 flag = TTY_NORMAL;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800728
Alan Cox33f0f882006-01-09 20:54:13 -0800729 if(tty_insert_flip_char(tty, ch, flag))
730 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 func_exit();
732}
733
734
735static inline void sx_receive(struct specialix_board * bp)
736{
737 struct specialix_port *port;
738 struct tty_struct *tty;
739 unsigned char count;
740
741 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800742
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 if (!(port = sx_get_port(bp, "Receive"))) {
744 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
745 func_exit();
746 return;
747 }
748 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800749
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 count = sx_in(bp, CD186x_RDCR);
751 dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
752 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800753
Alan Cox33f0f882006-01-09 20:54:13 -0800754 tty_buffer_request_room(tty, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
Alan Cox33f0f882006-01-09 20:54:13 -0800756 while (count--)
757 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
758 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 func_exit();
760}
761
762
763static inline void sx_transmit(struct specialix_board * bp)
764{
765 struct specialix_port *port;
766 struct tty_struct *tty;
767 unsigned char count;
768
769 func_enter();
770 if (!(port = sx_get_port(bp, "Transmit"))) {
771 func_exit();
772 return;
773 }
774 dprintk (SX_DEBUG_TX, "port: %p\n", port);
775 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800776
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 if (port->IER & IER_TXEMPTY) {
778 /* FIFO drained */
779 sx_out(bp, CD186x_CAR, port_No(port));
780 port->IER &= ~IER_TXEMPTY;
781 sx_out(bp, CD186x_IER, port->IER);
782 func_exit();
783 return;
784 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800785
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 if ((port->xmit_cnt <= 0 && !port->break_length)
787 || tty->stopped || tty->hw_stopped) {
788 sx_out(bp, CD186x_CAR, port_No(port));
789 port->IER &= ~IER_TXRDY;
790 sx_out(bp, CD186x_IER, port->IER);
791 func_exit();
792 return;
793 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800794
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 if (port->break_length) {
796 if (port->break_length > 0) {
797 if (port->COR2 & COR2_ETC) {
798 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
799 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
800 port->COR2 &= ~COR2_ETC;
801 }
802 count = min_t(int, port->break_length, 0xff);
803 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
804 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
805 sx_out(bp, CD186x_TDR, count);
806 if (!(port->break_length -= count))
807 port->break_length--;
808 } else {
809 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
810 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
811 sx_out(bp, CD186x_COR2, port->COR2);
812 sx_wait_CCR(bp);
813 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
814 port->break_length = 0;
815 }
816
817 func_exit();
818 return;
819 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800820
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 count = CD186x_NFIFO;
822 do {
823 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
824 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
825 if (--port->xmit_cnt <= 0)
826 break;
827 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800828
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 if (port->xmit_cnt <= 0) {
830 sx_out(bp, CD186x_CAR, port_No(port));
831 port->IER &= ~IER_TXRDY;
832 sx_out(bp, CD186x_IER, port->IER);
833 }
834 if (port->xmit_cnt <= port->wakeup_chars)
835 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
836
837 func_exit();
838}
839
840
841static inline void sx_check_modem(struct specialix_board * bp)
842{
843 struct specialix_port *port;
844 struct tty_struct *tty;
845 unsigned char mcr;
846 int msvr_cd;
847
848 dprintk (SX_DEBUG_SIGNALS, "Modem intr. ");
849 if (!(port = sx_get_port(bp, "Modem")))
850 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800851
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800853
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 mcr = sx_in(bp, CD186x_MCR);
855 printk ("mcr = %02x.\n", mcr);
856
857 if ((mcr & MCR_CDCHG)) {
858 dprintk (SX_DEBUG_SIGNALS, "CD just changed... ");
859 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
860 if (msvr_cd) {
861 dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
862 wake_up_interruptible(&port->open_wait);
863 } else {
864 dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n");
865 schedule_work(&port->tqueue_hangup);
866 }
867 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800868
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
870 if (mcr & MCR_CTSCHG) {
871 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
872 tty->hw_stopped = 0;
873 port->IER |= IER_TXRDY;
874 if (port->xmit_cnt <= port->wakeup_chars)
875 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
876 } else {
877 tty->hw_stopped = 1;
878 port->IER &= ~IER_TXRDY;
879 }
880 sx_out(bp, CD186x_IER, port->IER);
881 }
882 if (mcr & MCR_DSSXHG) {
883 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
884 tty->hw_stopped = 0;
885 port->IER |= IER_TXRDY;
886 if (port->xmit_cnt <= port->wakeup_chars)
887 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
888 } else {
889 tty->hw_stopped = 1;
890 port->IER &= ~IER_TXRDY;
891 }
892 sx_out(bp, CD186x_IER, port->IER);
893 }
894#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800895
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 /* Clear change bits */
897 sx_out(bp, CD186x_MCR, 0);
898}
899
900
901/* The main interrupt processing routine */
902static irqreturn_t sx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
903{
904 unsigned char status;
905 unsigned char ack;
906 struct specialix_board *bp;
907 unsigned long loop = 0;
908 int saved_reg;
909 unsigned long flags;
910
911 func_enter();
912
913 bp = dev_id;
914 spin_lock_irqsave(&bp->lock, flags);
915
916 dprintk (SX_DEBUG_FLOW, "enter %s port %d room: %ld\n", __FUNCTION__, port_No(sx_get_port(bp, "INT")), SERIAL_XMIT_SIZE - sx_get_port(bp, "ITN")->xmit_cnt - 1);
917 if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {
918 dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", irq);
919 spin_unlock_irqrestore(&bp->lock, flags);
920 func_exit();
921 return IRQ_NONE;
922 }
923
924 saved_reg = bp->reg;
925
926 while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
927 (SRSR_RREQint |
928 SRSR_TREQint |
Jeff Garzikd61780c2005-10-30 15:01:51 -0800929 SRSR_MREQint)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 if (status & SRSR_RREQint) {
931 ack = sx_in(bp, CD186x_RRAR);
932
933 if (ack == (SX_ID | GIVR_IT_RCV))
934 sx_receive(bp);
935 else if (ack == (SX_ID | GIVR_IT_REXC))
936 sx_receive_exc(bp);
937 else
938 printk(KERN_ERR "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
939 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800940
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 } else if (status & SRSR_TREQint) {
942 ack = sx_in(bp, CD186x_TRAR);
943
944 if (ack == (SX_ID | GIVR_IT_TX))
945 sx_transmit(bp);
946 else
947 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
948 board_No(bp), status, ack, port_No (sx_get_port (bp, "Int")));
949 } else if (status & SRSR_MREQint) {
950 ack = sx_in(bp, CD186x_MRAR);
951
Jeff Garzikd61780c2005-10-30 15:01:51 -0800952 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 sx_check_modem(bp);
954 else
955 printk(KERN_ERR "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
956 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800957
958 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959
960 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
961 }
962 bp->reg = saved_reg;
963 outb (bp->reg, bp->base + SX_ADDR_REG);
964 spin_unlock_irqrestore(&bp->lock, flags);
965 func_exit();
966 return IRQ_HANDLED;
967}
968
969
970/*
971 * Routines for open & close processing.
972 */
973
974static void turn_ints_off (struct specialix_board *bp)
975{
976 unsigned long flags;
977
978 func_enter();
979 if (bp->flags & SX_BOARD_IS_PCI) {
980 /* This was intended for enabeling the interrupt on the
981 * PCI card. However it seems that it's already enabled
982 * and as PCI interrupts can be shared, there is no real
983 * reason to have to turn it off. */
984 }
985
986 spin_lock_irqsave(&bp->lock, flags);
987 (void) sx_in_off (bp, 0); /* Turn off interrupts. */
988 spin_unlock_irqrestore(&bp->lock, flags);
989
990 func_exit();
991}
992
993static void turn_ints_on (struct specialix_board *bp)
994{
995 unsigned long flags;
996
997 func_enter();
998
999 if (bp->flags & SX_BOARD_IS_PCI) {
1000 /* play with the PCI chip. See comment above. */
1001 }
1002 spin_lock_irqsave(&bp->lock, flags);
1003 (void) sx_in (bp, 0); /* Turn ON interrupts. */
1004 spin_unlock_irqrestore(&bp->lock, flags);
1005
1006 func_exit();
1007}
1008
1009
1010/* Called with disabled interrupts */
1011static inline int sx_setup_board(struct specialix_board * bp)
1012{
1013 int error;
1014
Jeff Garzikd61780c2005-10-30 15:01:51 -08001015 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 return 0;
1017
1018 if (bp->flags & SX_BOARD_IS_PCI)
1019 error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT | SA_SHIRQ, "specialix IO8+", bp);
1020 else
1021 error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", bp);
1022
Jeff Garzikd61780c2005-10-30 15:01:51 -08001023 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 return error;
1025
1026 turn_ints_on (bp);
1027 bp->flags |= SX_BOARD_ACTIVE;
1028
1029 return 0;
1030}
1031
1032
1033/* Called with disabled interrupts */
1034static inline void sx_shutdown_board(struct specialix_board *bp)
1035{
1036 func_enter();
1037
1038 if (!(bp->flags & SX_BOARD_ACTIVE)) {
1039 func_exit();
1040 return;
1041 }
1042
1043 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001044
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 dprintk (SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
1046 bp->irq, board_No (bp));
1047 free_irq(bp->irq, bp);
1048
1049 turn_ints_off (bp);
1050
1051
1052 func_exit();
1053}
1054
1055
1056/*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001057 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 * Must be called with disabled interrupts
1059 */
1060static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
1061{
1062 struct tty_struct *tty;
1063 unsigned long baud;
1064 long tmp;
1065 unsigned char cor1 = 0, cor3 = 0;
1066 unsigned char mcor1 = 0, mcor2 = 0;
1067 static unsigned long again;
1068 unsigned long flags;
1069
1070 func_enter();
1071
1072 if (!(tty = port->tty) || !tty->termios) {
1073 func_exit();
1074 return;
1075 }
1076
1077 port->IER = 0;
1078 port->COR2 = 0;
1079 /* Select port on the board */
1080 spin_lock_irqsave(&bp->lock, flags);
1081 sx_out(bp, CD186x_CAR, port_No(port));
1082
1083 /* The Specialix board doens't implement the RTS lines.
1084 They are used to set the IRQ level. Don't touch them. */
1085 if (SX_CRTSCTS(tty))
1086 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1087 else
1088 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1089 spin_unlock_irqrestore(&bp->lock, flags);
1090 dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
1091 baud = C_BAUD(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001092
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 if (baud & CBAUDEX) {
1094 baud &= ~CBAUDEX;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001095 if (baud < 1 || baud > 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 port->tty->termios->c_cflag &= ~CBAUDEX;
1097 else
1098 baud += 15;
1099 }
1100 if (baud == 15) {
1101 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
1102 baud ++;
1103 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
1104 baud += 2;
1105 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001106
1107
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 if (!baud_table[baud]) {
1109 /* Drop DTR & exit */
1110 dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
1111 if (!SX_CRTSCTS (tty)) {
1112 port -> MSVR &= ~ MSVR_DTR;
1113 spin_lock_irqsave(&bp->lock, flags);
1114 sx_out(bp, CD186x_MSVR, port->MSVR );
1115 spin_unlock_irqrestore(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001116 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 else
1118 dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
1119 return;
1120 } else {
1121 /* Set DTR on */
1122 if (!SX_CRTSCTS (tty)) {
1123 port ->MSVR |= MSVR_DTR;
1124 }
1125 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001126
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001128 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 */
1130
1131 /* Set baud rate for port */
1132 tmp = port->custom_divisor ;
1133 if ( tmp )
1134 printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n"
1135 "This is an untested option, please be carefull.\n",
1136 port_No (port), tmp);
1137 else
1138 tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +
1139 CD186x_TPC/2) / CD186x_TPC);
1140
Jeff Garzikd61780c2005-10-30 15:01:51 -08001141 if ((tmp < 0x10) && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 again = jiffies + HZ * 60;
1143 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1144 if (tmp >= 12) {
1145 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1146 "Performance degradation is possible.\n"
1147 "Read specialix.txt for more info.\n",
1148 port_No (port), tmp);
1149 } else {
1150 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1151 "Warning: overstressing Cirrus chip. "
1152 "This might not work.\n"
Jeff Garzikd61780c2005-10-30 15:01:51 -08001153 "Read specialix.txt for more info.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 port_No (port), tmp);
1155 }
1156 }
1157 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001158 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1159 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1160 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1162 spin_unlock_irqrestore(&bp->lock, flags);
1163 if (port->custom_divisor) {
1164 baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;
1165 baud = ( baud + 5 ) / 10;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001166 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */
1168
1169 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001170 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1172 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001173
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 /* Receiver timeout will be transmission time for 1.5 chars */
1175 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1176 tmp = (tmp > 0xff) ? 0xff : tmp;
1177 spin_lock_irqsave(&bp->lock, flags);
1178 sx_out(bp, CD186x_RTPR, tmp);
1179 spin_unlock_irqrestore(&bp->lock, flags);
1180 switch (C_CSIZE(tty)) {
1181 case CS5:
1182 cor1 |= COR1_5BITS;
1183 break;
1184 case CS6:
1185 cor1 |= COR1_6BITS;
1186 break;
1187 case CS7:
1188 cor1 |= COR1_7BITS;
1189 break;
1190 case CS8:
1191 cor1 |= COR1_8BITS;
1192 break;
1193 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001194
1195 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001197
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 cor1 |= COR1_IGNORE;
1199 if (C_PARENB(tty)) {
1200 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001201 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001203 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 cor1 &= ~COR1_IGNORE;
1205 }
1206 /* Set marking of some errors */
1207 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001208 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001210 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001212 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1214 if (I_IGNBRK(tty)) {
1215 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001216 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 /* Real raw mode. Ignore all */
1218 port->mark_mask &= ~RCSR_OE;
1219 }
1220 /* Enable Hardware Flow Control */
1221 if (C_CRTSCTS(tty)) {
1222#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1223 port->IER |= IER_DSR | IER_CTS;
1224 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1225 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1226 spin_lock_irqsave(&bp->lock, flags);
1227 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
1228 spin_unlock_irqrestore(&bp->lock, flags);
1229#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001230 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231#endif
1232 }
1233 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1234 /* Some people reported that it works, but I still doubt it */
1235 if (I_IXON(tty)) {
1236 port->COR2 |= COR2_TXIBE;
1237 cor3 |= (COR3_FCT | COR3_SCDE);
1238 if (I_IXANY(tty))
1239 port->COR2 |= COR2_IXM;
1240 spin_lock_irqsave(&bp->lock, flags);
1241 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1242 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1243 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1244 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1245 spin_unlock_irqrestore(&bp->lock, flags);
1246 }
1247 if (!C_CLOCAL(tty)) {
1248 /* Enable CD check */
1249 port->IER |= IER_CD;
1250 mcor1 |= MCOR1_CDZD;
1251 mcor2 |= MCOR2_CDOD;
1252 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001253
1254 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 /* Enable receiver */
1256 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001257
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 /* Set input FIFO size (1-8 bytes) */
1259 cor3 |= sx_rxfifo;
1260 /* Setting up CD186x channel registers */
1261 spin_lock_irqsave(&bp->lock, flags);
1262 sx_out(bp, CD186x_COR1, cor1);
1263 sx_out(bp, CD186x_COR2, port->COR2);
1264 sx_out(bp, CD186x_COR3, cor3);
1265 spin_unlock_irqrestore(&bp->lock, flags);
1266 /* Make CD186x know about registers change */
1267 sx_wait_CCR(bp);
1268 spin_lock_irqsave(&bp->lock, flags);
1269 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1270 /* Setting up modem option registers */
1271 dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
1272 sx_out(bp, CD186x_MCOR1, mcor1);
1273 sx_out(bp, CD186x_MCOR2, mcor2);
1274 spin_unlock_irqrestore(&bp->lock, flags);
1275 /* Enable CD186x transmitter & receiver */
1276 sx_wait_CCR(bp);
1277 spin_lock_irqsave(&bp->lock, flags);
1278 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1279 /* Enable interrupts */
1280 sx_out(bp, CD186x_IER, port->IER);
1281 /* And finally set the modem lines... */
1282 sx_out(bp, CD186x_MSVR, port->MSVR);
1283 spin_unlock_irqrestore(&bp->lock, flags);
1284
1285 func_exit();
1286}
1287
1288
1289/* Must be called with interrupts enabled */
1290static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
1291{
1292 unsigned long flags;
1293
1294 func_enter();
1295
1296 if (port->flags & ASYNC_INITIALIZED) {
1297 func_exit();
1298 return 0;
1299 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001300
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 if (!port->xmit_buf) {
1302 /* We may sleep in get_zeroed_page() */
1303 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001304
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 if (!(tmp = get_zeroed_page(GFP_KERNEL))) {
1306 func_exit();
1307 return -ENOMEM;
1308 }
1309
1310 if (port->xmit_buf) {
1311 free_page(tmp);
1312 func_exit();
1313 return -ERESTARTSYS;
1314 }
1315 port->xmit_buf = (unsigned char *) tmp;
1316 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001317
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 spin_lock_irqsave(&port->lock, flags);
1319
Jeff Garzikd61780c2005-10-30 15:01:51 -08001320 if (port->tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 clear_bit(TTY_IO_ERROR, &port->tty->flags);
1322
1323 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1324 sx_change_speed(bp, port);
1325 port->flags |= ASYNC_INITIALIZED;
1326
1327 spin_unlock_irqrestore(&port->lock, flags);
1328
Jeff Garzikd61780c2005-10-30 15:01:51 -08001329
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 func_exit();
1331 return 0;
1332}
1333
1334
1335/* Must be called with interrupts disabled */
1336static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
1337{
1338 struct tty_struct *tty;
1339 int i;
1340 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001341
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 func_enter();
1343
1344 if (!(port->flags & ASYNC_INITIALIZED)) {
1345 func_exit();
1346 return;
1347 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001348
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 if (sx_debug & SX_DEBUG_FIFO) {
1350 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ",
1351 board_No(bp), port_No(port), port->overrun);
1352 for (i = 0; i < 10; i++) {
1353 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
1354 }
1355 dprintk(SX_DEBUG_FIFO, "].\n");
1356 }
1357
1358 if (port->xmit_buf) {
1359 free_page((unsigned long) port->xmit_buf);
1360 port->xmit_buf = NULL;
1361 }
1362
1363 /* Select port */
1364 spin_lock_irqsave(&bp->lock, flags);
1365 sx_out(bp, CD186x_CAR, port_No(port));
1366
1367 if (!(tty = port->tty) || C_HUPCL(tty)) {
1368 /* Drop DTR */
1369 sx_out(bp, CD186x_MSVDTR, 0);
1370 }
1371 spin_unlock_irqrestore(&bp->lock, flags);
1372 /* Reset port */
1373 sx_wait_CCR(bp);
1374 spin_lock_irqsave(&bp->lock, flags);
1375 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1376 /* Disable all interrupts from this port */
1377 port->IER = 0;
1378 sx_out(bp, CD186x_IER, port->IER);
1379 spin_unlock_irqrestore(&bp->lock, flags);
1380 if (tty)
1381 set_bit(TTY_IO_ERROR, &tty->flags);
1382 port->flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001383
1384 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 sx_shutdown_board(bp);
1386 func_exit();
1387}
1388
Jeff Garzikd61780c2005-10-30 15:01:51 -08001389
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390static int block_til_ready(struct tty_struct *tty, struct file * filp,
1391 struct specialix_port *port)
1392{
1393 DECLARE_WAITQUEUE(wait, current);
1394 struct specialix_board *bp = port_Board(port);
1395 int retval;
1396 int do_clocal = 0;
1397 int CD;
1398 unsigned long flags;
1399
1400 func_enter();
1401
1402 /*
1403 * If the device is in the middle of being closed, then block
1404 * until it's done, and then try again.
1405 */
1406 if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
1407 interruptible_sleep_on(&port->close_wait);
1408 if (port->flags & ASYNC_HUP_NOTIFY) {
1409 func_exit();
1410 return -EAGAIN;
1411 } else {
1412 func_exit();
1413 return -ERESTARTSYS;
1414 }
1415 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001416
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 /*
1418 * If non-blocking mode is set, or the port is not enabled,
1419 * then make the check up front and then exit.
1420 */
1421 if ((filp->f_flags & O_NONBLOCK) ||
1422 (tty->flags & (1 << TTY_IO_ERROR))) {
1423 port->flags |= ASYNC_NORMAL_ACTIVE;
1424 func_exit();
1425 return 0;
1426 }
1427
1428 if (C_CLOCAL(tty))
1429 do_clocal = 1;
1430
1431 /*
1432 * Block waiting for the carrier detect and the line to become
1433 * free (i.e., not in use by the callout). While we are in
1434 * this loop, info->count is dropped by one, so that
1435 * rs_close() knows when to free things. We restore it upon
1436 * exit, either normal or abnormal.
1437 */
1438 retval = 0;
1439 add_wait_queue(&port->open_wait, &wait);
1440 spin_lock_irqsave(&port->lock, flags);
1441 if (!tty_hung_up_p(filp)) {
1442 port->count--;
1443 }
1444 spin_unlock_irqrestore(&port->lock, flags);
1445 port->blocked_open++;
1446 while (1) {
1447 spin_lock_irqsave(&bp->lock, flags);
1448 sx_out(bp, CD186x_CAR, port_No(port));
1449 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
1450 if (SX_CRTSCTS (tty)) {
1451 /* Activate RTS */
1452 port->MSVR |= MSVR_DTR; /* WTF? */
1453 sx_out (bp, CD186x_MSVR, port->MSVR);
1454 } else {
1455 /* Activate DTR */
1456 port->MSVR |= MSVR_DTR;
1457 sx_out (bp, CD186x_MSVR, port->MSVR);
1458 }
1459 spin_unlock_irqrestore(&bp->lock, flags);
1460 set_current_state(TASK_INTERRUPTIBLE);
1461 if (tty_hung_up_p(filp) ||
1462 !(port->flags & ASYNC_INITIALIZED)) {
1463 if (port->flags & ASYNC_HUP_NOTIFY)
1464 retval = -EAGAIN;
1465 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001466 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 break;
1468 }
1469 if (!(port->flags & ASYNC_CLOSING) &&
1470 (do_clocal || CD))
1471 break;
1472 if (signal_pending(current)) {
1473 retval = -ERESTARTSYS;
1474 break;
1475 }
1476 schedule();
1477 }
1478
1479 set_current_state(TASK_RUNNING);
1480 remove_wait_queue(&port->open_wait, &wait);
1481 spin_lock_irqsave(&port->lock, flags);
1482 if (!tty_hung_up_p(filp)) {
1483 port->count++;
1484 }
1485 port->blocked_open--;
1486 spin_unlock_irqrestore(&port->lock, flags);
1487 if (retval) {
1488 func_exit();
1489 return retval;
1490 }
1491
1492 port->flags |= ASYNC_NORMAL_ACTIVE;
1493 func_exit();
1494 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001495}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496
1497
1498static int sx_open(struct tty_struct * tty, struct file * filp)
1499{
1500 int board;
1501 int error;
1502 struct specialix_port * port;
1503 struct specialix_board * bp;
1504 int i;
1505 unsigned long flags;
1506
1507 func_enter();
1508
1509 board = SX_BOARD(tty->index);
1510
1511 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1512 func_exit();
1513 return -ENODEV;
1514 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001515
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 bp = &sx_board[board];
1517 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1518 port->overrun = 0;
1519 for (i = 0; i < 10; i++)
1520 port->hits[i]=0;
1521
1522 dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n",
1523 board, bp, port, SX_PORT(tty->index));
1524
1525 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1526 func_enter();
1527 return -ENODEV;
1528 }
1529
1530 if ((error = sx_setup_board(bp))) {
1531 func_exit();
1532 return error;
1533 }
1534
1535 spin_lock_irqsave(&bp->lock, flags);
1536 port->count++;
1537 bp->count++;
1538 tty->driver_data = port;
1539 port->tty = tty;
1540 spin_unlock_irqrestore(&bp->lock, flags);
1541
1542 if ((error = sx_setup_port(bp, port))) {
1543 func_enter();
1544 return error;
1545 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001546
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 if ((error = block_til_ready(tty, filp, port))) {
1548 func_enter();
1549 return error;
1550 }
1551
1552 func_exit();
1553 return 0;
1554}
1555
1556
1557static void sx_close(struct tty_struct * tty, struct file * filp)
1558{
1559 struct specialix_port *port = (struct specialix_port *) tty->driver_data;
1560 struct specialix_board *bp;
1561 unsigned long flags;
1562 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001563
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 func_enter();
1565 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1566 func_exit();
1567 return;
1568 }
1569 spin_lock_irqsave(&port->lock, flags);
1570
1571 if (tty_hung_up_p(filp)) {
1572 spin_unlock_irqrestore(&port->lock, flags);
1573 func_exit();
1574 return;
1575 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001576
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577 bp = port_Board(port);
1578 if ((tty->count == 1) && (port->count != 1)) {
1579 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1580 " tty->count is 1, port count is %d\n",
1581 board_No(bp), port->count);
1582 port->count = 1;
1583 }
1584
1585 if (port->count > 1) {
1586 port->count--;
1587 bp->count--;
1588
1589 spin_unlock_irqrestore(&port->lock, flags);
1590
1591 func_exit();
1592 return;
1593 }
1594 port->flags |= ASYNC_CLOSING;
1595 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001596 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597 * the line discipline to only process XON/XOFF characters.
1598 */
1599 tty->closing = 1;
1600 spin_unlock_irqrestore(&port->lock, flags);
1601 dprintk (SX_DEBUG_OPEN, "Closing\n");
1602 if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
1603 tty_wait_until_sent(tty, port->closing_wait);
1604 }
1605 /*
1606 * At this point we stop accepting input. To do this, we
1607 * disable the receive line status interrupts, and tell the
1608 * interrupt driver to stop checking the data ready bit in the
1609 * line status register.
1610 */
1611 dprintk (SX_DEBUG_OPEN, "Closed\n");
1612 port->IER &= ~IER_RXD;
1613 if (port->flags & ASYNC_INITIALIZED) {
1614 port->IER &= ~IER_TXRDY;
1615 port->IER |= IER_TXEMPTY;
1616 spin_lock_irqsave(&bp->lock, flags);
1617 sx_out(bp, CD186x_CAR, port_No(port));
1618 sx_out(bp, CD186x_IER, port->IER);
1619 spin_unlock_irqrestore(&bp->lock, flags);
1620 /*
1621 * Before we drop DTR, make sure the UART transmitter
1622 * has completely drained; this is especially
1623 * important if there is a transmit FIFO!
1624 */
1625 timeout = jiffies+HZ;
1626 while(port->IER & IER_TXEMPTY) {
1627 set_current_state (TASK_INTERRUPTIBLE);
1628 msleep_interruptible(jiffies_to_msecs(port->timeout));
1629 if (time_after(jiffies, timeout)) {
1630 printk (KERN_INFO "Timeout waiting for close\n");
1631 break;
1632 }
1633 }
1634
1635 }
1636
1637 if (--bp->count < 0) {
1638 printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1639 board_No(bp), bp->count, tty->index);
1640 bp->count = 0;
1641 }
1642 if (--port->count < 0) {
1643 printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
1644 board_No(bp), port_No(port), port->count);
1645 port->count = 0;
1646 }
1647
1648 sx_shutdown_port(bp, port);
1649 if (tty->driver->flush_buffer)
1650 tty->driver->flush_buffer(tty);
1651 tty_ldisc_flush(tty);
1652 spin_lock_irqsave(&port->lock, flags);
1653 tty->closing = 0;
1654 port->event = 0;
1655 port->tty = NULL;
1656 spin_unlock_irqrestore(&port->lock, flags);
1657 if (port->blocked_open) {
1658 if (port->close_delay) {
1659 msleep_interruptible(jiffies_to_msecs(port->close_delay));
1660 }
1661 wake_up_interruptible(&port->open_wait);
1662 }
1663 port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1664 wake_up_interruptible(&port->close_wait);
1665
1666 func_exit();
1667}
1668
1669
Jeff Garzikd61780c2005-10-30 15:01:51 -08001670static int sx_write(struct tty_struct * tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 const unsigned char *buf, int count)
1672{
1673 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1674 struct specialix_board *bp;
1675 int c, total = 0;
1676 unsigned long flags;
1677
1678 func_enter();
1679 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1680 func_exit();
1681 return 0;
1682 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001683
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684 bp = port_Board(port);
1685
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001686 if (!port->xmit_buf || !tmp_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 func_exit();
1688 return 0;
1689 }
1690
1691 while (1) {
1692 spin_lock_irqsave(&port->lock, flags);
1693 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1694 SERIAL_XMIT_SIZE - port->xmit_head));
1695 if (c <= 0) {
1696 spin_unlock_irqrestore(&port->lock, flags);
1697 break;
1698 }
1699 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1700 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1701 port->xmit_cnt += c;
1702 spin_unlock_irqrestore(&port->lock, flags);
1703
1704 buf += c;
1705 count -= c;
1706 total += c;
1707 }
1708
1709 spin_lock_irqsave(&bp->lock, flags);
1710 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1711 !(port->IER & IER_TXRDY)) {
1712 port->IER |= IER_TXRDY;
1713 sx_out(bp, CD186x_CAR, port_No(port));
1714 sx_out(bp, CD186x_IER, port->IER);
1715 }
1716 spin_unlock_irqrestore(&bp->lock, flags);
1717 func_exit();
1718
1719 return total;
1720}
1721
1722
1723static void sx_put_char(struct tty_struct * tty, unsigned char ch)
1724{
1725 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1726 unsigned long flags;
1727 struct specialix_board * bp;
1728
1729 func_enter();
1730
1731 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1732 func_exit();
1733 return;
1734 }
1735 dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001736 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 func_exit();
1738 return;
1739 }
1740 bp = port_Board(port);
1741 spin_lock_irqsave(&port->lock, flags);
1742
1743 dprintk (SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", port->xmit_cnt, port->xmit_buf);
1744 if ((port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) || (!port->xmit_buf)) {
1745 spin_unlock_irqrestore(&port->lock, flags);
1746 dprintk (SX_DEBUG_TX, "Exit size\n");
1747 func_exit();
1748 return;
1749 }
1750 dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
1751 port->xmit_buf[port->xmit_head++] = ch;
1752 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1753 port->xmit_cnt++;
1754 spin_unlock_irqrestore(&port->lock, flags);
1755
1756 func_exit();
1757}
1758
1759
1760static void sx_flush_chars(struct tty_struct * tty)
1761{
1762 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1763 unsigned long flags;
1764 struct specialix_board * bp = port_Board(port);
1765
1766 func_enter();
1767
1768 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1769 func_exit();
1770 return;
1771 }
1772 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1773 !port->xmit_buf) {
1774 func_exit();
1775 return;
1776 }
1777 spin_lock_irqsave(&bp->lock, flags);
1778 port->IER |= IER_TXRDY;
1779 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1780 sx_out(port_Board(port), CD186x_IER, port->IER);
1781 spin_unlock_irqrestore(&bp->lock, flags);
1782
1783 func_exit();
1784}
1785
1786
1787static int sx_write_room(struct tty_struct * tty)
1788{
1789 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1790 int ret;
1791
1792 func_enter();
1793
1794 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1795 func_exit();
1796 return 0;
1797 }
1798
1799 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1800 if (ret < 0)
1801 ret = 0;
1802
1803 func_exit();
1804 return ret;
1805}
1806
1807
1808static int sx_chars_in_buffer(struct tty_struct *tty)
1809{
1810 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1811
1812 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001813
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1815 func_exit();
1816 return 0;
1817 }
1818 func_exit();
1819 return port->xmit_cnt;
1820}
1821
1822
1823static void sx_flush_buffer(struct tty_struct *tty)
1824{
1825 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1826 unsigned long flags;
1827 struct specialix_board * bp;
1828
1829 func_enter();
1830
1831 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1832 func_exit();
1833 return;
1834 }
1835
1836 bp = port_Board(port);
1837 spin_lock_irqsave(&port->lock, flags);
1838 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1839 spin_unlock_irqrestore(&port->lock, flags);
1840 tty_wakeup(tty);
1841
1842 func_exit();
1843}
1844
1845
1846static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1847{
1848 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1849 struct specialix_board * bp;
1850 unsigned char status;
1851 unsigned int result;
1852 unsigned long flags;
1853
1854 func_enter();
1855
1856 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1857 func_exit();
1858 return -ENODEV;
1859 }
1860
1861 bp = port_Board(port);
1862 spin_lock_irqsave (&bp->lock, flags);
1863 sx_out(bp, CD186x_CAR, port_No(port));
1864 status = sx_in(bp, CD186x_MSVR);
1865 spin_unlock_irqrestore(&bp->lock, flags);
1866 dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1867 port_No(port), status, sx_in (bp, CD186x_CAR));
1868 dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
1869 if (SX_CRTSCTS(port->tty)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001870 result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1872 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1873 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1874 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1875 } else {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001876 result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1878 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1879 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1880 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1881 }
1882
1883 func_exit();
1884
1885 return result;
1886}
1887
1888
1889static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1890 unsigned int set, unsigned int clear)
1891{
1892 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1893 unsigned long flags;
1894 struct specialix_board *bp;
1895
1896 func_enter();
1897
1898 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1899 func_exit();
1900 return -ENODEV;
1901 }
1902
1903 bp = port_Board(port);
1904
1905 spin_lock_irqsave(&port->lock, flags);
1906 /* if (set & TIOCM_RTS)
1907 port->MSVR |= MSVR_RTS; */
1908 /* if (set & TIOCM_DTR)
1909 port->MSVR |= MSVR_DTR; */
1910
1911 if (SX_CRTSCTS(port->tty)) {
1912 if (set & TIOCM_RTS)
1913 port->MSVR |= MSVR_DTR;
1914 } else {
1915 if (set & TIOCM_DTR)
1916 port->MSVR |= MSVR_DTR;
1917 }
1918
1919 /* if (clear & TIOCM_RTS)
1920 port->MSVR &= ~MSVR_RTS; */
1921 /* if (clear & TIOCM_DTR)
1922 port->MSVR &= ~MSVR_DTR; */
1923 if (SX_CRTSCTS(port->tty)) {
1924 if (clear & TIOCM_RTS)
1925 port->MSVR &= ~MSVR_DTR;
1926 } else {
1927 if (clear & TIOCM_DTR)
1928 port->MSVR &= ~MSVR_DTR;
1929 }
1930 spin_lock_irqsave(&bp->lock, flags);
1931 sx_out(bp, CD186x_CAR, port_No(port));
1932 sx_out(bp, CD186x_MSVR, port->MSVR);
1933 spin_unlock_irqrestore(&bp->lock, flags);
1934 spin_unlock_irqrestore(&port->lock, flags);
1935 func_exit();
1936 return 0;
1937}
1938
1939
1940static inline void sx_send_break(struct specialix_port * port, unsigned long length)
1941{
1942 struct specialix_board *bp = port_Board(port);
1943 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001944
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945 func_enter();
1946
1947 spin_lock_irqsave (&port->lock, flags);
1948 port->break_length = SPECIALIX_TPS / HZ * length;
1949 port->COR2 |= COR2_ETC;
1950 port->IER |= IER_TXRDY;
1951 spin_lock_irqsave(&bp->lock, flags);
1952 sx_out(bp, CD186x_CAR, port_No(port));
1953 sx_out(bp, CD186x_COR2, port->COR2);
1954 sx_out(bp, CD186x_IER, port->IER);
1955 spin_unlock_irqrestore(&bp->lock, flags);
1956 spin_unlock_irqrestore (&port->lock, flags);
1957 sx_wait_CCR(bp);
1958 spin_lock_irqsave(&bp->lock, flags);
1959 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1960 spin_unlock_irqrestore(&bp->lock, flags);
1961 sx_wait_CCR(bp);
1962
1963 func_exit();
1964}
1965
1966
1967static inline int sx_set_serial_info(struct specialix_port * port,
1968 struct serial_struct __user * newinfo)
1969{
1970 struct serial_struct tmp;
1971 struct specialix_board *bp = port_Board(port);
1972 int change_speed;
1973
1974 func_enter();
1975 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001976 if (!access_ok(VERIFY_READ, (void *) newinfo, sizeof(tmp))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 func_exit();
Jesper Juhle49332b2005-05-01 08:59:08 -07001978 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979 }
1980 */
1981 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1982 func_enter();
1983 return -EFAULT;
1984 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001985
1986#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 if ((tmp.irq != bp->irq) ||
1988 (tmp.port != bp->base) ||
1989 (tmp.type != PORT_CIRRUS) ||
1990 (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
1991 (tmp.custom_divisor != 0) ||
1992 (tmp.xmit_fifo_size != CD186x_NFIFO) ||
1993 (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) {
1994 func_exit();
1995 return -EINVAL;
1996 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001997#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998
1999 change_speed = ((port->flags & ASYNC_SPD_MASK) !=
2000 (tmp.flags & ASYNC_SPD_MASK));
2001 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002002
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 if (!capable(CAP_SYS_ADMIN)) {
2004 if ((tmp.close_delay != port->close_delay) ||
2005 (tmp.closing_wait != port->closing_wait) ||
2006 ((tmp.flags & ~ASYNC_USR_MASK) !=
2007 (port->flags & ~ASYNC_USR_MASK))) {
2008 func_exit();
2009 return -EPERM;
2010 }
2011 port->flags = ((port->flags & ~ASYNC_USR_MASK) |
2012 (tmp.flags & ASYNC_USR_MASK));
2013 port->custom_divisor = tmp.custom_divisor;
2014 } else {
2015 port->flags = ((port->flags & ~ASYNC_FLAGS) |
2016 (tmp.flags & ASYNC_FLAGS));
2017 port->close_delay = tmp.close_delay;
2018 port->closing_wait = tmp.closing_wait;
2019 port->custom_divisor = tmp.custom_divisor;
2020 }
2021 if (change_speed) {
2022 sx_change_speed(bp, port);
2023 }
2024 func_exit();
2025 return 0;
2026}
2027
2028
2029static inline int sx_get_serial_info(struct specialix_port * port,
2030 struct serial_struct __user *retinfo)
2031{
2032 struct serial_struct tmp;
2033 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002034
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 func_enter();
2036
2037 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07002038 if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)))
2039 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040 */
2041
2042 memset(&tmp, 0, sizeof(tmp));
2043 tmp.type = PORT_CIRRUS;
2044 tmp.line = port - sx_port;
2045 tmp.port = bp->base;
2046 tmp.irq = bp->irq;
2047 tmp.flags = port->flags;
2048 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
2049 tmp.close_delay = port->close_delay * HZ/100;
2050 tmp.closing_wait = port->closing_wait * HZ/100;
2051 tmp.custom_divisor = port->custom_divisor;
2052 tmp.xmit_fifo_size = CD186x_NFIFO;
2053 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
2054 func_exit();
2055 return -EFAULT;
2056 }
2057
2058 func_exit();
2059 return 0;
2060}
2061
2062
Jeff Garzikd61780c2005-10-30 15:01:51 -08002063static int sx_ioctl(struct tty_struct * tty, struct file * filp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 unsigned int cmd, unsigned long arg)
2065{
2066 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2067 int retval;
2068 void __user *argp = (void __user *)arg;
2069
2070 func_enter();
2071
2072 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
2073 func_exit();
2074 return -ENODEV;
2075 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002076
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 switch (cmd) {
2078 case TCSBRK: /* SVID version: non-zero arg --> no break */
2079 retval = tty_check_change(tty);
2080 if (retval) {
2081 func_exit();
2082 return retval;
2083 }
2084 tty_wait_until_sent(tty, 0);
2085 if (!arg)
2086 sx_send_break(port, HZ/4); /* 1/4 second */
2087 return 0;
2088 case TCSBRKP: /* support for POSIX tcsendbreak() */
2089 retval = tty_check_change(tty);
2090 if (retval) {
2091 func_exit();
2092 return retval;
2093 }
2094 tty_wait_until_sent(tty, 0);
2095 sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
2096 func_exit();
2097 return 0;
2098 case TIOCGSOFTCAR:
2099 if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) {
2100 func_exit();
2101 return -EFAULT;
2102 }
2103 func_exit();
2104 return 0;
2105 case TIOCSSOFTCAR:
2106 if (get_user(arg, (unsigned long __user *) argp)) {
2107 func_exit();
2108 return -EFAULT;
2109 }
2110 tty->termios->c_cflag =
2111 ((tty->termios->c_cflag & ~CLOCAL) |
2112 (arg ? CLOCAL : 0));
2113 func_exit();
2114 return 0;
2115 case TIOCGSERIAL:
2116 func_exit();
2117 return sx_get_serial_info(port, argp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002118 case TIOCSSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 func_exit();
2120 return sx_set_serial_info(port, argp);
2121 default:
2122 func_exit();
2123 return -ENOIOCTLCMD;
2124 }
2125 func_exit();
2126 return 0;
2127}
2128
2129
2130static void sx_throttle(struct tty_struct * tty)
2131{
2132 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2133 struct specialix_board *bp;
2134 unsigned long flags;
2135
2136 func_enter();
2137
2138 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
2139 func_exit();
2140 return;
2141 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002142
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002144
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 /* Use DTR instead of RTS ! */
Jeff Garzikd61780c2005-10-30 15:01:51 -08002146 if (SX_CRTSCTS (tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147 port->MSVR &= ~MSVR_DTR;
2148 else {
2149 /* Auch!!! I think the system shouldn't call this then. */
2150 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08002151 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152 When you see this in your logs, please report.... */
2153 printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
2154 port_No (port));
2155 }
2156 spin_lock_irqsave(&bp->lock, flags);
2157 sx_out(bp, CD186x_CAR, port_No(port));
2158 spin_unlock_irqrestore(&bp->lock, flags);
2159 if (I_IXOFF(tty)) {
2160 spin_unlock_irqrestore(&bp->lock, flags);
2161 sx_wait_CCR(bp);
2162 spin_lock_irqsave(&bp->lock, flags);
2163 sx_out(bp, CD186x_CCR, CCR_SSCH2);
2164 spin_unlock_irqrestore(&bp->lock, flags);
2165 sx_wait_CCR(bp);
2166 }
2167 spin_lock_irqsave(&bp->lock, flags);
2168 sx_out(bp, CD186x_MSVR, port->MSVR);
2169 spin_unlock_irqrestore(&bp->lock, flags);
2170
2171 func_exit();
2172}
2173
2174
2175static void sx_unthrottle(struct tty_struct * tty)
2176{
2177 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2178 struct specialix_board *bp;
2179 unsigned long flags;
2180
2181 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002182
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2184 func_exit();
2185 return;
2186 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002187
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002189
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190 spin_lock_irqsave(&port->lock, flags);
2191 /* XXXX Use DTR INSTEAD???? */
2192 if (SX_CRTSCTS(tty)) {
2193 port->MSVR |= MSVR_DTR;
2194 } /* Else clause: see remark in "sx_throttle"... */
2195 spin_lock_irqsave(&bp->lock, flags);
2196 sx_out(bp, CD186x_CAR, port_No(port));
2197 spin_unlock_irqrestore(&bp->lock, flags);
2198 if (I_IXOFF(tty)) {
2199 spin_unlock_irqrestore(&port->lock, flags);
2200 sx_wait_CCR(bp);
2201 spin_lock_irqsave(&bp->lock, flags);
2202 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2203 spin_unlock_irqrestore(&bp->lock, flags);
2204 sx_wait_CCR(bp);
2205 spin_lock_irqsave(&port->lock, flags);
2206 }
2207 spin_lock_irqsave(&bp->lock, flags);
2208 sx_out(bp, CD186x_MSVR, port->MSVR);
2209 spin_unlock_irqrestore(&bp->lock, flags);
2210 spin_unlock_irqrestore(&port->lock, flags);
2211
2212 func_exit();
2213}
2214
2215
2216static void sx_stop(struct tty_struct * tty)
2217{
2218 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2219 struct specialix_board *bp;
2220 unsigned long flags;
2221
2222 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002223
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2225 func_exit();
2226 return;
2227 }
2228
2229 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002230
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231 spin_lock_irqsave(&port->lock, flags);
2232 port->IER &= ~IER_TXRDY;
2233 spin_lock_irqsave(&bp->lock, flags);
2234 sx_out(bp, CD186x_CAR, port_No(port));
2235 sx_out(bp, CD186x_IER, port->IER);
2236 spin_unlock_irqrestore(&bp->lock, flags);
2237 spin_unlock_irqrestore(&port->lock, flags);
2238
2239 func_exit();
2240}
2241
2242
2243static void sx_start(struct tty_struct * tty)
2244{
2245 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2246 struct specialix_board *bp;
2247 unsigned long flags;
2248
2249 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002250
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2252 func_exit();
2253 return;
2254 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002255
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002257
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258 spin_lock_irqsave(&port->lock, flags);
2259 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2260 port->IER |= IER_TXRDY;
2261 spin_lock_irqsave(&bp->lock, flags);
2262 sx_out(bp, CD186x_CAR, port_No(port));
2263 sx_out(bp, CD186x_IER, port->IER);
2264 spin_unlock_irqrestore(&bp->lock, flags);
2265 }
2266 spin_unlock_irqrestore(&port->lock, flags);
2267
2268 func_exit();
2269}
2270
2271
2272/*
2273 * This routine is called from the work-queue when the interrupt
2274 * routine has signalled that a hangup has occurred. The path of
2275 * hangup processing is:
2276 *
2277 * serial interrupt routine -> (workqueue) ->
2278 * do_sx_hangup() -> tty->hangup() -> sx_hangup()
Jeff Garzikd61780c2005-10-30 15:01:51 -08002279 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 */
2281static void do_sx_hangup(void *private_)
2282{
2283 struct specialix_port *port = (struct specialix_port *) private_;
2284 struct tty_struct *tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002285
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 func_enter();
2287
2288 tty = port->tty;
2289 if (tty)
2290 tty_hangup(tty); /* FIXME: module removal race here */
2291
2292 func_exit();
2293}
2294
2295
2296static void sx_hangup(struct tty_struct * tty)
2297{
2298 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2299 struct specialix_board *bp;
2300 unsigned long flags;
2301
2302 func_enter();
2303
2304 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2305 func_exit();
2306 return;
2307 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002308
Linus Torvalds1da177e2005-04-16 15:20:36 -07002309 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002310
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311 sx_shutdown_port(bp, port);
2312 spin_lock_irqsave(&port->lock, flags);
2313 port->event = 0;
2314 bp->count -= port->count;
2315 if (bp->count < 0) {
2316 printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n",
2317 board_No(bp), bp->count, tty->index);
2318 bp->count = 0;
2319 }
2320 port->count = 0;
2321 port->flags &= ~ASYNC_NORMAL_ACTIVE;
2322 port->tty = NULL;
2323 spin_unlock_irqrestore(&port->lock, flags);
2324 wake_up_interruptible(&port->open_wait);
2325
2326 func_exit();
2327}
2328
2329
2330static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios)
2331{
2332 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2333 unsigned long flags;
2334 struct specialix_board * bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002335
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2337 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002338
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339 if (tty->termios->c_cflag == old_termios->c_cflag &&
2340 tty->termios->c_iflag == old_termios->c_iflag)
2341 return;
2342
2343 bp = port_Board(port);
2344 spin_lock_irqsave(&port->lock, flags);
2345 sx_change_speed(port_Board(port), port);
2346 spin_unlock_irqrestore(&port->lock, flags);
2347
2348 if ((old_termios->c_cflag & CRTSCTS) &&
2349 !(tty->termios->c_cflag & CRTSCTS)) {
2350 tty->hw_stopped = 0;
2351 sx_start(tty);
2352 }
2353}
2354
2355
2356static void do_softint(void *private_)
2357{
2358 struct specialix_port *port = (struct specialix_port *) private_;
2359 struct tty_struct *tty;
2360
2361 func_enter();
2362
2363 if(!(tty = port->tty)) {
2364 func_exit();
2365 return;
2366 }
2367
2368 if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
2369 tty_wakeup(tty);
2370 //wake_up_interruptible(&tty->write_wait);
2371 }
2372
2373 func_exit();
2374}
2375
2376static struct tty_operations sx_ops = {
2377 .open = sx_open,
2378 .close = sx_close,
2379 .write = sx_write,
2380 .put_char = sx_put_char,
2381 .flush_chars = sx_flush_chars,
2382 .write_room = sx_write_room,
2383 .chars_in_buffer = sx_chars_in_buffer,
2384 .flush_buffer = sx_flush_buffer,
2385 .ioctl = sx_ioctl,
2386 .throttle = sx_throttle,
2387 .unthrottle = sx_unthrottle,
2388 .set_termios = sx_set_termios,
2389 .stop = sx_stop,
2390 .start = sx_start,
2391 .hangup = sx_hangup,
2392 .tiocmget = sx_tiocmget,
2393 .tiocmset = sx_tiocmset,
2394};
2395
2396static int sx_init_drivers(void)
2397{
2398 int error;
2399 int i;
2400
2401 func_enter();
2402
2403 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2404 if (!specialix_driver) {
2405 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2406 func_exit();
2407 return 1;
2408 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002409
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410 if (!(tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL))) {
2411 printk(KERN_ERR "sx: Couldn't get free page.\n");
2412 put_tty_driver(specialix_driver);
2413 func_exit();
2414 return 1;
2415 }
2416 specialix_driver->owner = THIS_MODULE;
2417 specialix_driver->name = "ttyW";
2418 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2419 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2420 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2421 specialix_driver->init_termios = tty_std_termios;
2422 specialix_driver->init_termios.c_cflag =
2423 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
2424 specialix_driver->flags = TTY_DRIVER_REAL_RAW;
2425 tty_set_operations(specialix_driver, &sx_ops);
2426
2427 if ((error = tty_register_driver(specialix_driver))) {
2428 put_tty_driver(specialix_driver);
2429 free_page((unsigned long)tmp_buf);
2430 printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2431 error);
2432 func_exit();
2433 return 1;
2434 }
2435 memset(sx_port, 0, sizeof(sx_port));
2436 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2437 sx_port[i].magic = SPECIALIX_MAGIC;
2438 INIT_WORK(&sx_port[i].tqueue, do_softint, &sx_port[i]);
2439 INIT_WORK(&sx_port[i].tqueue_hangup, do_sx_hangup, &sx_port[i]);
2440 sx_port[i].close_delay = 50 * HZ/100;
2441 sx_port[i].closing_wait = 3000 * HZ/100;
2442 init_waitqueue_head(&sx_port[i].open_wait);
2443 init_waitqueue_head(&sx_port[i].close_wait);
2444 spin_lock_init(&sx_port[i].lock);
2445 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002446
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 func_exit();
2448 return 0;
2449}
2450
2451static void sx_release_drivers(void)
2452{
2453 func_enter();
2454
2455 free_page((unsigned long)tmp_buf);
2456 tty_unregister_driver(specialix_driver);
2457 put_tty_driver(specialix_driver);
2458 func_exit();
2459}
2460
Jeff Garzikd61780c2005-10-30 15:01:51 -08002461/*
2462 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463 */
2464static int __init specialix_init(void)
2465{
2466 int i;
2467 int found = 0;
2468
2469 func_enter();
2470
2471 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2472 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
2473#ifdef CONFIG_SPECIALIX_RTSCTS
2474 printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
2475#else
2476 printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2477#endif
Jeff Garzikd61780c2005-10-30 15:01:51 -08002478
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002480 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481
2482 if (sx_init_drivers()) {
2483 func_exit();
2484 return -EIO;
2485 }
2486
Jeff Garzikd61780c2005-10-30 15:01:51 -08002487 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2489 found++;
2490
2491#ifdef CONFIG_PCI
2492 {
2493 struct pci_dev *pdev = NULL;
2494
2495 i=0;
2496 while (i < SX_NBOARD) {
2497 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2498 i++;
2499 continue;
2500 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002501 pdev = pci_find_device (PCI_VENDOR_ID_SPECIALIX,
2502 PCI_DEVICE_ID_SPECIALIX_IO8,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503 pdev);
2504 if (!pdev) break;
2505
2506 if (pci_enable_device(pdev))
2507 continue;
2508
2509 sx_board[i].irq = pdev->irq;
2510
2511 sx_board[i].base = pci_resource_start (pdev, 2);
2512
2513 sx_board[i].flags |= SX_BOARD_IS_PCI;
2514 if (!sx_probe(&sx_board[i]))
2515 found ++;
2516 }
2517 }
2518#endif
2519
2520 if (!found) {
2521 sx_release_drivers();
2522 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2523 func_exit();
2524 return -EIO;
2525 }
2526
2527 func_exit();
2528 return 0;
2529}
2530
2531static int iobase[SX_NBOARD] = {0,};
2532
2533static int irq [SX_NBOARD] = {0,};
2534
2535module_param_array(iobase, int, NULL, 0);
2536module_param_array(irq, int, NULL, 0);
2537module_param(sx_debug, int, 0);
2538module_param(sx_rxfifo, int, 0);
2539#ifdef SPECIALIX_TIMER
2540module_param(sx_poll, int, 0);
2541#endif
2542
2543/*
2544 * You can setup up to 4 boards.
2545 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002546 * You should specify the IRQs too in that case "irq=....,...".
2547 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002549 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550 *
2551 */
2552static int __init specialix_init_module(void)
2553{
2554 int i;
2555
2556 func_enter();
2557
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
2559 for(i = 0; i < SX_NBOARD; i++) {
2560 sx_board[i].base = iobase[i];
2561 sx_board[i].irq = irq[i];
2562 sx_board[i].count= 0;
2563 }
2564 }
2565
2566 func_exit();
2567
2568 return specialix_init();
2569}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002570
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571static void __exit specialix_exit_module(void)
2572{
2573 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002574
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575 func_enter();
2576
2577 sx_release_drivers();
2578 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002579 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580 sx_release_io_range(&sx_board[i]);
2581#ifdef SPECIALIX_TIMER
2582 del_timer (&missed_irq_timer);
2583#endif
2584
2585 func_exit();
2586}
2587
2588module_init(specialix_init_module);
2589module_exit(specialix_exit_module);
2590
2591MODULE_LICENSE("GPL");