blob: 455855631aeff70f441e26be40f3a745a4cb5609 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070078#include <linux/module.h>
79
80#include <asm/io.h>
81#include <linux/kernel.h>
82#include <linux/sched.h>
83#include <linux/ioport.h>
84#include <linux/interrupt.h>
85#include <linux/errno.h>
86#include <linux/tty.h>
Alan Cox33f0f882006-01-09 20:54:13 -080087#include <linux/tty_flip.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070088#include <linux/mm.h>
89#include <linux/serial.h>
90#include <linux/fcntl.h>
91#include <linux/major.h>
92#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070093#include <linux/pci.h>
94#include <linux/init.h>
95#include <asm/uaccess.h>
96
97#include "specialix_io8.h"
98#include "cd1865.h"
99
100
101/*
102 This driver can spew a whole lot of debugging output at you. If you
103 need maximum performance, you should disable the DEBUG define. To
104 aid in debugging in the field, I'm leaving the compile-time debug
105 features enabled, and disable them "runtime". That allows me to
106 instruct people with problems to enable debugging without requiring
107 them to recompile...
108*/
109#define DEBUG
110
111static int sx_debug;
112static int sx_rxfifo = SPECIALIX_RXFIFO;
113
114#ifdef DEBUG
115#define dprintk(f, str...) if (sx_debug & f) printk (str)
116#else
117#define dprintk(f, str...) /* nothing */
118#endif
119
120#define SX_DEBUG_FLOW 0x0001
121#define SX_DEBUG_DATA 0x0002
122#define SX_DEBUG_PROBE 0x0004
123#define SX_DEBUG_CHAN 0x0008
124#define SX_DEBUG_INIT 0x0010
125#define SX_DEBUG_RX 0x0020
126#define SX_DEBUG_TX 0x0040
127#define SX_DEBUG_IRQ 0x0080
128#define SX_DEBUG_OPEN 0x0100
129#define SX_DEBUG_TERMIOS 0x0200
130#define SX_DEBUG_SIGNALS 0x0400
131#define SX_DEBUG_FIFO 0x0800
132
133
134#define func_enter() dprintk (SX_DEBUG_FLOW, "io8: enter %s\n",__FUNCTION__)
135#define func_exit() dprintk (SX_DEBUG_FLOW, "io8: exit %s\n", __FUNCTION__)
136
137#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1)
138
139
140/* Configurable options: */
141
142/* Am I paranoid or not ? ;-) */
143#define SPECIALIX_PARANOIA_CHECK
144
145/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help)
146 When the IRQ routine leaves the chip in a state that is keeps on
147 requiring attention, the timer doesn't help either. */
148#undef SPECIALIX_TIMER
149
150#ifdef SPECIALIX_TIMER
151static int sx_poll = HZ;
152#endif
153
154
155
Jeff Garzikd61780c2005-10-30 15:01:51 -0800156/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 * The following defines are mostly for testing purposes. But if you need
158 * some nice reporting in your syslog, you can define them also.
159 */
160#undef SX_REPORT_FIFO
161#undef SX_REPORT_OVERRUN
162
163
164
165#ifdef CONFIG_SPECIALIX_RTSCTS
166#define SX_CRTSCTS(bla) 1
167#else
168#define SX_CRTSCTS(tty) C_CRTSCTS(tty)
169#endif
170
171
172/* Used to be outb (0xff, 0x80); */
173#define short_pause() udelay (1)
174
175
176#define SPECIALIX_LEGAL_FLAGS \
177 (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
178 ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
179 ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
180
181#undef RS_EVENT_WRITE_WAKEUP
182#define RS_EVENT_WRITE_WAKEUP 0
183
184static struct tty_driver *specialix_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186static struct specialix_board sx_board[SX_NBOARD] = {
187 { 0, SX_IOBASE1, 9, },
188 { 0, SX_IOBASE2, 11, },
189 { 0, SX_IOBASE3, 12, },
190 { 0, SX_IOBASE4, 15, },
191};
192
193static struct specialix_port sx_port[SX_NBOARD * SX_NPORT];
194
195
196#ifdef SPECIALIX_TIMER
197static struct timer_list missed_irq_timer;
David Howells7d12e782006-10-05 14:55:46 +0100198static irqreturn_t sx_interrupt(int irq, void * dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199#endif
200
201
202
203static inline int sx_paranoia_check(struct specialix_port const * port,
204 char *name, const char *routine)
205{
206#ifdef SPECIALIX_PARANOIA_CHECK
207 static const char *badmagic =
208 KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n";
209 static const char *badinfo =
210 KERN_ERR "sx: Warning: null specialix port for device %s in %s\n";
Jeff Garzikd61780c2005-10-30 15:01:51 -0800211
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 if (!port) {
213 printk(badinfo, name, routine);
214 return 1;
215 }
216 if (port->magic != SPECIALIX_MAGIC) {
217 printk(badmagic, name, routine);
218 return 1;
219 }
220#endif
221 return 0;
222}
223
224
225/*
Jeff Garzikd61780c2005-10-30 15:01:51 -0800226 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 * Service functions for specialix IO8+ driver.
Jeff Garzikd61780c2005-10-30 15:01:51 -0800228 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 */
230
231/* Get board number from pointer */
232static inline int board_No (struct specialix_board * bp)
233{
234 return bp - sx_board;
235}
236
237
238/* Get port number from pointer */
239static inline int port_No (struct specialix_port const * port)
240{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800241 return SX_PORT(port - sx_port);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242}
243
244
245/* Get pointer to board from pointer to port */
246static inline struct specialix_board * port_Board(struct specialix_port const * port)
247{
248 return &sx_board[SX_BOARD(port - sx_port)];
249}
250
251
252/* Input Byte from CL CD186x register */
253static inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg)
254{
255 bp->reg = reg | 0x80;
256 outb (reg | 0x80, bp->base + SX_ADDR_REG);
257 return inb (bp->base + SX_DATA_REG);
258}
259
260
261/* Output Byte to CL CD186x register */
262static inline void sx_out(struct specialix_board * bp, unsigned short reg,
263 unsigned char val)
264{
265 bp->reg = reg | 0x80;
266 outb (reg | 0x80, bp->base + SX_ADDR_REG);
267 outb (val, bp->base + SX_DATA_REG);
268}
269
270
271/* Input Byte from CL CD186x register */
272static inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg)
273{
274 bp->reg = reg;
275 outb (reg, bp->base + SX_ADDR_REG);
276 return inb (bp->base + SX_DATA_REG);
277}
278
279
280/* Output Byte to CL CD186x register */
281static inline void sx_out_off(struct specialix_board * bp, unsigned short reg,
282 unsigned char val)
283{
284 bp->reg = reg;
285 outb (reg, bp->base + SX_ADDR_REG);
286 outb (val, bp->base + SX_DATA_REG);
287}
288
289
290/* Wait for Channel Command Register ready */
291static inline void sx_wait_CCR(struct specialix_board * bp)
292{
293 unsigned long delay, flags;
294 unsigned char ccr;
295
296 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
297 spin_lock_irqsave(&bp->lock, flags);
298 ccr = sx_in(bp, CD186x_CCR);
299 spin_unlock_irqrestore(&bp->lock, flags);
300 if (!ccr)
301 return;
302 udelay (1);
303 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800304
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
306}
307
308
309/* Wait for Channel Command Register ready */
310static inline void sx_wait_CCR_off(struct specialix_board * bp)
311{
312 unsigned long delay;
313 unsigned char crr;
314 unsigned long flags;
315
316 for (delay = SX_CCR_TIMEOUT; delay; delay--) {
317 spin_lock_irqsave(&bp->lock, flags);
318 crr = sx_in_off(bp, CD186x_CCR);
319 spin_unlock_irqrestore(&bp->lock, flags);
320 if (!crr)
321 return;
322 udelay (1);
323 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800324
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
326}
327
328
329/*
330 * specialix IO8+ IO range functions.
331 */
332
Jeff Garzikd61780c2005-10-30 15:01:51 -0800333static inline int sx_request_io_range(struct specialix_board * bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800335 return request_region(bp->base,
336 bp->flags & SX_BOARD_IS_PCI ? SX_PCI_IO_SPACE : SX_IO_SPACE,
337 "specialix IO8+") == NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338}
339
340
341static inline void sx_release_io_range(struct specialix_board * bp)
342{
Jeff Garzikd61780c2005-10-30 15:01:51 -0800343 release_region(bp->base,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 bp->flags&SX_BOARD_IS_PCI?SX_PCI_IO_SPACE:SX_IO_SPACE);
345}
346
Jeff Garzikd61780c2005-10-30 15:01:51 -0800347
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
349static int sx_set_irq ( struct specialix_board *bp)
350{
351 int virq;
352 int i;
353 unsigned long flags;
354
Jeff Garzikd61780c2005-10-30 15:01:51 -0800355 if (bp->flags & SX_BOARD_IS_PCI)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 return 1;
357 switch (bp->irq) {
358 /* In the same order as in the docs... */
359 case 15: virq = 0;break;
360 case 12: virq = 1;break;
361 case 11: virq = 2;break;
362 case 9: virq = 3;break;
363 default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq);
364 return 0;
365 }
366 spin_lock_irqsave(&bp->lock, flags);
367 for (i=0;i<2;i++) {
368 sx_out(bp, CD186x_CAR, i);
369 sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
370 }
371 spin_unlock_irqrestore(&bp->lock, flags);
372 return 1;
373}
374
375
376/* Reset and setup CD186x chip */
377static int sx_init_CD186x(struct specialix_board * bp)
378{
379 unsigned long flags;
380 int scaler;
381 int rv = 1;
382
383 func_enter();
384 sx_wait_CCR_off(bp); /* Wait for CCR ready */
385 spin_lock_irqsave(&bp->lock, flags);
386 sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
387 spin_unlock_irqrestore(&bp->lock, flags);
Jiri Slaby3e98cee2007-07-17 04:05:19 -0700388 msleep(50); /* Delay 0.05 sec */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 spin_lock_irqsave(&bp->lock, flags);
390 sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
391 sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
392 sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
393 sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
394 sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
395 /* Set RegAckEn */
396 sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800397
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 /* Setting up prescaler. We need 4 ticks per 1 ms */
399 scaler = SX_OSCFREQ/SPECIALIX_TPS;
400
401 sx_out_off(bp, CD186x_PPRH, scaler >> 8);
402 sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
403 spin_unlock_irqrestore(&bp->lock, flags);
404
405 if (!sx_set_irq (bp)) {
406 /* Figure out how to pass this along... */
407 printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq);
408 rv = 0;
409 }
410
411 func_exit();
412 return rv;
413}
414
415
416static int read_cross_byte (struct specialix_board *bp, int reg, int bit)
417{
418 int i;
419 int t;
420 unsigned long flags;
421
422 spin_lock_irqsave(&bp->lock, flags);
423 for (i=0, t=0;i<8;i++) {
424 sx_out_off (bp, CD186x_CAR, i);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800425 if (sx_in_off (bp, reg) & bit)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 t |= 1 << i;
427 }
428 spin_unlock_irqrestore(&bp->lock, flags);
429
430 return t;
431}
432
433
434#ifdef SPECIALIX_TIMER
435void missed_irq (unsigned long data)
436{
437 unsigned char irq;
438 unsigned long flags;
439 struct specialix_board *bp = (struct specialix_board *)data;
440
441 spin_lock_irqsave(&bp->lock, flags);
442 irq = sx_in ((struct specialix_board *)data, CD186x_SRSR) &
443 (SRSR_RREQint |
444 SRSR_TREQint |
445 SRSR_MREQint);
446 spin_unlock_irqrestore(&bp->lock, flags);
447 if (irq) {
448 printk (KERN_INFO "Missed interrupt... Calling int from timer. \n");
Jeff Garzikd61780c2005-10-30 15:01:51 -0800449 sx_interrupt (((struct specialix_board *)data)->irq,
Jiri Slabyd096f3e2007-02-12 00:52:30 -0800450 (void*)data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 }
Jiri Slaby40565f12007-02-12 00:52:31 -0800452 mod_timer(&missed_irq_timer, jiffies + sx_poll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453}
454#endif
455
456
457
458/* Main probing routine, also sets irq. */
459static int sx_probe(struct specialix_board *bp)
460{
461 unsigned char val1, val2;
462#if 0
463 int irqs = 0;
464 int retries;
465#endif
466 int rev;
467 int chip;
468
469 func_enter();
470
Jeff Garzikd61780c2005-10-30 15:01:51 -0800471 if (sx_request_io_range(bp)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 func_exit();
473 return 1;
474 }
475
476 /* Are the I/O ports here ? */
477 sx_out_off(bp, CD186x_PPRL, 0x5a);
478 short_pause ();
479 val1 = sx_in_off(bp, CD186x_PPRL);
480
481 sx_out_off(bp, CD186x_PPRL, 0xa5);
482 short_pause ();
483 val2 = sx_in_off(bp, CD186x_PPRL);
484
Jeff Garzikd61780c2005-10-30 15:01:51 -0800485
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 if ((val1 != 0x5a) || (val2 != 0xa5)) {
487 printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
488 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800489 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 func_exit();
491 return 1;
492 }
493
Jeff Garzikd61780c2005-10-30 15:01:51 -0800494 /* Check the DSR lines that Specialix uses as board
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 identification */
496 val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
497 val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
498 dprintk (SX_DEBUG_INIT, "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
499 board_No(bp), val1, val2);
500
501 /* They managed to switch the bit order between the docs and
502 the IO8+ card. The new PCI card now conforms to old docs.
503 They changed the PCI docs to reflect the situation on the
504 old card. */
505 val2 = (bp->flags & SX_BOARD_IS_PCI)?0x4d : 0xb2;
506 if (val1 != val2) {
507 printk(KERN_INFO "sx%d: specialix IO8+ ID %02x at 0x%03x not found (%02x).\n",
508 board_No(bp), val2, bp->base, val1);
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
514
515#if 0
516 /* It's time to find IRQ for this board */
517 for (retries = 0; retries < 5 && irqs <= 0; retries++) {
518 irqs = probe_irq_on();
519 sx_init_CD186x(bp); /* Reset CD186x chip */
520 sx_out(bp, CD186x_CAR, 2); /* Select port 2 */
521 sx_wait_CCR(bp);
522 sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */
523 sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */
Jiri Slaby3e98cee2007-07-17 04:05:19 -0700524 msleep(50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 irqs = probe_irq_off(irqs);
526
527 dprintk (SX_DEBUG_INIT, "SRSR = %02x, ", sx_in(bp, CD186x_SRSR));
528 dprintk (SX_DEBUG_INIT, "TRAR = %02x, ", sx_in(bp, CD186x_TRAR));
529 dprintk (SX_DEBUG_INIT, "GIVR = %02x, ", sx_in(bp, CD186x_GIVR));
530 dprintk (SX_DEBUG_INIT, "GICR = %02x, ", sx_in(bp, CD186x_GICR));
531 dprintk (SX_DEBUG_INIT, "\n");
532
533 /* Reset CD186x again */
534 if (!sx_init_CD186x(bp)) {
535 /* Hmmm. This is dead code anyway. */
536 }
537
538 dprintk (SX_DEBUG_INIT "val1 = %02x, val2 = %02x, val3 = %02x.\n",
Jeff Garzikd61780c2005-10-30 15:01:51 -0800539 val1, val2, val3);
540
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800542
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543#if 0
544 if (irqs <= 0) {
545 printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
546 board_No(bp), bp->base);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800547 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 func_exit();
549 return 1;
550 }
551#endif
552 printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
553 if (irqs > 0)
554 bp->irq = irqs;
555#endif
556 /* Reset CD186x again */
557 if (!sx_init_CD186x(bp)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800558 sx_release_io_range(bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 func_exit();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800560 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 }
562
563 sx_request_io_range(bp);
564 bp->flags |= SX_BOARD_PRESENT;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800565
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 /* Chip revcode pkgtype
567 GFRCR SRCR bit 7
568 CD180 rev B 0x81 0
569 CD180 rev C 0x82 0
570 CD1864 rev A 0x82 1
Jeff Garzikd61780c2005-10-30 15:01:51 -0800571 CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 CD1865 rev B 0x84 1
573 -- Thanks to Gwen Wang, Cirrus Logic.
574 */
575
576 switch (sx_in_off(bp, CD186x_GFRCR)) {
577 case 0x82:chip = 1864;rev='A';break;
578 case 0x83:chip = 1865;rev='A';break;
579 case 0x84:chip = 1865;rev='B';break;
580 case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
581 default:chip=-1;rev='x';
582 }
583
584 dprintk (SX_DEBUG_INIT, " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
585
586#ifdef SPECIALIX_TIMER
Jiri Slaby40565f12007-02-12 00:52:31 -0800587 setup_timer(&missed_irq_timer, missed_irq, (unsigned long)bp);
588 mod_timer(&missed_irq_timer, jiffies + sx_poll);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589#endif
590
591 printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
592 board_No(bp),
593 bp->base, bp->irq,
594 chip, rev);
595
596 func_exit();
597 return 0;
598}
599
Jeff Garzikd61780c2005-10-30 15:01:51 -0800600/*
601 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 * Interrupt processing routines.
603 * */
604
605static inline void sx_mark_event(struct specialix_port * port, int event)
606{
607 func_enter();
608
609 set_bit(event, &port->event);
610 schedule_work(&port->tqueue);
611
612 func_exit();
613}
614
615
616static inline struct specialix_port * sx_get_port(struct specialix_board * bp,
617 unsigned char const * what)
618{
619 unsigned char channel;
620 struct specialix_port * port = NULL;
621
622 channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
623 dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel);
624 if (channel < CD186x_NCH) {
625 port = &sx_port[board_No(bp) * SX_NPORT + channel];
626 dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%x\n",board_No(bp) * SX_NPORT + channel, port, port->flags & ASYNC_INITIALIZED);
627
628 if (port->flags & ASYNC_INITIALIZED) {
629 dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port);
630 func_exit();
631 return port;
632 }
633 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800634 printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 board_No(bp), what, channel);
636 return NULL;
637}
638
639
640static inline void sx_receive_exc(struct specialix_board * bp)
641{
642 struct specialix_port *port;
643 struct tty_struct *tty;
644 unsigned char status;
Alan Cox33f0f882006-01-09 20:54:13 -0800645 unsigned char ch, flag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646
647 func_enter();
648
649 port = sx_get_port(bp, "Receive");
650 if (!port) {
651 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
652 func_exit();
653 return;
654 }
655 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800656
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 status = sx_in(bp, CD186x_RCSR);
658
659 dprintk (SX_DEBUG_RX, "status: 0x%x\n", status);
660 if (status & RCSR_OE) {
661 port->overrun++;
662 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Overrun. Total %ld overruns.\n",
663 board_No(bp), port_No(port), port->overrun);
664 }
665 status &= port->mark_mask;
666
667 /* This flip buffer check needs to be below the reading of the
668 status register to reset the chip's IRQ.... */
Alan Cox33f0f882006-01-09 20:54:13 -0800669 if (tty_buffer_request_room(tty, 1) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: Working around flip buffer overflow.\n",
671 board_No(bp), port_No(port));
672 func_exit();
673 return;
674 }
675
676 ch = sx_in(bp, CD186x_RDR);
677 if (!status) {
678 func_exit();
679 return;
680 }
681 if (status & RCSR_TOUT) {
Jeff Garzikd61780c2005-10-30 15:01:51 -0800682 printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 board_No(bp), port_No(port));
684 func_exit();
685 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800686
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 } else if (status & RCSR_BREAK) {
688 dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n",
689 board_No(bp), port_No(port));
Alan Cox33f0f882006-01-09 20:54:13 -0800690 flag = TTY_BREAK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 if (port->flags & ASYNC_SAK)
692 do_SAK(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800693
694 } else if (status & RCSR_PE)
Alan Cox33f0f882006-01-09 20:54:13 -0800695 flag = TTY_PARITY;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800696
697 else if (status & RCSR_FE)
Alan Cox33f0f882006-01-09 20:54:13 -0800698 flag = TTY_FRAME;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800699
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 else if (status & RCSR_OE)
Alan Cox33f0f882006-01-09 20:54:13 -0800701 flag = TTY_OVERRUN;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800702
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 else
Alan Cox33f0f882006-01-09 20:54:13 -0800704 flag = TTY_NORMAL;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800705
Alan Cox33f0f882006-01-09 20:54:13 -0800706 if(tty_insert_flip_char(tty, ch, flag))
707 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 func_exit();
709}
710
711
712static inline void sx_receive(struct specialix_board * bp)
713{
714 struct specialix_port *port;
715 struct tty_struct *tty;
716 unsigned char count;
717
718 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -0800719
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 if (!(port = sx_get_port(bp, "Receive"))) {
721 dprintk (SX_DEBUG_RX, "Hmm, couldn't find port.\n");
722 func_exit();
723 return;
724 }
725 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800726
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 count = sx_in(bp, CD186x_RDCR);
728 dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count);
729 port->hits[count > 8 ? 9 : count]++;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800730
Alan Cox33f0f882006-01-09 20:54:13 -0800731 tty_buffer_request_room(tty, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732
Alan Cox33f0f882006-01-09 20:54:13 -0800733 while (count--)
734 tty_insert_flip_char(tty, sx_in(bp, CD186x_RDR), TTY_NORMAL);
735 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 func_exit();
737}
738
739
740static inline void sx_transmit(struct specialix_board * bp)
741{
742 struct specialix_port *port;
743 struct tty_struct *tty;
744 unsigned char count;
745
746 func_enter();
747 if (!(port = sx_get_port(bp, "Transmit"))) {
748 func_exit();
749 return;
750 }
751 dprintk (SX_DEBUG_TX, "port: %p\n", port);
752 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800753
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 if (port->IER & IER_TXEMPTY) {
755 /* FIFO drained */
756 sx_out(bp, CD186x_CAR, port_No(port));
757 port->IER &= ~IER_TXEMPTY;
758 sx_out(bp, CD186x_IER, port->IER);
759 func_exit();
760 return;
761 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800762
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 if ((port->xmit_cnt <= 0 && !port->break_length)
764 || tty->stopped || tty->hw_stopped) {
765 sx_out(bp, CD186x_CAR, port_No(port));
766 port->IER &= ~IER_TXRDY;
767 sx_out(bp, CD186x_IER, port->IER);
768 func_exit();
769 return;
770 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800771
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 if (port->break_length) {
773 if (port->break_length > 0) {
774 if (port->COR2 & COR2_ETC) {
775 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
776 sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
777 port->COR2 &= ~COR2_ETC;
778 }
779 count = min_t(int, port->break_length, 0xff);
780 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
781 sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
782 sx_out(bp, CD186x_TDR, count);
783 if (!(port->break_length -= count))
784 port->break_length--;
785 } else {
786 sx_out(bp, CD186x_TDR, CD186x_C_ESC);
787 sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
788 sx_out(bp, CD186x_COR2, port->COR2);
789 sx_wait_CCR(bp);
790 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
791 port->break_length = 0;
792 }
793
794 func_exit();
795 return;
796 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800797
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 count = CD186x_NFIFO;
799 do {
800 sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
801 port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
802 if (--port->xmit_cnt <= 0)
803 break;
804 } while (--count > 0);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800805
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 if (port->xmit_cnt <= 0) {
807 sx_out(bp, CD186x_CAR, port_No(port));
808 port->IER &= ~IER_TXRDY;
809 sx_out(bp, CD186x_IER, port->IER);
810 }
811 if (port->xmit_cnt <= port->wakeup_chars)
812 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
813
814 func_exit();
815}
816
817
818static inline void sx_check_modem(struct specialix_board * bp)
819{
820 struct specialix_port *port;
821 struct tty_struct *tty;
822 unsigned char mcr;
823 int msvr_cd;
824
825 dprintk (SX_DEBUG_SIGNALS, "Modem intr. ");
826 if (!(port = sx_get_port(bp, "Modem")))
827 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800828
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 tty = port->tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -0800830
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 mcr = sx_in(bp, CD186x_MCR);
832 printk ("mcr = %02x.\n", mcr);
833
834 if ((mcr & MCR_CDCHG)) {
835 dprintk (SX_DEBUG_SIGNALS, "CD just changed... ");
836 msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD;
837 if (msvr_cd) {
838 dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n");
839 wake_up_interruptible(&port->open_wait);
840 } else {
841 dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n");
842 schedule_work(&port->tqueue_hangup);
843 }
844 }
Jeff Garzikd61780c2005-10-30 15:01:51 -0800845
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
847 if (mcr & MCR_CTSCHG) {
848 if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
849 tty->hw_stopped = 0;
850 port->IER |= IER_TXRDY;
851 if (port->xmit_cnt <= port->wakeup_chars)
852 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
853 } else {
854 tty->hw_stopped = 1;
855 port->IER &= ~IER_TXRDY;
856 }
857 sx_out(bp, CD186x_IER, port->IER);
858 }
859 if (mcr & MCR_DSSXHG) {
860 if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
861 tty->hw_stopped = 0;
862 port->IER |= IER_TXRDY;
863 if (port->xmit_cnt <= port->wakeup_chars)
864 sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
865 } else {
866 tty->hw_stopped = 1;
867 port->IER &= ~IER_TXRDY;
868 }
869 sx_out(bp, CD186x_IER, port->IER);
870 }
871#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
Jeff Garzikd61780c2005-10-30 15:01:51 -0800872
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 /* Clear change bits */
874 sx_out(bp, CD186x_MCR, 0);
875}
876
877
878/* The main interrupt processing routine */
David Howells7d12e782006-10-05 14:55:46 +0100879static irqreturn_t sx_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880{
881 unsigned char status;
882 unsigned char ack;
883 struct specialix_board *bp;
884 unsigned long loop = 0;
885 int saved_reg;
886 unsigned long flags;
887
888 func_enter();
889
890 bp = dev_id;
891 spin_lock_irqsave(&bp->lock, flags);
892
893 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);
Jeff Garzikc7bec5a2006-10-06 15:00:58 -0400894 if (!(bp->flags & SX_BOARD_ACTIVE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 dprintk (SX_DEBUG_IRQ, "sx: False interrupt. irq %d.\n", irq);
896 spin_unlock_irqrestore(&bp->lock, flags);
897 func_exit();
898 return IRQ_NONE;
899 }
900
901 saved_reg = bp->reg;
902
903 while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
904 (SRSR_RREQint |
905 SRSR_TREQint |
Jeff Garzikd61780c2005-10-30 15:01:51 -0800906 SRSR_MREQint)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 if (status & SRSR_RREQint) {
908 ack = sx_in(bp, CD186x_RRAR);
909
910 if (ack == (SX_ID | GIVR_IT_RCV))
911 sx_receive(bp);
912 else if (ack == (SX_ID | GIVR_IT_REXC))
913 sx_receive_exc(bp);
914 else
915 printk(KERN_ERR "sx%d: status: 0x%x Bad receive ack 0x%02x.\n",
916 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800917
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 } else if (status & SRSR_TREQint) {
919 ack = sx_in(bp, CD186x_TRAR);
920
921 if (ack == (SX_ID | GIVR_IT_TX))
922 sx_transmit(bp);
923 else
924 printk(KERN_ERR "sx%d: status: 0x%x Bad transmit ack 0x%02x. port: %d\n",
925 board_No(bp), status, ack, port_No (sx_get_port (bp, "Int")));
926 } else if (status & SRSR_MREQint) {
927 ack = sx_in(bp, CD186x_MRAR);
928
Jeff Garzikd61780c2005-10-30 15:01:51 -0800929 if (ack == (SX_ID | GIVR_IT_MODEM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 sx_check_modem(bp);
931 else
932 printk(KERN_ERR "sx%d: status: 0x%x Bad modem ack 0x%02x.\n",
933 board_No(bp), status, ack);
Jeff Garzikd61780c2005-10-30 15:01:51 -0800934
935 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
937 sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
938 }
939 bp->reg = saved_reg;
940 outb (bp->reg, bp->base + SX_ADDR_REG);
941 spin_unlock_irqrestore(&bp->lock, flags);
942 func_exit();
943 return IRQ_HANDLED;
944}
945
946
947/*
948 * Routines for open & close processing.
949 */
950
951static void turn_ints_off (struct specialix_board *bp)
952{
953 unsigned long flags;
954
955 func_enter();
956 if (bp->flags & SX_BOARD_IS_PCI) {
957 /* This was intended for enabeling the interrupt on the
958 * PCI card. However it seems that it's already enabled
959 * and as PCI interrupts can be shared, there is no real
960 * reason to have to turn it off. */
961 }
962
963 spin_lock_irqsave(&bp->lock, flags);
964 (void) sx_in_off (bp, 0); /* Turn off interrupts. */
965 spin_unlock_irqrestore(&bp->lock, flags);
966
967 func_exit();
968}
969
970static void turn_ints_on (struct specialix_board *bp)
971{
972 unsigned long flags;
973
974 func_enter();
975
976 if (bp->flags & SX_BOARD_IS_PCI) {
977 /* play with the PCI chip. See comment above. */
978 }
979 spin_lock_irqsave(&bp->lock, flags);
980 (void) sx_in (bp, 0); /* Turn ON interrupts. */
981 spin_unlock_irqrestore(&bp->lock, flags);
982
983 func_exit();
984}
985
986
987/* Called with disabled interrupts */
988static inline int sx_setup_board(struct specialix_board * bp)
989{
990 int error;
991
Jeff Garzikd61780c2005-10-30 15:01:51 -0800992 if (bp->flags & SX_BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 return 0;
994
995 if (bp->flags & SX_BOARD_IS_PCI)
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -0700996 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED | IRQF_SHARED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 else
Thomas Gleixner0f2ed4c2006-07-01 19:29:33 -0700998 error = request_irq(bp->irq, sx_interrupt, IRQF_DISABLED, "specialix IO8+", bp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999
Jeff Garzikd61780c2005-10-30 15:01:51 -08001000 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 return error;
1002
1003 turn_ints_on (bp);
1004 bp->flags |= SX_BOARD_ACTIVE;
1005
1006 return 0;
1007}
1008
1009
1010/* Called with disabled interrupts */
1011static inline void sx_shutdown_board(struct specialix_board *bp)
1012{
1013 func_enter();
1014
1015 if (!(bp->flags & SX_BOARD_ACTIVE)) {
1016 func_exit();
1017 return;
1018 }
1019
1020 bp->flags &= ~SX_BOARD_ACTIVE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001021
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 dprintk (SX_DEBUG_IRQ, "Freeing IRQ%d for board %d.\n",
1023 bp->irq, board_No (bp));
1024 free_irq(bp->irq, bp);
1025
1026 turn_ints_off (bp);
1027
1028
1029 func_exit();
1030}
1031
1032
1033/*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001034 * Setting up port characteristics.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 * Must be called with disabled interrupts
1036 */
1037static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
1038{
1039 struct tty_struct *tty;
1040 unsigned long baud;
1041 long tmp;
1042 unsigned char cor1 = 0, cor3 = 0;
1043 unsigned char mcor1 = 0, mcor2 = 0;
1044 static unsigned long again;
1045 unsigned long flags;
1046
1047 func_enter();
1048
1049 if (!(tty = port->tty) || !tty->termios) {
1050 func_exit();
1051 return;
1052 }
1053
1054 port->IER = 0;
1055 port->COR2 = 0;
1056 /* Select port on the board */
1057 spin_lock_irqsave(&bp->lock, flags);
1058 sx_out(bp, CD186x_CAR, port_No(port));
1059
1060 /* The Specialix board doens't implement the RTS lines.
1061 They are used to set the IRQ level. Don't touch them. */
1062 if (SX_CRTSCTS(tty))
1063 port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1064 else
1065 port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
1066 spin_unlock_irqrestore(&bp->lock, flags);
1067 dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
Alan Cox67cc0162006-09-29 02:01:39 -07001068 baud = tty_get_baud_rate(tty);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001069
Alan Cox67cc0162006-09-29 02:01:39 -07001070 if (baud == 38400) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001072 baud = 57600;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001074 baud = 115200;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001076
Alan Cox67cc0162006-09-29 02:01:39 -07001077 if (!baud) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 /* Drop DTR & exit */
1079 dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
1080 if (!SX_CRTSCTS (tty)) {
1081 port -> MSVR &= ~ MSVR_DTR;
1082 spin_lock_irqsave(&bp->lock, flags);
1083 sx_out(bp, CD186x_MSVR, port->MSVR );
1084 spin_unlock_irqrestore(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001085 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 else
1087 dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n");
1088 return;
1089 } else {
1090 /* Set DTR on */
1091 if (!SX_CRTSCTS (tty)) {
1092 port ->MSVR |= MSVR_DTR;
1093 }
1094 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001095
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001097 * Now we must calculate some speed depended things
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 */
1099
1100 /* Set baud rate for port */
1101 tmp = port->custom_divisor ;
1102 if ( tmp )
1103 printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n"
1104 "This is an untested option, please be carefull.\n",
1105 port_No (port), tmp);
1106 else
Alan Cox67cc0162006-09-29 02:01:39 -07001107 tmp = (((SX_OSCFREQ + baud/2) / baud +
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 CD186x_TPC/2) / CD186x_TPC);
1109
Jeff Garzikd61780c2005-10-30 15:01:51 -08001110 if ((tmp < 0x10) && time_before(again, jiffies)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 again = jiffies + HZ * 60;
1112 /* Page 48 of version 2.0 of the CL-CD1865 databook */
1113 if (tmp >= 12) {
1114 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1115 "Performance degradation is possible.\n"
1116 "Read specialix.txt for more info.\n",
1117 port_No (port), tmp);
1118 } else {
1119 printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
1120 "Warning: overstressing Cirrus chip. "
1121 "This might not work.\n"
Jeff Garzikd61780c2005-10-30 15:01:51 -08001122 "Read specialix.txt for more info.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 port_No (port), tmp);
1124 }
1125 }
1126 spin_lock_irqsave(&bp->lock, flags);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001127 sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
1128 sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
1129 sx_out(bp, CD186x_RBPRL, tmp & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 sx_out(bp, CD186x_TBPRL, tmp & 0xff);
1131 spin_unlock_irqrestore(&bp->lock, flags);
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001132 if (port->custom_divisor)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor;
Adrian Bunka4bb2cf2006-10-17 00:10:00 -07001134 baud = (baud + 5) / 10; /* Estimated CPS */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135
1136 /* Two timer ticks seems enough to wakeup something like SLIP driver */
Jeff Garzikd61780c2005-10-30 15:01:51 -08001137 tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
1139 SERIAL_XMIT_SIZE - 1 : tmp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001140
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 /* Receiver timeout will be transmission time for 1.5 chars */
1142 tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
1143 tmp = (tmp > 0xff) ? 0xff : tmp;
1144 spin_lock_irqsave(&bp->lock, flags);
1145 sx_out(bp, CD186x_RTPR, tmp);
1146 spin_unlock_irqrestore(&bp->lock, flags);
1147 switch (C_CSIZE(tty)) {
1148 case CS5:
1149 cor1 |= COR1_5BITS;
1150 break;
1151 case CS6:
1152 cor1 |= COR1_6BITS;
1153 break;
1154 case CS7:
1155 cor1 |= COR1_7BITS;
1156 break;
1157 case CS8:
1158 cor1 |= COR1_8BITS;
1159 break;
1160 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001161
1162 if (C_CSTOPB(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 cor1 |= COR1_2SB;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001164
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 cor1 |= COR1_IGNORE;
1166 if (C_PARENB(tty)) {
1167 cor1 |= COR1_NORMPAR;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001168 if (C_PARODD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 cor1 |= COR1_ODDP;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001170 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 cor1 &= ~COR1_IGNORE;
1172 }
1173 /* Set marking of some errors */
1174 port->mark_mask = RCSR_OE | RCSR_TOUT;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001175 if (I_INPCK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 port->mark_mask |= RCSR_FE | RCSR_PE;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001177 if (I_BRKINT(tty) || I_PARMRK(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 port->mark_mask |= RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001179 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 port->mark_mask &= ~(RCSR_FE | RCSR_PE);
1181 if (I_IGNBRK(tty)) {
1182 port->mark_mask &= ~RCSR_BREAK;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001183 if (I_IGNPAR(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 /* Real raw mode. Ignore all */
1185 port->mark_mask &= ~RCSR_OE;
1186 }
1187 /* Enable Hardware Flow Control */
1188 if (C_CRTSCTS(tty)) {
1189#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
1190 port->IER |= IER_DSR | IER_CTS;
1191 mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
1192 mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
1193 spin_lock_irqsave(&bp->lock, flags);
1194 tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
1195 spin_unlock_irqrestore(&bp->lock, flags);
1196#else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001197 port->COR2 |= COR2_CTSAE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198#endif
1199 }
1200 /* Enable Software Flow Control. FIXME: I'm not sure about this */
1201 /* Some people reported that it works, but I still doubt it */
1202 if (I_IXON(tty)) {
1203 port->COR2 |= COR2_TXIBE;
1204 cor3 |= (COR3_FCT | COR3_SCDE);
1205 if (I_IXANY(tty))
1206 port->COR2 |= COR2_IXM;
1207 spin_lock_irqsave(&bp->lock, flags);
1208 sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
1209 sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
1210 sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
1211 sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
1212 spin_unlock_irqrestore(&bp->lock, flags);
1213 }
1214 if (!C_CLOCAL(tty)) {
1215 /* Enable CD check */
1216 port->IER |= IER_CD;
1217 mcor1 |= MCOR1_CDZD;
1218 mcor2 |= MCOR2_CDOD;
1219 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001220
1221 if (C_CREAD(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 /* Enable receiver */
1223 port->IER |= IER_RXD;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001224
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 /* Set input FIFO size (1-8 bytes) */
1226 cor3 |= sx_rxfifo;
1227 /* Setting up CD186x channel registers */
1228 spin_lock_irqsave(&bp->lock, flags);
1229 sx_out(bp, CD186x_COR1, cor1);
1230 sx_out(bp, CD186x_COR2, port->COR2);
1231 sx_out(bp, CD186x_COR3, cor3);
1232 spin_unlock_irqrestore(&bp->lock, flags);
1233 /* Make CD186x know about registers change */
1234 sx_wait_CCR(bp);
1235 spin_lock_irqsave(&bp->lock, flags);
1236 sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
1237 /* Setting up modem option registers */
1238 dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
1239 sx_out(bp, CD186x_MCOR1, mcor1);
1240 sx_out(bp, CD186x_MCOR2, mcor2);
1241 spin_unlock_irqrestore(&bp->lock, flags);
1242 /* Enable CD186x transmitter & receiver */
1243 sx_wait_CCR(bp);
1244 spin_lock_irqsave(&bp->lock, flags);
1245 sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
1246 /* Enable interrupts */
1247 sx_out(bp, CD186x_IER, port->IER);
1248 /* And finally set the modem lines... */
1249 sx_out(bp, CD186x_MSVR, port->MSVR);
1250 spin_unlock_irqrestore(&bp->lock, flags);
1251
1252 func_exit();
1253}
1254
1255
1256/* Must be called with interrupts enabled */
1257static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
1258{
1259 unsigned long flags;
1260
1261 func_enter();
1262
1263 if (port->flags & ASYNC_INITIALIZED) {
1264 func_exit();
1265 return 0;
1266 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001267
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 if (!port->xmit_buf) {
1269 /* We may sleep in get_zeroed_page() */
1270 unsigned long tmp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001271
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 if (!(tmp = get_zeroed_page(GFP_KERNEL))) {
1273 func_exit();
1274 return -ENOMEM;
1275 }
1276
1277 if (port->xmit_buf) {
1278 free_page(tmp);
1279 func_exit();
1280 return -ERESTARTSYS;
1281 }
1282 port->xmit_buf = (unsigned char *) tmp;
1283 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001284
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 spin_lock_irqsave(&port->lock, flags);
1286
Jeff Garzikd61780c2005-10-30 15:01:51 -08001287 if (port->tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 clear_bit(TTY_IO_ERROR, &port->tty->flags);
1289
1290 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1291 sx_change_speed(bp, port);
1292 port->flags |= ASYNC_INITIALIZED;
1293
1294 spin_unlock_irqrestore(&port->lock, flags);
1295
Jeff Garzikd61780c2005-10-30 15:01:51 -08001296
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 func_exit();
1298 return 0;
1299}
1300
1301
1302/* Must be called with interrupts disabled */
1303static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
1304{
1305 struct tty_struct *tty;
1306 int i;
1307 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001308
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 func_enter();
1310
1311 if (!(port->flags & ASYNC_INITIALIZED)) {
1312 func_exit();
1313 return;
1314 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001315
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 if (sx_debug & SX_DEBUG_FIFO) {
1317 dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ",
1318 board_No(bp), port_No(port), port->overrun);
1319 for (i = 0; i < 10; i++) {
1320 dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]);
1321 }
1322 dprintk(SX_DEBUG_FIFO, "].\n");
1323 }
1324
1325 if (port->xmit_buf) {
1326 free_page((unsigned long) port->xmit_buf);
1327 port->xmit_buf = NULL;
1328 }
1329
1330 /* Select port */
1331 spin_lock_irqsave(&bp->lock, flags);
1332 sx_out(bp, CD186x_CAR, port_No(port));
1333
1334 if (!(tty = port->tty) || C_HUPCL(tty)) {
1335 /* Drop DTR */
1336 sx_out(bp, CD186x_MSVDTR, 0);
1337 }
1338 spin_unlock_irqrestore(&bp->lock, flags);
1339 /* Reset port */
1340 sx_wait_CCR(bp);
1341 spin_lock_irqsave(&bp->lock, flags);
1342 sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
1343 /* Disable all interrupts from this port */
1344 port->IER = 0;
1345 sx_out(bp, CD186x_IER, port->IER);
1346 spin_unlock_irqrestore(&bp->lock, flags);
1347 if (tty)
1348 set_bit(TTY_IO_ERROR, &tty->flags);
1349 port->flags &= ~ASYNC_INITIALIZED;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001350
1351 if (!bp->count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 sx_shutdown_board(bp);
1353 func_exit();
1354}
1355
Jeff Garzikd61780c2005-10-30 15:01:51 -08001356
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357static int block_til_ready(struct tty_struct *tty, struct file * filp,
1358 struct specialix_port *port)
1359{
1360 DECLARE_WAITQUEUE(wait, current);
1361 struct specialix_board *bp = port_Board(port);
1362 int retval;
1363 int do_clocal = 0;
1364 int CD;
1365 unsigned long flags;
1366
1367 func_enter();
1368
1369 /*
1370 * If the device is in the middle of being closed, then block
1371 * until it's done, and then try again.
1372 */
1373 if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
1374 interruptible_sleep_on(&port->close_wait);
1375 if (port->flags & ASYNC_HUP_NOTIFY) {
1376 func_exit();
1377 return -EAGAIN;
1378 } else {
1379 func_exit();
1380 return -ERESTARTSYS;
1381 }
1382 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001383
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 /*
1385 * If non-blocking mode is set, or the port is not enabled,
1386 * then make the check up front and then exit.
1387 */
1388 if ((filp->f_flags & O_NONBLOCK) ||
1389 (tty->flags & (1 << TTY_IO_ERROR))) {
1390 port->flags |= ASYNC_NORMAL_ACTIVE;
1391 func_exit();
1392 return 0;
1393 }
1394
1395 if (C_CLOCAL(tty))
1396 do_clocal = 1;
1397
1398 /*
1399 * Block waiting for the carrier detect and the line to become
1400 * free (i.e., not in use by the callout). While we are in
1401 * this loop, info->count is dropped by one, so that
1402 * rs_close() knows when to free things. We restore it upon
1403 * exit, either normal or abnormal.
1404 */
1405 retval = 0;
1406 add_wait_queue(&port->open_wait, &wait);
1407 spin_lock_irqsave(&port->lock, flags);
1408 if (!tty_hung_up_p(filp)) {
1409 port->count--;
1410 }
1411 spin_unlock_irqrestore(&port->lock, flags);
1412 port->blocked_open++;
1413 while (1) {
1414 spin_lock_irqsave(&bp->lock, flags);
1415 sx_out(bp, CD186x_CAR, port_No(port));
1416 CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
1417 if (SX_CRTSCTS (tty)) {
1418 /* Activate RTS */
1419 port->MSVR |= MSVR_DTR; /* WTF? */
1420 sx_out (bp, CD186x_MSVR, port->MSVR);
1421 } else {
1422 /* Activate DTR */
1423 port->MSVR |= MSVR_DTR;
1424 sx_out (bp, CD186x_MSVR, port->MSVR);
1425 }
1426 spin_unlock_irqrestore(&bp->lock, flags);
1427 set_current_state(TASK_INTERRUPTIBLE);
1428 if (tty_hung_up_p(filp) ||
1429 !(port->flags & ASYNC_INITIALIZED)) {
1430 if (port->flags & ASYNC_HUP_NOTIFY)
1431 retval = -EAGAIN;
1432 else
Jeff Garzikd61780c2005-10-30 15:01:51 -08001433 retval = -ERESTARTSYS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 break;
1435 }
1436 if (!(port->flags & ASYNC_CLOSING) &&
1437 (do_clocal || CD))
1438 break;
1439 if (signal_pending(current)) {
1440 retval = -ERESTARTSYS;
1441 break;
1442 }
1443 schedule();
1444 }
1445
1446 set_current_state(TASK_RUNNING);
1447 remove_wait_queue(&port->open_wait, &wait);
1448 spin_lock_irqsave(&port->lock, flags);
1449 if (!tty_hung_up_p(filp)) {
1450 port->count++;
1451 }
1452 port->blocked_open--;
1453 spin_unlock_irqrestore(&port->lock, flags);
1454 if (retval) {
1455 func_exit();
1456 return retval;
1457 }
1458
1459 port->flags |= ASYNC_NORMAL_ACTIVE;
1460 func_exit();
1461 return 0;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001462}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463
1464
1465static int sx_open(struct tty_struct * tty, struct file * filp)
1466{
1467 int board;
1468 int error;
1469 struct specialix_port * port;
1470 struct specialix_board * bp;
1471 int i;
1472 unsigned long flags;
1473
1474 func_enter();
1475
1476 board = SX_BOARD(tty->index);
1477
1478 if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) {
1479 func_exit();
1480 return -ENODEV;
1481 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001482
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 bp = &sx_board[board];
1484 port = sx_port + board * SX_NPORT + SX_PORT(tty->index);
1485 port->overrun = 0;
1486 for (i = 0; i < 10; i++)
1487 port->hits[i]=0;
1488
1489 dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n",
1490 board, bp, port, SX_PORT(tty->index));
1491
1492 if (sx_paranoia_check(port, tty->name, "sx_open")) {
1493 func_enter();
1494 return -ENODEV;
1495 }
1496
1497 if ((error = sx_setup_board(bp))) {
1498 func_exit();
1499 return error;
1500 }
1501
1502 spin_lock_irqsave(&bp->lock, flags);
1503 port->count++;
1504 bp->count++;
1505 tty->driver_data = port;
1506 port->tty = tty;
1507 spin_unlock_irqrestore(&bp->lock, flags);
1508
1509 if ((error = sx_setup_port(bp, port))) {
1510 func_enter();
1511 return error;
1512 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001513
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 if ((error = block_til_ready(tty, filp, port))) {
1515 func_enter();
1516 return error;
1517 }
1518
1519 func_exit();
1520 return 0;
1521}
1522
1523
1524static void sx_close(struct tty_struct * tty, struct file * filp)
1525{
1526 struct specialix_port *port = (struct specialix_port *) tty->driver_data;
1527 struct specialix_board *bp;
1528 unsigned long flags;
1529 unsigned long timeout;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001530
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 func_enter();
1532 if (!port || sx_paranoia_check(port, tty->name, "close")) {
1533 func_exit();
1534 return;
1535 }
1536 spin_lock_irqsave(&port->lock, flags);
1537
1538 if (tty_hung_up_p(filp)) {
1539 spin_unlock_irqrestore(&port->lock, flags);
1540 func_exit();
1541 return;
1542 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001543
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 bp = port_Board(port);
1545 if ((tty->count == 1) && (port->count != 1)) {
1546 printk(KERN_ERR "sx%d: sx_close: bad port count;"
1547 " tty->count is 1, port count is %d\n",
1548 board_No(bp), port->count);
1549 port->count = 1;
1550 }
1551
1552 if (port->count > 1) {
1553 port->count--;
1554 bp->count--;
1555
1556 spin_unlock_irqrestore(&port->lock, flags);
1557
1558 func_exit();
1559 return;
1560 }
1561 port->flags |= ASYNC_CLOSING;
1562 /*
Jeff Garzikd61780c2005-10-30 15:01:51 -08001563 * Now we wait for the transmit buffer to clear; and we notify
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 * the line discipline to only process XON/XOFF characters.
1565 */
1566 tty->closing = 1;
1567 spin_unlock_irqrestore(&port->lock, flags);
1568 dprintk (SX_DEBUG_OPEN, "Closing\n");
1569 if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) {
1570 tty_wait_until_sent(tty, port->closing_wait);
1571 }
1572 /*
1573 * At this point we stop accepting input. To do this, we
1574 * disable the receive line status interrupts, and tell the
1575 * interrupt driver to stop checking the data ready bit in the
1576 * line status register.
1577 */
1578 dprintk (SX_DEBUG_OPEN, "Closed\n");
1579 port->IER &= ~IER_RXD;
1580 if (port->flags & ASYNC_INITIALIZED) {
1581 port->IER &= ~IER_TXRDY;
1582 port->IER |= IER_TXEMPTY;
1583 spin_lock_irqsave(&bp->lock, flags);
1584 sx_out(bp, CD186x_CAR, port_No(port));
1585 sx_out(bp, CD186x_IER, port->IER);
1586 spin_unlock_irqrestore(&bp->lock, flags);
1587 /*
1588 * Before we drop DTR, make sure the UART transmitter
1589 * has completely drained; this is especially
1590 * important if there is a transmit FIFO!
1591 */
1592 timeout = jiffies+HZ;
1593 while(port->IER & IER_TXEMPTY) {
1594 set_current_state (TASK_INTERRUPTIBLE);
1595 msleep_interruptible(jiffies_to_msecs(port->timeout));
1596 if (time_after(jiffies, timeout)) {
1597 printk (KERN_INFO "Timeout waiting for close\n");
1598 break;
1599 }
1600 }
1601
1602 }
1603
1604 if (--bp->count < 0) {
1605 printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d port: %d\n",
1606 board_No(bp), bp->count, tty->index);
1607 bp->count = 0;
1608 }
1609 if (--port->count < 0) {
1610 printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
1611 board_No(bp), port_No(port), port->count);
1612 port->count = 0;
1613 }
1614
1615 sx_shutdown_port(bp, port);
1616 if (tty->driver->flush_buffer)
1617 tty->driver->flush_buffer(tty);
1618 tty_ldisc_flush(tty);
1619 spin_lock_irqsave(&port->lock, flags);
1620 tty->closing = 0;
1621 port->event = 0;
1622 port->tty = NULL;
1623 spin_unlock_irqrestore(&port->lock, flags);
1624 if (port->blocked_open) {
1625 if (port->close_delay) {
1626 msleep_interruptible(jiffies_to_msecs(port->close_delay));
1627 }
1628 wake_up_interruptible(&port->open_wait);
1629 }
1630 port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
1631 wake_up_interruptible(&port->close_wait);
1632
1633 func_exit();
1634}
1635
1636
Jeff Garzikd61780c2005-10-30 15:01:51 -08001637static int sx_write(struct tty_struct * tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638 const unsigned char *buf, int count)
1639{
1640 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1641 struct specialix_board *bp;
1642 int c, total = 0;
1643 unsigned long flags;
1644
1645 func_enter();
1646 if (sx_paranoia_check(port, tty->name, "sx_write")) {
1647 func_exit();
1648 return 0;
1649 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001650
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 bp = port_Board(port);
1652
Jiri Slaby365e0222006-09-30 23:28:11 -07001653 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 func_exit();
1655 return 0;
1656 }
1657
1658 while (1) {
1659 spin_lock_irqsave(&port->lock, flags);
1660 c = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
1661 SERIAL_XMIT_SIZE - port->xmit_head));
1662 if (c <= 0) {
1663 spin_unlock_irqrestore(&port->lock, flags);
1664 break;
1665 }
1666 memcpy(port->xmit_buf + port->xmit_head, buf, c);
1667 port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
1668 port->xmit_cnt += c;
1669 spin_unlock_irqrestore(&port->lock, flags);
1670
1671 buf += c;
1672 count -= c;
1673 total += c;
1674 }
1675
1676 spin_lock_irqsave(&bp->lock, flags);
1677 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
1678 !(port->IER & IER_TXRDY)) {
1679 port->IER |= IER_TXRDY;
1680 sx_out(bp, CD186x_CAR, port_No(port));
1681 sx_out(bp, CD186x_IER, port->IER);
1682 }
1683 spin_unlock_irqrestore(&bp->lock, flags);
1684 func_exit();
1685
1686 return total;
1687}
1688
1689
1690static void sx_put_char(struct tty_struct * tty, unsigned char ch)
1691{
1692 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1693 unsigned long flags;
1694 struct specialix_board * bp;
1695
1696 func_enter();
1697
1698 if (sx_paranoia_check(port, tty->name, "sx_put_char")) {
1699 func_exit();
1700 return;
1701 }
1702 dprintk (SX_DEBUG_TX, "check tty: %p %p\n", tty, port->xmit_buf);
Eric Sesterhenn326f28e92006-06-25 05:48:48 -07001703 if (!port->xmit_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 func_exit();
1705 return;
1706 }
1707 bp = port_Board(port);
1708 spin_lock_irqsave(&port->lock, flags);
1709
1710 dprintk (SX_DEBUG_TX, "xmit_cnt: %d xmit_buf: %p\n", port->xmit_cnt, port->xmit_buf);
1711 if ((port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) || (!port->xmit_buf)) {
1712 spin_unlock_irqrestore(&port->lock, flags);
1713 dprintk (SX_DEBUG_TX, "Exit size\n");
1714 func_exit();
1715 return;
1716 }
1717 dprintk (SX_DEBUG_TX, "Handle xmit: %p %p\n", port, port->xmit_buf);
1718 port->xmit_buf[port->xmit_head++] = ch;
1719 port->xmit_head &= SERIAL_XMIT_SIZE - 1;
1720 port->xmit_cnt++;
1721 spin_unlock_irqrestore(&port->lock, flags);
1722
1723 func_exit();
1724}
1725
1726
1727static void sx_flush_chars(struct tty_struct * tty)
1728{
1729 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1730 unsigned long flags;
1731 struct specialix_board * bp = port_Board(port);
1732
1733 func_enter();
1734
1735 if (sx_paranoia_check(port, tty->name, "sx_flush_chars")) {
1736 func_exit();
1737 return;
1738 }
1739 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
1740 !port->xmit_buf) {
1741 func_exit();
1742 return;
1743 }
1744 spin_lock_irqsave(&bp->lock, flags);
1745 port->IER |= IER_TXRDY;
1746 sx_out(port_Board(port), CD186x_CAR, port_No(port));
1747 sx_out(port_Board(port), CD186x_IER, port->IER);
1748 spin_unlock_irqrestore(&bp->lock, flags);
1749
1750 func_exit();
1751}
1752
1753
1754static int sx_write_room(struct tty_struct * tty)
1755{
1756 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1757 int ret;
1758
1759 func_enter();
1760
1761 if (sx_paranoia_check(port, tty->name, "sx_write_room")) {
1762 func_exit();
1763 return 0;
1764 }
1765
1766 ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1767 if (ret < 0)
1768 ret = 0;
1769
1770 func_exit();
1771 return ret;
1772}
1773
1774
1775static int sx_chars_in_buffer(struct tty_struct *tty)
1776{
1777 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1778
1779 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08001780
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781 if (sx_paranoia_check(port, tty->name, "sx_chars_in_buffer")) {
1782 func_exit();
1783 return 0;
1784 }
1785 func_exit();
1786 return port->xmit_cnt;
1787}
1788
1789
1790static void sx_flush_buffer(struct tty_struct *tty)
1791{
1792 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1793 unsigned long flags;
1794 struct specialix_board * bp;
1795
1796 func_enter();
1797
1798 if (sx_paranoia_check(port, tty->name, "sx_flush_buffer")) {
1799 func_exit();
1800 return;
1801 }
1802
1803 bp = port_Board(port);
1804 spin_lock_irqsave(&port->lock, flags);
1805 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1806 spin_unlock_irqrestore(&port->lock, flags);
1807 tty_wakeup(tty);
1808
1809 func_exit();
1810}
1811
1812
1813static int sx_tiocmget(struct tty_struct *tty, struct file *file)
1814{
1815 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1816 struct specialix_board * bp;
1817 unsigned char status;
1818 unsigned int result;
1819 unsigned long flags;
1820
1821 func_enter();
1822
1823 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1824 func_exit();
1825 return -ENODEV;
1826 }
1827
1828 bp = port_Board(port);
1829 spin_lock_irqsave (&bp->lock, flags);
1830 sx_out(bp, CD186x_CAR, port_No(port));
1831 status = sx_in(bp, CD186x_MSVR);
1832 spin_unlock_irqrestore(&bp->lock, flags);
1833 dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n",
1834 port_No(port), status, sx_in (bp, CD186x_CAR));
1835 dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port);
1836 if (SX_CRTSCTS(port->tty)) {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001837 result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001838 | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
1839 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1840 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1841 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1842 } else {
Jeff Garzikd61780c2005-10-30 15:01:51 -08001843 result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844 | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
1845 | ((status & MSVR_CD) ? TIOCM_CAR : 0)
1846 |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
1847 | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
1848 }
1849
1850 func_exit();
1851
1852 return result;
1853}
1854
1855
1856static int sx_tiocmset(struct tty_struct *tty, struct file *file,
1857 unsigned int set, unsigned int clear)
1858{
1859 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
1860 unsigned long flags;
1861 struct specialix_board *bp;
1862
1863 func_enter();
1864
1865 if (sx_paranoia_check(port, tty->name, __FUNCTION__)) {
1866 func_exit();
1867 return -ENODEV;
1868 }
1869
1870 bp = port_Board(port);
1871
1872 spin_lock_irqsave(&port->lock, flags);
1873 /* if (set & TIOCM_RTS)
1874 port->MSVR |= MSVR_RTS; */
1875 /* if (set & TIOCM_DTR)
1876 port->MSVR |= MSVR_DTR; */
1877
1878 if (SX_CRTSCTS(port->tty)) {
1879 if (set & TIOCM_RTS)
1880 port->MSVR |= MSVR_DTR;
1881 } else {
1882 if (set & TIOCM_DTR)
1883 port->MSVR |= MSVR_DTR;
1884 }
1885
1886 /* if (clear & TIOCM_RTS)
1887 port->MSVR &= ~MSVR_RTS; */
1888 /* if (clear & TIOCM_DTR)
1889 port->MSVR &= ~MSVR_DTR; */
1890 if (SX_CRTSCTS(port->tty)) {
1891 if (clear & TIOCM_RTS)
1892 port->MSVR &= ~MSVR_DTR;
1893 } else {
1894 if (clear & TIOCM_DTR)
1895 port->MSVR &= ~MSVR_DTR;
1896 }
1897 spin_lock_irqsave(&bp->lock, flags);
1898 sx_out(bp, CD186x_CAR, port_No(port));
1899 sx_out(bp, CD186x_MSVR, port->MSVR);
1900 spin_unlock_irqrestore(&bp->lock, flags);
1901 spin_unlock_irqrestore(&port->lock, flags);
1902 func_exit();
1903 return 0;
1904}
1905
1906
1907static inline void sx_send_break(struct specialix_port * port, unsigned long length)
1908{
1909 struct specialix_board *bp = port_Board(port);
1910 unsigned long flags;
Jeff Garzikd61780c2005-10-30 15:01:51 -08001911
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912 func_enter();
1913
1914 spin_lock_irqsave (&port->lock, flags);
1915 port->break_length = SPECIALIX_TPS / HZ * length;
1916 port->COR2 |= COR2_ETC;
1917 port->IER |= IER_TXRDY;
1918 spin_lock_irqsave(&bp->lock, flags);
1919 sx_out(bp, CD186x_CAR, port_No(port));
1920 sx_out(bp, CD186x_COR2, port->COR2);
1921 sx_out(bp, CD186x_IER, port->IER);
1922 spin_unlock_irqrestore(&bp->lock, flags);
1923 spin_unlock_irqrestore (&port->lock, flags);
1924 sx_wait_CCR(bp);
1925 spin_lock_irqsave(&bp->lock, flags);
1926 sx_out(bp, CD186x_CCR, CCR_CORCHG2);
1927 spin_unlock_irqrestore(&bp->lock, flags);
1928 sx_wait_CCR(bp);
1929
1930 func_exit();
1931}
1932
1933
1934static inline int sx_set_serial_info(struct specialix_port * port,
1935 struct serial_struct __user * newinfo)
1936{
1937 struct serial_struct tmp;
1938 struct specialix_board *bp = port_Board(port);
1939 int change_speed;
1940
1941 func_enter();
1942 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07001943 if (!access_ok(VERIFY_READ, (void *) newinfo, sizeof(tmp))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 func_exit();
Jesper Juhle49332b2005-05-01 08:59:08 -07001945 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 }
1947 */
1948 if (copy_from_user(&tmp, newinfo, sizeof(tmp))) {
1949 func_enter();
1950 return -EFAULT;
1951 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001952
1953#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 if ((tmp.irq != bp->irq) ||
1955 (tmp.port != bp->base) ||
1956 (tmp.type != PORT_CIRRUS) ||
1957 (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
1958 (tmp.custom_divisor != 0) ||
1959 (tmp.xmit_fifo_size != CD186x_NFIFO) ||
1960 (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) {
1961 func_exit();
1962 return -EINVAL;
1963 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08001964#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965
1966 change_speed = ((port->flags & ASYNC_SPD_MASK) !=
1967 (tmp.flags & ASYNC_SPD_MASK));
1968 change_speed |= (tmp.custom_divisor != port->custom_divisor);
Jeff Garzikd61780c2005-10-30 15:01:51 -08001969
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970 if (!capable(CAP_SYS_ADMIN)) {
1971 if ((tmp.close_delay != port->close_delay) ||
1972 (tmp.closing_wait != port->closing_wait) ||
1973 ((tmp.flags & ~ASYNC_USR_MASK) !=
1974 (port->flags & ~ASYNC_USR_MASK))) {
1975 func_exit();
1976 return -EPERM;
1977 }
1978 port->flags = ((port->flags & ~ASYNC_USR_MASK) |
1979 (tmp.flags & ASYNC_USR_MASK));
1980 port->custom_divisor = tmp.custom_divisor;
1981 } else {
1982 port->flags = ((port->flags & ~ASYNC_FLAGS) |
1983 (tmp.flags & ASYNC_FLAGS));
1984 port->close_delay = tmp.close_delay;
1985 port->closing_wait = tmp.closing_wait;
1986 port->custom_divisor = tmp.custom_divisor;
1987 }
1988 if (change_speed) {
1989 sx_change_speed(bp, port);
1990 }
1991 func_exit();
1992 return 0;
1993}
1994
1995
1996static inline int sx_get_serial_info(struct specialix_port * port,
1997 struct serial_struct __user *retinfo)
1998{
1999 struct serial_struct tmp;
2000 struct specialix_board *bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002001
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 func_enter();
2003
2004 /*
Jesper Juhle49332b2005-05-01 08:59:08 -07002005 if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)))
2006 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 */
2008
2009 memset(&tmp, 0, sizeof(tmp));
2010 tmp.type = PORT_CIRRUS;
2011 tmp.line = port - sx_port;
2012 tmp.port = bp->base;
2013 tmp.irq = bp->irq;
2014 tmp.flags = port->flags;
2015 tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
2016 tmp.close_delay = port->close_delay * HZ/100;
2017 tmp.closing_wait = port->closing_wait * HZ/100;
2018 tmp.custom_divisor = port->custom_divisor;
2019 tmp.xmit_fifo_size = CD186x_NFIFO;
2020 if (copy_to_user(retinfo, &tmp, sizeof(tmp))) {
2021 func_exit();
2022 return -EFAULT;
2023 }
2024
2025 func_exit();
2026 return 0;
2027}
2028
2029
Jeff Garzikd61780c2005-10-30 15:01:51 -08002030static int sx_ioctl(struct tty_struct * tty, struct file * filp,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 unsigned int cmd, unsigned long arg)
2032{
2033 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2034 int retval;
2035 void __user *argp = (void __user *)arg;
2036
2037 func_enter();
2038
2039 if (sx_paranoia_check(port, tty->name, "sx_ioctl")) {
2040 func_exit();
2041 return -ENODEV;
2042 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002043
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044 switch (cmd) {
2045 case TCSBRK: /* SVID version: non-zero arg --> no break */
2046 retval = tty_check_change(tty);
2047 if (retval) {
2048 func_exit();
2049 return retval;
2050 }
2051 tty_wait_until_sent(tty, 0);
2052 if (!arg)
2053 sx_send_break(port, HZ/4); /* 1/4 second */
2054 return 0;
2055 case TCSBRKP: /* support for POSIX tcsendbreak() */
2056 retval = tty_check_change(tty);
2057 if (retval) {
2058 func_exit();
2059 return retval;
2060 }
2061 tty_wait_until_sent(tty, 0);
2062 sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
2063 func_exit();
2064 return 0;
2065 case TIOCGSOFTCAR:
2066 if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) {
2067 func_exit();
2068 return -EFAULT;
2069 }
2070 func_exit();
2071 return 0;
2072 case TIOCSSOFTCAR:
2073 if (get_user(arg, (unsigned long __user *) argp)) {
2074 func_exit();
2075 return -EFAULT;
2076 }
2077 tty->termios->c_cflag =
2078 ((tty->termios->c_cflag & ~CLOCAL) |
2079 (arg ? CLOCAL : 0));
2080 func_exit();
2081 return 0;
2082 case TIOCGSERIAL:
2083 func_exit();
2084 return sx_get_serial_info(port, argp);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002085 case TIOCSSERIAL:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 func_exit();
2087 return sx_set_serial_info(port, argp);
2088 default:
2089 func_exit();
2090 return -ENOIOCTLCMD;
2091 }
2092 func_exit();
2093 return 0;
2094}
2095
2096
2097static void sx_throttle(struct tty_struct * tty)
2098{
2099 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2100 struct specialix_board *bp;
2101 unsigned long flags;
2102
2103 func_enter();
2104
2105 if (sx_paranoia_check(port, tty->name, "sx_throttle")) {
2106 func_exit();
2107 return;
2108 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002109
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002111
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112 /* Use DTR instead of RTS ! */
Jeff Garzikd61780c2005-10-30 15:01:51 -08002113 if (SX_CRTSCTS (tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114 port->MSVR &= ~MSVR_DTR;
2115 else {
2116 /* Auch!!! I think the system shouldn't call this then. */
2117 /* Or maybe we're supposed (allowed?) to do our side of hw
Jeff Garzikd61780c2005-10-30 15:01:51 -08002118 handshake anyway, even when hardware handshake is off.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 When you see this in your logs, please report.... */
2120 printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
2121 port_No (port));
2122 }
2123 spin_lock_irqsave(&bp->lock, flags);
2124 sx_out(bp, CD186x_CAR, port_No(port));
2125 spin_unlock_irqrestore(&bp->lock, flags);
2126 if (I_IXOFF(tty)) {
2127 spin_unlock_irqrestore(&bp->lock, flags);
2128 sx_wait_CCR(bp);
2129 spin_lock_irqsave(&bp->lock, flags);
2130 sx_out(bp, CD186x_CCR, CCR_SSCH2);
2131 spin_unlock_irqrestore(&bp->lock, flags);
2132 sx_wait_CCR(bp);
2133 }
2134 spin_lock_irqsave(&bp->lock, flags);
2135 sx_out(bp, CD186x_MSVR, port->MSVR);
2136 spin_unlock_irqrestore(&bp->lock, flags);
2137
2138 func_exit();
2139}
2140
2141
2142static void sx_unthrottle(struct tty_struct * tty)
2143{
2144 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2145 struct specialix_board *bp;
2146 unsigned long flags;
2147
2148 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002149
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150 if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) {
2151 func_exit();
2152 return;
2153 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002154
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002156
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 spin_lock_irqsave(&port->lock, flags);
2158 /* XXXX Use DTR INSTEAD???? */
2159 if (SX_CRTSCTS(tty)) {
2160 port->MSVR |= MSVR_DTR;
2161 } /* Else clause: see remark in "sx_throttle"... */
2162 spin_lock_irqsave(&bp->lock, flags);
2163 sx_out(bp, CD186x_CAR, port_No(port));
2164 spin_unlock_irqrestore(&bp->lock, flags);
2165 if (I_IXOFF(tty)) {
2166 spin_unlock_irqrestore(&port->lock, flags);
2167 sx_wait_CCR(bp);
2168 spin_lock_irqsave(&bp->lock, flags);
2169 sx_out(bp, CD186x_CCR, CCR_SSCH1);
2170 spin_unlock_irqrestore(&bp->lock, flags);
2171 sx_wait_CCR(bp);
2172 spin_lock_irqsave(&port->lock, flags);
2173 }
2174 spin_lock_irqsave(&bp->lock, flags);
2175 sx_out(bp, CD186x_MSVR, port->MSVR);
2176 spin_unlock_irqrestore(&bp->lock, flags);
2177 spin_unlock_irqrestore(&port->lock, flags);
2178
2179 func_exit();
2180}
2181
2182
2183static void sx_stop(struct tty_struct * tty)
2184{
2185 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2186 struct specialix_board *bp;
2187 unsigned long flags;
2188
2189 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002190
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191 if (sx_paranoia_check(port, tty->name, "sx_stop")) {
2192 func_exit();
2193 return;
2194 }
2195
2196 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002197
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198 spin_lock_irqsave(&port->lock, flags);
2199 port->IER &= ~IER_TXRDY;
2200 spin_lock_irqsave(&bp->lock, flags);
2201 sx_out(bp, CD186x_CAR, port_No(port));
2202 sx_out(bp, CD186x_IER, port->IER);
2203 spin_unlock_irqrestore(&bp->lock, flags);
2204 spin_unlock_irqrestore(&port->lock, flags);
2205
2206 func_exit();
2207}
2208
2209
2210static void sx_start(struct tty_struct * tty)
2211{
2212 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2213 struct specialix_board *bp;
2214 unsigned long flags;
2215
2216 func_enter();
Jeff Garzikd61780c2005-10-30 15:01:51 -08002217
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218 if (sx_paranoia_check(port, tty->name, "sx_start")) {
2219 func_exit();
2220 return;
2221 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002222
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002224
Linus Torvalds1da177e2005-04-16 15:20:36 -07002225 spin_lock_irqsave(&port->lock, flags);
2226 if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
2227 port->IER |= IER_TXRDY;
2228 spin_lock_irqsave(&bp->lock, flags);
2229 sx_out(bp, CD186x_CAR, port_No(port));
2230 sx_out(bp, CD186x_IER, port->IER);
2231 spin_unlock_irqrestore(&bp->lock, flags);
2232 }
2233 spin_unlock_irqrestore(&port->lock, flags);
2234
2235 func_exit();
2236}
2237
2238
2239/*
2240 * This routine is called from the work-queue when the interrupt
2241 * routine has signalled that a hangup has occurred. The path of
2242 * hangup processing is:
2243 *
2244 * serial interrupt routine -> (workqueue) ->
2245 * do_sx_hangup() -> tty->hangup() -> sx_hangup()
Jeff Garzikd61780c2005-10-30 15:01:51 -08002246 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 */
David Howellsc4028952006-11-22 14:57:56 +00002248static void do_sx_hangup(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249{
David Howellsc4028952006-11-22 14:57:56 +00002250 struct specialix_port *port =
2251 container_of(work, struct specialix_port, tqueue_hangup);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 struct tty_struct *tty;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002253
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254 func_enter();
2255
2256 tty = port->tty;
2257 if (tty)
2258 tty_hangup(tty); /* FIXME: module removal race here */
2259
2260 func_exit();
2261}
2262
2263
2264static void sx_hangup(struct tty_struct * tty)
2265{
2266 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2267 struct specialix_board *bp;
2268 unsigned long flags;
2269
2270 func_enter();
2271
2272 if (sx_paranoia_check(port, tty->name, "sx_hangup")) {
2273 func_exit();
2274 return;
2275 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002276
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 bp = port_Board(port);
Jeff Garzikd61780c2005-10-30 15:01:51 -08002278
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 sx_shutdown_port(bp, port);
2280 spin_lock_irqsave(&port->lock, flags);
2281 port->event = 0;
2282 bp->count -= port->count;
2283 if (bp->count < 0) {
2284 printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n",
2285 board_No(bp), bp->count, tty->index);
2286 bp->count = 0;
2287 }
2288 port->count = 0;
2289 port->flags &= ~ASYNC_NORMAL_ACTIVE;
2290 port->tty = NULL;
2291 spin_unlock_irqrestore(&port->lock, flags);
2292 wake_up_interruptible(&port->open_wait);
2293
2294 func_exit();
2295}
2296
2297
Alan Cox606d0992006-12-08 02:38:45 -08002298static void sx_set_termios(struct tty_struct * tty, struct ktermios * old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299{
2300 struct specialix_port *port = (struct specialix_port *)tty->driver_data;
2301 unsigned long flags;
2302 struct specialix_board * bp;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002303
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 if (sx_paranoia_check(port, tty->name, "sx_set_termios"))
2305 return;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002306
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 if (tty->termios->c_cflag == old_termios->c_cflag &&
2308 tty->termios->c_iflag == old_termios->c_iflag)
2309 return;
2310
2311 bp = port_Board(port);
2312 spin_lock_irqsave(&port->lock, flags);
2313 sx_change_speed(port_Board(port), port);
2314 spin_unlock_irqrestore(&port->lock, flags);
2315
2316 if ((old_termios->c_cflag & CRTSCTS) &&
2317 !(tty->termios->c_cflag & CRTSCTS)) {
2318 tty->hw_stopped = 0;
2319 sx_start(tty);
2320 }
2321}
2322
2323
David Howellsc4028952006-11-22 14:57:56 +00002324static void do_softint(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325{
David Howellsc4028952006-11-22 14:57:56 +00002326 struct specialix_port *port =
2327 container_of(work, struct specialix_port, tqueue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328 struct tty_struct *tty;
2329
2330 func_enter();
2331
2332 if(!(tty = port->tty)) {
2333 func_exit();
2334 return;
2335 }
2336
Jiri Slabyb963a842007-02-10 01:44:55 -08002337 if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339
2340 func_exit();
2341}
2342
Jeff Dikeb68e31d2006-10-02 02:17:18 -07002343static const struct tty_operations sx_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344 .open = sx_open,
2345 .close = sx_close,
2346 .write = sx_write,
2347 .put_char = sx_put_char,
2348 .flush_chars = sx_flush_chars,
2349 .write_room = sx_write_room,
2350 .chars_in_buffer = sx_chars_in_buffer,
2351 .flush_buffer = sx_flush_buffer,
2352 .ioctl = sx_ioctl,
2353 .throttle = sx_throttle,
2354 .unthrottle = sx_unthrottle,
2355 .set_termios = sx_set_termios,
2356 .stop = sx_stop,
2357 .start = sx_start,
2358 .hangup = sx_hangup,
2359 .tiocmget = sx_tiocmget,
2360 .tiocmset = sx_tiocmset,
2361};
2362
2363static int sx_init_drivers(void)
2364{
2365 int error;
2366 int i;
2367
2368 func_enter();
2369
2370 specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT);
2371 if (!specialix_driver) {
2372 printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n");
2373 func_exit();
2374 return 1;
2375 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002376
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377 specialix_driver->owner = THIS_MODULE;
2378 specialix_driver->name = "ttyW";
2379 specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
2380 specialix_driver->type = TTY_DRIVER_TYPE_SERIAL;
2381 specialix_driver->subtype = SERIAL_TYPE_NORMAL;
2382 specialix_driver->init_termios = tty_std_termios;
2383 specialix_driver->init_termios.c_cflag =
2384 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
Alan Cox606d0992006-12-08 02:38:45 -08002385 specialix_driver->init_termios.c_ispeed = 9600;
2386 specialix_driver->init_termios.c_ospeed = 9600;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387 specialix_driver->flags = TTY_DRIVER_REAL_RAW;
2388 tty_set_operations(specialix_driver, &sx_ops);
2389
2390 if ((error = tty_register_driver(specialix_driver))) {
2391 put_tty_driver(specialix_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392 printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
2393 error);
2394 func_exit();
2395 return 1;
2396 }
2397 memset(sx_port, 0, sizeof(sx_port));
2398 for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
2399 sx_port[i].magic = SPECIALIX_MAGIC;
David Howellsc4028952006-11-22 14:57:56 +00002400 INIT_WORK(&sx_port[i].tqueue, do_softint);
2401 INIT_WORK(&sx_port[i].tqueue_hangup, do_sx_hangup);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402 sx_port[i].close_delay = 50 * HZ/100;
2403 sx_port[i].closing_wait = 3000 * HZ/100;
2404 init_waitqueue_head(&sx_port[i].open_wait);
2405 init_waitqueue_head(&sx_port[i].close_wait);
2406 spin_lock_init(&sx_port[i].lock);
2407 }
Jeff Garzikd61780c2005-10-30 15:01:51 -08002408
Linus Torvalds1da177e2005-04-16 15:20:36 -07002409 func_exit();
2410 return 0;
2411}
2412
2413static void sx_release_drivers(void)
2414{
2415 func_enter();
2416
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417 tty_unregister_driver(specialix_driver);
2418 put_tty_driver(specialix_driver);
2419 func_exit();
2420}
2421
Jeff Garzikd61780c2005-10-30 15:01:51 -08002422/*
2423 * This routine must be called by kernel at boot time
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424 */
2425static int __init specialix_init(void)
2426{
2427 int i;
2428 int found = 0;
2429
2430 func_enter();
2431
2432 printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n");
2433 printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
2434#ifdef CONFIG_SPECIALIX_RTSCTS
2435 printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
2436#else
2437 printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
2438#endif
Jeff Garzikd61780c2005-10-30 15:01:51 -08002439
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440 for (i = 0; i < SX_NBOARD; i++)
Ingo Molnar34af9462006-06-27 02:53:55 -07002441 spin_lock_init(&sx_board[i].lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002442
2443 if (sx_init_drivers()) {
2444 func_exit();
2445 return -EIO;
2446 }
2447
Jeff Garzikd61780c2005-10-30 15:01:51 -08002448 for (i = 0; i < SX_NBOARD; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449 if (sx_board[i].base && !sx_probe(&sx_board[i]))
2450 found++;
2451
2452#ifdef CONFIG_PCI
2453 {
2454 struct pci_dev *pdev = NULL;
2455
2456 i=0;
2457 while (i < SX_NBOARD) {
2458 if (sx_board[i].flags & SX_BOARD_PRESENT) {
2459 i++;
2460 continue;
2461 }
Alan Cox606d0992006-12-08 02:38:45 -08002462 pdev = pci_get_device (PCI_VENDOR_ID_SPECIALIX,
Jeff Garzikd61780c2005-10-30 15:01:51 -08002463 PCI_DEVICE_ID_SPECIALIX_IO8,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464 pdev);
2465 if (!pdev) break;
2466
2467 if (pci_enable_device(pdev))
2468 continue;
2469
2470 sx_board[i].irq = pdev->irq;
2471
2472 sx_board[i].base = pci_resource_start (pdev, 2);
2473
2474 sx_board[i].flags |= SX_BOARD_IS_PCI;
2475 if (!sx_probe(&sx_board[i]))
2476 found ++;
2477 }
Alan Cox606d0992006-12-08 02:38:45 -08002478 /* May exit pci_get sequence early with lots of boards */
2479 if (pdev != NULL)
2480 pci_dev_put(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481 }
2482#endif
2483
2484 if (!found) {
2485 sx_release_drivers();
2486 printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
2487 func_exit();
2488 return -EIO;
2489 }
2490
2491 func_exit();
2492 return 0;
2493}
2494
2495static int iobase[SX_NBOARD] = {0,};
2496
2497static int irq [SX_NBOARD] = {0,};
2498
2499module_param_array(iobase, int, NULL, 0);
2500module_param_array(irq, int, NULL, 0);
2501module_param(sx_debug, int, 0);
2502module_param(sx_rxfifo, int, 0);
2503#ifdef SPECIALIX_TIMER
2504module_param(sx_poll, int, 0);
2505#endif
2506
2507/*
2508 * You can setup up to 4 boards.
2509 * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter.
Jeff Garzikd61780c2005-10-30 15:01:51 -08002510 * You should specify the IRQs too in that case "irq=....,...".
2511 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512 * More than 4 boards in one computer is not possible, as the card can
Jeff Garzikd61780c2005-10-30 15:01:51 -08002513 * only use 4 different interrupts.
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514 *
2515 */
2516static int __init specialix_init_module(void)
2517{
2518 int i;
2519
2520 func_enter();
2521
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522 if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
2523 for(i = 0; i < SX_NBOARD; i++) {
2524 sx_board[i].base = iobase[i];
2525 sx_board[i].irq = irq[i];
2526 sx_board[i].count= 0;
2527 }
2528 }
2529
2530 func_exit();
2531
2532 return specialix_init();
2533}
Jeff Garzikd61780c2005-10-30 15:01:51 -08002534
Linus Torvalds1da177e2005-04-16 15:20:36 -07002535static void __exit specialix_exit_module(void)
2536{
2537 int i;
Jeff Garzikd61780c2005-10-30 15:01:51 -08002538
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539 func_enter();
2540
2541 sx_release_drivers();
2542 for (i = 0; i < SX_NBOARD; i++)
Jeff Garzikd61780c2005-10-30 15:01:51 -08002543 if (sx_board[i].flags & SX_BOARD_PRESENT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544 sx_release_io_range(&sx_board[i]);
2545#ifdef SPECIALIX_TIMER
Jiri Slaby40565f12007-02-12 00:52:31 -08002546 del_timer_sync(&missed_irq_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547#endif
2548
2549 func_exit();
2550}
2551
Chuck Short76910302006-07-10 04:43:59 -07002552static struct pci_device_id specialx_pci_tbl[] __devinitdata = {
2553 { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) },
2554 { }
2555};
2556MODULE_DEVICE_TABLE(pci, specialx_pci_tbl);
2557
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558module_init(specialix_init_module);
2559module_exit(specialix_exit_module);
2560
2561MODULE_LICENSE("GPL");